servo/components/webgl/webgl_limits.rs
sagudev 955149b194
Split webgl into separate crate (#36718)
canvas crate was home for both webgl and 2dcanvas impl, but it makes
more sense to separate these two, by splitting webgl into standalone
crate (like we already have for webgpu).

Testing: Rust for fearless refactoring (but there are WPT tests just in
case)

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-04-30 05:27:44 +00:00

259 lines
11 KiB
Rust

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use canvas_traits::webgl::{GLLimits, WebGLVersion};
use glow::{self as gl, Context as Gl, HasContext};
type GLenum = u32;
pub trait GLLimitsDetect {
fn detect(gl: &Gl, webgl_version: WebGLVersion) -> Self;
}
impl GLLimitsDetect for GLLimits {
fn detect(gl: &Gl, webgl_version: WebGLVersion) -> GLLimits {
let max_vertex_attribs = gl.get_integer(gl::MAX_VERTEX_ATTRIBS);
let max_tex_size = gl.get_integer(gl::MAX_TEXTURE_SIZE);
let max_cube_map_tex_size = gl.get_integer(gl::MAX_CUBE_MAP_TEXTURE_SIZE);
let max_combined_texture_image_units = gl.get_integer(gl::MAX_COMBINED_TEXTURE_IMAGE_UNITS);
let max_renderbuffer_size = gl.get_integer(gl::MAX_RENDERBUFFER_SIZE);
let max_texture_image_units = gl.get_integer(gl::MAX_TEXTURE_IMAGE_UNITS);
let max_vertex_texture_image_units = gl.get_integer(gl::MAX_VERTEX_TEXTURE_IMAGE_UNITS);
// TODO: better value for this?
let max_client_wait_timeout_webgl = std::time::Duration::new(1, 0);
// Based on:
// https://searchfox.org/mozilla-central/rev/5a744713370ec47969595e369fd5125f123e6d24/dom/canvas/WebGLContextValidate.cpp#523-558
let (
max_fragment_uniform_vectors,
max_varying_vectors,
max_vertex_uniform_vectors,
max_vertex_output_vectors,
max_fragment_input_vectors,
);
if gl.version().is_embedded {
max_fragment_uniform_vectors = gl.get_integer(gl::MAX_FRAGMENT_UNIFORM_VECTORS);
max_varying_vectors = gl.get_integer(gl::MAX_VARYING_VECTORS);
max_vertex_uniform_vectors = gl.get_integer(gl::MAX_VERTEX_UNIFORM_VECTORS);
max_vertex_output_vectors = gl
.try_get_integer(gl::MAX_VERTEX_OUTPUT_COMPONENTS)
.map(|c| c / 4)
.unwrap_or(max_varying_vectors);
max_fragment_input_vectors = gl
.try_get_integer(gl::MAX_FRAGMENT_INPUT_COMPONENTS)
.map(|c| c / 4)
.unwrap_or(max_vertex_output_vectors);
} else {
max_fragment_uniform_vectors = gl.get_integer(gl::MAX_FRAGMENT_UNIFORM_COMPONENTS) / 4;
max_vertex_uniform_vectors = gl.get_integer(gl::MAX_VERTEX_UNIFORM_COMPONENTS) / 4;
max_fragment_input_vectors = gl
.try_get_integer(gl::MAX_FRAGMENT_INPUT_COMPONENTS)
.or_else(|| gl.try_get_integer(gl::MAX_VARYING_COMPONENTS))
.map(|c| c / 4)
.unwrap_or_else(|| gl.get_integer(gl::MAX_VARYING_VECTORS));
max_vertex_output_vectors = gl
.try_get_integer(gl::MAX_VERTEX_OUTPUT_COMPONENTS)
.map(|c| c / 4)
.unwrap_or(max_fragment_input_vectors);
max_varying_vectors = max_vertex_output_vectors
.min(max_fragment_input_vectors)
.max(4);
};
let (
max_uniform_block_size,
max_uniform_buffer_bindings,
min_program_texel_offset,
max_program_texel_offset,
max_transform_feedback_separate_attribs,
max_draw_buffers,
max_color_attachments,
max_combined_uniform_blocks,
max_combined_vertex_uniform_components,
max_combined_fragment_uniform_components,
max_vertex_uniform_blocks,
max_vertex_uniform_components,
max_fragment_uniform_blocks,
max_fragment_uniform_components,
max_3d_texture_size,
max_array_texture_layers,
uniform_buffer_offset_alignment,
max_element_index,
max_elements_indices,
max_elements_vertices,
max_fragment_input_components,
max_samples,
max_server_wait_timeout,
max_texture_lod_bias,
max_varying_components,
max_vertex_output_components,
);
if webgl_version == WebGLVersion::WebGL2 {
max_uniform_block_size = gl.get_integer64(gl::MAX_UNIFORM_BLOCK_SIZE);
max_uniform_buffer_bindings = gl.get_integer(gl::MAX_UNIFORM_BUFFER_BINDINGS);
min_program_texel_offset = gl.get_signed_integer(gl::MIN_PROGRAM_TEXEL_OFFSET);
max_program_texel_offset = gl.get_integer(gl::MAX_PROGRAM_TEXEL_OFFSET);
max_transform_feedback_separate_attribs =
gl.get_integer(gl::MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS);
max_color_attachments = gl.get_integer(gl::MAX_COLOR_ATTACHMENTS);
max_draw_buffers = gl
.get_integer(gl::MAX_DRAW_BUFFERS)
.min(max_color_attachments);
max_combined_uniform_blocks = gl.get_integer(gl::MAX_COMBINED_UNIFORM_BLOCKS);
max_combined_vertex_uniform_components =
gl.get_integer64(gl::MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS);
max_combined_fragment_uniform_components =
gl.get_integer64(gl::MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS);
max_vertex_uniform_blocks = gl.get_integer(gl::MAX_VERTEX_UNIFORM_BLOCKS);
max_vertex_uniform_components = gl.get_integer(gl::MAX_VERTEX_UNIFORM_COMPONENTS);
max_fragment_uniform_blocks = gl.get_integer(gl::MAX_FRAGMENT_UNIFORM_BLOCKS);
max_fragment_uniform_components = gl.get_integer(gl::MAX_FRAGMENT_UNIFORM_COMPONENTS);
uniform_buffer_offset_alignment = gl.get_integer(gl::UNIFORM_BUFFER_OFFSET_ALIGNMENT);
max_3d_texture_size = gl.get_integer(gl::MAX_3D_TEXTURE_SIZE);
max_array_texture_layers = gl.get_integer(gl::MAX_ARRAY_TEXTURE_LAYERS);
max_element_index = gl
.try_get_integer64(gl::MAX_ELEMENT_INDEX)
.unwrap_or(u32::MAX as u64); // requires GL 4.3
max_elements_indices = gl.get_integer(gl::MAX_ELEMENTS_INDICES);
max_elements_vertices = gl.get_integer(gl::MAX_ELEMENTS_VERTICES);
max_fragment_input_components = gl.get_integer(gl::MAX_FRAGMENT_INPUT_COMPONENTS);
max_samples = gl.get_integer(gl::MAX_SAMPLES);
max_server_wait_timeout =
std::time::Duration::from_nanos(gl.get_integer64(gl::MAX_SERVER_WAIT_TIMEOUT));
max_texture_lod_bias = gl.get_float(gl::MAX_TEXTURE_LOD_BIAS);
max_varying_components = gl.try_get_integer(gl::MAX_VARYING_COMPONENTS).unwrap_or(
// macOS Core Profile is buggy. The spec says this value is 4 * MAX_VARYING_VECTORS.
max_varying_vectors * 4,
);
max_vertex_output_components = gl.get_integer(gl::MAX_VERTEX_OUTPUT_COMPONENTS);
} else {
max_uniform_block_size = 0;
max_uniform_buffer_bindings = 0;
min_program_texel_offset = 0;
max_program_texel_offset = 0;
max_transform_feedback_separate_attribs = 0;
max_color_attachments = 1;
max_draw_buffers = 1;
max_combined_uniform_blocks = 0;
max_combined_vertex_uniform_components = 0;
max_combined_fragment_uniform_components = 0;
max_vertex_uniform_blocks = 0;
max_vertex_uniform_components = 0;
max_fragment_uniform_blocks = 0;
max_fragment_uniform_components = 0;
uniform_buffer_offset_alignment = 0;
max_3d_texture_size = 0;
max_array_texture_layers = 0;
max_element_index = 0;
max_elements_indices = 0;
max_elements_vertices = 0;
max_fragment_input_components = 0;
max_samples = 0;
max_server_wait_timeout = std::time::Duration::default();
max_texture_lod_bias = 0.0;
max_varying_components = 0;
max_vertex_output_components = 0;
}
GLLimits {
max_vertex_attribs,
max_tex_size,
max_cube_map_tex_size,
max_combined_texture_image_units,
max_fragment_uniform_vectors,
max_renderbuffer_size,
max_texture_image_units,
max_varying_vectors,
max_vertex_texture_image_units,
max_vertex_uniform_vectors,
max_client_wait_timeout_webgl,
max_transform_feedback_separate_attribs,
max_vertex_output_vectors,
max_fragment_input_vectors,
max_uniform_buffer_bindings,
min_program_texel_offset,
max_program_texel_offset,
max_color_attachments,
max_draw_buffers,
max_uniform_block_size,
max_combined_uniform_blocks,
max_combined_vertex_uniform_components,
max_combined_fragment_uniform_components,
max_vertex_uniform_blocks,
max_vertex_uniform_components,
max_fragment_uniform_blocks,
max_fragment_uniform_components,
max_3d_texture_size,
max_array_texture_layers,
uniform_buffer_offset_alignment,
max_element_index,
max_elements_indices,
max_elements_vertices,
max_fragment_input_components,
max_samples,
max_server_wait_timeout,
max_texture_lod_bias,
max_varying_components,
max_vertex_output_components,
}
}
}
trait GLExt {
fn try_get_integer(self, parameter: GLenum) -> Option<u32>;
fn try_get_integer64(self, parameter: GLenum) -> Option<u64>;
fn try_get_signed_integer(self, parameter: GLenum) -> Option<i32>;
fn try_get_float(self, parameter: GLenum) -> Option<f32>;
fn get_integer(self, parameter: GLenum) -> u32;
fn get_integer64(self, parameter: GLenum) -> u64;
fn get_signed_integer(self, parameter: GLenum) -> i32;
fn get_float(self, parameter: GLenum) -> f32;
}
macro_rules! create_fun {
($tryer:ident, $getter:ident, $gltype:ty, $glcall:ident, $rstype:ty) => {
#[allow(unsafe_code)]
fn $tryer(self, parameter: GLenum) -> Option<$rstype> {
let mut value = [<$gltype>::default()];
unsafe {
self.$glcall(parameter, &mut value);
}
if unsafe { self.get_error() } != gl::NO_ERROR {
None
} else {
Some(value[0] as $rstype)
}
}
fn $getter(self, parameter: GLenum) -> $rstype {
self.$tryer(parameter).unwrap()
}
};
}
impl GLExt for &Gl {
create_fun!(
try_get_integer,
get_integer,
i32,
get_parameter_i32_slice,
u32
);
create_fun!(
try_get_integer64,
get_integer64,
i64,
get_parameter_i64_slice,
u64
);
create_fun!(
try_get_signed_integer,
get_signed_integer,
i32,
get_parameter_i32_slice,
i32
);
create_fun!(try_get_float, get_float, f32, get_parameter_f32_slice, f32);
}