glium/draw_parameters/
mod.rs

1//! Describes miscellaneous parameters to be used when drawing.
2//!
3//! # Example
4//!
5//! ```rust
6//! let params = glium::DrawParameters {
7//!     depth: glium::Depth {
8//!         test: glium::draw_parameters::DepthTest::IfLess,
9//!         write: true,
10//!         .. Default::default()
11//!     },
12//!     scissor: Some(glium::Rect { bottom: 0, left: 100, width: 100, height: 200 }),
13//!     .. Default::default()
14//! };
15//! ```
16//!
17//! # Queries
18//!
19//! Query objects allow you to obtain information about the rendering process. For example, a
20//! `SamplesPassedQuery` allows you to know the number of samples that have been drawn.
21//!
22//! ```no_run
23//! # use glutin::surface::{ResizeableSurface, SurfaceTypeTrait};
24//! # fn example<T>(display: glium::Display<T>) where T: SurfaceTypeTrait + ResizeableSurface {
25//! let query = glium::draw_parameters::SamplesPassedQuery::new(&display).unwrap();
26//! let params = glium::DrawParameters {
27//!     samples_passed_query: Some((&query).into()),
28//!     .. Default::default()
29//! };
30//! # }
31//! ```
32//!
33//! After drawing with these parameters, you can retrieve the value inside the query:
34//!
35//! ```no_run
36//! # fn example(query: glium::draw_parameters::SamplesPassedQuery) {
37//! let value = query.get();
38//! # }
39//! ```
40//!
41//! This operation will consume the query and block until the GPU has finished drawing. Instead,
42//! you can also use the query as a condition for drawing:
43//!
44//! ```no_run
45//! # fn example(query: glium::draw_parameters::SamplesPassedQuery) {
46//! let params = glium::DrawParameters {
47//!     condition: Some(glium::draw_parameters::ConditionalRendering {
48//!         query: (&query).into(),
49//!         wait: true,
50//!         per_region: true,
51//!     }),
52//!     .. Default::default()
53//! };
54//! # }
55//! ```
56//!
57//! If you use conditional rendering, glium will submit the draw command but the GPU will execute
58//! it only if the query contains a value different from 0.
59//!
60//! ## WrongQueryOperation errors
61//!
62//! OpenGL puts some restrictions about the usage of queries. If you draw one or several times
63//! with a query, then draw *without* that query, then the query cannot be used again. Trying
64//! to draw with it results in a `WrongQueryOperation` error returned by the `draw` function.
65//!
66//! For the same reasons, as soon as you call `is_ready` on a query it will stop being usable.
67
68use 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/// Describes how triangles should be filtered before the fragment processing. Backface culling
98/// is purely an optimization. If you don't know what this does, just use `CullingDisabled`.
99///
100/// # Backface culling
101///
102/// After the vertex shader stage, the GPU knows the 2D coordinates of each vertex of
103/// each triangle.
104///
105/// For a given triangle, there are only two situations:
106///
107/// - The vertices are arranged in a clockwise direction on the screen.
108/// - The vertices are arranged in a counterclockwise direction on the screen.
109///
110/// If you wish so, you can ask the GPU to discard all the primitives that belong to one
111/// of these two categories.
112///
113/// ## Example
114///
115/// The vertices of this triangle are counter-clock-wise.
116///
117/// <svg width="556.84381" height="509.69049" version="1.1">
118///   <g transform="translate(-95.156215,-320.37201)">
119///     <path style="fill:none;stroke:#000000;stroke-width:4;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" d="M 324.25897,418.99654 539.42145,726.08292 212.13204,741.23521 z" />
120///     <text style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" x="296.98483" y="400.81378"><tspan x="296.98483" y="400.81378">1</tspan></text>
121///     <text style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" x="175.22902" y="774.8031"><tspan x="175.22902" y="774.8031">2</tspan></text>
122///     <text style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" x="555.58386" y="748.30627"><tspan x="555.58386" y="748.30627">3</tspan></text>
123///   </g>
124/// </svg>
125///
126/// # Usage
127///
128/// The trick is that if you make a 180° rotation of a shape, all triangles that were
129/// clockwise become counterclockwise and vice versa.
130///
131/// Therefore you can arrange your model so that the triangles that are facing the screen
132/// are all either clockwise or counterclockwise, and all the triangle are *not* facing
133/// the screen are the other one.
134///
135/// By doing so you can use backface culling to discard all the triangles that are not
136/// facing the screen, and increase your framerate.
137///
138#[derive(Clone, Copy, Debug, PartialEq, Eq)]
139pub enum BackfaceCullingMode {
140    /// All triangles are always drawn.
141    CullingDisabled,
142
143    /// Triangles whose vertices are counterclockwise won't be drawn.
144    CullCounterClockwise,
145
146    /// Triangles whose vertices are clockwise won't be drawn.
147    CullClockwise
148}
149
150/// Defines how the device should render polygons.
151///
152/// The usual value is `Fill`, which fills the content of polygon with the color. However other
153/// values are sometimes useful, especially for debugging purposes.
154///
155/// # Example
156///
157/// The same triangle drawn respectively with `Fill`, `Line` and `Point` (barely visible).
158///
159/// <svg width="890.26135" height="282.59375" version="1.1">
160///  <g transform="translate(0,-769.9375)">
161///     <path style="fill:#ff0000;fill-opacity:1;stroke:none" d="M 124.24877,771.03979 258.59906,1051.8622 0,1003.3749 z" />
162///     <path style="fill:none;fill-opacity:1;stroke:#ff0000;stroke-opacity:1" d="M 444.46713,771.03979 578.81742,1051.8622 320.21836,1003.3749 z" />
163///     <path style="fill:#ff0000;fill-opacity:1;stroke:none" d="m 814.91074,385.7662 c 0,0.0185 -0.015,0.0335 -0.0335,0.0335 -0.0185,0 -0.0335,-0.015 -0.0335,-0.0335 0,-0.0185 0.015,-0.0335 0.0335,-0.0335 0.0185,0 0.0335,0.015 0.0335,0.0335 z" transform="matrix(18.833333,0,0,18.833333,-14715.306,-6262.0056)" />
164///     <path style="fill:#ff0000;fill-opacity:1;stroke:none" d="m 814.91074,385.7662 c 0,0.0185 -0.015,0.0335 -0.0335,0.0335 -0.0185,0 -0.0335,-0.015 -0.0335,-0.0335 0,-0.0185 0.015,-0.0335 0.0335,-0.0335 0.0185,0 0.0335,0.015 0.0335,0.0335 z" transform="matrix(18.833333,0,0,18.833333,-14591.26,-6493.994)" />
165///     <path style="fill:#ff0000;fill-opacity:1;stroke:none" d="m 814.91074,385.7662 c 0,0.0185 -0.015,0.0335 -0.0335,0.0335 -0.0185,0 -0.0335,-0.015 -0.0335,-0.0335 0,-0.0185 0.015,-0.0335 0.0335,-0.0335 0.0185,0 0.0335,0.015 0.0335,0.0335 z" transform="matrix(18.833333,0,0,18.833333,-14457.224,-6213.6135)" />
166///  </g>
167/// </svg>
168///
169#[derive(Clone, Copy, Debug, PartialEq, Eq)]
170pub enum PolygonMode {
171    /// Only draw a single point at each vertex.
172    ///
173    /// All attributes that apply to points (`point_size`) are used when using this mode.
174    Point,
175
176    /// Only draw a line in the boundaries of each polygon.
177    ///
178    /// All attributes that apply to lines (`line_width`) are used when using this mode.
179    Line,
180
181    /// Fill the content of the polygon. This is the default mode.
182    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/// Specifies a hint for the smoothing.
197///
198/// Note that this is just a hint and the driver may disregard it.
199#[derive(Clone, Copy, Debug, PartialEq, Eq)]
200pub enum Smooth {
201    /// The most efficient option should be chosen.
202    Fastest,
203
204    /// The most correct, or highest quality, option should be chosen.
205    Nicest,
206
207    /// No preference.
208    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/// The vertex to use for flat shading.
223#[derive(Clone, Copy, Debug, PartialEq, Eq)]
224pub enum ProvokingVertex {
225    /// Use the last vertex of each primitive.
226    LastVertex,
227
228    /// Use the first vertex of each primitive.
229    ///
230    /// Note that for triangle fans, this is not the first vertex but the second vertex.
231    FirstVertex,
232}
233
234/// Represents the parameters to use when drawing.
235///
236/// Example:
237///
238/// ```
239/// let params = glium::DrawParameters {
240///     depth: glium::Depth {
241///         test: glium::DepthTest::IfLess,
242///         write: true,
243///         .. Default::default()
244///     },
245///     .. Default::default()
246/// };
247/// ```
248///
249#[derive(Clone, Debug)]
250pub struct DrawParameters<'a> {
251    /// How the fragment will interact with the depth buffer.
252    pub depth: Depth,
253
254    /// How the fragment will interact with the stencil buffer.
255    pub stencil: Stencil,
256
257    /// The effect that the GPU will use to merge the existing pixel with the pixel that is
258    /// being written.
259    pub blend: Blend,
260
261    /// Allows you to disable some color components.
262    ///
263    /// This affects all attachments to the framebuffer. It's at the same level as the
264    /// blending function.
265    ///
266    /// The parameters are in order: red, green, blue, alpha. `true` means that the given
267    /// component will be written, `false` means that it will be ignored. The default value
268    /// is `(true, true, true, true)`.
269    pub color_mask: (bool, bool, bool, bool),
270
271    /// Width in pixels of the lines to draw when drawing lines.
272    ///
273    /// `None` means "don't care". Use this when you don't draw lines.
274    pub line_width: Option<f32>,
275
276    /// Diameter in pixels of the points to draw when drawing points.
277    ///
278    /// `None` means "don't care". Use this when you don't draw points.
279    pub point_size: Option<f32>,
280
281    /// If the bit corresponding to 2^i is 1 in the bitmask, then GL_CLIP_DISTANCEi is enabled.
282    ///
283    /// The most common value for GL_MAX_CLIP_DISTANCES is 8, so 32 bits in the mask is plenty.
284    ///
285    /// See `https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/gl_ClipDistance.xhtml`.
286    pub clip_planes_bitmask: u32,
287
288    /// Whether or not the GPU should filter out some faces.
289    ///
290    /// After the vertex shader stage, the GPU will try to remove the faces that aren't facing
291    /// the camera.
292    ///
293    /// See the `BackfaceCullingMode` documentation for more infos.
294    pub backface_culling: BackfaceCullingMode,
295
296    /// How to render polygons. The default value is `Fill`.
297    ///
298    /// See the documentation of `PolygonMode` for more infos.
299    pub polygon_mode: PolygonMode,
300
301    /// Whether multisample antialiasing (MSAA) should be used. Default value is `true`.
302    ///
303    /// Note that you will need to set the appropriate option when creating the window.
304    /// The recommended way to do is to leave this to `true`, and adjust the option when
305    /// creating the window.
306    pub multisampling: bool,
307
308    /// Whether dithering is activated. Default value is `true`.
309    ///
310    /// Dithering will smoothen the transition between colors in your color buffer.
311    pub dithering: bool,
312
313    /// The viewport to use when drawing.
314    ///
315    /// The X and Y positions of your vertices are mapped to the viewport so that `(-1, -1)`
316    /// corresponds to the lower-left hand corner and `(1, 1)` corresponds to the top-right
317    /// hand corner. Any pixel outside of the viewport is discarded.
318    ///
319    /// You can specify a viewport greater than the target if you want to stretch the image.
320    ///
321    /// `None` means "use the whole surface".
322    pub viewport: Option<Rect>,
323
324    /// If specified, only pixels in this rect will be displayed. Default is `None`.
325    ///
326    /// This is different from a viewport. The image will stretch to fill the viewport, but
327    /// not the scissor box.
328    pub scissor: Option<Rect>,
329
330    /// If `false`, the pipeline will stop after the primitives generation stage. The default
331    /// value is `true`.
332    ///
333    /// If `false`, the fragment shader of your program won't be executed.
334    ///
335    /// If `false`, drawing may return `TransformFeedbackNotSupported` if the backend doesn't
336    /// support this feature.
337    ///
338    /// This parameter may seem pointless, but it can be useful when you use transform
339    /// feedback or if you just use your shaders to write to a buffer.
340    pub draw_primitives: bool,
341
342    /// If set, each sample (ie. usually each pixel) written to the output adds one to the
343    /// counter of the `SamplesPassedQuery`.
344    pub samples_passed_query: Option<SamplesQueryParam<'a>>,
345
346    /// If set, the time it took for the GPU to execute this draw command is added to the total
347    /// stored inside the `TimeElapsedQuery`.
348    pub time_elapsed_query: Option<&'a TimeElapsedQuery>,
349
350    /// If set, the number of primitives generated is added to the total stored inside the query.
351    pub primitives_generated_query: Option<&'a PrimitivesGeneratedQuery>,
352
353    /// If set, the number of vertices written by transform feedback.
354    pub transform_feedback_primitives_written_query:
355                                    Option<&'a TransformFeedbackPrimitivesWrittenQuery>,
356
357    /// If set, the commands will only be executed if the specified query contains `true` or
358    /// a number different than 0.
359    pub condition: Option<ConditionalRendering<'a>>,
360
361    /// If set, then the generated primitives will be written back to a buffer.
362    pub transform_feedback: Option<&'a TransformFeedbackSession<'a>>,
363
364    /// If set, then the generated primitives will be smoothed.
365    ///
366    /// Note that blending needs to be enabled for this to work.
367    pub smooth: Option<Smooth>,
368
369    /// In your vertex shader or geometry shader, you have the possibility to mark some output
370    /// varyings as `flat`. If this is the case, the value of one of the vertices will be used
371    /// for the whole primitive. This variable allows you to specify which vertex.
372    ///
373    /// The default value is `LastVertex`, as this is the default in OpenGL. Any other value can
374    /// potentially trigger a `ProvokingVertexNotSupported` error. Most notably OpenGL ES doesn't
375    /// support anything else but `LastVertex`.
376    pub provoking_vertex: ProvokingVertex,
377
378    /// Hint for the GPU of the bounding box of the geometry.
379    ///
380    /// If you're using geometry shaders or tessellation shaders, it can be extremely advantageous
381    /// for the GPU to know where on the screen the primitive is. This field specifies the
382    /// bounding box (`x`, `y`, `z`, `w`) of the primitive and serves as a hint to the GPU.
383    ///
384    /// The GPU is free not to draw samples outside of the bounding box. Whether the samples are
385    /// drawn is implementation-specific.
386    ///
387    /// This field is useless if you're not using a geometry shader or tessellation shader.
388    ///
389    /// Since this is purely an optimization, this parameter is ignored if the backend doesn't
390    /// support it.
391    pub primitive_bounding_box: (Range<f32>, Range<f32>, Range<f32>, Range<f32>),
392
393    /// If enabled, will split the index buffer (if any is used in the draw call)
394    /// at the MAX value of the IndexType (u8::MAX, u16::MAX or u32::MAX) and start a new primitive
395    /// of the same type ("primitive restarting"). Supported on > OpenGL 3.1 or OpenGL ES 3.0.
396    /// If the backend does not support GL_PRIMITIVE_RESTART_FIXED_INDEX, an Error
397    /// of type `FixedIndexRestartingNotSupported` will be returned.
398    pub primitive_restart_index: bool,
399
400    /// If enabled, shifts the depth value of towards of away from the camera. This is useful for
401    /// drawing decals and wireframes, for example.
402    pub polygon_offset: PolygonOffset,
403
404    /// Clip control origin. The default value is `LowerLeft`.
405    pub clip_control_origin: ClipControlOrigin,
406
407    /// Clip control depth mode. The default value is `NegativeOneToOne`.
408    pub clip_control_depth: ClipControlDepth,
409}
410
411/// Condition whether to render or not.
412#[derive(Debug, Copy, Clone)]
413pub struct ConditionalRendering<'a> {
414    /// The query to use.
415    pub query: SamplesQueryParam<'a>,
416
417    /// If true, the GPU will wait until the query result has been obtained. If false, the GPU
418    /// is free to ignore the query and draw anyway.
419    pub wait: bool,
420
421    /// If true, only samples that match those that were written with the query active will
422    /// be drawn.
423    pub per_region: bool,
424}
425
426/// The query to use for samples counting.
427#[derive(Debug, Copy, Clone)]
428pub enum SamplesQueryParam<'a> {
429    /// A `SamplesPassedQuery`.
430    SamplesPassedQuery(&'a SamplesPassedQuery),
431    /// A `AnySamplesPassedQuery`.
432    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/// Specifies the depth offset applied to rendered geometry
450#[derive(Debug, Copy, Clone)]
451pub struct PolygonOffset {
452    /// Scale polygon depth with a factor
453    pub factor: f32,
454    /// Add a constant value to polygon depth
455    pub units: f32,
456    /// If true, the depth offset is enabled for points
457    pub point: bool,
458    /// If true, the depth offset is enabled for lines
459    pub line: bool,
460    /// If true, the depth offset is enabled for triangles
461    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/// Specifies the clip control origin.
477#[derive(Clone, Copy, Debug, PartialEq, Eq)]
478pub enum ClipControlOrigin {
479    /// The clip space origin is at the lower-left corner of the viewport. This is the default state.
480    LowerLeft,
481
482    /// The clip space origin is at the upper-left corner of the viewport. This will vertically invert the geometry inside the viewport.
483    UpperLeft,
484}
485
486/// Specifies the clip control depth mode.
487#[derive(Clone, Copy, Debug, PartialEq, Eq)]
488pub enum ClipControlDepth {
489    /// The near and far clipping planes correspond to Z coordinates of -1 and +1. This is the default state.
490    NegativeOneToOne,
491
492    /// The near and far clipping planes correspond to Z coordinates of 0 and +1. This may improve numerical precision of depth mapping.
493    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
530/// DEPRECATED. Checks parameters and returns an error if something is wrong.
531pub 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    // back-face culling
623    // note: we never change the value of `glFrontFace`, whose default is GL_CCW
624    //  that's why `CullClockwise` uses `GL_BACK` for example
625    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    // polygon mode
655    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    // Not supported on GLES
667    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    // viewport
724    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    // scissor
755    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        // check if smoothing is supported, it isn't on OpenGL ES
872        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            // point
880            PrimitiveType::Points =>
881                return Err(DrawError::SmoothingNotSupported),
882
883            // line
884            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            // polygon
899            _ => 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            // point
915            PrimitiveType::Points => (),
916
917            // line
918            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            // polygon
928            _ => 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    // TODO: use GL_PRIMITIVE_RESTART (if possible) if
1004    // GL_PRIMITIVE_RESTART_FIXED_INDEX is not supported
1005    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}