diff options
Diffstat (limited to 'components/script/dom/gpudevice.rs')
-rw-r--r-- | components/script/dom/gpudevice.rs | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/components/script/dom/gpudevice.rs b/components/script/dom/gpudevice.rs index 371643b6027..d2d2b2013d8 100644 --- a/components/script/dom/gpudevice.rs +++ b/components/script/dom/gpudevice.rs @@ -5,6 +5,10 @@ #![allow(unsafe_code)] use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::GPUAdapterBinding::GPULimits; +use crate::dom::bindings::codegen::Bindings::GPUBindGroupLayoutBinding::{ + GPUBindGroupLayoutBindings, GPUBindGroupLayoutDescriptor, GPUBindingType, +}; use crate::dom::bindings::codegen::Bindings::GPUBufferBinding::GPUBufferDescriptor; use crate::dom::bindings::codegen::Bindings::GPUDeviceBinding::{self, GPUDeviceMethods}; use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods; @@ -15,6 +19,7 @@ use crate::dom::bindings::str::DOMString; use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; use crate::dom::gpuadapter::GPUAdapter; +use crate::dom::gpubindgrouplayout::GPUBindGroupLayout; use crate::dom::gpubuffer::{GPUBuffer, GPUBufferState}; use crate::dom::window::Window; use crate::script_runtime::JSContext as SafeJSContext; @@ -23,7 +28,9 @@ use ipc_channel::ipc; use js::jsapi::{Heap, JSObject}; use js::jsval::{JSVal, ObjectValue, UndefinedValue}; use js::typedarray::{ArrayBuffer, CreateWith}; +use std::collections::{HashMap, HashSet}; use std::ptr::{self, NonNull}; +use webgpu::wgpu::binding_model::{BindGroupLayoutBinding, BindingType, ShaderStage}; use webgpu::wgpu::resource::{BufferDescriptor, BufferUsage}; use webgpu::{WebGPU, WebGPUBuffer, WebGPUDevice, WebGPURequest}; @@ -229,4 +236,177 @@ impl GPUDeviceMethods for GPUDevice { self.resolve_create_buffer_mapped(cx, buffer, array_buffer, wgpu_descriptor, valid) } } + + /// https://gpuweb.github.io/gpuweb/#GPUDevice-createBindGroupLayout + fn CreateBindGroupLayout( + &self, + descriptor: &GPUBindGroupLayoutDescriptor, + ) -> DomRoot<GPUBindGroupLayout> { + #[derive(Clone)] + struct MaxLimits { + max_uniform_buffers_per_shader_stage: i32, + max_storage_buffers_per_shader_stage: i32, + max_sampled_textures_per_shader_stage: i32, + max_storage_textures_per_shader_stage: i32, + max_samplers_per_shader_stage: i32, + } + let mut storeBindings = HashSet::new(); + // TODO: We should have these limits on device creation + let limits = GPULimits::empty(); + + let mut validation_map = HashMap::new(); + let maxLimits = MaxLimits { + max_uniform_buffers_per_shader_stage: limits.maxUniformBuffersPerShaderStage as i32, + max_storage_buffers_per_shader_stage: limits.maxStorageBuffersPerShaderStage as i32, + max_sampled_textures_per_shader_stage: limits.maxSampledTexturesPerShaderStage as i32, + max_storage_textures_per_shader_stage: limits.maxStorageTexturesPerShaderStage as i32, + max_samplers_per_shader_stage: limits.maxSamplersPerShaderStage as i32, + }; + validation_map.insert( + webgpu::wgpu::binding_model::ShaderStage::VERTEX, + maxLimits.clone(), + ); + validation_map.insert( + webgpu::wgpu::binding_model::ShaderStage::FRAGMENT, + maxLimits.clone(), + ); + validation_map.insert( + webgpu::wgpu::binding_model::ShaderStage::COMPUTE, + maxLimits.clone(), + ); + let mut max_dynamic_uniform_buffers_per_pipeline_layout = + limits.maxDynamicUniformBuffersPerPipelineLayout as i32; + let mut max_dynamic_storage_buffers_per_pipeline_layout = + limits.maxDynamicStorageBuffersPerPipelineLayout as i32; + let mut valid = true; + + let bindings = descriptor + .bindings + .iter() + .map(|bind| { + // TODO: binding must be >= 0 + storeBindings.insert(bind.binding); + let visibility = match ShaderStage::from_bits(bind.visibility) { + Some(visibility) => visibility, + None => { + valid = false; + ShaderStage::from_bits(0).unwrap() + }, + }; + let ty = match bind.type_ { + GPUBindingType::Uniform_buffer => { + if let Some(limit) = validation_map.get_mut(&visibility) { + limit.max_uniform_buffers_per_shader_stage -= 1; + } + if bind.hasDynamicOffset { + max_dynamic_uniform_buffers_per_pipeline_layout -= 1; + }; + BindingType::UniformBuffer + }, + GPUBindingType::Storage_buffer => { + if let Some(limit) = validation_map.get_mut(&visibility) { + limit.max_storage_buffers_per_shader_stage -= 1; + } + if bind.hasDynamicOffset { + max_dynamic_storage_buffers_per_pipeline_layout -= 1; + }; + BindingType::StorageBuffer + }, + GPUBindingType::Readonly_storage_buffer => { + if let Some(limit) = validation_map.get_mut(&visibility) { + limit.max_storage_buffers_per_shader_stage -= 1; + } + if bind.hasDynamicOffset { + max_dynamic_storage_buffers_per_pipeline_layout -= 1; + }; + BindingType::ReadonlyStorageBuffer + }, + GPUBindingType::Sampled_texture => { + if let Some(limit) = validation_map.get_mut(&visibility) { + limit.max_sampled_textures_per_shader_stage -= 1; + } + if bind.hasDynamicOffset { + valid = false + }; + BindingType::SampledTexture + }, + GPUBindingType::Storage_texture => { + if let Some(limit) = validation_map.get_mut(&visibility) { + limit.max_storage_textures_per_shader_stage -= 1; + } + if bind.hasDynamicOffset { + valid = false + }; + BindingType::StorageTexture + }, + GPUBindingType::Sampler => { + if let Some(limit) = validation_map.get_mut(&visibility) { + limit.max_samplers_per_shader_stage -= 1; + } + if bind.hasDynamicOffset { + valid = false + }; + BindingType::Sampler + }, + }; + + BindGroupLayoutBinding { + binding: bind.binding, + visibility, + ty, + dynamic: bind.hasDynamicOffset, + multisampled: bind.multisampled, + texture_dimension: webgpu::wgpu::resource::TextureViewDimension::D2, // Use as default for now + } + }) + .collect::<Vec<BindGroupLayoutBinding>>(); + + // bindings are unique + valid &= storeBindings.len() == bindings.len(); + + // Ensure that values do not exceed the max limit for each ShaderStage. + valid &= validation_map.values().all(|stage| { + stage.max_uniform_buffers_per_shader_stage >= 0 && + stage.max_storage_buffers_per_shader_stage >= 0 && + stage.max_sampled_textures_per_shader_stage >= 0 && + stage.max_storage_textures_per_shader_stage >= 0 && + stage.max_samplers_per_shader_stage >= 0 + }); + + // DynamicValues does not exceed the max limit for the pipeline + valid &= max_dynamic_uniform_buffers_per_pipeline_layout >= 0 && + max_dynamic_storage_buffers_per_pipeline_layout >= 0; + + let (sender, receiver) = ipc::channel().unwrap(); + if let Some(window) = self.global().downcast::<Window>() { + let id = window + .Navigator() + .create_bind_group_layout_id(self.device.0.backend()); + self.channel + .0 + .send(WebGPURequest::CreateBindGroupLayout( + sender, + self.device, + id, + bindings.clone(), + )) + .expect("Failed to create WebGPU BindGroupLayout"); + } + let bgl = receiver.recv().unwrap(); + + let binds = descriptor + .bindings + .iter() + .map(|bind| GPUBindGroupLayoutBindings { + binding: bind.binding, + hasDynamicOffset: bind.hasDynamicOffset, + multisampled: bind.multisampled, + type_: bind.type_, + visibility: bind.visibility, + //texture_dimension: bind.texture_dimension + }) + .collect::<Vec<_>>(); + + GPUBindGroupLayout::new(&self.global(), self.channel.clone(), bgl, binds, valid) + } } |