1use crate::gl;
69use crate::context;
70use crate::context::Context;
71use crate::version::Version;
72use crate::version::Api;
73
74use crate::index::PrimitiveType;
75
76use crate::QueryExt;
77use crate::CapabilitiesSource;
78use crate::DrawError;
79use crate::Rect;
80use crate::ToGlEnum;
81use crate::vertex::TransformFeedbackSession;
82
83use std::ops::Range;
84
85pub use self::blend::{Blend, BlendingFunction, LinearBlendingFactor};
86pub use self::depth::{Depth, DepthTest, DepthClamp};
87pub use self::query::{QueryCreationError};
88pub use self::query::{SamplesPassedQuery, TimeElapsedQuery, PrimitivesGeneratedQuery};
89pub use self::query::{AnySamplesPassedQuery, TransformFeedbackPrimitivesWrittenQuery};
90pub use self::stencil::{StencilTest, StencilOperation, Stencil};
91
92mod blend;
93mod depth;
94mod query;
95mod stencil;
96
97#[derive(Clone, Copy, Debug, PartialEq, Eq)]
139pub enum BackfaceCullingMode {
140 CullingDisabled,
142
143 CullCounterClockwise,
145
146 CullClockwise
148}
149
150#[derive(Clone, Copy, Debug, PartialEq, Eq)]
170pub enum PolygonMode {
171 Point,
175
176 Line,
180
181 Fill,
183}
184
185impl ToGlEnum for PolygonMode {
186 #[inline]
187 fn to_glenum(&self) -> gl::types::GLenum {
188 match *self {
189 PolygonMode::Point => gl::POINT,
190 PolygonMode::Line => gl::LINE,
191 PolygonMode::Fill => gl::FILL,
192 }
193 }
194}
195
196#[derive(Clone, Copy, Debug, PartialEq, Eq)]
200pub enum Smooth {
201 Fastest,
203
204 Nicest,
206
207 DontCare,
209}
210
211impl ToGlEnum for Smooth {
212 #[inline]
213 fn to_glenum(&self) -> gl::types::GLenum {
214 match *self {
215 Smooth::Fastest => gl::FASTEST,
216 Smooth::Nicest => gl::NICEST,
217 Smooth::DontCare => gl::DONT_CARE,
218 }
219 }
220}
221
222#[derive(Clone, Copy, Debug, PartialEq, Eq)]
224pub enum ProvokingVertex {
225 LastVertex,
227
228 FirstVertex,
232}
233
234#[derive(Clone, Debug)]
250pub struct DrawParameters<'a> {
251 pub depth: Depth,
253
254 pub stencil: Stencil,
256
257 pub blend: Blend,
260
261 pub color_mask: (bool, bool, bool, bool),
270
271 pub line_width: Option<f32>,
275
276 pub point_size: Option<f32>,
280
281 pub clip_planes_bitmask: u32,
287
288 pub backface_culling: BackfaceCullingMode,
295
296 pub polygon_mode: PolygonMode,
300
301 pub multisampling: bool,
307
308 pub dithering: bool,
312
313 pub viewport: Option<Rect>,
323
324 pub scissor: Option<Rect>,
329
330 pub draw_primitives: bool,
341
342 pub samples_passed_query: Option<SamplesQueryParam<'a>>,
345
346 pub time_elapsed_query: Option<&'a TimeElapsedQuery>,
349
350 pub primitives_generated_query: Option<&'a PrimitivesGeneratedQuery>,
352
353 pub transform_feedback_primitives_written_query:
355 Option<&'a TransformFeedbackPrimitivesWrittenQuery>,
356
357 pub condition: Option<ConditionalRendering<'a>>,
360
361 pub transform_feedback: Option<&'a TransformFeedbackSession<'a>>,
363
364 pub smooth: Option<Smooth>,
368
369 pub provoking_vertex: ProvokingVertex,
377
378 pub primitive_bounding_box: (Range<f32>, Range<f32>, Range<f32>, Range<f32>),
392
393 pub primitive_restart_index: bool,
399
400 pub polygon_offset: PolygonOffset,
403
404 pub clip_control_origin: ClipControlOrigin,
406
407 pub clip_control_depth: ClipControlDepth,
409}
410
411#[derive(Debug, Copy, Clone)]
413pub struct ConditionalRendering<'a> {
414 pub query: SamplesQueryParam<'a>,
416
417 pub wait: bool,
420
421 pub per_region: bool,
424}
425
426#[derive(Debug, Copy, Clone)]
428pub enum SamplesQueryParam<'a> {
429 SamplesPassedQuery(&'a SamplesPassedQuery),
431 AnySamplesPassedQuery(&'a AnySamplesPassedQuery),
433}
434
435impl<'a> From<&'a SamplesPassedQuery> for SamplesQueryParam<'a> {
436 #[inline]
437 fn from(r: &'a SamplesPassedQuery) -> SamplesQueryParam<'a> {
438 SamplesQueryParam::SamplesPassedQuery(r)
439 }
440}
441
442impl<'a> From<&'a AnySamplesPassedQuery> for SamplesQueryParam<'a> {
443 #[inline]
444 fn from(r: &'a AnySamplesPassedQuery) -> SamplesQueryParam<'a> {
445 SamplesQueryParam::AnySamplesPassedQuery(r)
446 }
447}
448
449#[derive(Debug, Copy, Clone)]
451pub struct PolygonOffset {
452 pub factor: f32,
454 pub units: f32,
456 pub point: bool,
458 pub line: bool,
460 pub fill: bool,
462}
463
464impl Default for PolygonOffset {
465 fn default() -> Self {
466 PolygonOffset{
467 factor: 0.0,
468 units: 0.0,
469 point: false,
470 line: false,
471 fill: false
472 }
473 }
474}
475
476#[derive(Clone, Copy, Debug, PartialEq, Eq)]
478pub enum ClipControlOrigin {
479 LowerLeft,
481
482 UpperLeft,
484}
485
486#[derive(Clone, Copy, Debug, PartialEq, Eq)]
488pub enum ClipControlDepth {
489 NegativeOneToOne,
491
492 ZeroToOne,
494}
495
496impl<'a> Default for DrawParameters<'a> {
497 fn default() -> DrawParameters<'a> {
498 DrawParameters {
499 depth: Depth::default(),
500 stencil: Default::default(),
501 blend: Default::default(),
502 color_mask: (true, true, true, true),
503 line_width: None,
504 point_size: None,
505 backface_culling: BackfaceCullingMode::CullingDisabled,
506 polygon_mode: PolygonMode::Fill,
507 clip_planes_bitmask: 0,
508 multisampling: true,
509 dithering: true,
510 viewport: None,
511 scissor: None,
512 draw_primitives: true,
513 samples_passed_query: None,
514 time_elapsed_query: None,
515 primitives_generated_query: None,
516 transform_feedback_primitives_written_query: None,
517 condition: None,
518 transform_feedback: None,
519 smooth: None,
520 provoking_vertex: ProvokingVertex::LastVertex,
521 primitive_bounding_box: (-1.0 .. 1.0, -1.0 .. 1.0, -1.0 .. 1.0, -1.0 .. 1.0),
522 primitive_restart_index: false,
523 polygon_offset: Default::default(),
524 clip_control_origin: ClipControlOrigin::LowerLeft,
525 clip_control_depth: ClipControlDepth::NegativeOneToOne,
526 }
527 }
528}
529
530pub fn validate(context: &Context, params: &DrawParameters<'_>) -> Result<(), DrawError> {
532 if params.depth.range.0 < 0.0 || params.depth.range.0 > 1.0 ||
533 params.depth.range.1 < 0.0 || params.depth.range.1 > 1.0
534 {
535 return Err(DrawError::InvalidDepthRange);
536 }
537
538 if !params.draw_primitives && context.get_opengl_version() < &Version(Api::Gl, 3, 0) &&
539 !context.get_extensions().gl_ext_transform_feedback
540 {
541 return Err(DrawError::RasterizerDiscardNotSupported);
542 }
543
544 Ok(())
545}
546
547#[doc(hidden)]
548pub fn sync(ctxt: &mut context::CommandContext<'_>, draw_parameters: &DrawParameters<'_>,
549 dimensions: (u32, u32), primitives_types: PrimitiveType) -> Result<(), DrawError>
550{
551 depth::sync_depth(ctxt, &draw_parameters.depth)?;
552 stencil::sync_stencil(ctxt, &draw_parameters.stencil);
553 blend::sync_blending(ctxt, draw_parameters.blend)?;
554 sync_color_mask(ctxt, draw_parameters.color_mask);
555 sync_line_width(ctxt, draw_parameters.line_width);
556 sync_point_size(ctxt, draw_parameters.point_size);
557 sync_polygon_mode(ctxt, draw_parameters.backface_culling, draw_parameters.polygon_mode);
558 sync_clip_planes_bitmask(ctxt, draw_parameters.clip_planes_bitmask)?;
559 sync_multisampling(ctxt, draw_parameters.multisampling);
560 sync_dithering(ctxt, draw_parameters.dithering);
561 sync_viewport_scissor(ctxt, draw_parameters.viewport, draw_parameters.scissor,
562 dimensions);
563 sync_rasterizer_discard(ctxt, draw_parameters.draw_primitives)?;
564 sync_queries(ctxt, draw_parameters.samples_passed_query,
565 draw_parameters.time_elapsed_query,
566 draw_parameters.primitives_generated_query,
567 draw_parameters.transform_feedback_primitives_written_query)?;
568 sync_conditional_render(ctxt, draw_parameters.condition);
569 sync_smooth(ctxt, draw_parameters.smooth, primitives_types)?;
570 sync_provoking_vertex(ctxt, draw_parameters.provoking_vertex)?;
571 sync_primitive_bounding_box(ctxt, &draw_parameters.primitive_bounding_box);
572 sync_primitive_restart_index(ctxt, draw_parameters.primitive_restart_index)?;
573 sync_polygon_offset(ctxt, draw_parameters.polygon_offset);
574 sync_clip_control(ctxt, draw_parameters.clip_control_origin,
575 draw_parameters.clip_control_depth)?;
576
577 Ok(())
578}
579
580fn sync_color_mask(ctxt: &mut context::CommandContext<'_>, mask: (bool, bool, bool, bool)) {
581 let mask = (
582 if mask.0 { 1 } else { 0 },
583 if mask.1 { 1 } else { 0 },
584 if mask.2 { 1 } else { 0 },
585 if mask.3 { 1 } else { 0 },
586 );
587
588 if ctxt.state.color_mask != mask {
589 unsafe {
590 ctxt.gl.ColorMask(mask.0, mask.1, mask.2, mask.3);
591 }
592
593 ctxt.state.color_mask = mask;
594 }
595}
596
597fn sync_line_width(ctxt: &mut context::CommandContext<'_>, line_width: Option<f32>) {
598 if let Some(line_width) = line_width {
599 if ctxt.state.line_width != line_width {
600 unsafe {
601 ctxt.gl.LineWidth(line_width);
602 ctxt.state.line_width = line_width;
603 }
604 }
605 }
606}
607
608fn sync_point_size(ctxt: &mut context::CommandContext<'_>, point_size: Option<f32>) {
609 if let Some(point_size) = point_size {
610 if ctxt.state.point_size != point_size {
611 unsafe {
612 ctxt.gl.PointSize(point_size);
613 ctxt.state.point_size = point_size;
614 }
615 }
616 }
617}
618
619fn sync_polygon_mode(ctxt: &mut context::CommandContext<'_>, backface_culling: BackfaceCullingMode,
620 polygon_mode: PolygonMode)
621{
622 match backface_culling {
626 BackfaceCullingMode::CullingDisabled => unsafe {
627 if ctxt.state.enabled_cull_face {
628 ctxt.gl.Disable(gl::CULL_FACE);
629 ctxt.state.enabled_cull_face = false;
630 }
631 },
632 BackfaceCullingMode::CullCounterClockwise => unsafe {
633 if !ctxt.state.enabled_cull_face {
634 ctxt.gl.Enable(gl::CULL_FACE);
635 ctxt.state.enabled_cull_face = true;
636 }
637 if ctxt.state.cull_face != gl::FRONT {
638 ctxt.gl.CullFace(gl::FRONT);
639 ctxt.state.cull_face = gl::FRONT;
640 }
641 },
642 BackfaceCullingMode::CullClockwise => unsafe {
643 if !ctxt.state.enabled_cull_face {
644 ctxt.gl.Enable(gl::CULL_FACE);
645 ctxt.state.enabled_cull_face = true;
646 }
647 if ctxt.state.cull_face != gl::BACK {
648 ctxt.gl.CullFace(gl::BACK);
649 ctxt.state.cull_face = gl::BACK;
650 }
651 },
652 }
653
654 unsafe {
656 let polygon_mode = polygon_mode.to_glenum();
657 if ctxt.state.polygon_mode != polygon_mode {
658 ctxt.gl.PolygonMode(gl::FRONT_AND_BACK, polygon_mode);
659 ctxt.state.polygon_mode = polygon_mode;
660 }
661 }
662}
663
664fn sync_clip_planes_bitmask(ctxt: &mut context::CommandContext<'_>, clip_planes_bitmask: u32)
665 -> Result<(), DrawError> {
666 if !(ctxt.version >= &Version(Api::Gl, 1, 0)) {
668 return Ok(());
669 }
670 unsafe {
671 let mut max_clip_planes: gl::types::GLint = 0;
672 ctxt.gl.GetIntegerv(gl::MAX_CLIP_DISTANCES, &mut max_clip_planes);
673 for i in 0..32 {
674 if clip_planes_bitmask & (1 << i) != ctxt.state.enabled_clip_planes & (1 << i) {
675 if clip_planes_bitmask & (1 << i) != 0 {
676 if i < max_clip_planes {
677 ctxt.gl.Enable(gl::CLIP_DISTANCE0 + i as u32);
678 ctxt.state.enabled_clip_planes |= 1 << i;
679 } else {
680 return Err(DrawError::ClipPlaneIndexOutOfBounds);
681 }
682 } else if i < max_clip_planes {
683 ctxt.gl.Disable(gl::CLIP_DISTANCE0 + i as u32);
684 ctxt.state.enabled_clip_planes &= !(1 << i);
685 }
686 }
687 }
688 Ok(())
689 }
690}
691
692fn sync_multisampling(ctxt: &mut context::CommandContext<'_>, multisampling: bool) {
693 if ctxt.state.enabled_multisample != multisampling {
694 unsafe {
695 if multisampling {
696 ctxt.gl.Enable(gl::MULTISAMPLE);
697 ctxt.state.enabled_multisample = true;
698 } else {
699 ctxt.gl.Disable(gl::MULTISAMPLE);
700 ctxt.state.enabled_multisample = false;
701 }
702 }
703 }
704}
705
706fn sync_dithering(ctxt: &mut context::CommandContext<'_>, dithering: bool) {
707 if ctxt.state.enabled_dither != dithering {
708 unsafe {
709 if dithering {
710 ctxt.gl.Enable(gl::DITHER);
711 ctxt.state.enabled_dither = true;
712 } else {
713 ctxt.gl.Disable(gl::DITHER);
714 ctxt.state.enabled_dither = false;
715 }
716 }
717 }
718}
719
720fn sync_viewport_scissor(ctxt: &mut context::CommandContext<'_>, viewport: Option<Rect>,
721 scissor: Option<Rect>, surface_dimensions: (u32, u32))
722{
723 if let Some(viewport) = viewport {
725 assert!(viewport.width <= ctxt.capabilities.max_viewport_dims.0 as u32,
726 "Viewport dimensions are too large");
727 assert!(viewport.height <= ctxt.capabilities.max_viewport_dims.1 as u32,
728 "Viewport dimensions are too large");
729
730 let viewport = (viewport.left as gl::types::GLint, viewport.bottom as gl::types::GLint,
731 viewport.width as gl::types::GLsizei,
732 viewport.height as gl::types::GLsizei);
733
734 if ctxt.state.viewport != Some(viewport) {
735 unsafe { ctxt.gl.Viewport(viewport.0, viewport.1, viewport.2, viewport.3); }
736 ctxt.state.viewport = Some(viewport);
737 }
738
739 } else {
740 assert!(surface_dimensions.0 <= ctxt.capabilities.max_viewport_dims.0 as u32,
741 "Viewport dimensions are too large");
742 assert!(surface_dimensions.1 <= ctxt.capabilities.max_viewport_dims.1 as u32,
743 "Viewport dimensions are too large");
744
745 let viewport = (0, 0, surface_dimensions.0 as gl::types::GLsizei,
746 surface_dimensions.1 as gl::types::GLsizei);
747
748 if ctxt.state.viewport != Some(viewport) {
749 unsafe { ctxt.gl.Viewport(viewport.0, viewport.1, viewport.2, viewport.3); }
750 ctxt.state.viewport = Some(viewport);
751 }
752 }
753
754 if let Some(scissor) = scissor {
756 let scissor = (scissor.left as gl::types::GLint, scissor.bottom as gl::types::GLint,
757 scissor.width as gl::types::GLsizei,
758 scissor.height as gl::types::GLsizei);
759
760 unsafe {
761 if ctxt.state.scissor != Some(scissor) {
762 ctxt.gl.Scissor(scissor.0, scissor.1, scissor.2, scissor.3);
763 ctxt.state.scissor = Some(scissor);
764 }
765
766 if !ctxt.state.enabled_scissor_test {
767 ctxt.gl.Enable(gl::SCISSOR_TEST);
768 ctxt.state.enabled_scissor_test = true;
769 }
770 }
771 } else {
772 unsafe {
773 if ctxt.state.enabled_scissor_test {
774 ctxt.gl.Disable(gl::SCISSOR_TEST);
775 ctxt.state.enabled_scissor_test = false;
776 }
777 }
778 }
779}
780
781fn sync_rasterizer_discard(ctxt: &mut context::CommandContext<'_>, draw_primitives: bool)
782 -> Result<(), DrawError>
783{
784 if ctxt.state.enabled_rasterizer_discard == draw_primitives {
785 if ctxt.version >= &Version(Api::Gl, 3, 0) {
786 if draw_primitives {
787 unsafe { ctxt.gl.Disable(gl::RASTERIZER_DISCARD); }
788 ctxt.state.enabled_rasterizer_discard = false;
789 } else {
790 unsafe { ctxt.gl.Enable(gl::RASTERIZER_DISCARD); }
791 ctxt.state.enabled_rasterizer_discard = true;
792 }
793
794 } else if ctxt.extensions.gl_ext_transform_feedback {
795 if draw_primitives {
796 unsafe { ctxt.gl.Disable(gl::RASTERIZER_DISCARD_EXT); }
797 ctxt.state.enabled_rasterizer_discard = false;
798 } else {
799 unsafe { ctxt.gl.Enable(gl::RASTERIZER_DISCARD_EXT); }
800 ctxt.state.enabled_rasterizer_discard = true;
801 }
802
803 } else {
804 return Err(DrawError::RasterizerDiscardNotSupported);
805 }
806 }
807
808 Ok(())
809}
810
811fn sync_queries(ctxt: &mut context::CommandContext<'_>,
812 samples_passed_query: Option<SamplesQueryParam<'_>>,
813 time_elapsed_query: Option<&TimeElapsedQuery>,
814 primitives_generated_query: Option<&PrimitivesGeneratedQuery>,
815 transform_feedback_primitives_written_query:
816 Option<&TransformFeedbackPrimitivesWrittenQuery>)
817 -> Result<(), DrawError>
818{
819 if let Some(SamplesQueryParam::SamplesPassedQuery(q)) = samples_passed_query {
820 q.begin_query(ctxt)?;
821 } else if let Some(SamplesQueryParam::AnySamplesPassedQuery(q)) = samples_passed_query {
822 q.begin_query(ctxt)?;
823 } else {
824 TimeElapsedQuery::end_samples_passed_query(ctxt);
825 }
826
827 if let Some(time_elapsed_query) = time_elapsed_query {
828 time_elapsed_query.begin_query(ctxt)?;
829 } else {
830 TimeElapsedQuery::end_time_elapsed_query(ctxt);
831 }
832
833 if let Some(primitives_generated_query) = primitives_generated_query {
834 primitives_generated_query.begin_query(ctxt)?;
835 } else {
836 TimeElapsedQuery::end_primitives_generated_query(ctxt);
837 }
838
839 if let Some(tfq) = transform_feedback_primitives_written_query {
840 tfq.begin_query(ctxt)?;
841 } else {
842 TimeElapsedQuery::end_transform_feedback_primitives_written_query(ctxt);
843 }
844
845 Ok(())
846}
847
848fn sync_conditional_render(ctxt: &mut context::CommandContext<'_>,
849 condition: Option<ConditionalRendering<'_>>)
850{
851 if let Some(ConditionalRendering { query, wait, per_region }) = condition {
852 match query {
853 SamplesQueryParam::SamplesPassedQuery(ref q) => {
854 q.begin_conditional_render(ctxt, wait, per_region);
855 },
856 SamplesQueryParam::AnySamplesPassedQuery(ref q) => {
857 q.begin_conditional_render(ctxt, wait, per_region);
858 },
859 }
860
861 } else {
862 TimeElapsedQuery::end_conditional_render(ctxt);
863 }
864}
865
866fn sync_smooth(ctxt: &mut context::CommandContext<'_>,
867 smooth: Option<Smooth>,
868 primitive_type: PrimitiveType) -> Result<(), DrawError> {
869
870 if let Some(smooth) = smooth {
871 if !(ctxt.version >= &Version(Api::Gl, 1, 0)) {
873 return Err(DrawError::SmoothingNotSupported);
874 }
875
876 let hint = smooth.to_glenum();
877
878 match primitive_type {
879 PrimitiveType::Points =>
881 return Err(DrawError::SmoothingNotSupported),
882
883 PrimitiveType::LinesList | PrimitiveType::LinesListAdjacency |
885 PrimitiveType::LineStrip | PrimitiveType::LineStripAdjacency |
886 PrimitiveType::LineLoop => unsafe {
887 if !ctxt.state.enabled_line_smooth {
888 ctxt.state.enabled_line_smooth = true;
889 ctxt.gl.Enable(gl::LINE_SMOOTH);
890 }
891
892 if ctxt.state.smooth.0 != hint {
893 ctxt.state.smooth.0 = hint;
894 ctxt.gl.Hint(gl::LINE_SMOOTH_HINT, hint);
895 }
896 },
897
898 _ => unsafe {
900 if !ctxt.state.enabled_polygon_smooth {
901 ctxt.state.enabled_polygon_smooth = true;
902 ctxt.gl.Enable(gl::POLYGON_SMOOTH);
903 }
904
905 if ctxt.state.smooth.1 != hint {
906 ctxt.state.smooth.1 = hint;
907 ctxt.gl.Hint(gl::POLYGON_SMOOTH_HINT, hint);
908 }
909 }
910 }
911 }
912 else {
913 match primitive_type {
914 PrimitiveType::Points => (),
916
917 PrimitiveType::LinesList | PrimitiveType::LinesListAdjacency |
919 PrimitiveType::LineStrip | PrimitiveType::LineStripAdjacency |
920 PrimitiveType::LineLoop => unsafe {
921 if ctxt.state.enabled_line_smooth {
922 ctxt.state.enabled_line_smooth = false;
923 ctxt.gl.Disable(gl::LINE_SMOOTH);
924 }
925 },
926
927 _ => unsafe {
929 if ctxt.state.enabled_polygon_smooth {
930 ctxt.state.enabled_polygon_smooth = false;
931 ctxt.gl.Disable(gl::POLYGON_SMOOTH);
932 }
933 }
934 }
935 }
936
937 Ok(())
938}
939
940fn sync_provoking_vertex(ctxt: &mut context::CommandContext<'_>, value: ProvokingVertex)
941 -> Result<(), DrawError>
942{
943 let value = match value {
944 ProvokingVertex::LastVertex => gl::LAST_VERTEX_CONVENTION,
945 ProvokingVertex::FirstVertex => gl::FIRST_VERTEX_CONVENTION,
946 };
947
948 if ctxt.state.provoking_vertex == value {
949 return Ok(());
950 }
951
952 if ctxt.version >= &Version(Api::Gl, 3, 2) || ctxt.extensions.gl_arb_provoking_vertex {
953 unsafe { ctxt.gl.ProvokingVertex(value); }
954 ctxt.state.provoking_vertex = value;
955
956 } else if ctxt.extensions.gl_ext_provoking_vertex {
957 unsafe { ctxt.gl.ProvokingVertexEXT(value); }
958 ctxt.state.provoking_vertex = value;
959
960 } else {
961 return Err(DrawError::ProvokingVertexNotSupported);
962 }
963
964 Ok(())
965}
966
967fn sync_primitive_bounding_box(ctxt: &mut context::CommandContext<'_>,
968 bb: &(Range<f32>, Range<f32>, Range<f32>, Range<f32>))
969{
970 let value = (bb.0.start, bb.1.start, bb.2.start, bb.3.start,
971 bb.0.end, bb.1.end, bb.2.end, bb.3.end);
972
973 if ctxt.state.primitive_bounding_box == value {
974 return;
975 }
976
977 if ctxt.version >= &Version(Api::GlEs, 3, 2) {
978 unsafe { ctxt.gl.PrimitiveBoundingBox(value.0, value.1, value.2, value.3,
979 value.4, value.5, value.6, value.7); }
980 ctxt.state.primitive_bounding_box = value;
981
982 } else if ctxt.extensions.gl_arb_es3_2_compatibility {
983 unsafe { ctxt.gl.PrimitiveBoundingBoxARB(value.0, value.1, value.2, value.3,
984 value.4, value.5, value.6, value.7); }
985 ctxt.state.primitive_bounding_box = value;
986
987 } else if ctxt.extensions.gl_oes_primitive_bounding_box {
988 unsafe { ctxt.gl.PrimitiveBoundingBoxOES(value.0, value.1, value.2, value.3,
989 value.4, value.5, value.6, value.7); }
990 ctxt.state.primitive_bounding_box = value;
991
992 } else if ctxt.extensions.gl_ext_primitive_bounding_box {
993 unsafe { ctxt.gl.PrimitiveBoundingBoxEXT(value.0, value.1, value.2, value.3,
994 value.4, value.5, value.6, value.7); }
995 ctxt.state.primitive_bounding_box = value;
996 }
997}
998
999fn sync_primitive_restart_index(ctxt: &mut context::CommandContext<'_>,
1000 enabled: bool)
1001 -> Result<(), DrawError>
1002{
1003 if ctxt.version >= &Version(Api::Gl, 3, 1) || ctxt.version >= &Version(Api::GlEs, 3, 0) ||
1006 ctxt.extensions.gl_arb_es3_compatibility
1007 {
1008 if ctxt.state.enabled_primitive_fixed_restart != enabled {
1009 if enabled {
1010 unsafe { ctxt.gl.Enable(gl::PRIMITIVE_RESTART_FIXED_INDEX); }
1011 ctxt.state.enabled_primitive_fixed_restart = true;
1012 } else {
1013 unsafe { ctxt.gl.Disable(gl::PRIMITIVE_RESTART_FIXED_INDEX); }
1014 ctxt.state.enabled_primitive_fixed_restart = false;
1015 }
1016 }
1017 } else if enabled {
1018 return Err(DrawError::FixedIndexRestartingNotSupported);
1019 }
1020
1021
1022 Ok(())
1023}
1024
1025fn set_flag_enabled(ctxt: &mut context::CommandContext<'_>, cap: gl::types::GLenum, enabled: bool) {
1026 if enabled {
1027 unsafe { ctxt.gl.Enable(cap); }
1028 } else {
1029 unsafe { ctxt.gl.Disable(cap); }
1030 }
1031}
1032
1033fn sync_polygon_offset(ctxt: &mut context::CommandContext<'_>, offset: PolygonOffset) {
1034 let (factor, units) = ctxt.state.polygon_offset;
1035
1036 if ctxt.state.polygon_offset != (offset.factor, offset.units) {
1037 unsafe {
1038 ctxt.gl.PolygonOffset(offset.factor, offset.units);
1039 }
1040 ctxt.state.polygon_offset = (offset.factor, offset.units);
1041 }
1042
1043 if offset.point != ctxt.state.enabled_polygon_offset_point {
1044 ctxt.state.enabled_polygon_offset_point = offset.point;
1045 set_flag_enabled(ctxt, gl::POLYGON_OFFSET_POINT, offset.point);
1046 }
1047
1048 if offset.line != ctxt.state.enabled_polygon_offset_line {
1049 ctxt.state.enabled_polygon_offset_line = offset.line;
1050 set_flag_enabled(ctxt, gl::POLYGON_OFFSET_LINE, offset.line);
1051 }
1052
1053 if offset.fill != ctxt.state.enabled_polygon_offset_fill {
1054 ctxt.state.enabled_polygon_offset_fill = offset.fill;
1055 set_flag_enabled(ctxt, gl::POLYGON_OFFSET_FILL, offset.fill);
1056 }
1057}
1058
1059fn sync_clip_control(ctxt: &mut context::CommandContext<'_>,
1060 origin: ClipControlOrigin,
1061 depth: ClipControlDepth)
1062 -> Result<(), DrawError> {
1063 let origin = match origin {
1064 ClipControlOrigin::LowerLeft => gl::LOWER_LEFT,
1065 ClipControlOrigin::UpperLeft => gl::UPPER_LEFT,
1066 };
1067 let depth = match depth {
1068 ClipControlDepth::NegativeOneToOne => gl::NEGATIVE_ONE_TO_ONE,
1069 ClipControlDepth::ZeroToOne => gl::ZERO_TO_ONE,
1070 };
1071
1072 if ctxt.state.clip_control == (origin, depth) {
1073 return Ok(());
1074 }
1075
1076 if ctxt.version >= &Version(Api::Gl, 4, 5) || ctxt.extensions.gl_arb_clip_control {
1077 unsafe { ctxt.gl.ClipControl(origin, depth); }
1078 ctxt.state.clip_control = (origin, depth);
1079 } else {
1080 return Err(DrawError::ClipControlNotSupported);
1081 }
1082
1083 Ok(())
1084}