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