1use 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 let max_client_wait_timeout_webgl = std::time::Duration::new(1, 0);
27
28 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); 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 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}