three_d/core/
context.rs

1use super::*;
2use std::collections::HashMap;
3use std::sync::Arc;
4use std::sync::RwLock;
5
6#[doc(hidden)]
7pub use crate::context::HasContext;
8
9///
10/// Contains the low-level OpenGL/WebGL graphics context as well as other "global" variables.
11/// Implements Deref with the low-level graphics context as target, so you can call low-level functionality
12/// directly on this struct. Use the [context](crate::context) module to get access to low-level constants and structs.
13///
14#[derive(Clone)]
15pub struct Context {
16    context: Arc<crate::context::Context>,
17    pub(super) vao: crate::context::VertexArray,
18    /// A cache of programs to avoid recompiling a [Program] every frame.
19    pub programs: Arc<RwLock<HashMap<Vec<u8>, Program>>>,
20}
21
22impl Context {
23    ///
24    /// Creates a new mid-level context, used in this [core](crate::core) module, from a low-level OpenGL/WebGL context from the [context](crate::context) module.
25    /// This should only be called directly if you are creating a low-level context yourself (ie. not using the features in the [window](crate::window) module).
26    /// Since the content in the [context](crate::context) module is just a re-export of [glow](https://crates.io/crates/glow),
27    /// you can also call this method with a reference counter to a glow context created using glow and not the re-export in [context](crate::context).
28    ///
29    pub fn from_gl_context(context: Arc<crate::context::Context>) -> Result<Self, CoreError> {
30        unsafe {
31            if !context.version().is_embedded {
32                // Enable seamless cube map textures - not available on OpenGL ES and WebGL
33                context.enable(crate::context::TEXTURE_CUBE_MAP_SEAMLESS);
34            }
35            context.pixel_store_i32(crate::context::UNPACK_ALIGNMENT, 1);
36            context.pixel_store_i32(crate::context::PACK_ALIGNMENT, 1);
37        };
38        let c = unsafe {
39            // Create one Vertex Array Object which is then reused all the time.
40            let vao = context
41                .create_vertex_array()
42                .map_err(CoreError::ContextCreation)?;
43            Self {
44                context,
45                vao,
46                programs: Arc::new(RwLock::new(HashMap::new())),
47            }
48        };
49        Ok(c)
50    }
51
52    ///
53    /// Set the scissor test for this context (see [ScissorBox]).
54    ///
55    pub fn set_scissor(&self, scissor_box: ScissorBox) {
56        unsafe {
57            if scissor_box.width > 0 && scissor_box.height > 0 {
58                self.enable(crate::context::SCISSOR_TEST);
59                self.scissor(
60                    scissor_box.x,
61                    scissor_box.y,
62                    scissor_box.width as i32,
63                    scissor_box.height as i32,
64                );
65            } else {
66                self.disable(crate::context::SCISSOR_TEST);
67            }
68        }
69    }
70
71    ///
72    /// Set the viewport for this context (See [Viewport]).
73    ///
74    pub fn set_viewport(&self, viewport: Viewport) {
75        unsafe {
76            self.viewport(
77                viewport.x,
78                viewport.y,
79                viewport.width as i32,
80                viewport.height as i32,
81            );
82        }
83    }
84
85    ///
86    /// Set the face culling for this context (see [Cull]).
87    ///
88    pub fn set_cull(&self, cull: Cull) {
89        unsafe {
90            match cull {
91                Cull::None => {
92                    self.disable(crate::context::CULL_FACE);
93                }
94                Cull::Back => {
95                    self.enable(crate::context::CULL_FACE);
96                    self.cull_face(crate::context::BACK);
97                }
98                Cull::Front => {
99                    self.enable(crate::context::CULL_FACE);
100                    self.cull_face(crate::context::FRONT);
101                }
102                Cull::FrontAndBack => {
103                    self.enable(crate::context::CULL_FACE);
104                    self.cull_face(crate::context::FRONT_AND_BACK);
105                }
106            }
107        }
108    }
109
110    ///
111    /// Set the write mask for this context (see [WriteMask]).
112    ///
113    pub fn set_write_mask(&self, write_mask: WriteMask) {
114        unsafe {
115            self.color_mask(
116                write_mask.red,
117                write_mask.green,
118                write_mask.blue,
119                write_mask.alpha,
120            );
121            self.depth_mask(write_mask.depth);
122        }
123    }
124
125    ///
126    /// Set the depth test for this context (see [DepthTest]).
127    ///
128    pub fn set_depth_test(&self, depth_test: DepthTest) {
129        unsafe {
130            self.enable(crate::context::DEPTH_TEST);
131            match depth_test {
132                DepthTest::Never => {
133                    self.depth_func(crate::context::NEVER);
134                }
135                DepthTest::Less => {
136                    self.depth_func(crate::context::LESS);
137                }
138                DepthTest::Equal => {
139                    self.depth_func(crate::context::EQUAL);
140                }
141                DepthTest::LessOrEqual => {
142                    self.depth_func(crate::context::LEQUAL);
143                }
144                DepthTest::Greater => {
145                    self.depth_func(crate::context::GREATER);
146                }
147                DepthTest::NotEqual => {
148                    self.depth_func(crate::context::NOTEQUAL);
149                }
150                DepthTest::GreaterOrEqual => {
151                    self.depth_func(crate::context::GEQUAL);
152                }
153                DepthTest::Always => {
154                    self.depth_func(crate::context::ALWAYS);
155                }
156            }
157        }
158    }
159
160    ///
161    /// Set the blend state for this context (see [Blend]).
162    ///
163    pub fn set_blend(&self, blend: Blend) {
164        unsafe {
165            if let Blend::Enabled {
166                source_rgb_multiplier,
167                source_alpha_multiplier,
168                destination_rgb_multiplier,
169                destination_alpha_multiplier,
170                rgb_equation,
171                alpha_equation,
172            } = blend
173            {
174                self.enable(crate::context::BLEND);
175                self.blend_func_separate(
176                    Self::blend_const_from_multiplier(source_rgb_multiplier),
177                    Self::blend_const_from_multiplier(destination_rgb_multiplier),
178                    Self::blend_const_from_multiplier(source_alpha_multiplier),
179                    Self::blend_const_from_multiplier(destination_alpha_multiplier),
180                );
181                self.blend_equation_separate(
182                    Self::blend_const_from_equation(rgb_equation),
183                    Self::blend_const_from_equation(alpha_equation),
184                );
185            } else {
186                self.disable(crate::context::BLEND);
187            }
188        }
189    }
190
191    fn blend_const_from_multiplier(multiplier: BlendMultiplierType) -> u32 {
192        match multiplier {
193            BlendMultiplierType::Zero => crate::context::ZERO,
194            BlendMultiplierType::One => crate::context::ONE,
195            BlendMultiplierType::SrcColor => crate::context::SRC_COLOR,
196            BlendMultiplierType::OneMinusSrcColor => crate::context::ONE_MINUS_SRC_COLOR,
197            BlendMultiplierType::DstColor => crate::context::DST_COLOR,
198            BlendMultiplierType::OneMinusDstColor => crate::context::ONE_MINUS_DST_COLOR,
199            BlendMultiplierType::SrcAlpha => crate::context::SRC_ALPHA,
200            BlendMultiplierType::OneMinusSrcAlpha => crate::context::ONE_MINUS_SRC_ALPHA,
201            BlendMultiplierType::DstAlpha => crate::context::DST_ALPHA,
202            BlendMultiplierType::OneMinusDstAlpha => crate::context::ONE_MINUS_DST_ALPHA,
203            BlendMultiplierType::SrcAlphaSaturate => crate::context::SRC_ALPHA_SATURATE,
204        }
205    }
206    fn blend_const_from_equation(equation: BlendEquationType) -> u32 {
207        match equation {
208            BlendEquationType::Add => crate::context::FUNC_ADD,
209            BlendEquationType::Subtract => crate::context::FUNC_SUBTRACT,
210            BlendEquationType::ReverseSubtract => crate::context::FUNC_REVERSE_SUBTRACT,
211            BlendEquationType::Min => crate::context::MIN,
212            BlendEquationType::Max => crate::context::MAX,
213        }
214    }
215
216    ///
217    /// Set the render states for this context (see [RenderStates]).
218    ///
219    pub fn set_render_states(&self, render_states: RenderStates) {
220        self.set_cull(render_states.cull);
221        self.set_write_mask(render_states.write_mask);
222        if !render_states.write_mask.depth && render_states.depth_test == DepthTest::Always {
223            unsafe { self.disable(crate::context::DEPTH_TEST) }
224        } else {
225            self.set_depth_test(render_states.depth_test);
226        }
227        self.set_blend(render_states.blend);
228    }
229
230    ///
231    /// Returns an error if an GPU-side error has happened while rendering which can be used to check for errors while developing.
232    /// Can also be used in production to handle unexpected rendering errors, but do not call it too often to avoid performance problems.
233    ///
234    pub fn error_check(&self) -> Result<(), CoreError> {
235        self.framebuffer_check()?;
236        unsafe {
237            let e = self.get_error();
238            if e != crate::context::NO_ERROR {
239                Err(CoreError::ContextError(
240                    match e {
241                        crate::context::INVALID_ENUM => "Invalid enum",
242                        crate::context::INVALID_VALUE => "Invalid value",
243                        crate::context::INVALID_OPERATION => "Invalid operation",
244                        crate::context::INVALID_FRAMEBUFFER_OPERATION => {
245                            "Invalid framebuffer operation"
246                        }
247                        crate::context::OUT_OF_MEMORY => "Out of memory",
248                        crate::context::STACK_OVERFLOW => "Stack overflow",
249                        crate::context::STACK_UNDERFLOW => "Stack underflow",
250                        _ => "Unknown",
251                    }
252                    .to_string(),
253                ))?;
254            }
255        }
256        Ok(())
257    }
258
259    fn framebuffer_check(&self) -> Result<(), CoreError> {
260        unsafe {
261            match self.check_framebuffer_status(crate::context::FRAMEBUFFER) {
262                crate::context::FRAMEBUFFER_COMPLETE => Ok(()),
263                crate::context::FRAMEBUFFER_INCOMPLETE_ATTACHMENT => Err(CoreError::ContextError(
264                    "FRAMEBUFFER_INCOMPLETE_ATTACHMENT".to_string(),
265                )),
266                crate::context::FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER => Err(CoreError::ContextError(
267                    "FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER".to_string(),
268                )),
269                crate::context::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT => {
270                    Err(CoreError::ContextError(
271                        "FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT".to_string(),
272                    ))
273                }
274                crate::context::FRAMEBUFFER_UNSUPPORTED => Err(CoreError::ContextError(
275                    "FRAMEBUFFER_UNSUPPORTED".to_string(),
276                )),
277                crate::context::FRAMEBUFFER_UNDEFINED => {
278                    Err(CoreError::ContextError("FRAMEBUFFER_UNDEFINED".to_string()))
279                }
280                crate::context::FRAMEBUFFER_INCOMPLETE_READ_BUFFER => Err(CoreError::ContextError(
281                    "FRAMEBUFFER_INCOMPLETE_READ_BUFFER".to_string(),
282                )),
283                crate::context::FRAMEBUFFER_INCOMPLETE_MULTISAMPLE => Err(CoreError::ContextError(
284                    "FRAMEBUFFER_INCOMPLETE_MULTISAMPLE".to_string(),
285                )),
286                crate::context::FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS => Err(
287                    CoreError::ContextError("FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS".to_string()),
288                ),
289                _ => Err(CoreError::ContextError(
290                    "Unknown framebuffer error".to_string(),
291                )),
292            }?;
293        }
294        Ok(())
295    }
296}
297
298impl std::fmt::Debug for Context {
299    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
300        let mut d = f.debug_struct("Context");
301        d.field("programs", &self.programs.read().unwrap().len());
302        d.finish()
303    }
304}
305
306impl std::ops::Deref for Context {
307    type Target = Arc<crate::context::Context>;
308    fn deref(&self) -> &Self::Target {
309        &self.context
310    }
311}