diff options
author | bors-servo <servo-ops@mozilla.com> | 2020-08-07 15:38:17 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-08-07 15:38:17 -0400 |
commit | 53467b80b9e79b27beeb305d77ebca5b18515d2f (patch) | |
tree | 573ec82b7510af0a73fc8d37c02eed69cbc2492a /components/script/dom | |
parent | 9eab0acbb666d80c593eb69aab2e9cba0303c573 (diff) | |
parent | ecb8c914466adad7ee736422c0d9634b51ea07fe (diff) | |
download | servo-53467b80b9e79b27beeb305d77ebca5b18515d2f.tar.gz servo-53467b80b9e79b27beeb305d77ebca5b18515d2f.zip |
Auto merge of #27536 - kunalmohan:update-wgpu, r=kvark
Major fixes in error reporting in GPUCommandEncoder and ErrorScope Model
<!-- Please describe your changes on the following line: -->
1. Update wgpu to use the error model on wgpu-core side. Register error Ids separately.
2. ~~Record errors only in `GPUCommandEncoder.finish()`. Errors are no longer recorded in ErrorScopes in transfer commands or while recording passes. Any errors that occur are stored on the server-side in `error_command_encoders: HashMap<id::CommandEncoderId, String>` and reported on `CommandEncoderFinish`. Note: This should be reverted when the spec gets updated.~~
3. Correct a major flaw in ErrorScope Model. If multiple operations are issued inside scope and an early operation fails, the promise resolves and script execution continues. The scope, however, was not popped until the results of all its operations were received. This meant that if the user issues another operation, it was assumed to be issued in an error scope that has already been popped by the user, which led to the failure of a number of tests. This is solved by storing a `popped` boolean to check whether `popErrorScope()` has been called on a scope or not. Operation is now issued in the lastest scope for which `popped == false`.
One of the tests used to crash, but it no longer does (All subtests under it fail now). That explains the large number of failing test expectations that have been added. Most of them fail due to the tests being outdated. I'll switch to the updated branch in the next PR.
r?@kvark
---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: -->
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
<!-- Either: -->
- [X] There are tests for these changes
<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->
<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
Diffstat (limited to 'components/script/dom')
-rw-r--r-- | components/script/dom/gpucommandencoder.rs | 217 | ||||
-rw-r--r-- | components/script/dom/gpucomputepassencoder.rs | 44 | ||||
-rw-r--r-- | components/script/dom/gpudevice.rs | 343 | ||||
-rw-r--r-- | components/script/dom/gpurenderpassencoder.rs | 79 | ||||
-rw-r--r-- | components/script/dom/gputexture.rs | 67 |
5 files changed, 430 insertions, 320 deletions
diff --git a/components/script/dom/gpucommandencoder.rs b/components/script/dom/gpucommandencoder.rs index 9fabd77a6fa..7d0a4153289 100644 --- a/components/script/dom/gpucommandencoder.rs +++ b/components/script/dom/gpucommandencoder.rs @@ -125,14 +125,32 @@ impl GPUCommandEncoderMethods for GPUCommandEncoder { &self, descriptor: &GPUComputePassDescriptor, ) -> DomRoot<GPUComputePassEncoder> { + let scope_id = self.device.use_current_scope(); self.set_state( GPUCommandEncoderState::EncodingComputePass, GPUCommandEncoderState::Open, ); + let (compute_pass, res) = if !self.valid.get() { + ( + None, + WebGPUOpResult::ValidationError(String::from( + "CommandEncoder is not in Open State", + )), + ) + } else { + ( + Some(wgpu_com::ComputePass::new(self.encoder.0)), + WebGPUOpResult::Success, + ) + }; + + self.device.handle_server_msg(scope_id, res); + GPUComputePassEncoder::new( &self.global(), self.channel.clone(), &self, + compute_pass, descriptor.parent.label.as_ref().cloned(), ) } @@ -142,102 +160,118 @@ impl GPUCommandEncoderMethods for GPUCommandEncoder { &self, descriptor: &GPURenderPassDescriptor, ) -> DomRoot<GPURenderPassEncoder> { + let scope_id = self.device.use_current_scope(); self.set_state( GPUCommandEncoderState::EncodingRenderPass, GPUCommandEncoderState::Open, ); - let depth_stencil = descriptor.depthStencilAttachment.as_ref().map(|depth| { - let (depth_load_op, clear_depth) = match depth.depthLoadValue { - GPULoadOpOrFloat::GPULoadOp(_) => (wgpu_com::LoadOp::Load, 0.0f32), - GPULoadOpOrFloat::Float(f) => (wgpu_com::LoadOp::Clear, *f), - }; - let (stencil_load_op, clear_stencil) = match depth.stencilLoadValue { - GPUStencilLoadValue::GPULoadOp(_) => (wgpu_com::LoadOp::Load, 0u32), - GPUStencilLoadValue::RangeEnforcedUnsignedLong(l) => (wgpu_com::LoadOp::Clear, l), - }; - let depth_channel = wgpu_com::PassChannel { - load_op: depth_load_op, - store_op: match depth.depthStoreOp { - GPUStoreOp::Store => wgpu_com::StoreOp::Store, - GPUStoreOp::Clear => wgpu_com::StoreOp::Clear, - }, - clear_value: clear_depth, - read_only: depth.depthReadOnly, - }; - let stencil_channel = wgpu_com::PassChannel { - load_op: stencil_load_op, - store_op: match depth.stencilStoreOp { - GPUStoreOp::Store => wgpu_com::StoreOp::Store, - GPUStoreOp::Clear => wgpu_com::StoreOp::Clear, - }, - clear_value: clear_stencil, - read_only: depth.stencilReadOnly, - }; - wgpu_com::DepthStencilAttachmentDescriptor { - attachment: depth.attachment.id().0, - depth: depth_channel, - stencil: stencil_channel, - } - }); - - let desc = wgpu_com::RenderPassDescriptor { - color_attachments: Cow::Owned( - descriptor - .colorAttachments - .iter() - .map(|color| { - let (load_op, clear_value) = match color.loadValue { - GPUColorLoad::GPULoadOp(_) => { - (wgpu_com::LoadOp::Load, wgt::Color::TRANSPARENT) - }, - GPUColorLoad::DoubleSequence(ref s) => { - let mut w = s.clone(); - if w.len() < 3 { - w.resize(3, Finite::wrap(0.0f64)); - } - w.resize(4, Finite::wrap(1.0f64)); - ( + let (render_pass, res) = if !self.valid.get() { + ( + None, + WebGPUOpResult::ValidationError(String::from( + "CommandEncoder is not in Open State", + )), + ) + } else { + let depth_stencil = descriptor.depthStencilAttachment.as_ref().map(|depth| { + let (depth_load_op, clear_depth) = match depth.depthLoadValue { + GPULoadOpOrFloat::GPULoadOp(_) => (wgpu_com::LoadOp::Load, 0.0f32), + GPULoadOpOrFloat::Float(f) => (wgpu_com::LoadOp::Clear, *f), + }; + let (stencil_load_op, clear_stencil) = match depth.stencilLoadValue { + GPUStencilLoadValue::GPULoadOp(_) => (wgpu_com::LoadOp::Load, 0u32), + GPUStencilLoadValue::RangeEnforcedUnsignedLong(l) => { + (wgpu_com::LoadOp::Clear, l) + }, + }; + let depth_channel = wgpu_com::PassChannel { + load_op: depth_load_op, + store_op: match depth.depthStoreOp { + GPUStoreOp::Store => wgpu_com::StoreOp::Store, + GPUStoreOp::Clear => wgpu_com::StoreOp::Clear, + }, + clear_value: clear_depth, + read_only: depth.depthReadOnly, + }; + let stencil_channel = wgpu_com::PassChannel { + load_op: stencil_load_op, + store_op: match depth.stencilStoreOp { + GPUStoreOp::Store => wgpu_com::StoreOp::Store, + GPUStoreOp::Clear => wgpu_com::StoreOp::Clear, + }, + clear_value: clear_stencil, + read_only: depth.stencilReadOnly, + }; + wgpu_com::DepthStencilAttachmentDescriptor { + attachment: depth.attachment.id().0, + depth: depth_channel, + stencil: stencil_channel, + } + }); + + let desc = wgpu_com::RenderPassDescriptor { + color_attachments: Cow::Owned( + descriptor + .colorAttachments + .iter() + .map(|color| { + let (load_op, clear_value) = match color.loadValue { + GPUColorLoad::GPULoadOp(_) => { + (wgpu_com::LoadOp::Load, wgt::Color::TRANSPARENT) + }, + GPUColorLoad::DoubleSequence(ref s) => { + let mut w = s.clone(); + if w.len() < 3 { + w.resize(3, Finite::wrap(0.0f64)); + } + w.resize(4, Finite::wrap(1.0f64)); + ( + wgpu_com::LoadOp::Clear, + wgt::Color { + r: *w[0], + g: *w[1], + b: *w[2], + a: *w[3], + }, + ) + }, + GPUColorLoad::GPUColorDict(ref d) => ( wgpu_com::LoadOp::Clear, wgt::Color { - r: *w[0], - g: *w[1], - b: *w[2], - a: *w[3], + r: *d.r, + g: *d.g, + b: *d.b, + a: *d.a, }, - ) - }, - GPUColorLoad::GPUColorDict(ref d) => ( - wgpu_com::LoadOp::Clear, - wgt::Color { - r: *d.r, - g: *d.g, - b: *d.b, - a: *d.a, + ), + }; + let channel = wgpu_com::PassChannel { + load_op, + store_op: match color.storeOp { + GPUStoreOp::Store => wgpu_com::StoreOp::Store, + GPUStoreOp::Clear => wgpu_com::StoreOp::Clear, }, - ), - }; - let channel = wgpu_com::PassChannel { - load_op, - store_op: match color.storeOp { - GPUStoreOp::Store => wgpu_com::StoreOp::Store, - GPUStoreOp::Clear => wgpu_com::StoreOp::Clear, - }, - clear_value, - read_only: false, - }; - wgpu_com::ColorAttachmentDescriptor { - attachment: color.attachment.id().0, - resolve_target: color.resolveTarget.as_ref().map(|t| t.id().0), - channel, - } - }) - .collect::<Vec<_>>(), - ), - depth_stencil_attachment: depth_stencil.as_ref(), + clear_value, + read_only: false, + }; + wgpu_com::ColorAttachmentDescriptor { + attachment: color.attachment.id().0, + resolve_target: color.resolveTarget.as_ref().map(|t| t.id().0), + channel, + } + }) + .collect::<Vec<_>>(), + ), + depth_stencil_attachment: depth_stencil.as_ref(), + }; + ( + Some(wgpu_com::RenderPass::new(self.encoder.0, desc)), + WebGPUOpResult::Success, + ) }; - let render_pass = wgpu_com::RenderPass::new(self.encoder.0, desc); + self.device.handle_server_msg(scope_id, res); GPURenderPassEncoder::new( &self.global(), @@ -257,10 +291,9 @@ impl GPUCommandEncoderMethods for GPUCommandEncoder { destination_offset: GPUSize64, size: GPUSize64, ) { - let valid = *self.state.borrow() == GPUCommandEncoderState::Open; let scope_id = self.device.use_current_scope(); - if !valid { + if !(*self.state.borrow() == GPUCommandEncoderState::Open) { self.device.handle_server_msg( scope_id, WebGPUOpResult::ValidationError(String::from( @@ -299,10 +332,9 @@ impl GPUCommandEncoderMethods for GPUCommandEncoder { destination: &GPUTextureCopyView, copy_size: GPUExtent3D, ) { - let valid = *self.state.borrow() == GPUCommandEncoderState::Open; let scope_id = self.device.use_current_scope(); - if !valid { + if !(*self.state.borrow() == GPUCommandEncoderState::Open) { self.device.handle_server_msg( scope_id, WebGPUOpResult::ValidationError(String::from( @@ -341,10 +373,9 @@ impl GPUCommandEncoderMethods for GPUCommandEncoder { destination: &GPUBufferCopyView, copy_size: GPUExtent3D, ) { - let valid = *self.state.borrow() == GPUCommandEncoderState::Open; let scope_id = self.device.use_current_scope(); - if !valid { + if !(*self.state.borrow() == GPUCommandEncoderState::Open) { self.device.handle_server_msg( scope_id, WebGPUOpResult::ValidationError(String::from( @@ -383,10 +414,9 @@ impl GPUCommandEncoderMethods for GPUCommandEncoder { destination: &GPUTextureCopyView, copy_size: GPUExtent3D, ) { - let valid = *self.state.borrow() == GPUCommandEncoderState::Open; let scope_id = self.device.use_current_scope(); - if !valid { + if !(*self.state.borrow() == GPUCommandEncoderState::Open) { self.device.handle_server_msg( scope_id, WebGPUOpResult::ValidationError(String::from( @@ -423,6 +453,7 @@ impl GPUCommandEncoderMethods for GPUCommandEncoder { WebGPURequest::CommandEncoderFinish { command_encoder_id: self.encoder.0, device_id: self.device.id().0, + is_error: !self.valid.get(), // TODO(zakorgy): We should use `_descriptor` here after it's not empty // and the underlying wgpu-core struct is serializable }, diff --git a/components/script/dom/gpucomputepassencoder.rs b/components/script/dom/gpucomputepassencoder.rs index c55eaf86fe2..a73ee8bde34 100644 --- a/components/script/dom/gpucomputepassencoder.rs +++ b/components/script/dom/gpucomputepassencoder.rs @@ -33,13 +33,14 @@ impl GPUComputePassEncoder { fn new_inherited( channel: WebGPU, parent: &GPUCommandEncoder, + compute_pass: Option<ComputePass>, label: Option<USVString>, ) -> Self { Self { channel, reflector_: Reflector::new(), label: DomRefCell::new(label), - compute_pass: DomRefCell::new(Some(ComputePass::new(parent.id().0))), + compute_pass: DomRefCell::new(compute_pass), command_encoder: Dom::from_ref(parent), } } @@ -48,10 +49,16 @@ impl GPUComputePassEncoder { global: &GlobalScope, channel: WebGPU, parent: &GPUCommandEncoder, + compute_pass: Option<ComputePass>, label: Option<USVString>, ) -> DomRoot<Self> { reflect_dom_object( - Box::new(GPUComputePassEncoder::new_inherited(channel, parent, label)), + Box::new(GPUComputePassEncoder::new_inherited( + channel, + parent, + compute_pass, + label, + )), global, ) } @@ -88,24 +95,23 @@ impl GPUComputePassEncoderMethods for GPUComputePassEncoder { /// https://gpuweb.github.io/gpuweb/#dom-gpurenderpassencoder-endpass fn EndPass(&self) { - if let Some(compute_pass) = self.compute_pass.borrow_mut().take() { - self.channel - .0 - .send(( - self.command_encoder.device().use_current_scope(), - WebGPURequest::RunComputePass { - command_encoder_id: self.command_encoder.id().0, - device_id: self.command_encoder.device().id().0, - compute_pass, - }, - )) - .expect("Failed to send RunComputePass"); + let compute_pass = self.compute_pass.borrow_mut().take(); + self.channel + .0 + .send(( + self.command_encoder.device().use_current_scope(), + WebGPURequest::RunComputePass { + command_encoder_id: self.command_encoder.id().0, + device_id: self.command_encoder.device().id().0, + compute_pass, + }, + )) + .expect("Failed to send RunComputePass"); - self.command_encoder.set_state( - GPUCommandEncoderState::Open, - GPUCommandEncoderState::EncodingComputePass, - ); - } + self.command_encoder.set_state( + GPUCommandEncoderState::Open, + GPUCommandEncoderState::EncodingComputePass, + ); } /// https://gpuweb.github.io/gpuweb/#dom-gpuprogrammablepassencoder-setbindgroup diff --git a/components/script/dom/gpudevice.rs b/components/script/dom/gpudevice.rs index 9e3e6d4976d..e54c0d1540a 100644 --- a/components/script/dom/gpudevice.rs +++ b/components/script/dom/gpudevice.rs @@ -64,7 +64,7 @@ use crate::script_runtime::JSContext as SafeJSContext; use dom_struct::dom_struct; use js::jsapi::{Heap, JSObject}; use std::borrow::Cow; -use std::cell::RefCell; +use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::ptr::NonNull; use std::rc::Rc; @@ -83,9 +83,16 @@ struct ErrorScopeInfo { } #[derive(JSTraceable, MallocSizeOf)] +struct ErrorScopeMetadata { + id: ErrorScopeId, + filter: GPUErrorFilter, + popped: Cell<bool>, +} + +#[derive(JSTraceable, MallocSizeOf)] struct ScopeContext { error_scopes: HashMap<ErrorScopeId, ErrorScopeInfo>, - scope_stack: Vec<(ErrorScopeId, GPUErrorFilter)>, + scope_stack: Vec<ErrorScopeMetadata>, next_scope_id: ErrorScopeId, } @@ -189,8 +196,8 @@ impl GPUDevice { .scope_stack .iter() .rev() - .find(|&&(id, fil)| id <= s_id && fil == filter) - .map(|(id, _)| *id); + .find(|meta| meta.id <= s_id && meta.filter == filter) + .map(|meta| meta.id); if let Some(s) = scop { self.handle_error(s, err); } else { @@ -237,14 +244,19 @@ impl GPUDevice { }; if remove { let _ = context.error_scopes.remove(&scope); - context.scope_stack.retain(|(id, _)| *id != scope); + context.scope_stack.retain(|meta| meta.id != scope); } } pub fn use_current_scope(&self) -> Option<ErrorScopeId> { let mut context = self.scope_context.borrow_mut(); - let scope_id = context.scope_stack.last().copied(); - scope_id.and_then(|(s_id, _)| { + let scope_id = context + .scope_stack + .iter() + .rev() + .find(|meta| !meta.popped.get()) + .map(|meta| meta.id); + scope_id.and_then(|s_id| { context.error_scopes.get_mut(&s_id).map(|mut scope| { scope.op_count += 1; s_id @@ -293,15 +305,12 @@ impl GPUDeviceMethods for GPUDevice { /// https://gpuweb.github.io/gpuweb/#dom-gpudevice-createbuffer fn CreateBuffer(&self, descriptor: &GPUBufferDescriptor) -> DomRoot<GPUBuffer> { - let wgpu_descriptor = wgt::BufferDescriptor { + let desc = wgt::BufferUsage::from_bits(descriptor.usage).map(|usg| wgt::BufferDescriptor { label: descriptor.parent.label.as_ref().map(|s| s.to_string()), size: descriptor.size, - usage: match wgt::BufferUsage::from_bits(descriptor.usage) { - Some(u) => u, - None => wgt::BufferUsage::empty(), - }, + usage: usg, mapped_at_creation: descriptor.mappedAtCreation, - }; + }); let id = self .global() .wgpu_id_hub() @@ -309,6 +318,13 @@ impl GPUDeviceMethods for GPUDevice { .create_buffer_id(self.device.0.backend()); let scope_id = self.use_current_scope(); + if desc.is_none() { + self.handle_server_msg( + scope_id, + WebGPUOpResult::ValidationError(String::from("Invalid GPUBufferUsage")), + ); + } + self.channel .0 .send(( @@ -316,7 +332,7 @@ impl GPUDeviceMethods for GPUDevice { WebGPURequest::CreateBuffer { device_id: self.device.0, buffer_id: id, - descriptor: wgpu_descriptor, + descriptor: desc, }, )) .expect("Failed to create WebGPU buffer"); @@ -357,13 +373,17 @@ impl GPUDeviceMethods for GPUDevice { &self, descriptor: &GPUBindGroupLayoutDescriptor, ) -> DomRoot<GPUBindGroupLayout> { + let mut valid = true; let entries = descriptor .entries .iter() .map(|bind| { let visibility = match wgt::ShaderStage::from_bits(bind.visibility) { Some(visibility) => visibility, - None => wgt::ShaderStage::from_bits(0).unwrap(), + None => { + valid = false; + wgt::ShaderStage::empty() + }, }; let ty = match bind.type_ { GPUBindingType::Uniform_buffer => wgt::BindingType::UniformBuffer { @@ -435,13 +455,21 @@ impl GPUDeviceMethods for GPUDevice { let scope_id = self.use_current_scope(); - let desc = wgt::BindGroupLayoutDescriptor { - label: descriptor - .parent - .label - .as_ref() - .map(|s| Cow::Owned(s.to_string())), - entries: Cow::Owned(entries), + let desc = if valid { + Some(wgt::BindGroupLayoutDescriptor { + label: descriptor + .parent + .label + .as_ref() + .map(|s| Cow::Owned(s.to_string())), + entries: Cow::Owned(entries), + }) + } else { + self.handle_server_msg( + scope_id, + WebGPUOpResult::ValidationError(String::from("Invalid GPUShaderStage")), + ); + None }; let bind_group_layout_id = self @@ -695,22 +723,20 @@ impl GPUDeviceMethods for GPUDevice { /// https://gpuweb.github.io/gpuweb/#dom-gpudevice-createtexture fn CreateTexture(&self, descriptor: &GPUTextureDescriptor) -> DomRoot<GPUTexture> { let size = convert_texture_size_to_dict(&descriptor.size); - let desc = wgt::TextureDescriptor { - label: descriptor.parent.label.as_ref().map(|s| s.to_string()), - size: convert_texture_size_to_wgt(&size), - mip_level_count: descriptor.mipLevelCount, - sample_count: descriptor.sampleCount, - dimension: match descriptor.dimension { - GPUTextureDimension::_1d => wgt::TextureDimension::D1, - GPUTextureDimension::_2d => wgt::TextureDimension::D2, - GPUTextureDimension::_3d => wgt::TextureDimension::D3, - }, - format: convert_texture_format(descriptor.format), - usage: match wgt::TextureUsage::from_bits(descriptor.usage) { - Some(t) => t, - None => wgt::TextureUsage::empty(), - }, - }; + let desc = + wgt::TextureUsage::from_bits(descriptor.usage).map(|usg| wgt::TextureDescriptor { + label: descriptor.parent.label.as_ref().map(|s| s.to_string()), + size: convert_texture_size_to_wgt(&size), + mip_level_count: descriptor.mipLevelCount, + sample_count: descriptor.sampleCount, + dimension: match descriptor.dimension { + GPUTextureDimension::_1d => wgt::TextureDimension::D1, + GPUTextureDimension::_2d => wgt::TextureDimension::D2, + GPUTextureDimension::_3d => wgt::TextureDimension::D3, + }, + format: convert_texture_format(descriptor.format), + usage: usg, + }); let texture_id = self .global() @@ -719,7 +745,12 @@ impl GPUDeviceMethods for GPUDevice { .create_texture_id(self.device.0.backend()); let scope_id = self.use_current_scope(); - + if desc.is_none() { + self.handle_server_msg( + scope_id, + WebGPUOpResult::ValidationError(String::from("Invalid GPUTextureUsage")), + ); + } self.channel .0 .send(( @@ -803,110 +834,124 @@ impl GPUDeviceMethods for GPUDevice { ) -> DomRoot<GPURenderPipeline> { let ref rs_desc = descriptor.rasterizationState; let ref vs_desc = descriptor.vertexState; + let scope_id = self.use_current_scope(); + let mut valid = true; + let color_states = Cow::Owned( + descriptor + .colorStates + .iter() + .map(|state| wgt::ColorStateDescriptor { + format: convert_texture_format(state.format), + alpha_blend: convert_blend_descriptor(&state.alphaBlend), + color_blend: convert_blend_descriptor(&state.colorBlend), + write_mask: match wgt::ColorWrite::from_bits(state.writeMask) { + Some(mask) => mask, + None => { + valid = false; + wgt::ColorWrite::empty() + }, + }, + }) + .collect::<Vec<_>>(), + ); - let desc = wgpu_pipe::RenderPipelineDescriptor { - layout: descriptor.parent.layout.id().0, - vertex_stage: wgpu_pipe::ProgrammableStageDescriptor { - module: descriptor.vertexStage.module.id().0, - entry_point: Cow::Owned(descriptor.vertexStage.entryPoint.to_string()), - }, - fragment_stage: descriptor.fragmentStage.as_ref().map(|stage| { - wgpu_pipe::ProgrammableStageDescriptor { - module: stage.module.id().0, - entry_point: Cow::Owned(stage.entryPoint.to_string()), - } - }), - rasterization_state: Some(wgt::RasterizationStateDescriptor { - front_face: match rs_desc.frontFace { - GPUFrontFace::Ccw => wgt::FrontFace::Ccw, - GPUFrontFace::Cw => wgt::FrontFace::Cw, + let desc = if valid { + Some(wgpu_pipe::RenderPipelineDescriptor { + layout: descriptor.parent.layout.id().0, + vertex_stage: wgpu_pipe::ProgrammableStageDescriptor { + module: descriptor.vertexStage.module.id().0, + entry_point: Cow::Owned(descriptor.vertexStage.entryPoint.to_string()), }, - cull_mode: match rs_desc.cullMode { - GPUCullMode::None => wgt::CullMode::None, - GPUCullMode::Front => wgt::CullMode::Front, - GPUCullMode::Back => wgt::CullMode::Back, + fragment_stage: descriptor.fragmentStage.as_ref().map(|stage| { + wgpu_pipe::ProgrammableStageDescriptor { + module: stage.module.id().0, + entry_point: Cow::Owned(stage.entryPoint.to_string()), + } + }), + rasterization_state: Some(wgt::RasterizationStateDescriptor { + front_face: match rs_desc.frontFace { + GPUFrontFace::Ccw => wgt::FrontFace::Ccw, + GPUFrontFace::Cw => wgt::FrontFace::Cw, + }, + cull_mode: match rs_desc.cullMode { + GPUCullMode::None => wgt::CullMode::None, + GPUCullMode::Front => wgt::CullMode::Front, + GPUCullMode::Back => wgt::CullMode::Back, + }, + clamp_depth: rs_desc.clampDepth, + depth_bias: rs_desc.depthBias, + depth_bias_slope_scale: *rs_desc.depthBiasSlopeScale, + depth_bias_clamp: *rs_desc.depthBiasClamp, + }), + primitive_topology: match descriptor.primitiveTopology { + GPUPrimitiveTopology::Point_list => wgt::PrimitiveTopology::PointList, + GPUPrimitiveTopology::Line_list => wgt::PrimitiveTopology::LineList, + GPUPrimitiveTopology::Line_strip => wgt::PrimitiveTopology::LineStrip, + GPUPrimitiveTopology::Triangle_list => wgt::PrimitiveTopology::TriangleList, + GPUPrimitiveTopology::Triangle_strip => wgt::PrimitiveTopology::TriangleStrip, }, - clamp_depth: rs_desc.clampDepth, - depth_bias: rs_desc.depthBias, - depth_bias_slope_scale: *rs_desc.depthBiasSlopeScale, - depth_bias_clamp: *rs_desc.depthBiasClamp, - }), - primitive_topology: match descriptor.primitiveTopology { - GPUPrimitiveTopology::Point_list => wgt::PrimitiveTopology::PointList, - GPUPrimitiveTopology::Line_list => wgt::PrimitiveTopology::LineList, - GPUPrimitiveTopology::Line_strip => wgt::PrimitiveTopology::LineStrip, - GPUPrimitiveTopology::Triangle_list => wgt::PrimitiveTopology::TriangleList, - GPUPrimitiveTopology::Triangle_strip => wgt::PrimitiveTopology::TriangleStrip, - }, - color_states: Cow::Owned( - descriptor - .colorStates - .iter() - .map(|state| wgt::ColorStateDescriptor { - format: convert_texture_format(state.format), - alpha_blend: convert_blend_descriptor(&state.alphaBlend), - color_blend: convert_blend_descriptor(&state.colorBlend), - write_mask: match wgt::ColorWrite::from_bits(state.writeMask) { - Some(mask) => mask, - None => wgt::ColorWrite::empty(), + color_states, + depth_stencil_state: descriptor.depthStencilState.as_ref().map(|dss_desc| { + wgt::DepthStencilStateDescriptor { + format: convert_texture_format(dss_desc.format), + depth_write_enabled: dss_desc.depthWriteEnabled, + depth_compare: convert_compare_function(dss_desc.depthCompare), + stencil_front: wgt::StencilStateFaceDescriptor { + compare: convert_compare_function(dss_desc.stencilFront.compare), + fail_op: convert_stencil_op(dss_desc.stencilFront.failOp), + depth_fail_op: convert_stencil_op(dss_desc.stencilFront.depthFailOp), + pass_op: convert_stencil_op(dss_desc.stencilFront.passOp), }, - }) - .collect::<Vec<_>>(), - ), - depth_stencil_state: descriptor.depthStencilState.as_ref().map(|dss_desc| { - wgt::DepthStencilStateDescriptor { - format: convert_texture_format(dss_desc.format), - depth_write_enabled: dss_desc.depthWriteEnabled, - depth_compare: convert_compare_function(dss_desc.depthCompare), - stencil_front: wgt::StencilStateFaceDescriptor { - compare: convert_compare_function(dss_desc.stencilFront.compare), - fail_op: convert_stencil_op(dss_desc.stencilFront.failOp), - depth_fail_op: convert_stencil_op(dss_desc.stencilFront.depthFailOp), - pass_op: convert_stencil_op(dss_desc.stencilFront.passOp), - }, - stencil_back: wgt::StencilStateFaceDescriptor { - compare: convert_compare_function(dss_desc.stencilBack.compare), - fail_op: convert_stencil_op(dss_desc.stencilBack.failOp), - depth_fail_op: convert_stencil_op(dss_desc.stencilBack.depthFailOp), - pass_op: convert_stencil_op(dss_desc.stencilBack.passOp), + stencil_back: wgt::StencilStateFaceDescriptor { + compare: convert_compare_function(dss_desc.stencilBack.compare), + fail_op: convert_stencil_op(dss_desc.stencilBack.failOp), + depth_fail_op: convert_stencil_op(dss_desc.stencilBack.depthFailOp), + pass_op: convert_stencil_op(dss_desc.stencilBack.passOp), + }, + stencil_read_mask: dss_desc.stencilReadMask, + stencil_write_mask: dss_desc.stencilWriteMask, + } + }), + vertex_state: wgt::VertexStateDescriptor { + index_format: match vs_desc.indexFormat { + GPUIndexFormat::Uint16 => wgt::IndexFormat::Uint16, + GPUIndexFormat::Uint32 => wgt::IndexFormat::Uint32, }, - stencil_read_mask: dss_desc.stencilReadMask, - stencil_write_mask: dss_desc.stencilWriteMask, - } - }), - vertex_state: wgt::VertexStateDescriptor { - index_format: match vs_desc.indexFormat { - GPUIndexFormat::Uint16 => wgt::IndexFormat::Uint16, - GPUIndexFormat::Uint32 => wgt::IndexFormat::Uint32, + vertex_buffers: Cow::Owned( + vs_desc + .vertexBuffers + .iter() + .map(|buffer| wgt::VertexBufferDescriptor { + stride: buffer.arrayStride, + step_mode: match buffer.stepMode { + GPUInputStepMode::Vertex => wgt::InputStepMode::Vertex, + GPUInputStepMode::Instance => wgt::InputStepMode::Instance, + }, + attributes: Cow::Owned( + buffer + .attributes + .iter() + .map(|att| wgt::VertexAttributeDescriptor { + format: convert_vertex_format(att.format), + offset: att.offset, + shader_location: att.shaderLocation, + }) + .collect::<Vec<_>>(), + ), + }) + .collect::<Vec<_>>(), + ), }, - vertex_buffers: Cow::Owned( - vs_desc - .vertexBuffers - .iter() - .map(|buffer| wgt::VertexBufferDescriptor { - stride: buffer.arrayStride, - step_mode: match buffer.stepMode { - GPUInputStepMode::Vertex => wgt::InputStepMode::Vertex, - GPUInputStepMode::Instance => wgt::InputStepMode::Instance, - }, - attributes: Cow::Owned( - buffer - .attributes - .iter() - .map(|att| wgt::VertexAttributeDescriptor { - format: convert_vertex_format(att.format), - offset: att.offset, - shader_location: att.shaderLocation, - }) - .collect::<Vec<_>>(), - ), - }) - .collect::<Vec<_>>(), - ), - }, - sample_count: descriptor.sampleCount, - sample_mask: descriptor.sampleMask, - alpha_to_coverage_enabled: descriptor.alphaToCoverageEnabled, + sample_count: descriptor.sampleCount, + sample_mask: descriptor.sampleMask, + alpha_to_coverage_enabled: descriptor.alphaToCoverageEnabled, + }) + } else { + self.handle_server_msg( + scope_id, + WebGPUOpResult::ValidationError(String::from("Invalid GPUColorWriteFlags")), + ); + None }; let render_pipeline_id = self @@ -915,8 +960,6 @@ impl GPUDeviceMethods for GPUDevice { .lock() .create_render_pipeline_id(self.device.0.backend()); - let scope_id = self.use_current_scope(); - self.channel .0 .send(( @@ -986,7 +1029,11 @@ impl GPUDeviceMethods for GPUDevice { promise: None, }; let res = context.error_scopes.insert(scope_id, err_scope); - context.scope_stack.push((scope_id, filter)); + context.scope_stack.push(ErrorScopeMetadata { + id: scope_id, + filter, + popped: Cell::new(false), + }); assert!(res.is_none()); } @@ -994,12 +1041,14 @@ impl GPUDeviceMethods for GPUDevice { fn PopErrorScope(&self, comp: InRealm) -> Rc<Promise> { let mut context = self.scope_context.borrow_mut(); let promise = Promise::new_in_current_realm(&self.global(), comp); - let scope_id = if let Some((e, _)) = context.scope_stack.last() { - *e - } else { - promise.reject_error(Error::Operation); - return promise; - }; + let scope_id = + if let Some(meta) = context.scope_stack.iter().rev().find(|m| !m.popped.get()) { + meta.popped.set(true); + meta.id + } else { + promise.reject_error(Error::Operation); + return promise; + }; let remove = if let Some(mut err_scope) = context.error_scopes.get_mut(&scope_id) { if let Some(ref e) = err_scope.error { match e { @@ -1017,7 +1066,7 @@ impl GPUDeviceMethods for GPUDevice { }; if remove { let _ = context.error_scopes.remove(&scope_id); - let _ = context.scope_stack.pop(); + context.scope_stack.retain(|meta| meta.id != scope_id); } promise } diff --git a/components/script/dom/gpurenderpassencoder.rs b/components/script/dom/gpurenderpassencoder.rs index 0e68b384ac5..9d1abf12511 100644 --- a/components/script/dom/gpurenderpassencoder.rs +++ b/components/script/dom/gpurenderpassencoder.rs @@ -35,7 +35,7 @@ pub struct GPURenderPassEncoder { impl GPURenderPassEncoder { fn new_inherited( channel: WebGPU, - render_pass: RenderPass, + render_pass: Option<RenderPass>, parent: &GPUCommandEncoder, label: Option<USVString>, ) -> Self { @@ -43,7 +43,7 @@ impl GPURenderPassEncoder { channel, reflector_: Reflector::new(), label: DomRefCell::new(label), - render_pass: DomRefCell::new(Some(render_pass)), + render_pass: DomRefCell::new(render_pass), command_encoder: Dom::from_ref(parent), } } @@ -51,7 +51,7 @@ impl GPURenderPassEncoder { pub fn new( global: &GlobalScope, channel: WebGPU, - render_pass: RenderPass, + render_pass: Option<RenderPass>, parent: &GPUCommandEncoder, label: Option<USVString>, ) -> DomRoot<Self> { @@ -126,27 +126,27 @@ impl GPURenderPassEncoderMethods for GPURenderPassEncoder { /// https://gpuweb.github.io/gpuweb/#dom-gpurenderpassencoder-setblendcolor fn SetBlendColor(&self, color: GPUColor) { - let colors = match color { - GPUColor::GPUColorDict(d) => wgt::Color { - r: *d.r, - g: *d.g, - b: *d.b, - a: *d.a, - }, - GPUColor::DoubleSequence(mut s) => { - if s.len() < 3 { - s.resize(3, Finite::wrap(0.0f64)); - } - s.resize(4, Finite::wrap(1.0f64)); - wgt::Color { - r: *s[0], - g: *s[1], - b: *s[2], - a: *s[3], - } - }, - }; if let Some(render_pass) = self.render_pass.borrow_mut().as_mut() { + let colors = match color { + GPUColor::GPUColorDict(d) => wgt::Color { + r: *d.r, + g: *d.g, + b: *d.b, + a: *d.a, + }, + GPUColor::DoubleSequence(mut s) => { + if s.len() < 3 { + s.resize(3, Finite::wrap(0.0f64)); + } + s.resize(4, Finite::wrap(1.0f64)); + wgt::Color { + r: *s[0], + g: *s[1], + b: *s[2], + a: *s[3], + } + }, + }; wgpu_render::wgpu_render_pass_set_blend_color(render_pass, &colors); } } @@ -160,24 +160,23 @@ impl GPURenderPassEncoderMethods for GPURenderPassEncoder { /// https://gpuweb.github.io/gpuweb/#dom-gpurenderpassencoder-endpass fn EndPass(&self) { - if let Some(render_pass) = self.render_pass.borrow_mut().take() { - self.channel - .0 - .send(( - self.command_encoder.device().use_current_scope(), - WebGPURequest::RunRenderPass { - command_encoder_id: self.command_encoder.id().0, - device_id: self.command_encoder.device().id().0, - render_pass, - }, - )) - .expect("Failed to send RunRenderPass"); + let render_pass = self.render_pass.borrow_mut().take(); + self.channel + .0 + .send(( + self.command_encoder.device().use_current_scope(), + WebGPURequest::RunRenderPass { + command_encoder_id: self.command_encoder.id().0, + device_id: self.command_encoder.device().id().0, + render_pass, + }, + )) + .expect("Failed to send RunRenderPass"); - self.command_encoder.set_state( - GPUCommandEncoderState::Open, - GPUCommandEncoderState::EncodingRenderPass, - ); - } + self.command_encoder.set_state( + GPUCommandEncoderState::Open, + GPUCommandEncoderState::EncodingRenderPass, + ); } /// https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-setpipeline diff --git a/components/script/dom/gputexture.rs b/components/script/dom/gputexture.rs index 64b56e82607..a7137d1bd61 100644 --- a/components/script/dom/gputexture.rs +++ b/components/script/dom/gputexture.rs @@ -16,8 +16,11 @@ use crate::dom::globalscope::GlobalScope; use crate::dom::gpudevice::{convert_texture_format, convert_texture_view_dimension, GPUDevice}; use crate::dom::gputextureview::GPUTextureView; use dom_struct::dom_struct; +use std::num::NonZeroU32; use std::string::String; -use webgpu::{wgt, WebGPU, WebGPURequest, WebGPUTexture, WebGPUTextureView}; +use webgpu::{ + identity::WebGPUOpResult, wgt, WebGPU, WebGPURequest, WebGPUTexture, WebGPUTextureView, +}; #[dom_struct] pub struct GPUTexture { @@ -137,24 +140,48 @@ impl GPUTextureMethods for GPUTexture { }; let format = descriptor.format.unwrap_or(self.format); - - let desc = wgt::TextureViewDescriptor { - label: descriptor - .parent - .label - .as_ref() - .map(|s| String::from(s.as_ref())), - 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, - GPUTextureAspect::Depth_only => wgt::TextureAspect::DepthOnly, - }, - base_mip_level: descriptor.baseMipLevel, - level_count: descriptor.mipLevelCount.as_ref().copied(), - base_array_layer: descriptor.baseArrayLayer, - array_layer_count: descriptor.arrayLayerCount.as_ref().copied(), + let scope_id = self.device.use_current_scope(); + let mut valid = true; + let level_count = descriptor.mipLevelCount.and_then(|count| { + if count == 0 { + valid = false; + } + NonZeroU32::new(count) + }); + let array_layer_count = descriptor.arrayLayerCount.and_then(|count| { + if count == 0 { + valid = false; + } + NonZeroU32::new(count) + }); + + let desc = if valid { + Some(wgt::TextureViewDescriptor { + label: descriptor + .parent + .label + .as_ref() + .map(|s| String::from(s.as_ref())), + 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, + GPUTextureAspect::Depth_only => wgt::TextureAspect::DepthOnly, + }, + base_mip_level: descriptor.baseMipLevel, + level_count, + base_array_layer: descriptor.baseArrayLayer, + array_layer_count, + }) + } else { + self.device.handle_server_msg( + scope_id, + WebGPUOpResult::ValidationError(String::from( + "arrayLayerCount and mipLevelCount cannot be 0", + )), + ); + None }; let texture_view_id = self @@ -163,8 +190,6 @@ impl GPUTextureMethods for GPUTexture { .lock() .create_texture_view_id(self.device.id().0.backend()); - let scope_id = self.device.use_current_scope(); - self.channel .0 .send(( |