use serde::{Deserialize, Serialize};
use tracing::debug;
use crate::server::{
dtos::{
browser_profile_dto::{BrowserProfileFullData, Mode, WebGL2Maximum},
start_dto::StartRequest,
},
error::Error,
};
use super::{FromStartRequest, Navigator, Screen};
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub(super) struct WebGL2ExtraParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub(super) uniform_buffer_offset_alignment: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(super) max_elements_vertices: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(super) max_elements_indices: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(super) max_draw_buffers: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(super) min_program_texel_offset: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(super) max_program_texel_offset: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(super) max_color_attachments: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(super) max_vertex_texture_image_units: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(super) max_texture_image_units: Option<u64>,
pub(super) max_3d_texture_size: u64,
pub(super) max_texture_lod_bias: f64,
pub(super) max_fragment_uniform_components: u64,
pub(super) max_vertex_uniform_components: u64,
pub(super) max_array_texture_layers: u64,
pub(super) max_varying_components: u64,
pub(super) max_transform_feedback_separate_components: u64,
pub(super) max_transform_feedback_interleaved_components: u64,
pub(super) max_samples: u64,
pub(super) max_vertex_uniform_blocks: u64,
pub(super) max_fragment_uniform_blocks: u64,
pub(super) max_combined_uniform_blocks: u64,
pub(super) max_uniform_buffer_bindings: u64,
pub(super) max_uniform_block_size: u64,
pub(super) max_combined_vertex_uniform_components: u64,
pub(super) max_combined_fragment_uniform_components: u64,
pub(super) max_vertex_output_components: u64,
pub(super) max_fragment_input_components: u64,
pub(super) max_element_index: u64,
pub(super) max_texture_size: u64,
pub(super) max_vertex_attribs: u64,
pub(super) max_vertex_uniform_vectors: u64,
pub(super) max_varying_vectors: u64,
pub(super) max_combined_texture_image_units: u64,
pub(super) max_fragment_uniform_vectors: u64,
pub(super) max_cube_map_texture_size: u64,
pub(super) max_renderbuffer_size: u64,
}
impl From<&WebGL2Maximum> for Option<WebGL2ExtraParams> {
fn from(maximum: &WebGL2Maximum) -> Self {
macro_rules! check {
($value:expr) => {
if $value < 0 {
return None;
} else {
$value as u64
}
};
}
macro_rules! optional {
($value:expr) => {
if $value < 0 {
None
} else {
Some($value as u64)
}
};
}
let max_texture_lod_bias = if maximum.max_texture_lod_bias > 0. {
maximum.max_texture_lod_bias
} else {
return None;
};
let uniform_buffer_offset_alignment =
optional!(maximum.uniform_buffer_offset_alignment);
let max_elements_vertices = optional!(maximum.max_elements_vertices);
let max_elements_indices = optional!(maximum.max_elements_indices);
let max_draw_buffers = optional!(maximum.max_draw_buffers);
let min_program_texel_offset = optional!(maximum.min_program_texel_offset);
let max_program_texel_offset = optional!(maximum.max_program_texel_offset);
let max_color_attachments = optional!(maximum.max_color_attachments);
let max_vertex_texture_image_units =
optional!(maximum.max_vertex_texture_image_units);
let max_texture_image_units = optional!(maximum.max_texture_image_units);
let max_3d_texture_size = check!(maximum.max_3d_texture_size);
let max_fragment_uniform_components =
check!(maximum.max_fragment_uniform_components);
let max_vertex_uniform_components =
check!(maximum.max_vertex_uniform_components);
let max_array_texture_layers = check!(maximum.max_array_texture_layers);
let max_varying_components = check!(maximum.max_varying_components);
let max_transform_feedback_separate_components =
check!(maximum.max_transform_feedback_separate_components);
let max_transform_feedback_interleaved_components =
check!(maximum.max_transform_feedback_interleaved_components);
let max_samples = check!(maximum.max_samples);
let max_vertex_uniform_blocks = check!(maximum.max_vertex_uniform_blocks);
let max_fragment_uniform_blocks =
check!(maximum.max_fragment_uniform_blocks);
let max_combined_uniform_blocks =
check!(maximum.max_combined_uniform_blocks);
let max_uniform_buffer_bindings =
check!(maximum.max_uniform_buffer_bindings);
let max_uniform_block_size = check!(maximum.max_uniform_block_size);
let max_combined_vertex_uniform_components =
check!(maximum.max_combined_vertex_uniform_components);
let max_combined_fragment_uniform_components =
check!(maximum.max_combined_fragment_uniform_components);
let max_vertex_output_components =
check!(maximum.max_vertex_output_components);
let max_fragment_input_components =
check!(maximum.max_fragment_input_components);
let max_element_index = check!(maximum.max_element_index);
let max_texture_size = check!(maximum.max_texture_size);
let max_vertex_attribs = check!(maximum.max_vertex_attribs);
let max_vertex_uniform_vectors = check!(maximum.max_vertex_uniform_vectors);
let max_varying_vectors = check!(maximum.max_varying_vectors);
let max_combined_texture_image_units =
check!(maximum.max_combined_texture_image_units);
let max_fragment_uniform_vectors =
check!(maximum.max_fragment_uniform_vectors);
let max_cube_map_texture_size = check!(maximum.max_cube_map_texture_size);
let max_renderbuffer_size = check!(maximum.max_renderbuffer_size);
let res = WebGL2ExtraParams {
uniform_buffer_offset_alignment,
max_elements_vertices,
max_elements_indices,
max_draw_buffers,
min_program_texel_offset,
max_program_texel_offset,
max_color_attachments,
max_vertex_texture_image_units,
max_texture_image_units,
max_3d_texture_size,
max_texture_lod_bias,
max_fragment_uniform_components,
max_vertex_uniform_components,
max_array_texture_layers,
max_varying_components,
max_transform_feedback_separate_components,
max_transform_feedback_interleaved_components,
max_samples,
max_vertex_uniform_blocks,
max_fragment_uniform_blocks,
max_combined_uniform_blocks,
max_uniform_buffer_bindings,
max_uniform_block_size,
max_combined_vertex_uniform_components,
max_combined_fragment_uniform_components,
max_vertex_output_components,
max_fragment_input_components,
max_element_index,
max_texture_size,
max_vertex_attribs,
max_vertex_uniform_vectors,
max_varying_vectors,
max_combined_texture_image_units,
max_fragment_uniform_vectors,
max_cube_map_texture_size,
max_renderbuffer_size,
};
Some(res)
}
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct WebGL {
pub(super) unmasked_vendor: String,
pub(super) unmasked_renderer: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub(super) extra_params: Option<WebGL2ExtraParams>,
}
impl FromStartRequest<WebGL> for WebGL {
fn from_start_request(
bp: &BrowserProfileFullData,
_request: &StartRequest,
_navigator: &Navigator,
_screen: &Screen,
_token: &str,
) -> Result<Self, Error> {
let mut unmasked_vendor = "".to_string();
let mut unmasked_renderer = "".to_string();
if bp.webgl_info.mode == Mode::Manual {
unmasked_vendor = bp.webgl_info.vendor.clone().unwrap_or_default();
unmasked_renderer = bp.webgl_info.renderer.clone().unwrap_or_default();
}
let extra_params = match (bp.webgl.mode, &bp.webgl2_maximum) {
(Mode::Real, _) => None, (_, Some(extra)) => Option::<WebGL2ExtraParams>::from(extra), (_, _) => None, };
Ok(Self {
unmasked_vendor,
unmasked_renderer,
extra_params,
})
}
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub(super) struct WebGPULimits {
pub(super) max_bind_groups: u32,
pub(super) max_bindings_per_bind_group: u32,
pub(super) max_buffer_size: u64,
pub(super) max_color_attachments_bytes_per_sample: u32,
pub(super) max_color_attachments: u32,
pub(super) max_compute_invocations_per_workgroup: u32,
pub(super) max_compute_workgroup_size_x: u32,
pub(super) max_compute_workgroup_size_y: u32,
pub(super) max_compute_workgroup_size_z: u32,
pub(super) max_compute_workgroup_storage_size: u32,
pub(super) max_compute_workgroups_per_dimension: u32,
pub(super) max_dynamic_storage_buffers_per_pipeline_layout: u32,
pub(super) max_dynamic_uniform_buffers_per_pipeline_layout: u32,
pub(super) max_inter_stage_shader_components: u32,
pub(super) max_inter_stage_shader_variables: u32,
pub(super) max_sampled_textures_per_shader_stage: u32,
pub(super) max_samplers_per_shader_stage: u32,
pub(super) max_storage_buffer_binding_size: u64,
pub(super) max_storage_buffers_per_shader_stage: u32,
pub(super) max_storage_textures_per_shader_stage: u32,
pub(super) max_texture_array_layers: u32,
pub(super) max_texture_dimension_1d: u32,
pub(super) max_texture_dimension_2d: u32,
pub(super) max_texture_dimension_3d: u32,
pub(super) max_uniform_buffer_binding_size: u64,
pub(super) max_uniform_buffers_per_shader_stage: u32,
pub(super) max_vertex_attributes: u32,
pub(super) max_vertex_buffer_array_stride: u32,
pub(super) max_vertex_buffers: u32,
pub(super) min_storage_buffer_offset_alignment: u32,
pub(super) min_uniform_buffer_offset_alignment: u32,
}
impl From<&crate::server::dtos::browser_profile_dto::Limits> for WebGPULimits {
fn from(
limits_dto: &crate::server::dtos::browser_profile_dto::Limits,
) -> Self {
Self {
max_bind_groups: limits_dto.max_bind_groups,
max_bindings_per_bind_group: limits_dto.max_bindings_per_bind_group,
max_buffer_size: limits_dto.max_buffer_size,
max_color_attachments_bytes_per_sample: limits_dto
.max_color_attachment_bytes_per_sample,
max_color_attachments: limits_dto.max_color_attachments,
max_compute_invocations_per_workgroup: limits_dto
.max_compute_invocations_per_workgroup,
max_compute_workgroup_size_x: limits_dto.max_compute_workgroup_size_x,
max_compute_workgroup_size_y: limits_dto.max_compute_workgroup_size_y,
max_compute_workgroup_size_z: limits_dto.max_compute_workgroup_size_z,
max_compute_workgroup_storage_size: limits_dto
.max_compute_workgroup_storage_size,
max_compute_workgroups_per_dimension: limits_dto
.max_compute_workgroups_per_dimension,
max_dynamic_storage_buffers_per_pipeline_layout: limits_dto
.max_dynamic_storage_buffers_per_pipeline_layout,
max_dynamic_uniform_buffers_per_pipeline_layout: limits_dto
.max_dynamic_uniform_buffers_per_pipeline_layout,
max_inter_stage_shader_components: limits_dto
.max_inter_stage_shader_components,
max_inter_stage_shader_variables: limits_dto
.max_inter_stage_shader_variables,
max_sampled_textures_per_shader_stage: limits_dto
.max_sampled_textures_per_shader_stage,
max_samplers_per_shader_stage: limits_dto.max_samplers_per_shader_stage,
max_storage_buffer_binding_size: limits_dto
.max_storage_buffer_binding_size,
max_storage_buffers_per_shader_stage: limits_dto
.max_storage_buffers_per_shader_stage,
max_storage_textures_per_shader_stage: limits_dto
.max_storage_textures_per_shader_stage,
max_texture_array_layers: limits_dto.max_texture_array_layers,
max_texture_dimension_1d: limits_dto.max_texture_dimension_1d,
max_texture_dimension_2d: limits_dto.max_texture_dimension_2d,
max_texture_dimension_3d: limits_dto.max_texture_dimension_3d,
max_uniform_buffer_binding_size: limits_dto
.max_uniform_buffer_binding_size,
max_uniform_buffers_per_shader_stage: limits_dto
.max_uniform_buffers_per_shader_stage,
max_vertex_attributes: limits_dto.max_vertex_attributes,
max_vertex_buffer_array_stride: limits_dto.max_vertex_buffer_array_stride,
max_vertex_buffers: limits_dto.max_vertex_buffers,
min_storage_buffer_offset_alignment: limits_dto
.min_storage_buffer_offset_alignment,
min_uniform_buffer_offset_alignment: limits_dto
.min_uniform_buffer_offset_alignment,
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct WebGPU {
pub(super) vendor: String,
pub(super) architecture: String,
pub(super) device: String,
pub(super) description: String,
pub(super) driver: String,
pub(super) limits: WebGPULimits,
}
impl FromStartRequest<Option<WebGPU>> for Option<WebGPU> {
fn from_start_request(
bp: &BrowserProfileFullData,
_request: &StartRequest,
_navigator: &Navigator,
_screen: &Screen,
_token: &str,
) -> Result<Self, Error> {
if bp.webgpu.mode != Mode::Manual {
return Ok(None);
}
let (info, limits) = match (
bp.webgpu.info.clone(),
bp.webgpu.limits.clone(),
bp.webgpu.get_preferred_canvas_format.clone(),
) {
(Some(info), Some(limits), Some(_)) => {
debug!("WebGPU is in manual mode, but parameters are not set. Setting it to Off");
(info, limits)
}
_ => return Ok(None),
};
Ok(Some(WebGPU {
vendor: info.vendor.clone(),
architecture: info.architecture.clone(),
device: info.device.clone(),
description: info.description.clone(),
driver: String::new(), limits: (&limits).into(),
}))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::server::dtos::browser_profile_dto::{
Info, WebGPUInfo, Webgl, WebglInfo,
};
use crate::server::services::browser_profile_services::config::consts::LINUX_MOCK_PROFILE;
fn create_base_profile() -> BrowserProfileFullData {
BrowserProfileFullData {
webgl_info: WebglInfo {
mode: Mode::Auto,
vendor: None,
renderer: None,
},
webgl: Webgl {
mode: Mode::Auto,
noise: None,
},
webgl2_maximum: None,
webgpu: WebGPUInfo {
mode: Mode::Auto,
info: None,
limits: None,
get_preferred_canvas_format: None,
},
..LINUX_MOCK_PROFILE.clone()
}
}
fn get_mock_webgl2_maximum() -> WebGL2Maximum {
WebGL2Maximum {
uniform_buffer_offset_alignment: 256,
max_elements_vertices: 1000000,
max_elements_indices: 1000000,
max_texture_size: 16384,
max_3d_texture_size: 2048,
max_texture_lod_bias: 2.0,
max_fragment_uniform_components: 4096,
max_vertex_uniform_components: 4096,
max_array_texture_layers: 2048,
max_varying_components: 128,
max_transform_feedback_separate_components: 4,
max_transform_feedback_interleaved_components: 64,
max_samples: 8,
max_vertex_uniform_blocks: 16,
max_fragment_uniform_blocks: 16,
max_combined_uniform_blocks: 32,
max_uniform_buffer_bindings: 32,
max_uniform_block_size: 65536,
max_combined_vertex_uniform_components: 266240,
max_combined_fragment_uniform_components: 266240,
max_vertex_output_components: 128,
max_fragment_input_components: 128,
max_element_index: 4294967295,
max_vertex_attribs: 16,
max_vertex_uniform_vectors: 1024,
max_varying_vectors: 32,
max_combined_texture_image_units: 32,
max_fragment_uniform_vectors: 1024,
max_cube_map_texture_size: 16384,
max_renderbuffer_size: 16384,
max_draw_buffers: 8,
min_program_texel_offset: -8,
max_program_texel_offset: 7,
max_color_attachments: 8,
max_vertex_texture_image_units: 16,
max_texture_image_units: 16,
max_viewport_dims: [16384, 16384],
max_transform_feedback_separate_attribs: 4,
}
}
#[test]
fn test_webgl2_extra_params_conversion() {
let maximum = get_mock_webgl2_maximum();
let params = Option::<WebGL2ExtraParams>::from(&maximum);
assert!(params.is_some());
let params = params.unwrap();
assert_eq!(params.uniform_buffer_offset_alignment, Some(256));
assert_eq!(params.max_elements_vertices, Some(1000000));
assert_eq!(params.max_texture_size, 16384);
assert_eq!(params.max_3d_texture_size, 2048);
assert_eq!(params.max_texture_lod_bias, 2.0);
}
#[test]
fn test_webgl2_extra_params_invalid_values() {
let maximum = WebGL2Maximum {
max_texture_lod_bias: -1.0, ..get_mock_webgl2_maximum()
};
let params = Option::<WebGL2ExtraParams>::from(&maximum);
assert!(params.is_none());
}
#[test]
fn test_webgl_manual_mode() -> Result<(), Error> {
let mut profile = create_base_profile();
profile.webgl_info.mode = Mode::Manual;
profile.webgl_info.vendor = Some("Test Vendor".to_string());
profile.webgl_info.renderer = Some("Test Renderer".to_string());
let request = StartRequest::get_mock();
let navigator = Navigator::default();
let screen = Screen::default();
let token = String::new();
let result = WebGL::from_start_request(
&profile, &request, &navigator, &screen, &token,
)?;
assert_eq!(result.unmasked_vendor, "Test Vendor");
assert_eq!(result.unmasked_renderer, "Test Renderer");
assert!(result.extra_params.is_none());
Ok(())
}
#[test]
fn test_webgl_with_webgl2_params() -> Result<(), Error> {
let mut profile = create_base_profile();
profile.webgl.mode = Mode::Manual; profile.webgl2_maximum = Some(WebGL2Maximum {
max_texture_size: 16384,
max_texture_lod_bias: 2.0,
..get_mock_webgl2_maximum()
});
let request = StartRequest::get_mock();
let navigator = Navigator::default();
let screen = Screen::default();
let token = String::new();
let result = WebGL::from_start_request(
&profile, &request, &navigator, &screen, &token,
)?;
assert!(result.extra_params.is_some());
let params = result.extra_params.unwrap();
assert_eq!(params.max_texture_size, 16384);
assert_eq!(params.max_texture_lod_bias, 2.0);
Ok(())
}
#[test]
fn test_webgpu_manual_mode() -> Result<(), Error> {
let mut profile = create_base_profile();
profile.webgpu.mode = Mode::Manual;
profile.webgpu.info = Some(Info {
architecture: "Test Architecture".to_string(),
description: "Test Description".to_string(),
device: "Test Device".to_string(),
vendor: "Test Vendor".to_string(),
});
profile.webgpu.limits =
Some(crate::server::dtos::browser_profile_dto::Limits {
max_texture_dimension_1d: 8192,
max_texture_dimension_2d: 8192,
max_texture_dimension_3d: 2048,
..crate::server::dtos::browser_profile_dto::Limits::get_mock()
});
profile.webgpu.get_preferred_canvas_format = Some("bgra8unorm".to_string());
let request = StartRequest::get_mock();
let navigator = Navigator::default();
let screen = Screen::default();
let token = String::new();
let result = Option::<WebGPU>::from_start_request(
&profile, &request, &navigator, &screen, &token,
)?;
assert!(result.is_some());
let webgpu = result.unwrap();
assert_eq!(webgpu.vendor, "Test Vendor");
assert_eq!(webgpu.architecture, "Test Architecture");
assert_eq!(webgpu.device, "Test Device");
assert_eq!(webgpu.description, "Test Description");
assert_eq!(webgpu.driver, "");
assert_eq!(webgpu.limits.max_texture_dimension_1d, 8192);
assert_eq!(webgpu.limits.max_texture_dimension_2d, 8192);
assert_eq!(webgpu.limits.max_texture_dimension_3d, 2048);
Ok(())
}
#[test]
fn test_webgpu_auto_mode() -> Result<(), Error> {
let profile = create_base_profile(); let request = StartRequest::get_mock();
let navigator = Navigator::default();
let screen = Screen::default();
let token = String::new();
let result = Option::<WebGPU>::from_start_request(
&profile, &request, &navigator, &screen, &token,
)?;
assert!(result.is_none());
Ok(())
}
#[test]
fn test_webgpu_manual_mode_missing_params() -> Result<(), Error> {
let mut profile = create_base_profile();
profile.webgpu.mode = Mode::Manual;
let request = StartRequest::get_mock();
let navigator = Navigator::default();
let screen = Screen::default();
let token = String::new();
let result = Option::<WebGPU>::from_start_request(
&profile, &request, &navigator, &screen, &token,
)?;
assert!(result.is_none());
Ok(())
}
}