use crate::gl;
use crate::context;
use crate::context::Context;
use crate::version::Version;
use crate::version::Api;
use crate::index::PrimitiveType;
use crate::QueryExt;
use crate::CapabilitiesSource;
use crate::DrawError;
use crate::Rect;
use crate::ToGlEnum;
use crate::vertex::TransformFeedbackSession;
use std::ops::Range;
pub use self::blend::{Blend, BlendingFunction, LinearBlendingFactor};
pub use self::depth::{Depth, DepthTest, DepthClamp};
pub use self::query::{QueryCreationError};
pub use self::query::{SamplesPassedQuery, TimeElapsedQuery, PrimitivesGeneratedQuery};
pub use self::query::{AnySamplesPassedQuery, TransformFeedbackPrimitivesWrittenQuery};
pub use self::stencil::{StencilTest, StencilOperation, Stencil};
mod blend;
mod depth;
mod query;
mod stencil;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BackfaceCullingMode {
CullingDisabled,
CullCounterClockwise,
CullClockwise
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PolygonMode {
Point,
Line,
Fill,
}
impl ToGlEnum for PolygonMode {
#[inline]
fn to_glenum(&self) -> gl::types::GLenum {
match *self {
PolygonMode::Point => gl::POINT,
PolygonMode::Line => gl::LINE,
PolygonMode::Fill => gl::FILL,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Smooth {
Fastest,
Nicest,
DontCare,
}
impl ToGlEnum for Smooth {
#[inline]
fn to_glenum(&self) -> gl::types::GLenum {
match *self {
Smooth::Fastest => gl::FASTEST,
Smooth::Nicest => gl::NICEST,
Smooth::DontCare => gl::DONT_CARE,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ProvokingVertex {
LastVertex,
FirstVertex,
}
#[derive(Clone, Debug)]
pub struct DrawParameters<'a> {
pub depth: Depth,
pub stencil: Stencil,
pub blend: Blend,
pub color_mask: (bool, bool, bool, bool),
pub line_width: Option<f32>,
pub point_size: Option<f32>,
pub clip_planes_bitmask: u32,
pub backface_culling: BackfaceCullingMode,
pub polygon_mode: PolygonMode,
pub multisampling: bool,
pub dithering: bool,
pub viewport: Option<Rect>,
pub scissor: Option<Rect>,
pub draw_primitives: bool,
pub samples_passed_query: Option<SamplesQueryParam<'a>>,
pub time_elapsed_query: Option<&'a TimeElapsedQuery>,
pub primitives_generated_query: Option<&'a PrimitivesGeneratedQuery>,
pub transform_feedback_primitives_written_query:
Option<&'a TransformFeedbackPrimitivesWrittenQuery>,
pub condition: Option<ConditionalRendering<'a>>,
pub transform_feedback: Option<&'a TransformFeedbackSession<'a>>,
pub smooth: Option<Smooth>,
pub provoking_vertex: ProvokingVertex,
pub primitive_bounding_box: (Range<f32>, Range<f32>, Range<f32>, Range<f32>),
pub primitive_restart_index: bool,
pub polygon_offset: PolygonOffset,
pub clip_control_origin: ClipControlOrigin,
pub clip_control_depth: ClipControlDepth,
}
#[derive(Debug, Copy, Clone)]
pub struct ConditionalRendering<'a> {
pub query: SamplesQueryParam<'a>,
pub wait: bool,
pub per_region: bool,
}
#[derive(Debug, Copy, Clone)]
pub enum SamplesQueryParam<'a> {
SamplesPassedQuery(&'a SamplesPassedQuery),
AnySamplesPassedQuery(&'a AnySamplesPassedQuery),
}
impl<'a> From<&'a SamplesPassedQuery> for SamplesQueryParam<'a> {
#[inline]
fn from(r: &'a SamplesPassedQuery) -> SamplesQueryParam<'a> {
SamplesQueryParam::SamplesPassedQuery(r)
}
}
impl<'a> From<&'a AnySamplesPassedQuery> for SamplesQueryParam<'a> {
#[inline]
fn from(r: &'a AnySamplesPassedQuery) -> SamplesQueryParam<'a> {
SamplesQueryParam::AnySamplesPassedQuery(r)
}
}
#[derive(Debug, Copy, Clone)]
pub struct PolygonOffset {
pub factor: f32,
pub units: f32,
pub point: bool,
pub line: bool,
pub fill: bool,
}
impl Default for PolygonOffset {
fn default() -> Self {
PolygonOffset{
factor: 0.0,
units: 0.0,
point: false,
line: false,
fill: false
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ClipControlOrigin {
LowerLeft,
UpperLeft,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ClipControlDepth {
NegativeOneToOne,
ZeroToOne,
}
impl<'a> Default for DrawParameters<'a> {
fn default() -> DrawParameters<'a> {
DrawParameters {
depth: Depth::default(),
stencil: Default::default(),
blend: Default::default(),
color_mask: (true, true, true, true),
line_width: None,
point_size: None,
backface_culling: BackfaceCullingMode::CullingDisabled,
polygon_mode: PolygonMode::Fill,
clip_planes_bitmask: 0,
multisampling: true,
dithering: true,
viewport: None,
scissor: None,
draw_primitives: true,
samples_passed_query: None,
time_elapsed_query: None,
primitives_generated_query: None,
transform_feedback_primitives_written_query: None,
condition: None,
transform_feedback: None,
smooth: None,
provoking_vertex: ProvokingVertex::LastVertex,
primitive_bounding_box: (-1.0 .. 1.0, -1.0 .. 1.0, -1.0 .. 1.0, -1.0 .. 1.0),
primitive_restart_index: false,
polygon_offset: Default::default(),
clip_control_origin: ClipControlOrigin::LowerLeft,
clip_control_depth: ClipControlDepth::NegativeOneToOne,
}
}
}
pub fn validate(context: &Context, params: &DrawParameters<'_>) -> Result<(), DrawError> {
if params.depth.range.0 < 0.0 || params.depth.range.0 > 1.0 ||
params.depth.range.1 < 0.0 || params.depth.range.1 > 1.0
{
return Err(DrawError::InvalidDepthRange);
}
if !params.draw_primitives && context.get_opengl_version() < &Version(Api::Gl, 3, 0) &&
!context.get_extensions().gl_ext_transform_feedback
{
return Err(DrawError::RasterizerDiscardNotSupported);
}
Ok(())
}
#[doc(hidden)]
pub fn sync(ctxt: &mut context::CommandContext<'_>, draw_parameters: &DrawParameters<'_>,
dimensions: (u32, u32), primitives_types: PrimitiveType) -> Result<(), DrawError>
{
depth::sync_depth(ctxt, &draw_parameters.depth)?;
stencil::sync_stencil(ctxt, &draw_parameters.stencil);
blend::sync_blending(ctxt, draw_parameters.blend)?;
sync_color_mask(ctxt, draw_parameters.color_mask);
sync_line_width(ctxt, draw_parameters.line_width);
sync_point_size(ctxt, draw_parameters.point_size);
sync_polygon_mode(ctxt, draw_parameters.backface_culling, draw_parameters.polygon_mode);
sync_clip_planes_bitmask(ctxt, draw_parameters.clip_planes_bitmask)?;
sync_multisampling(ctxt, draw_parameters.multisampling);
sync_dithering(ctxt, draw_parameters.dithering);
sync_viewport_scissor(ctxt, draw_parameters.viewport, draw_parameters.scissor,
dimensions);
sync_rasterizer_discard(ctxt, draw_parameters.draw_primitives)?;
sync_queries(ctxt, draw_parameters.samples_passed_query,
draw_parameters.time_elapsed_query,
draw_parameters.primitives_generated_query,
draw_parameters.transform_feedback_primitives_written_query)?;
sync_conditional_render(ctxt, draw_parameters.condition);
sync_smooth(ctxt, draw_parameters.smooth, primitives_types)?;
sync_provoking_vertex(ctxt, draw_parameters.provoking_vertex)?;
sync_primitive_bounding_box(ctxt, &draw_parameters.primitive_bounding_box);
sync_primitive_restart_index(ctxt, draw_parameters.primitive_restart_index)?;
sync_polygon_offset(ctxt, draw_parameters.polygon_offset);
sync_clip_control(ctxt, draw_parameters.clip_control_origin,
draw_parameters.clip_control_depth)?;
Ok(())
}
fn sync_color_mask(ctxt: &mut context::CommandContext<'_>, mask: (bool, bool, bool, bool)) {
let mask = (
if mask.0 { 1 } else { 0 },
if mask.1 { 1 } else { 0 },
if mask.2 { 1 } else { 0 },
if mask.3 { 1 } else { 0 },
);
if ctxt.state.color_mask != mask {
unsafe {
ctxt.gl.ColorMask(mask.0, mask.1, mask.2, mask.3);
}
ctxt.state.color_mask = mask;
}
}
fn sync_line_width(ctxt: &mut context::CommandContext<'_>, line_width: Option<f32>) {
if let Some(line_width) = line_width {
if ctxt.state.line_width != line_width {
unsafe {
ctxt.gl.LineWidth(line_width);
ctxt.state.line_width = line_width;
}
}
}
}
fn sync_point_size(ctxt: &mut context::CommandContext<'_>, point_size: Option<f32>) {
if let Some(point_size) = point_size {
if ctxt.state.point_size != point_size {
unsafe {
ctxt.gl.PointSize(point_size);
ctxt.state.point_size = point_size;
}
}
}
}
fn sync_polygon_mode(ctxt: &mut context::CommandContext<'_>, backface_culling: BackfaceCullingMode,
polygon_mode: PolygonMode)
{
match backface_culling {
BackfaceCullingMode::CullingDisabled => unsafe {
if ctxt.state.enabled_cull_face {
ctxt.gl.Disable(gl::CULL_FACE);
ctxt.state.enabled_cull_face = false;
}
},
BackfaceCullingMode::CullCounterClockwise => unsafe {
if !ctxt.state.enabled_cull_face {
ctxt.gl.Enable(gl::CULL_FACE);
ctxt.state.enabled_cull_face = true;
}
if ctxt.state.cull_face != gl::FRONT {
ctxt.gl.CullFace(gl::FRONT);
ctxt.state.cull_face = gl::FRONT;
}
},
BackfaceCullingMode::CullClockwise => unsafe {
if !ctxt.state.enabled_cull_face {
ctxt.gl.Enable(gl::CULL_FACE);
ctxt.state.enabled_cull_face = true;
}
if ctxt.state.cull_face != gl::BACK {
ctxt.gl.CullFace(gl::BACK);
ctxt.state.cull_face = gl::BACK;
}
},
}
unsafe {
let polygon_mode = polygon_mode.to_glenum();
if ctxt.state.polygon_mode != polygon_mode {
ctxt.gl.PolygonMode(gl::FRONT_AND_BACK, polygon_mode);
ctxt.state.polygon_mode = polygon_mode;
}
}
}
fn sync_clip_planes_bitmask(ctxt: &mut context::CommandContext<'_>, clip_planes_bitmask: u32)
-> Result<(), DrawError> {
if !(ctxt.version >= &Version(Api::Gl, 1, 0)) {
return Ok(());
}
unsafe {
let mut max_clip_planes: gl::types::GLint = 0;
ctxt.gl.GetIntegerv(gl::MAX_CLIP_DISTANCES, &mut max_clip_planes);
for i in 0..32 {
if clip_planes_bitmask & (1 << i) != ctxt.state.enabled_clip_planes & (1 << i) {
if clip_planes_bitmask & (1 << i) != 0 {
if i < max_clip_planes {
ctxt.gl.Enable(gl::CLIP_DISTANCE0 + i as u32);
ctxt.state.enabled_clip_planes |= 1 << i;
} else {
return Err(DrawError::ClipPlaneIndexOutOfBounds);
}
} else if i < max_clip_planes {
ctxt.gl.Disable(gl::CLIP_DISTANCE0 + i as u32);
ctxt.state.enabled_clip_planes &= !(1 << i);
}
}
}
Ok(())
}
}
fn sync_multisampling(ctxt: &mut context::CommandContext<'_>, multisampling: bool) {
if ctxt.state.enabled_multisample != multisampling {
unsafe {
if multisampling {
ctxt.gl.Enable(gl::MULTISAMPLE);
ctxt.state.enabled_multisample = true;
} else {
ctxt.gl.Disable(gl::MULTISAMPLE);
ctxt.state.enabled_multisample = false;
}
}
}
}
fn sync_dithering(ctxt: &mut context::CommandContext<'_>, dithering: bool) {
if ctxt.state.enabled_dither != dithering {
unsafe {
if dithering {
ctxt.gl.Enable(gl::DITHER);
ctxt.state.enabled_dither = true;
} else {
ctxt.gl.Disable(gl::DITHER);
ctxt.state.enabled_dither = false;
}
}
}
}
fn sync_viewport_scissor(ctxt: &mut context::CommandContext<'_>, viewport: Option<Rect>,
scissor: Option<Rect>, surface_dimensions: (u32, u32))
{
if let Some(viewport) = viewport {
assert!(viewport.width <= ctxt.capabilities.max_viewport_dims.0 as u32,
"Viewport dimensions are too large");
assert!(viewport.height <= ctxt.capabilities.max_viewport_dims.1 as u32,
"Viewport dimensions are too large");
let viewport = (viewport.left as gl::types::GLint, viewport.bottom as gl::types::GLint,
viewport.width as gl::types::GLsizei,
viewport.height as gl::types::GLsizei);
if ctxt.state.viewport != Some(viewport) {
unsafe { ctxt.gl.Viewport(viewport.0, viewport.1, viewport.2, viewport.3); }
ctxt.state.viewport = Some(viewport);
}
} else {
assert!(surface_dimensions.0 <= ctxt.capabilities.max_viewport_dims.0 as u32,
"Viewport dimensions are too large");
assert!(surface_dimensions.1 <= ctxt.capabilities.max_viewport_dims.1 as u32,
"Viewport dimensions are too large");
let viewport = (0, 0, surface_dimensions.0 as gl::types::GLsizei,
surface_dimensions.1 as gl::types::GLsizei);
if ctxt.state.viewport != Some(viewport) {
unsafe { ctxt.gl.Viewport(viewport.0, viewport.1, viewport.2, viewport.3); }
ctxt.state.viewport = Some(viewport);
}
}
if let Some(scissor) = scissor {
let scissor = (scissor.left as gl::types::GLint, scissor.bottom as gl::types::GLint,
scissor.width as gl::types::GLsizei,
scissor.height as gl::types::GLsizei);
unsafe {
if ctxt.state.scissor != Some(scissor) {
ctxt.gl.Scissor(scissor.0, scissor.1, scissor.2, scissor.3);
ctxt.state.scissor = Some(scissor);
}
if !ctxt.state.enabled_scissor_test {
ctxt.gl.Enable(gl::SCISSOR_TEST);
ctxt.state.enabled_scissor_test = true;
}
}
} else {
unsafe {
if ctxt.state.enabled_scissor_test {
ctxt.gl.Disable(gl::SCISSOR_TEST);
ctxt.state.enabled_scissor_test = false;
}
}
}
}
fn sync_rasterizer_discard(ctxt: &mut context::CommandContext<'_>, draw_primitives: bool)
-> Result<(), DrawError>
{
if ctxt.state.enabled_rasterizer_discard == draw_primitives {
if ctxt.version >= &Version(Api::Gl, 3, 0) {
if draw_primitives {
unsafe { ctxt.gl.Disable(gl::RASTERIZER_DISCARD); }
ctxt.state.enabled_rasterizer_discard = false;
} else {
unsafe { ctxt.gl.Enable(gl::RASTERIZER_DISCARD); }
ctxt.state.enabled_rasterizer_discard = true;
}
} else if ctxt.extensions.gl_ext_transform_feedback {
if draw_primitives {
unsafe { ctxt.gl.Disable(gl::RASTERIZER_DISCARD_EXT); }
ctxt.state.enabled_rasterizer_discard = false;
} else {
unsafe { ctxt.gl.Enable(gl::RASTERIZER_DISCARD_EXT); }
ctxt.state.enabled_rasterizer_discard = true;
}
} else {
return Err(DrawError::RasterizerDiscardNotSupported);
}
}
Ok(())
}
fn sync_queries(ctxt: &mut context::CommandContext<'_>,
samples_passed_query: Option<SamplesQueryParam<'_>>,
time_elapsed_query: Option<&TimeElapsedQuery>,
primitives_generated_query: Option<&PrimitivesGeneratedQuery>,
transform_feedback_primitives_written_query:
Option<&TransformFeedbackPrimitivesWrittenQuery>)
-> Result<(), DrawError>
{
if let Some(SamplesQueryParam::SamplesPassedQuery(q)) = samples_passed_query {
q.begin_query(ctxt)?;
} else if let Some(SamplesQueryParam::AnySamplesPassedQuery(q)) = samples_passed_query {
q.begin_query(ctxt)?;
} else {
TimeElapsedQuery::end_samples_passed_query(ctxt);
}
if let Some(time_elapsed_query) = time_elapsed_query {
time_elapsed_query.begin_query(ctxt)?;
} else {
TimeElapsedQuery::end_time_elapsed_query(ctxt);
}
if let Some(primitives_generated_query) = primitives_generated_query {
primitives_generated_query.begin_query(ctxt)?;
} else {
TimeElapsedQuery::end_primitives_generated_query(ctxt);
}
if let Some(tfq) = transform_feedback_primitives_written_query {
tfq.begin_query(ctxt)?;
} else {
TimeElapsedQuery::end_transform_feedback_primitives_written_query(ctxt);
}
Ok(())
}
fn sync_conditional_render(ctxt: &mut context::CommandContext<'_>,
condition: Option<ConditionalRendering<'_>>)
{
if let Some(ConditionalRendering { query, wait, per_region }) = condition {
match query {
SamplesQueryParam::SamplesPassedQuery(ref q) => {
q.begin_conditional_render(ctxt, wait, per_region);
},
SamplesQueryParam::AnySamplesPassedQuery(ref q) => {
q.begin_conditional_render(ctxt, wait, per_region);
},
}
} else {
TimeElapsedQuery::end_conditional_render(ctxt);
}
}
fn sync_smooth(ctxt: &mut context::CommandContext<'_>,
smooth: Option<Smooth>,
primitive_type: PrimitiveType) -> Result<(), DrawError> {
if let Some(smooth) = smooth {
if !(ctxt.version >= &Version(Api::Gl, 1, 0)) {
return Err(DrawError::SmoothingNotSupported);
}
let hint = smooth.to_glenum();
match primitive_type {
PrimitiveType::Points =>
return Err(DrawError::SmoothingNotSupported),
PrimitiveType::LinesList | PrimitiveType::LinesListAdjacency |
PrimitiveType::LineStrip | PrimitiveType::LineStripAdjacency |
PrimitiveType::LineLoop => unsafe {
if !ctxt.state.enabled_line_smooth {
ctxt.state.enabled_line_smooth = true;
ctxt.gl.Enable(gl::LINE_SMOOTH);
}
if ctxt.state.smooth.0 != hint {
ctxt.state.smooth.0 = hint;
ctxt.gl.Hint(gl::LINE_SMOOTH_HINT, hint);
}
},
_ => unsafe {
if !ctxt.state.enabled_polygon_smooth {
ctxt.state.enabled_polygon_smooth = true;
ctxt.gl.Enable(gl::POLYGON_SMOOTH);
}
if ctxt.state.smooth.1 != hint {
ctxt.state.smooth.1 = hint;
ctxt.gl.Hint(gl::POLYGON_SMOOTH_HINT, hint);
}
}
}
}
else {
match primitive_type {
PrimitiveType::Points => (),
PrimitiveType::LinesList | PrimitiveType::LinesListAdjacency |
PrimitiveType::LineStrip | PrimitiveType::LineStripAdjacency |
PrimitiveType::LineLoop => unsafe {
if ctxt.state.enabled_line_smooth {
ctxt.state.enabled_line_smooth = false;
ctxt.gl.Disable(gl::LINE_SMOOTH);
}
},
_ => unsafe {
if ctxt.state.enabled_polygon_smooth {
ctxt.state.enabled_polygon_smooth = false;
ctxt.gl.Disable(gl::POLYGON_SMOOTH);
}
}
}
}
Ok(())
}
fn sync_provoking_vertex(ctxt: &mut context::CommandContext<'_>, value: ProvokingVertex)
-> Result<(), DrawError>
{
let value = match value {
ProvokingVertex::LastVertex => gl::LAST_VERTEX_CONVENTION,
ProvokingVertex::FirstVertex => gl::FIRST_VERTEX_CONVENTION,
};
if ctxt.state.provoking_vertex == value {
return Ok(());
}
if ctxt.version >= &Version(Api::Gl, 3, 2) || ctxt.extensions.gl_arb_provoking_vertex {
unsafe { ctxt.gl.ProvokingVertex(value); }
ctxt.state.provoking_vertex = value;
} else if ctxt.extensions.gl_ext_provoking_vertex {
unsafe { ctxt.gl.ProvokingVertexEXT(value); }
ctxt.state.provoking_vertex = value;
} else {
return Err(DrawError::ProvokingVertexNotSupported);
}
Ok(())
}
fn sync_primitive_bounding_box(ctxt: &mut context::CommandContext<'_>,
bb: &(Range<f32>, Range<f32>, Range<f32>, Range<f32>))
{
let value = (bb.0.start, bb.1.start, bb.2.start, bb.3.start,
bb.0.end, bb.1.end, bb.2.end, bb.3.end);
if ctxt.state.primitive_bounding_box == value {
return;
}
if ctxt.version >= &Version(Api::GlEs, 3, 2) {
unsafe { ctxt.gl.PrimitiveBoundingBox(value.0, value.1, value.2, value.3,
value.4, value.5, value.6, value.7); }
ctxt.state.primitive_bounding_box = value;
} else if ctxt.extensions.gl_arb_es3_2_compatibility {
unsafe { ctxt.gl.PrimitiveBoundingBoxARB(value.0, value.1, value.2, value.3,
value.4, value.5, value.6, value.7); }
ctxt.state.primitive_bounding_box = value;
} else if ctxt.extensions.gl_oes_primitive_bounding_box {
unsafe { ctxt.gl.PrimitiveBoundingBoxOES(value.0, value.1, value.2, value.3,
value.4, value.5, value.6, value.7); }
ctxt.state.primitive_bounding_box = value;
} else if ctxt.extensions.gl_ext_primitive_bounding_box {
unsafe { ctxt.gl.PrimitiveBoundingBoxEXT(value.0, value.1, value.2, value.3,
value.4, value.5, value.6, value.7); }
ctxt.state.primitive_bounding_box = value;
}
}
fn sync_primitive_restart_index(ctxt: &mut context::CommandContext<'_>,
enabled: bool)
-> Result<(), DrawError>
{
if ctxt.version >= &Version(Api::Gl, 3, 1) || ctxt.version >= &Version(Api::GlEs, 3, 0) ||
ctxt.extensions.gl_arb_es3_compatibility
{
if ctxt.state.enabled_primitive_fixed_restart != enabled {
if enabled {
unsafe { ctxt.gl.Enable(gl::PRIMITIVE_RESTART_FIXED_INDEX); }
ctxt.state.enabled_primitive_fixed_restart = true;
} else {
unsafe { ctxt.gl.Disable(gl::PRIMITIVE_RESTART_FIXED_INDEX); }
ctxt.state.enabled_primitive_fixed_restart = false;
}
}
} else if enabled {
return Err(DrawError::FixedIndexRestartingNotSupported);
}
Ok(())
}
fn set_flag_enabled(ctxt: &mut context::CommandContext<'_>, cap: gl::types::GLenum, enabled: bool) {
if enabled {
unsafe { ctxt.gl.Enable(cap); }
} else {
unsafe { ctxt.gl.Disable(cap); }
}
}
fn sync_polygon_offset(ctxt: &mut context::CommandContext<'_>, offset: PolygonOffset) {
let (factor, units) = ctxt.state.polygon_offset;
if ctxt.state.polygon_offset != (offset.factor, offset.units) {
unsafe {
ctxt.gl.PolygonOffset(offset.factor, offset.units);
}
ctxt.state.polygon_offset = (offset.factor, offset.units);
}
if offset.point != ctxt.state.enabled_polygon_offset_point {
ctxt.state.enabled_polygon_offset_point = offset.point;
set_flag_enabled(ctxt, gl::POLYGON_OFFSET_POINT, offset.point);
}
if offset.line != ctxt.state.enabled_polygon_offset_line {
ctxt.state.enabled_polygon_offset_line = offset.line;
set_flag_enabled(ctxt, gl::POLYGON_OFFSET_LINE, offset.line);
}
if offset.fill != ctxt.state.enabled_polygon_offset_fill {
ctxt.state.enabled_polygon_offset_fill = offset.fill;
set_flag_enabled(ctxt, gl::POLYGON_OFFSET_FILL, offset.fill);
}
}
fn sync_clip_control(ctxt: &mut context::CommandContext<'_>,
origin: ClipControlOrigin,
depth: ClipControlDepth)
-> Result<(), DrawError> {
let origin = match origin {
ClipControlOrigin::LowerLeft => gl::LOWER_LEFT,
ClipControlOrigin::UpperLeft => gl::UPPER_LEFT,
};
let depth = match depth {
ClipControlDepth::NegativeOneToOne => gl::NEGATIVE_ONE_TO_ONE,
ClipControlDepth::ZeroToOne => gl::ZERO_TO_ONE,
};
if ctxt.state.clip_control == (origin, depth) {
return Ok(());
}
if ctxt.version >= &Version(Api::Gl, 4, 5) || ctxt.extensions.gl_arb_clip_control {
unsafe { ctxt.gl.ClipControl(origin, depth); }
ctxt.state.clip_control = (origin, depth);
} else {
return Err(DrawError::ClipControlNotSupported);
}
Ok(())
}