diff --git a/components/script/dom/gpubindgroup.rs b/components/script/dom/gpubindgroup.rs index 4767d28d0f16..53b005fd7f05 100644 --- a/components/script/dom/gpubindgroup.rs +++ b/components/script/dom/gpubindgroup.rs @@ -3,36 +3,81 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::dom::bindings::cell::DomRefCell; -use crate::dom::bindings::codegen::Bindings::GPUBindGroupBinding::GPUBindGroupMethods; +use crate::dom::bindings::codegen::Bindings::GPUBindGroupBinding::{ + GPUBindGroupEntry, GPUBindGroupMethods, +}; use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; -use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::str::DOMString; use crate::dom::globalscope::GlobalScope; +use crate::dom::gpubindgrouplayout::GPUBindGroupLayout; +use crate::dom::gpubuffer::GPUBuffer; +use crate::dom::gputextureview::TextureSubresource; use dom_struct::dom_struct; use std::cell::Cell; -use webgpu::WebGPUBindGroup; +use std::collections::HashMap; +use webgpu::{WebGPUBindGroup, WebGPUDevice}; #[dom_struct] pub struct GPUBindGroup { reflector_: Reflector, label: DomRefCell>, bind_group: WebGPUBindGroup, + device: WebGPUDevice, + layout: Dom, + #[ignore_malloc_size_of = "defined in webgpu"] + entries: Vec, + used_buffers: HashMap, u32>, + used_textures: HashMap, valid: Cell, } impl GPUBindGroup { - fn new_inherited(bind_group: WebGPUBindGroup, valid: bool) -> Self { + fn new_inherited( + bind_group: WebGPUBindGroup, + device: WebGPUDevice, + valid: bool, + entries: Vec, + layout: &GPUBindGroupLayout, + used_buffers: HashMap, u32>, + used_textures: HashMap, + ) -> Self { Self { reflector_: Reflector::new(), label: DomRefCell::new(None), bind_group, + device, valid: Cell::new(valid), + layout: Dom::from_ref(layout), + entries, + used_buffers: used_buffers + .into_iter() + .map(|(key, value)| (Dom::from_ref(&*key), value)) + .collect(), + used_textures, } } - pub fn new(global: &GlobalScope, bind_group: WebGPUBindGroup, valid: bool) -> DomRoot { + pub fn new( + global: &GlobalScope, + bind_group: WebGPUBindGroup, + device: WebGPUDevice, + valid: bool, + entries: Vec, + layout: &GPUBindGroupLayout, + used_buffers: HashMap, u32>, + used_textures: HashMap, + ) -> DomRoot { reflect_dom_object( - Box::new(GPUBindGroup::new_inherited(bind_group, valid)), + Box::new(GPUBindGroup::new_inherited( + bind_group, + device, + valid, + entries, + layout, + used_buffers, + used_textures, + )), global, ) } diff --git a/components/script/dom/gpubindgrouplayout.rs b/components/script/dom/gpubindgrouplayout.rs index 8c1412562617..aadc75557b2c 100644 --- a/components/script/dom/gpubindgrouplayout.rs +++ b/components/script/dom/gpubindgrouplayout.rs @@ -12,6 +12,7 @@ use crate::dom::bindings::str::DOMString; use crate::dom::globalscope::GlobalScope; use dom_struct::dom_struct; use std::cell::Cell; +use std::collections::HashMap; use webgpu::{WebGPU, WebGPUBindGroupLayout}; #[dom_struct] @@ -20,7 +21,7 @@ pub struct GPUBindGroupLayout { label: DomRefCell>, bind_group_layout: WebGPUBindGroupLayout, #[ignore_malloc_size_of = "defined in webgpu"] - bindings: Vec, + entry_map: HashMap, #[ignore_malloc_size_of = "defined in webgpu"] channel: WebGPU, valid: Cell, @@ -30,7 +31,7 @@ impl GPUBindGroupLayout { fn new_inherited( channel: WebGPU, bind_group_layout: WebGPUBindGroupLayout, - bindings: Vec, + entry_map: HashMap, valid: bool, ) -> Self { Self { @@ -38,7 +39,7 @@ impl GPUBindGroupLayout { channel, label: DomRefCell::new(None), bind_group_layout, - bindings, + entry_map, valid: Cell::new(valid), } } @@ -47,14 +48,14 @@ impl GPUBindGroupLayout { global: &GlobalScope, channel: WebGPU, bind_group_layout: WebGPUBindGroupLayout, - bindings: Vec, + entry_map: HashMap, valid: bool, ) -> DomRoot { reflect_dom_object( Box::new(GPUBindGroupLayout::new_inherited( channel, bind_group_layout, - bindings, + entry_map, valid, )), global, @@ -71,8 +72,8 @@ impl GPUBindGroupLayout { self.bind_group_layout } - pub fn bindings(&self) -> &[GPUBindGroupLayoutEntry] { - &self.bindings + pub fn entries(&self) -> &HashMap { + &self.entry_map } } diff --git a/components/script/dom/gpubuffer.rs b/components/script/dom/gpubuffer.rs index 1c756dae15f5..a23a30a4b11c 100644 --- a/components/script/dom/gpubuffer.rs +++ b/components/script/dom/gpubuffer.rs @@ -110,7 +110,7 @@ impl GPUBuffer { self.state.borrow() } - pub fn valid(&self) -> bool { + pub fn is_valid(&self) -> bool { self.valid.get() } } diff --git a/components/script/dom/gpucommandencoder.rs b/components/script/dom/gpucommandencoder.rs index d0ba2dc8624b..0615492dec6f 100644 --- a/components/script/dom/gpucommandencoder.rs +++ b/components/script/dom/gpucommandencoder.rs @@ -240,8 +240,8 @@ impl GPUCommandEncoderMethods for GPUCommandEncoder { None => false, }; valid &= (*self.state.borrow() == GPUCommandEncoderState::Open) && - source.valid() && - destination.valid() & + source.is_valid() && + destination.is_valid() & !(size & BUFFER_COPY_ALIGN_MASK == 0) & !(source_offset & BUFFER_COPY_ALIGN_MASK == 0) & !(destination_offset & BUFFER_COPY_ALIGN_MASK == 0) & diff --git a/components/script/dom/gpudevice.rs b/components/script/dom/gpudevice.rs index fbbfa5417616..dbbc64708495 100644 --- a/components/script/dom/gpudevice.rs +++ b/components/script/dom/gpudevice.rs @@ -6,11 +6,14 @@ use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::GPUAdapterBinding::GPULimits; -use crate::dom::bindings::codegen::Bindings::GPUBindGroupBinding::GPUBindGroupDescriptor; +use crate::dom::bindings::codegen::Bindings::GPUBindGroupBinding::{ + GPUBindGroupDescriptor, GPUBindGroupEntry, GPUBindingResource, GPUBufferBindings, +}; use crate::dom::bindings::codegen::Bindings::GPUBindGroupLayoutBinding::{ GPUBindGroupLayoutDescriptor, GPUBindGroupLayoutEntry, GPUBindingType, }; use crate::dom::bindings::codegen::Bindings::GPUBufferBinding::GPUBufferDescriptor; +use crate::dom::bindings::codegen::Bindings::GPUBufferUsageBinding::GPUBufferUsageConstants; use crate::dom::bindings::codegen::Bindings::GPUComputePipelineBinding::GPUComputePipelineDescriptor; use crate::dom::bindings::codegen::Bindings::GPUDeviceBinding::{ GPUCommandEncoderDescriptor, GPUDeviceMethods, @@ -29,6 +32,7 @@ use crate::dom::bindings::codegen::Bindings::GPUTextureBinding::{ GPUExtent3D, GPUExtent3DDict, GPUTextureComponentType, GPUTextureDescriptor, GPUTextureDimension, GPUTextureFormat, }; +use crate::dom::bindings::codegen::Bindings::GPUTextureUsageBinding::GPUTextureUsageConstants; use crate::dom::bindings::codegen::Bindings::GPUTextureViewBinding::GPUTextureViewDimension; use crate::dom::bindings::codegen::UnionTypes::Uint32ArrayOrString::{String, Uint32Array}; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; @@ -49,13 +53,14 @@ use crate::dom::gpurenderpipeline::GPURenderPipeline; use crate::dom::gpusampler::GPUSampler; use crate::dom::gpushadermodule::GPUShaderModule; use crate::dom::gputexture::GPUTexture; +use crate::dom::gputextureview::{GPUTextureView, TextureSubresource}; use crate::script_runtime::JSContext as SafeJSContext; use arrayvec::ArrayVec; use dom_struct::dom_struct; use js::jsapi::{Heap, JSObject}; use js::jsval::{JSVal, ObjectValue}; use js::typedarray::{ArrayBuffer, CreateWith}; -use std::collections::{HashMap, HashSet}; +use std::collections::{hash_map::Entry, HashMap, HashSet}; use std::ptr::{self, NonNull}; use webgpu::wgpu::binding_model::{ BindGroupEntry, BindGroupLayoutEntry, BindingResource, BindingType, BufferBinding, @@ -153,6 +158,81 @@ impl GPUDevice { ) } } + + fn validate_texture_view_binding( + &self, + texture_view: &GPUTextureView, + binding: &GPUBindGroupLayoutEntry, + ) -> bool { + let mut valid = if let Some(d) = binding.viewDimension { + texture_view.descriptor().dimension.unwrap() == d + } else { + false + }; + let view_component = get_component_from_format(texture_view.descriptor().format.unwrap()); + valid &= if let Some(c) = binding.textureComponentType { + view_component == c + } else { + false + }; + valid &= if binding.multisampled { + texture_view.texture().sample_count() > 1 + } else { + texture_view.texture().sample_count() == 1 + }; + valid &= match binding.type_ { + GPUBindingType::Sampled_texture => { + match wgt::TextureUsage::from_bits(texture_view.texture().usage()) { + Some(u) => u.contains(wgt::TextureUsage::SAMPLED), + None => false, + } + }, + GPUBindingType::Readonly_storage_texture | + GPUBindingType::Writeonly_storage_texture => { + match wgt::TextureUsage::from_bits(texture_view.texture().usage()) { + Some(u) => u.contains(wgt::TextureUsage::STORAGE), + None => false, + } + }, + _ => false, + }; + valid + } + + fn validate_buffer_binding( + &self, + buffer_bind: &GPUBufferBindings, + binding: &GPUBindGroupLayoutEntry, + ) -> bool { + let mut valid = match binding.type_ { + GPUBindingType::Uniform_buffer => { + match wgt::BufferUsage::from_bits(buffer_bind.buffer.usage()) { + Some(u) => { + let v = if let Some(s) = buffer_bind.size { + s <= GPULimits::empty().maxUniformBufferBindingSize.into() + } else { + true + }; + v && u.contains(wgt::BufferUsage::UNIFORM) + }, + None => false, + } + }, + GPUBindingType::Storage_buffer | GPUBindingType::Readonly_storage_buffer => { + match wgt::BufferUsage::from_bits(buffer_bind.buffer.usage()) { + Some(u) => u.contains(wgt::BufferUsage::STORAGE), + None => false, + } + }, + _ => false, + }; + valid &= if let Some(s) = buffer_bind.size { + buffer_bind.offset + s <= buffer_bind.buffer.size() && buffer_bind.offset > 0 + } else { + buffer_bind.offset > 0 && buffer_bind.offset < buffer_bind.buffer.size() + }; + valid + } } impl GPUDeviceMethods for GPUDevice { @@ -303,11 +383,10 @@ impl GPUDeviceMethods for GPUDevice { limits.maxDynamicStorageBuffersPerPipelineLayout as i32; let mut valid = true; - let bindings = descriptor + let entries = descriptor .entries .iter() .map(|bind| { - // TODO: binding must be >= 0 storeBindings.insert(bind.binding); let visibility = match wgt::ShaderStage::from_bits(bind.visibility) { Some(visibility) => visibility, @@ -324,6 +403,10 @@ impl GPUDeviceMethods for GPUDevice { if bind.hasDynamicOffset { max_dynamic_uniform_buffers_per_pipeline_layout -= 1; }; + valid &= bind.viewDimension.is_none() && + bind.textureComponentType.is_none() && + !bind.multisampled && + bind.storageTextureFormat.is_none(); BindingType::UniformBuffer }, GPUBindingType::Storage_buffer => { @@ -333,6 +416,11 @@ impl GPUDeviceMethods for GPUDevice { if bind.hasDynamicOffset { max_dynamic_storage_buffers_per_pipeline_layout -= 1; }; + valid &= bind.viewDimension.is_none() && + !visibility.contains(wgt::ShaderStage::VERTEX) && + bind.textureComponentType.is_none() && + !bind.multisampled && + bind.storageTextureFormat.is_none(); BindingType::StorageBuffer }, GPUBindingType::Readonly_storage_buffer => { @@ -342,44 +430,59 @@ impl GPUDeviceMethods for GPUDevice { if bind.hasDynamicOffset { max_dynamic_storage_buffers_per_pipeline_layout -= 1; }; + valid &= bind.viewDimension.is_none() && + bind.textureComponentType.is_none() && + !bind.multisampled && + bind.storageTextureFormat.is_none(); 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 - }; + valid &= !bind.hasDynamicOffset && bind.storageTextureFormat.is_none(); BindingType::SampledTexture }, GPUBindingType::Readonly_storage_texture => { if let Some(limit) = validation_map.get_mut(&visibility) { limit.max_storage_textures_per_shader_stage -= 1; } - if bind.hasDynamicOffset { - valid = false - }; + valid &= !bind.hasDynamicOffset && + bind.textureComponentType.is_none() && + !bind.multisampled; BindingType::ReadonlyStorageTexture }, GPUBindingType::Writeonly_storage_texture => { if let Some(limit) = validation_map.get_mut(&visibility) { limit.max_storage_textures_per_shader_stage -= 1; } - if bind.hasDynamicOffset { - valid = false - }; + valid &= !bind.hasDynamicOffset && + bind.textureComponentType.is_none() && + !bind.multisampled; BindingType::WriteonlyStorageTexture }, GPUBindingType::Sampler => { if let Some(limit) = validation_map.get_mut(&visibility) { limit.max_samplers_per_shader_stage -= 1; } - if bind.hasDynamicOffset { - valid = false - }; + valid &= !bind.hasDynamicOffset && + bind.viewDimension.is_none() && + bind.textureComponentType.is_none() && + !bind.multisampled && + bind.storageTextureFormat.is_none(); BindingType::Sampler }, + GPUBindingType::Comparison_sampler => { + if let Some(limit) = validation_map.get_mut(&visibility) { + limit.max_samplers_per_shader_stage -= 1; + } + valid &= !bind.hasDynamicOffset && + bind.viewDimension.is_none() && + bind.textureComponentType.is_none() && + !bind.multisampled && + bind.storageTextureFormat.is_none(); + BindingType::ComparisonSampler + }, }; BindGroupLayoutEntry { @@ -388,22 +491,30 @@ impl GPUDeviceMethods for GPUDevice { ty, has_dynamic_offset: bind.hasDynamicOffset, multisampled: bind.multisampled, - texture_component_type: match bind.textureComponentType { - GPUTextureComponentType::Float => wgt::TextureComponentType::Float, - GPUTextureComponentType::Sint => wgt::TextureComponentType::Sint, - GPUTextureComponentType::Uint => wgt::TextureComponentType::Uint, + texture_component_type: if let Some(c) = bind.textureComponentType { + match c { + GPUTextureComponentType::Float => wgt::TextureComponentType::Float, + GPUTextureComponentType::Sint => wgt::TextureComponentType::Sint, + GPUTextureComponentType::Uint => wgt::TextureComponentType::Uint, + } + } else { + wgt::TextureComponentType::Float }, storage_texture_format: match bind.storageTextureFormat { Some(s) => convert_texture_format(s), None => wgt::TextureFormat::Bgra8UnormSrgb, }, - view_dimension: convert_texture_view_dimension(bind.viewDimension), + view_dimension: bind + .viewDimension + .map_or(wgt::TextureViewDimension::D2, |v| { + convert_texture_view_dimension(v) + }), } }) .collect::>(); // bindings are unique - valid &= storeBindings.len() == bindings.len(); + valid &= storeBindings.len() == entries.len(); // Ensure that values do not exceed the max limit for each ShaderStage. valid &= validation_map.values().all(|stage| { @@ -428,26 +539,28 @@ impl GPUDeviceMethods for GPUDevice { .send(WebGPURequest::CreateBindGroupLayout { device_id: self.device.0, bind_group_layout_id, - bindings: bindings.clone(), + entries: entries.clone(), }) .expect("Failed to create WebGPU BindGroupLayout"); let bgl = webgpu::WebGPUBindGroupLayout(bind_group_layout_id); - let binds = descriptor - .entries - .iter() - .map(|bind| GPUBindGroupLayoutEntry { - binding: bind.binding, - hasDynamicOffset: bind.hasDynamicOffset, - multisampled: bind.multisampled, - type_: bind.type_, - visibility: bind.visibility, - viewDimension: bind.viewDimension, - textureComponentType: bind.textureComponentType, - storageTextureFormat: bind.storageTextureFormat, - }) - .collect::>(); + let mut binds = HashMap::new(); + descriptor.entries.iter().for_each(|bind| { + binds.insert( + bind.binding, + GPUBindGroupLayoutEntry { + binding: bind.binding, + hasDynamicOffset: bind.hasDynamicOffset, + multisampled: bind.multisampled, + type_: bind.type_, + visibility: bind.visibility, + viewDimension: bind.viewDimension, + textureComponentType: bind.textureComponentType, + storageTextureFormat: bind.storageTextureFormat, + }, + ); + }); GPUBindGroupLayout::new(&self.global(), self.channel.clone(), bgl, binds, valid) } @@ -471,7 +584,7 @@ impl GPUDeviceMethods for GPUDevice { bind_group_layouts.push(id); bgl_ids.push(id.0); } - each.bindings().iter().for_each(|bind| { + each.entries().values().for_each(|bind| { match bind.type_ { GPUBindingType::Uniform_buffer => { if bind.hasDynamicOffset { @@ -518,10 +631,11 @@ impl GPUDeviceMethods for GPUDevice { /// https://gpuweb.github.io/gpuweb/#dom-gpudevice-createbindgroup fn CreateBindGroup(&self, descriptor: &GPUBindGroupDescriptor) -> DomRoot { - let alignment: u64 = 256; - let mut valid = descriptor.layout.bindings().len() == descriptor.entries.len(); + //let alignment: u64 = 256; + let mut valid = descriptor.layout.is_valid() && + descriptor.layout.entries().len() == descriptor.entries.len(); - valid &= descriptor.entries.iter().all(|bind| { + /*valid &= descriptor.entries.iter().all(|bind| { let buffer_size = bind.resource.buffer.size(); let resource_size = bind.resource.size.unwrap_or(buffer_size); let length = bind.resource.offset.checked_add(resource_size); @@ -531,32 +645,149 @@ impl GPUDeviceMethods for GPUDevice { buffer_size >= length.unwrap() && // check buffer OOB bind.resource.offset % alignment == 0 && // check alignment bind.resource.offset < buffer_size && // on Vulkan offset must be less than size of buffer - descriptor.layout.bindings().iter().any(|layout_bind| { - let ty = match layout_bind.type_ { - GPUBindingType::Storage_buffer => wgt::BufferUsage::STORAGE, + descriptor.layout.entries().iter().any(|layout_bind| { + match layout_bind.type_ { + GPUBindingType::Storage_buffer => usage.contains(wgt::BufferUsage::STORAGE), // GPUBindingType::Readonly_storage_buffer => BufferUsage::STORAGE_READ, - GPUBindingType::Uniform_buffer => wgt::BufferUsage::UNIFORM, + GPUBindingType::Uniform_buffer => usage.contains(wgt::BufferUsage::UNIFORM), + GPUBindingType::Sampler => bind.resource _ => unimplemented!(), }; // binding must be present in layout - layout_bind.binding == bind.binding && - // binding must contain one buffer of its type - usage.contains(ty) + layout_bind.binding == bind.binding }) + });*/ + let mut bindings = HashSet::new(); + let mut used_buffers = HashMap::new(); + let mut used_textures = HashMap::new(); + valid &= descriptor.entries.iter().all(|bind| { + bindings.insert(bind.binding); + if let Some(layout_bind) = descriptor + .layout + .entries() + .values() + .find(|lb| lb.binding == bind.binding) + { + match layout_bind.type_ { + GPUBindingType::Sampler => match bind.resource { + GPUBindingResource::GPUSampler(ref s) => s.is_valid() && !s.compare(), + _ => false, + }, + GPUBindingType::Comparison_sampler => match bind.resource { + GPUBindingResource::GPUSampler(ref s) => s.is_valid() && s.compare(), + _ => false, + }, + GPUBindingType::Sampled_texture => match bind.resource { + GPUBindingResource::GPUTextureView(ref t) => { + let desc = t.descriptor(); + for i in desc.baseMipLevel..desc.mipLevelCount { + for j in desc.baseArrayLayer..desc.arrayLayerCount { + let subresource = TextureSubresource { + texture: DomRoot::from_ref(t.texture()), + mipmap_level: i, + array_layer: j, + }; + match used_textures.entry(subresource) { + Entry::Vacant(v) => { + v.insert(GPUTextureUsageConstants::SAMPLED); + }, + Entry::Occupied(mut o) => { + *o.get_mut() += GPUTextureUsageConstants::SAMPLED; + }, + } + } + } + t.is_valid() && self.validate_texture_view_binding(t, layout_bind) + }, + _ => false, + }, + GPUBindingType::Readonly_storage_texture | + GPUBindingType::Writeonly_storage_texture => match bind.resource { + GPUBindingResource::GPUTextureView(ref t) => { + let desc = t.descriptor(); + for i in desc.baseMipLevel..desc.mipLevelCount { + for j in desc.baseArrayLayer..desc.arrayLayerCount { + let subresource = TextureSubresource { + texture: DomRoot::from_ref(t.texture()), + mipmap_level: i, + array_layer: j, + }; + match used_textures.entry(subresource) { + Entry::Vacant(v) => { + v.insert(GPUTextureUsageConstants::STORAGE); + }, + Entry::Occupied(mut o) => { + *o.get_mut() += GPUTextureUsageConstants::STORAGE; + }, + } + } + } + t.is_valid() && + self.validate_texture_view_binding(t, layout_bind) && + t.descriptor().format == layout_bind.storageTextureFormat + }, + _ => false, + }, + GPUBindingType::Uniform_buffer => match bind.resource { + GPUBindingResource::GPUBufferBindings(ref b) => { + match used_buffers.entry(DomRoot::from_ref(&*b.buffer)) { + Entry::Vacant(v) => { + v.insert(GPUBufferUsageConstants::UNIFORM); + }, + Entry::Occupied(mut o) => { + *o.get_mut() += GPUBufferUsageConstants::UNIFORM; + }, + } + b.buffer.is_valid() && self.validate_buffer_binding(b, layout_bind) + }, + _ => false, + }, + GPUBindingType::Storage_buffer | GPUBindingType::Readonly_storage_buffer => { + match bind.resource { + GPUBindingResource::GPUBufferBindings(ref b) => { + match used_buffers.entry(DomRoot::from_ref(&*b.buffer)) { + Entry::Vacant(v) => { + v.insert(GPUBufferUsageConstants::STORAGE); + }, + Entry::Occupied(mut o) => { + *o.get_mut() += GPUBufferUsageConstants::STORAGE; + }, + } + b.buffer.is_valid() && self.validate_buffer_binding(b, layout_bind) + }, + _ => false, + } + }, + } + } else { + false + } }); - let bindings = descriptor + valid &= bindings.len() == descriptor.entries.len(); + + let entries = descriptor .entries .iter() .map(|bind| BindGroupEntry { binding: bind.binding, - resource: BindingResource::Buffer(BufferBinding { - buffer: bind.resource.buffer.id().0, - offset: bind.resource.offset, - size: wgt::BufferSize( - bind.resource.size.unwrap_or(bind.resource.buffer.size()), - ), - }), + resource: match bind.resource { + GPUBindingResource::GPUSampler(ref s) => BindingResource::Sampler(s.id().0), + GPUBindingResource::GPUTextureView(ref t) => { + BindingResource::TextureView(t.id().0) + }, + GPUBindingResource::GPUBufferBindings(ref b) => { + BindingResource::Buffer(BufferBinding { + buffer: b.buffer.id().0, + offset: b.offset, + size: if let Some(s) = b.size { + wgt::BufferSize(s) + } else { + wgt::BufferSize::WHOLE + }, + }) + }, + }, }) .collect::>(); @@ -571,12 +802,44 @@ impl GPUDeviceMethods for GPUDevice { device_id: self.device.0, bind_group_id, bind_group_layout_id: descriptor.layout.id().0, - bindings, + entries, }) .expect("Failed to create WebGPU BindGroup"); + let desc_entries = descriptor + .entries + .iter() + .map(|bind| GPUBindGroupEntry { + binding: bind.binding, + resource: match bind.resource { + GPUBindingResource::GPUSampler(ref s) => { + GPUBindingResource::GPUSampler(DomRoot::from_ref(&*s)) + }, + GPUBindingResource::GPUTextureView(ref t) => { + GPUBindingResource::GPUTextureView(DomRoot::from_ref(&*t)) + }, + GPUBindingResource::GPUBufferBindings(ref b) => { + GPUBindingResource::GPUBufferBindings(GPUBufferBindings { + buffer: DomRoot::from_ref(&*b.buffer), + offset: b.offset, + size: b.size, + }) + }, + }, + }) + .collect::>(); + let bind_group = webgpu::WebGPUBindGroup(bind_group_id); - GPUBindGroup::new(&self.global(), bind_group, valid) + GPUBindGroup::new( + &self.global(), + bind_group, + self.device, + valid, + desc_entries, + &*descriptor.layout, + used_buffers, + used_textures, + ) } /// https://gpuweb.github.io/gpuweb/#dom-gpudevice-createshadermodule @@ -1094,3 +1357,46 @@ fn convert_texture_size_to_wgt(size: &GPUExtent3DDict) -> wgt::Extent3d { depth: size.depth, } } + +fn get_component_from_format(format: GPUTextureFormat) -> GPUTextureComponentType { + match format { + GPUTextureFormat::R8unorm | + GPUTextureFormat::R8snorm | + GPUTextureFormat::R16float | + GPUTextureFormat::Rg8unorm | + GPUTextureFormat::Rg8snorm | + GPUTextureFormat::R32float | + GPUTextureFormat::Rg16float | + GPUTextureFormat::Rgba8unorm | + GPUTextureFormat::Rgba8unorm_srgb | + GPUTextureFormat::Rgba8snorm | + GPUTextureFormat::Bgra8unorm | + GPUTextureFormat::Bgra8unorm_srgb | + GPUTextureFormat::Rgb10a2unorm | + GPUTextureFormat::Rg11b10float | + GPUTextureFormat::Rg32float | + GPUTextureFormat::Rgba16float | + GPUTextureFormat::Rgba32float | + GPUTextureFormat::Depth32float | + GPUTextureFormat::Depth24plus | + GPUTextureFormat::Depth24plus_stencil8 => GPUTextureComponentType::Float, + GPUTextureFormat::R8uint | + GPUTextureFormat::R16uint | + GPUTextureFormat::Rg8uint | + GPUTextureFormat::R32uint | + GPUTextureFormat::Rg16uint | + GPUTextureFormat::Rgba8uint | + GPUTextureFormat::Rg32uint | + GPUTextureFormat::Rgba16uint | + GPUTextureFormat::Rgba32uint => GPUTextureComponentType::Uint, + GPUTextureFormat::R8sint | + GPUTextureFormat::R16sint | + GPUTextureFormat::Rg8sint | + GPUTextureFormat::R32sint | + GPUTextureFormat::Rg16sint | + GPUTextureFormat::Rgba8sint | + GPUTextureFormat::Rg32sint | + GPUTextureFormat::Rgba16sint | + GPUTextureFormat::Rgba32sint => GPUTextureComponentType::Sint, + } +} diff --git a/components/script/dom/gpusampler.rs b/components/script/dom/gpusampler.rs index db85f60e9b6d..488d3df1d05c 100644 --- a/components/script/dom/gpusampler.rs +++ b/components/script/dom/gpusampler.rs @@ -64,6 +64,20 @@ impl GPUSampler { } } +impl GPUSampler { + pub fn id(&self) -> WebGPUSampler { + self.sampler + } + + pub fn is_valid(&self) -> bool { + self.valid.get() + } + + pub fn compare(&self) -> bool { + self.compare_enable + } +} + impl GPUSamplerMethods for GPUSampler { /// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label fn GetLabel(&self) -> Option { diff --git a/components/script/dom/gputexture.rs b/components/script/dom/gputexture.rs index 48060efa6664..54b5610ae480 100644 --- a/components/script/dom/gputexture.rs +++ b/components/script/dom/gputexture.rs @@ -3,11 +3,12 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::GPUObjectBaseBinding::GPUObjectDescriptorBase; use crate::dom::bindings::codegen::Bindings::GPUTextureBinding::{ GPUExtent3DDict, GPUTextureDimension, GPUTextureFormat, GPUTextureMethods, }; use crate::dom::bindings::codegen::Bindings::GPUTextureViewBinding::{ - GPUTextureAspect, GPUTextureViewDescriptor, + GPUTextureAspect, GPUTextureViewDescriptor, GPUTextureViewDimension, }; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; use crate::dom::bindings::root::DomRoot; @@ -107,6 +108,14 @@ impl GPUTexture { pub fn id(&self) -> WebGPUTexture { self.texture } + + pub fn sample_count(&self) -> u32 { + self.sample_count + } + + pub fn usage(&self) -> u32 { + self.texture_usage + } } impl GPUTextureMethods for GPUTexture { @@ -122,23 +131,28 @@ impl GPUTextureMethods for GPUTexture { /// https://gpuweb.github.io/gpuweb/#dom-gputexture-createview fn CreateView(&self, descriptor: &GPUTextureViewDescriptor) -> DomRoot { + let dimension = if let Some(d) = descriptor.dimension { + d + } else { + match self.dimension { + GPUTextureDimension::_1d => GPUTextureViewDimension::_1d, + GPUTextureDimension::_2d => { + if self.texture_size.depth > 1 && descriptor.arrayLayerCount == 0 { + GPUTextureViewDimension::_2d_array + } else { + GPUTextureViewDimension::_2d + } + }, + GPUTextureDimension::_3d => GPUTextureViewDimension::_3d, + } + }; + + let format = descriptor.format.unwrap_or(self.format); + let desc = wgt::TextureViewDescriptor { label: Default::default(), - format: convert_texture_format(descriptor.format.unwrap_or(self.format)), - dimension: match descriptor.dimension { - Some(d) => convert_texture_view_dimension(d), - None => match self.dimension { - GPUTextureDimension::_1d => wgt::TextureViewDimension::D1, - GPUTextureDimension::_2d => { - if self.texture_size.depth > 1 && descriptor.arrayLayerCount == 0 { - wgt::TextureViewDimension::D2Array - } else { - wgt::TextureViewDimension::D2 - } - }, - GPUTextureDimension::_3d => wgt::TextureViewDimension::D3, - }, - }, + format: convert_texture_format(format), + dimension: convert_texture_view_dimension(dimension), aspect: match descriptor.aspect { GPUTextureAspect::All => wgt::TextureAspect::All, GPUTextureAspect::Stencil_only => wgt::TextureAspect::StencilOnly, @@ -175,7 +189,32 @@ impl GPUTextureMethods for GPUTexture { let texture_view = WebGPUTextureView(texture_view_id); - GPUTextureView::new(&self.global(), texture_view, self.device, true) + let desc = GPUTextureViewDescriptor { + parent: GPUObjectDescriptorBase { + label: descriptor + .parent + .label + .as_ref() + .map(|l| l.as_ref().map(|u| u.clone())), + }, + arrayLayerCount: if descriptor.arrayLayerCount == 0 { + self.texture_size.depth - descriptor.baseArrayLayer + } else { + descriptor.arrayLayerCount + }, + aspect: descriptor.aspect, + baseArrayLayer: descriptor.baseArrayLayer, + baseMipLevel: descriptor.baseMipLevel, + dimension: Some(dimension), + format: Some(descriptor.format.unwrap_or(self.format)), + mipLevelCount: if descriptor.mipLevelCount == 0 { + self.mip_level_count - descriptor.baseMipLevel + } else { + descriptor.mipLevelCount + }, + }; + + GPUTextureView::new(&self.global(), texture_view, &self, true, desc) } /// https://gpuweb.github.io/gpuweb/#dom-gputexture-destroy diff --git a/components/script/dom/gputextureview.rs b/components/script/dom/gputextureview.rs index f3bb4209708d..e1c14ef1cd59 100644 --- a/components/script/dom/gputextureview.rs +++ b/components/script/dom/gputextureview.rs @@ -3,43 +3,85 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::GPUTextureViewBinding::GPUTextureViewDescriptor; use crate::dom::bindings::codegen::Bindings::GPUTextureViewBinding::GPUTextureViewMethods; use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; -use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::str::DOMString; use crate::dom::globalscope::GlobalScope; +use crate::dom::gputexture::GPUTexture; use dom_struct::dom_struct; use std::cell::Cell; -use webgpu::{WebGPUDevice, WebGPUTextureView}; +use std::hash::{Hash, Hasher}; +use webgpu::WebGPUTextureView; + +#[derive(MallocSizeOf, JSTraceable)] +pub struct TextureSubresource { + pub texture: DomRoot, + pub mipmap_level: u32, + pub array_layer: u32, +} + +impl PartialEq for TextureSubresource { + fn eq(&self, other: &Self) -> bool { + self.texture.id().0 == other.texture.id().0 && + self.mipmap_level == other.mipmap_level && + self.array_layer == other.array_layer + } +} + +impl Eq for TextureSubresource {} + +impl Hash for TextureSubresource { + fn hash(&self, state: &mut H) { + self.texture.id().0.hash(state); + self.mipmap_level.hash(state); + self.array_layer.hash(state); + } +} #[dom_struct] pub struct GPUTextureView { reflector_: Reflector, label: DomRefCell>, texture_view: WebGPUTextureView, - device: WebGPUDevice, + texture: Dom, valid: Cell, + #[ignore_malloc_size_of = "defined in webgpu"] + descriptor: GPUTextureViewDescriptor, } impl GPUTextureView { - fn new_inherited(texture_view: WebGPUTextureView, device: WebGPUDevice, valid: bool) -> Self { + fn new_inherited( + texture_view: WebGPUTextureView, + texture: &GPUTexture, + valid: bool, + descriptor: GPUTextureViewDescriptor, + ) -> GPUTextureView { Self { reflector_: Reflector::new(), - device, + texture: Dom::from_ref(texture), label: DomRefCell::new(None), texture_view, valid: Cell::new(valid), + descriptor, } } pub fn new( global: &GlobalScope, texture_view: WebGPUTextureView, - device: WebGPUDevice, + texture: &GPUTexture, valid: bool, - ) -> DomRoot { + descriptor: GPUTextureViewDescriptor, + ) -> DomRoot { reflect_dom_object( - Box::new(GPUTextureView::new_inherited(texture_view, device, valid)), + Box::new(GPUTextureView::new_inherited( + texture_view, + texture, + valid, + descriptor, + )), global, ) } @@ -49,6 +91,18 @@ impl GPUTextureView { pub fn id(&self) -> WebGPUTextureView { self.texture_view } + + pub fn is_valid(&self) -> bool { + self.valid.get() + } + + pub fn descriptor(&self) -> &GPUTextureViewDescriptor { + &self.descriptor + } + + pub fn texture(&self) -> &GPUTexture { + &*self.texture + } } impl GPUTextureViewMethods for GPUTextureView { diff --git a/components/script/dom/webidls/GPUAdapter.webidl b/components/script/dom/webidls/GPUAdapter.webidl index 8d2e34f4a572..7fc2e8438f54 100644 --- a/components/script/dom/webidls/GPUAdapter.webidl +++ b/components/script/dom/webidls/GPUAdapter.webidl @@ -24,12 +24,13 @@ enum GPUExtensionName { }; dictionary GPULimits { - unsigned long maxBindGroups = 4; - unsigned long maxDynamicUniformBuffersPerPipelineLayout = 8; - unsigned long maxDynamicStorageBuffersPerPipelineLayout = 4; - unsigned long maxSampledTexturesPerShaderStage = 16; - unsigned long maxSamplersPerShaderStage = 16; - unsigned long maxStorageBuffersPerShaderStage = 4; - unsigned long maxStorageTexturesPerShaderStage = 4; - unsigned long maxUniformBuffersPerShaderStage = 12; + GPUSize32 maxBindGroups = 4; + GPUSize32 maxDynamicUniformBuffersPerPipelineLayout = 8; + GPUSize32 maxDynamicStorageBuffersPerPipelineLayout = 4; + GPUSize32 maxSampledTexturesPerShaderStage = 16; + GPUSize32 maxSamplersPerShaderStage = 16; + GPUSize32 maxStorageBuffersPerShaderStage = 4; + GPUSize32 maxStorageTexturesPerShaderStage = 4; + GPUSize32 maxUniformBuffersPerShaderStage = 12; + GPUSize32 maxUniformBufferBindingSize = 16384; }; diff --git a/components/script/dom/webidls/GPUBindGroup.webidl b/components/script/dom/webidls/GPUBindGroup.webidl index f905755d6fb8..7bfa023acba6 100644 --- a/components/script/dom/webidls/GPUBindGroup.webidl +++ b/components/script/dom/webidls/GPUBindGroup.webidl @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ // https://gpuweb.github.io/gpuweb/#gpubindgrouplayout -[Exposed=(Window, DedicatedWorker), Serializable, Pref="dom.webgpu.enabled"] +[Exposed=(Window, DedicatedWorker), Pref="dom.webgpu.enabled"] interface GPUBindGroup { }; GPUBindGroup includes GPUObjectBase; @@ -13,10 +13,10 @@ dictionary GPUBindGroupDescriptor : GPUObjectDescriptorBase { required sequence entries; }; -typedef /*(GPUSampler or GPUTextureView or*/ GPUBufferBindings/*)*/ GPUBindingResource; +typedef (GPUSampler or GPUTextureView or GPUBufferBindings) GPUBindingResource; dictionary GPUBindGroupEntry { - required unsigned long binding; + required GPUIndex32 binding; required GPUBindingResource resource; }; diff --git a/components/script/dom/webidls/GPUBindGroupLayout.webidl b/components/script/dom/webidls/GPUBindGroupLayout.webidl index 8b2d63a84ffc..32ae601440e3 100644 --- a/components/script/dom/webidls/GPUBindGroupLayout.webidl +++ b/components/script/dom/webidls/GPUBindGroupLayout.webidl @@ -16,11 +16,19 @@ dictionary GPUBindGroupLayoutEntry { required GPUIndex32 binding; required GPUShaderStageFlags visibility; required GPUBindingType type; - GPUTextureViewDimension viewDimension = "2d"; - GPUTextureComponentType textureComponentType = "float"; - GPUTextureFormat storageTextureFormat; - boolean multisampled = false; + + // Used for uniform buffer and storage buffer bindings. boolean hasDynamicOffset = false; + + // Used for sampled texture and storage texture bindings. + GPUTextureViewDimension viewDimension; + + // Used for sampled texture bindings. + GPUTextureComponentType textureComponentType; + boolean multisampled = false; + + // Used for storage texture bindings. + GPUTextureFormat storageTextureFormat; }; enum GPUBindingType { @@ -31,5 +39,5 @@ enum GPUBindingType { "sampled-texture", "readonly-storage-texture", "writeonly-storage-texture", - //"comparison-sampler", + "comparison-sampler", }; diff --git a/components/webgpu/lib.rs b/components/webgpu/lib.rs index 9f04b2bd58b6..45a7e4533647 100644 --- a/components/webgpu/lib.rs +++ b/components/webgpu/lib.rs @@ -73,12 +73,12 @@ pub enum WebGPURequest { device_id: id::DeviceId, bind_group_id: id::BindGroupId, bind_group_layout_id: id::BindGroupLayoutId, - bindings: Vec, + entries: Vec, }, CreateBindGroupLayout { device_id: id::DeviceId, bind_group_layout_id: id::BindGroupLayoutId, - bindings: Vec, + entries: Vec, }, CreateBuffer { device_id: id::DeviceId, @@ -334,13 +334,13 @@ impl WGPU { device_id, bind_group_id, bind_group_layout_id, - bindings, + entries, } => { let global = &self.global; let descriptor = BindGroupDescriptor { layout: bind_group_layout_id, - entries: bindings.as_ptr(), - entries_length: bindings.len(), + entries: entries.as_ptr(), + entries_length: entries.len(), label: ptr::null(), }; let _ = gfx_select!(bind_group_id => @@ -349,12 +349,12 @@ impl WGPU { WebGPURequest::CreateBindGroupLayout { device_id, bind_group_layout_id, - bindings, + entries, } => { let global = &self.global; let descriptor = BindGroupLayoutDescriptor { - entries: bindings.as_ptr(), - entries_length: bindings.len(), + entries: entries.as_ptr(), + entries_length: entries.len(), label: ptr::null(), }; let _ = gfx_select!(bind_group_layout_id =>