Skip to main content

webgl/
webgl_limits.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use glow::{self as gl, Context as Gl, HasContext};
6use num_traits::AsPrimitive;
7use servo_canvas_traits::webgl::{GLLimits, WebGLVersion};
8type GLenum = u32;
9
10pub trait GLLimitsDetect {
11    fn detect(gl: &Gl, webgl_version: WebGLVersion) -> Self;
12}
13
14impl GLLimitsDetect for GLLimits {
15    fn detect(gl: &Gl, webgl_version: WebGLVersion) -> GLLimits {
16        let max_vertex_attribs = gl.get_integer(gl::MAX_VERTEX_ATTRIBS);
17        let max_tex_size = gl.get_integer(gl::MAX_TEXTURE_SIZE);
18        let max_3d_tex_size = gl.get_integer(gl::MAX_3D_TEXTURE_SIZE);
19        let max_cube_map_tex_size = gl.get_integer(gl::MAX_CUBE_MAP_TEXTURE_SIZE);
20        let max_combined_texture_image_units = gl.get_integer(gl::MAX_COMBINED_TEXTURE_IMAGE_UNITS);
21        let max_renderbuffer_size = gl.get_integer(gl::MAX_RENDERBUFFER_SIZE);
22        let max_texture_image_units = gl.get_integer(gl::MAX_TEXTURE_IMAGE_UNITS);
23        let max_vertex_texture_image_units = gl.get_integer(gl::MAX_VERTEX_TEXTURE_IMAGE_UNITS);
24
25        // TODO: better value for this?
26        let max_client_wait_timeout_webgl = std::time::Duration::new(1, 0);
27
28        // Based on:
29        // https://searchfox.org/mozilla-central/rev/5a744713370ec47969595e369fd5125f123e6d24/dom/canvas/WebGLContextValidate.cpp#523-558
30        let (
31            max_fragment_uniform_vectors,
32            max_varying_vectors,
33            max_vertex_uniform_vectors,
34            max_vertex_output_vectors,
35            max_fragment_input_vectors,
36        );
37        if gl.version().is_embedded {
38            max_fragment_uniform_vectors = gl.get_integer(gl::MAX_FRAGMENT_UNIFORM_VECTORS);
39            max_varying_vectors = gl.get_integer(gl::MAX_VARYING_VECTORS);
40            max_vertex_uniform_vectors = gl.get_integer(gl::MAX_VERTEX_UNIFORM_VECTORS);
41            max_vertex_output_vectors = gl
42                .try_get_integer(gl::MAX_VERTEX_OUTPUT_COMPONENTS)
43                .map(|c| c / 4)
44                .unwrap_or(max_varying_vectors);
45            max_fragment_input_vectors = gl
46                .try_get_integer(gl::MAX_FRAGMENT_INPUT_COMPONENTS)
47                .map(|c| c / 4)
48                .unwrap_or(max_vertex_output_vectors);
49        } else {
50            max_fragment_uniform_vectors = gl.get_integer(gl::MAX_FRAGMENT_UNIFORM_COMPONENTS) / 4;
51            max_vertex_uniform_vectors = gl.get_integer(gl::MAX_VERTEX_UNIFORM_COMPONENTS) / 4;
52
53            max_fragment_input_vectors = gl
54                .try_get_integer(gl::MAX_FRAGMENT_INPUT_COMPONENTS)
55                .or_else(|| gl.try_get_integer(gl::MAX_VARYING_COMPONENTS))
56                .map(|c| c / 4)
57                .unwrap_or_else(|| gl.get_integer(gl::MAX_VARYING_VECTORS));
58            max_vertex_output_vectors = gl
59                .try_get_integer(gl::MAX_VERTEX_OUTPUT_COMPONENTS)
60                .map(|c| c / 4)
61                .unwrap_or(max_fragment_input_vectors);
62            max_varying_vectors = max_vertex_output_vectors
63                .min(max_fragment_input_vectors)
64                .max(4);
65        };
66
67        let (
68            max_uniform_block_size,
69            max_uniform_buffer_bindings,
70            min_program_texel_offset,
71            max_program_texel_offset,
72            max_transform_feedback_separate_attribs,
73            max_draw_buffers,
74            max_color_attachments,
75            max_combined_uniform_blocks,
76            max_combined_vertex_uniform_components,
77            max_combined_fragment_uniform_components,
78            max_vertex_uniform_blocks,
79            max_vertex_uniform_components,
80            max_fragment_uniform_blocks,
81            max_fragment_uniform_components,
82            max_3d_texture_size,
83            max_array_texture_layers,
84            uniform_buffer_offset_alignment,
85            max_element_index,
86            max_elements_indices,
87            max_elements_vertices,
88            max_fragment_input_components,
89            max_samples,
90            max_server_wait_timeout,
91            max_texture_lod_bias,
92            max_varying_components,
93            max_vertex_output_components,
94        );
95        if webgl_version == WebGLVersion::WebGL2 {
96            max_uniform_block_size = gl.get_integer64(gl::MAX_UNIFORM_BLOCK_SIZE);
97            max_uniform_buffer_bindings = gl.get_integer(gl::MAX_UNIFORM_BUFFER_BINDINGS);
98            min_program_texel_offset = gl.get_signed_integer(gl::MIN_PROGRAM_TEXEL_OFFSET);
99            max_program_texel_offset = gl.get_integer(gl::MAX_PROGRAM_TEXEL_OFFSET);
100            max_transform_feedback_separate_attribs =
101                gl.get_integer(gl::MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS);
102            max_color_attachments = gl.get_integer(gl::MAX_COLOR_ATTACHMENTS);
103            max_draw_buffers = gl
104                .get_integer(gl::MAX_DRAW_BUFFERS)
105                .min(max_color_attachments);
106            max_combined_uniform_blocks = gl.get_integer(gl::MAX_COMBINED_UNIFORM_BLOCKS);
107            max_combined_vertex_uniform_components =
108                gl.get_integer64(gl::MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS);
109            max_combined_fragment_uniform_components =
110                gl.get_integer64(gl::MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS);
111            max_vertex_uniform_blocks = gl.get_integer(gl::MAX_VERTEX_UNIFORM_BLOCKS);
112            max_vertex_uniform_components = gl.get_integer(gl::MAX_VERTEX_UNIFORM_COMPONENTS);
113            max_fragment_uniform_blocks = gl.get_integer(gl::MAX_FRAGMENT_UNIFORM_BLOCKS);
114            max_fragment_uniform_components = gl.get_integer(gl::MAX_FRAGMENT_UNIFORM_COMPONENTS);
115            uniform_buffer_offset_alignment = gl.get_integer(gl::UNIFORM_BUFFER_OFFSET_ALIGNMENT);
116            max_3d_texture_size = gl.get_integer(gl::MAX_3D_TEXTURE_SIZE);
117            max_array_texture_layers = gl.get_integer(gl::MAX_ARRAY_TEXTURE_LAYERS);
118            max_element_index = gl
119                .try_get_integer64(gl::MAX_ELEMENT_INDEX)
120                .unwrap_or(u32::MAX as u64); // requires GL 4.3
121            max_elements_indices = gl.get_integer(gl::MAX_ELEMENTS_INDICES);
122            max_elements_vertices = gl.get_integer(gl::MAX_ELEMENTS_VERTICES);
123            max_fragment_input_components = gl.get_integer(gl::MAX_FRAGMENT_INPUT_COMPONENTS);
124            max_samples = gl.get_integer(gl::MAX_SAMPLES);
125            max_server_wait_timeout =
126                std::time::Duration::from_nanos(gl.get_integer64(gl::MAX_SERVER_WAIT_TIMEOUT));
127            max_texture_lod_bias = gl.get_float(gl::MAX_TEXTURE_LOD_BIAS);
128            max_varying_components = gl.try_get_integer(gl::MAX_VARYING_COMPONENTS).unwrap_or(
129                // macOS Core Profile is buggy. The spec says this value is 4 * MAX_VARYING_VECTORS.
130                max_varying_vectors * 4,
131            );
132            max_vertex_output_components = gl.get_integer(gl::MAX_VERTEX_OUTPUT_COMPONENTS);
133        } else {
134            max_uniform_block_size = 0;
135            max_uniform_buffer_bindings = 0;
136            min_program_texel_offset = 0;
137            max_program_texel_offset = 0;
138            max_transform_feedback_separate_attribs = 0;
139            max_color_attachments = 1;
140            max_draw_buffers = 1;
141            max_combined_uniform_blocks = 0;
142            max_combined_vertex_uniform_components = 0;
143            max_combined_fragment_uniform_components = 0;
144            max_vertex_uniform_blocks = 0;
145            max_vertex_uniform_components = 0;
146            max_fragment_uniform_blocks = 0;
147            max_fragment_uniform_components = 0;
148            uniform_buffer_offset_alignment = 0;
149            max_3d_texture_size = 0;
150            max_array_texture_layers = 0;
151            max_element_index = 0;
152            max_elements_indices = 0;
153            max_elements_vertices = 0;
154            max_fragment_input_components = 0;
155            max_samples = 0;
156            max_server_wait_timeout = std::time::Duration::default();
157            max_texture_lod_bias = 0.0;
158            max_varying_components = 0;
159            max_vertex_output_components = 0;
160        }
161
162        GLLimits {
163            max_vertex_attribs,
164            max_tex_size,
165            max_3d_tex_size,
166            max_cube_map_tex_size,
167            max_combined_texture_image_units,
168            max_fragment_uniform_vectors,
169            max_renderbuffer_size,
170            max_texture_image_units,
171            max_varying_vectors,
172            max_vertex_texture_image_units,
173            max_vertex_uniform_vectors,
174            max_client_wait_timeout_webgl,
175            max_transform_feedback_separate_attribs,
176            max_vertex_output_vectors,
177            max_fragment_input_vectors,
178            max_uniform_buffer_bindings,
179            min_program_texel_offset,
180            max_program_texel_offset,
181            max_color_attachments,
182            max_draw_buffers,
183            max_uniform_block_size,
184            max_combined_uniform_blocks,
185            max_combined_vertex_uniform_components,
186            max_combined_fragment_uniform_components,
187            max_vertex_uniform_blocks,
188            max_vertex_uniform_components,
189            max_fragment_uniform_blocks,
190            max_fragment_uniform_components,
191            max_3d_texture_size,
192            max_array_texture_layers,
193            uniform_buffer_offset_alignment,
194            max_element_index,
195            max_elements_indices,
196            max_elements_vertices,
197            max_fragment_input_components,
198            max_samples,
199            max_server_wait_timeout,
200            max_texture_lod_bias,
201            max_varying_components,
202            max_vertex_output_components,
203        }
204    }
205}
206
207trait GLExt {
208    fn try_get_integer(self, parameter: GLenum) -> Option<u32>;
209    fn try_get_integer64(self, parameter: GLenum) -> Option<u64>;
210    fn try_get_signed_integer(self, parameter: GLenum) -> Option<i32>;
211    fn try_get_float(self, parameter: GLenum) -> Option<f32>;
212    fn get_integer(self, parameter: GLenum) -> u32;
213    fn get_integer64(self, parameter: GLenum) -> u64;
214    fn get_signed_integer(self, parameter: GLenum) -> i32;
215    fn get_float(self, parameter: GLenum) -> f32;
216}
217
218#[expect(unsafe_code)]
219fn try_gl_parameter<T, U>(
220    gl: &Gl,
221    parameter: GLenum,
222    f: unsafe fn(&Gl, GLenum, &mut [T]),
223) -> Option<U>
224where
225    T: 'static + Default + Copy + AsPrimitive<U>,
226    U: 'static + Copy,
227{
228    let mut value = [T::default()];
229    unsafe {
230        f(gl, parameter, &mut value);
231    }
232    if unsafe { gl.get_error() } != gl::NO_ERROR {
233        None
234    } else {
235        Some(value[0].as_())
236    }
237}
238
239impl GLExt for &Gl {
240    fn try_get_integer(self, parameter: GLenum) -> Option<u32> {
241        try_gl_parameter::<i32, _>(self, parameter, Gl::get_parameter_i32_slice)
242    }
243
244    fn get_integer(self, parameter: GLenum) -> u32 {
245        self.try_get_integer(parameter).unwrap()
246    }
247
248    fn try_get_integer64(self, parameter: GLenum) -> Option<u64> {
249        try_gl_parameter::<i64, _>(self, parameter, Gl::get_parameter_i64_slice)
250    }
251
252    fn get_integer64(self, parameter: GLenum) -> u64 {
253        self.try_get_integer64(parameter).unwrap()
254    }
255
256    fn try_get_signed_integer(self, parameter: GLenum) -> Option<i32> {
257        try_gl_parameter::<i32, _>(self, parameter, Gl::get_parameter_i32_slice)
258    }
259
260    fn get_signed_integer(self, parameter: GLenum) -> i32 {
261        self.try_get_signed_integer(parameter).unwrap()
262    }
263
264    fn try_get_float(self, parameter: GLenum) -> Option<f32> {
265        try_gl_parameter::<f32, _>(self, parameter, Gl::get_parameter_f32_slice)
266    }
267
268    fn get_float(self, parameter: GLenum) -> f32 {
269        self.try_get_float(parameter).unwrap()
270    }
271}