diff --git a/ext/webgpu/wrap/adapter.rs b/ext/webgpu/wrap/adapter.rs index a314c8854e..fc73b1e8cf 100644 --- a/ext/webgpu/wrap/adapter.rs +++ b/ext/webgpu/wrap/adapter.rs @@ -10,6 +10,7 @@ use deno_core::GarbageCollected; use deno_core::WebIDL; use super::device::GPUDevice; +use crate::wrap::webidl::features_to_feature_names; use crate::Instance; #[derive(WebIDL)] @@ -67,6 +68,7 @@ impl GPUAdapter { fn features(&self, scope: &mut v8::HandleScope) -> v8::Global { self.features.get(scope, || { let features = self.instance.adapter_features(self.id); + let features = features_to_feature_names(features); /* function createGPUSupportedFeatures(features) { @@ -106,8 +108,7 @@ impl GPUAdapter { #[webidl] descriptor: GPUDeviceDescriptor, ) -> Result { let features = self.instance.adapter_features(self.id); - let supported_features = - crate::wrap::webidl::features_to_feature_names(&features); + let supported_features = features_to_feature_names(features); let required_features = descriptor .required_features .iter() @@ -150,10 +151,11 @@ impl GPUAdapter { queue, label: descriptor.label, queue_obj: SameObject::new(), - info: self.info.clone(), + adapter_info: self.info.clone(), error_handler: Arc::new(super::error::DeviceErrorHandler::new(sender)), adapter: self.id, lost_receiver: receiver, + limits: SameObject::new(), }) } } @@ -171,7 +173,7 @@ pub enum CreateDeviceError { Device(#[from] wgpu_core::instance::RequestDeviceError), } -pub struct GPUSupportedLimits(wgpu_types::Limits); +pub struct GPUSupportedLimits(pub wgpu_types::Limits); impl GarbageCollected for GPUSupportedLimits {} diff --git a/ext/webgpu/wrap/buffer.rs b/ext/webgpu/wrap/buffer.rs index 41abf33ead..ed470e1688 100644 --- a/ext/webgpu/wrap/buffer.rs +++ b/ext/webgpu/wrap/buffer.rs @@ -37,6 +37,9 @@ pub enum BufferError { #[class("DOMExceptionOperationError")] #[error(transparent)] Access(#[from] wgpu_core::resource::BufferAccessError), + #[class(inherit)] + #[error(transparent)] + Other(#[from] JsErrorBox), } pub struct GPUBuffer { @@ -178,7 +181,13 @@ impl GPUBuffer { if err.is_some() { self.error_handler.push_error(err); - // TODO: throw new DOMException("validation error occurred", "OperationError"); + return Err( + JsErrorBox::new( + "DOMExceptionOperationError", + "validation error occurred", + ) + .into(), + ); } } @@ -231,7 +240,7 @@ impl GPUBuffer { let ab = v8::ArrayBuffer::new(scope, slice.len()); v8::Uint8Array::new(scope, ab, 0, slice.len()); - // TODO: sure buf + // TODO: store buf self .mapped_js_buffers .borrow_mut() diff --git a/ext/webgpu/wrap/command_encoder.rs b/ext/webgpu/wrap/command_encoder.rs index c8e1163853..b3d02d1c2b 100644 --- a/ext/webgpu/wrap/command_encoder.rs +++ b/ext/webgpu/wrap/command_encoder.rs @@ -7,6 +7,7 @@ use deno_core::cppgc::Ptr; use deno_core::op2; use deno_core::GarbageCollected; use deno_core::WebIDL; +use deno_error::JsErrorBox; use wgpu_core::command::ImageCopyBuffer; use wgpu_core::command::PassChannel; @@ -39,7 +40,7 @@ impl GPUCommandEncoder { fn begin_render_pass( &self, #[webidl] descriptor: crate::wrap::render_pass::GPURenderPassDescriptor, - ) -> GPURenderPassEncoder { + ) -> Result { let color_attachments = Cow::Owned( descriptor .color_attachments @@ -66,9 +67,11 @@ impl GPUCommandEncoder { let depth_stencil_attachment = descriptor.depth_stencil_attachment.map(|attachment| { - // TODO: `depthLoadOp` is cheked to ensure its value is not "clear" - // when `depthClearValue` is undefined, so the default 0.0 doesn't matter. - wgpu_core::command::RenderPassDepthStencilAttachment { + if attachment.depth_load_op.as_ref().is_some_and(|op| matches!(op, GPULoadOp::Clear)) && attachment.depth_clear_value.is_none() { + return Err(JsErrorBox::type_error(r#"'depthClearValue' must be specified when 'depthLoadOp' is "clear""#)); + } + + Ok(wgpu_core::command::RenderPassDepthStencilAttachment { view: attachment.view.id, depth: PassChannel { load_op: attachment.depth_load_op.unwrap_or(GPULoadOp::Load).into(), @@ -91,8 +94,8 @@ impl GPUCommandEncoder { clear_value: attachment.stencil_clear_value, read_only: false, }, - } - }); + }) + }).transpose()?; let timestamp_writes = descriptor.timestamp_writes.map(|timestamp_writes| { @@ -120,12 +123,12 @@ impl GPUCommandEncoder { self.error_handler.push_error(err); - GPURenderPassEncoder { + Ok(GPURenderPassEncoder { instance: self.instance.clone(), error_handler: self.error_handler.clone(), render_pass: RefCell::new(render_pass), label: descriptor.label, - } + }) } #[cppgc] diff --git a/ext/webgpu/wrap/compute_pass.rs b/ext/webgpu/wrap/compute_pass.rs index 520ac4af75..5990f09023 100644 --- a/ext/webgpu/wrap/compute_pass.rs +++ b/ext/webgpu/wrap/compute_pass.rs @@ -9,6 +9,7 @@ use deno_core::v8; use deno_core::webidl::IntOptions; use deno_core::webidl::Nullable; use deno_core::webidl::WebIdlConverter; +use deno_core::webidl::WebIdlError; use deno_core::GarbageCollected; use deno_core::WebIDL; @@ -128,7 +129,7 @@ impl GPUComputePassEncoder { dynamic_offsets: v8::Local<'a, v8::Value>, dynamic_offsets_data_start: v8::Local<'a, v8::Value>, dynamic_offsets_data_length: v8::Local<'a, v8::Value>, - ) { + ) -> Result<(), WebIdlError> { const PREFIX: &str = "Failed to execute 'setBindGroup' on 'GPUComputePassEncoder'"; let offsets = @@ -142,8 +143,7 @@ impl GPUComputePassEncoder { clamp: false, enforce_range: true, }, - ) - .unwrap(); // TODO: dont unwrap err + )?; let len = u32::convert( scope, dynamic_offsets, @@ -153,8 +153,7 @@ impl GPUComputePassEncoder { clamp: false, enforce_range: true, }, - ) - .unwrap(); // TODO: dont unwrap err + )?; // TODO @@ -169,8 +168,7 @@ impl GPUComputePassEncoder { clamp: false, enforce_range: true, }, - ) - .unwrap() // TODO: dont unwrap err + )? .unwrap_or_default() }; @@ -185,6 +183,8 @@ impl GPUComputePassEncoder { .err(); self.error_handler.push_error(err); + + Ok(()) } } diff --git a/ext/webgpu/wrap/device.rs b/ext/webgpu/wrap/device.rs index bd0f15f4bd..483977258a 100644 --- a/ext/webgpu/wrap/device.rs +++ b/ext/webgpu/wrap/device.rs @@ -23,6 +23,7 @@ use super::sampler::GPUSampler; use super::shader::GPUShaderModule; use super::texture::GPUTexture; use crate::wrap::adapter::GPUAdapterInfo; +use crate::wrap::adapter::GPUSupportedLimits; use crate::wrap::command_encoder::GPUCommandEncoder; use crate::wrap::query_set::GPUQuerySet; use crate::wrap::render_bundle::GPURenderBundleEncoder; @@ -37,10 +38,11 @@ pub struct GPUDevice { pub label: String, - //TODO: pub features: SameObject<()>, - //TODO: pub limits: SameObject<()>, + pub features: SameObject, + pub limits: SameObject, + pub adapter_info: Arc>, + pub queue_obj: SameObject, - pub info: Arc>, pub error_handler: super::error::ErrorHandler, pub lost_receiver: tokio::sync::oneshot::Receiver<()>, @@ -60,8 +62,12 @@ impl GPUDevice { } #[getter] - fn limits(&self) { - todo!() + #[global] + fn limits(&self, scope: &mut v8::HandleScope) -> v8::Global { + self.limits.get(scope, || { + let adapter_limits = self.instance.device_limits(self.id); + GPUSupportedLimits(adapter_limits) + }) } #[getter] @@ -70,7 +76,7 @@ impl GPUDevice { &self, scope: &mut v8::HandleScope, ) -> v8::Global { - self.info.get(scope, || { + self.adapter_info.get(scope, || { let info = self.instance.adapter_get_info(self.adapter); GPUAdapterInfo(info) }) diff --git a/ext/webgpu/wrap/render_bundle.rs b/ext/webgpu/wrap/render_bundle.rs index 9f8e976436..1a10f36077 100644 --- a/ext/webgpu/wrap/render_bundle.rs +++ b/ext/webgpu/wrap/render_bundle.rs @@ -10,9 +10,11 @@ use deno_core::v8; use deno_core::webidl::IntOptions; use deno_core::webidl::Nullable; use deno_core::webidl::WebIdlConverter; +use deno_core::webidl::WebIdlError; use deno_core::webidl::WebIdlInterfaceConverter; use deno_core::GarbageCollected; use deno_core::WebIDL; +use deno_error::JsErrorBox; use crate::wrap::buffer::GPUBuffer; use crate::wrap::texture::GPUTextureFormat; @@ -22,7 +24,7 @@ pub struct GPURenderBundleEncoder { pub instance: Instance, pub error_handler: super::error::ErrorHandler, - pub encoder: RefCell, + pub encoder: RefCell>, pub label: String, } @@ -42,7 +44,7 @@ impl GPURenderBundleEncoder { }; let (id, err) = self.instance.render_bundle_encoder_finish( - self.encoder.into_inner(), // TODO + self.encoder.borrow_mut().take().unwrap(), &wgpu_descriptor, None, ); @@ -55,32 +57,54 @@ impl GPURenderBundleEncoder { } } - fn push_debug_group(&self, #[webidl] group_label: String) { + fn push_debug_group( + &self, + #[webidl] group_label: String, + ) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + let label = std::ffi::CString::new(group_label).unwrap(); unsafe { wgpu_core::command::bundle_ffi::wgpu_render_bundle_push_debug_group( - &mut self.encoder.borrow_mut(), + encoder, label.as_ptr(), ); } + + Ok(()) } #[fast] - fn pop_debug_group(&self) { - wgpu_core::command::bundle_ffi::wgpu_render_bundle_pop_debug_group( - &mut self.encoder.borrow_mut(), - ); + fn pop_debug_group(&self) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + wgpu_core::command::bundle_ffi::wgpu_render_bundle_pop_debug_group(encoder); + Ok(()) } - fn insert_debug_marker(&self, #[webidl] marker_label: String) { + fn insert_debug_marker( + &self, + #[webidl] marker_label: String, + ) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + let label = std::ffi::CString::new(marker_label).unwrap(); unsafe { wgpu_core::command::bundle_ffi::wgpu_render_bundle_insert_debug_marker( - &mut self.encoder.borrow_mut(), + encoder, label.as_ptr(), ); } + Ok(()) } fn set_bind_group<'a>( @@ -91,7 +115,7 @@ impl GPURenderBundleEncoder { dynamic_offsets: v8::Local<'a, v8::Value>, dynamic_offsets_data_start: v8::Local<'a, v8::Value>, dynamic_offsets_data_length: v8::Local<'a, v8::Value>, - ) { + ) -> Result<(), SetBindGroupError> { const PREFIX: &str = "Failed to execute 'setBindGroup' on 'GPUComputePassEncoder'"; let offsets = @@ -105,8 +129,7 @@ impl GPURenderBundleEncoder { clamp: false, enforce_range: true, }, - ) - .unwrap(); // TODO: dont unwrap err + )?; let len = u32::convert( scope, dynamic_offsets, @@ -116,8 +139,7 @@ impl GPURenderBundleEncoder { clamp: false, enforce_range: true, }, - ) - .unwrap(); // TODO: dont unwrap err + )?; // TODO @@ -132,30 +154,41 @@ impl GPURenderBundleEncoder { clamp: false, enforce_range: true, }, - ) - .unwrap() // TODO: dont unwrap err + )? .unwrap_or_default() }; + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + unsafe { wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_bind_group( - &mut self.encoder.borrow_mut(), + encoder, index, bind_group.into_option().map(|bind_group| bind_group.id), offsets.as_ptr(), offsets.len(), ); } + Ok(()) } fn set_pipeline( &self, #[webidl] pipeline: Ptr, - ) { + ) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_pipeline( - &mut self.encoder.borrow_mut(), + encoder, pipeline.id, ); + Ok(()) } #[required(2)] @@ -165,13 +198,19 @@ impl GPURenderBundleEncoder { #[webidl] index_format: crate::wrap::render_pipeline::GPUIndexFormat, #[webidl/*(default = 0, options(enforce_range = true))*/] offset: u64, #[webidl/*(options(enforce_range = true))*/] size: Option, - ) { - self.encoder.borrow_mut().set_index_buffer( + ) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + + encoder.set_index_buffer( buffer.id, index_format.into(), offset, size.and_then(NonZeroU64::new), ); + Ok(()) } #[required(2)] @@ -181,14 +220,20 @@ impl GPURenderBundleEncoder { #[webidl] buffer: Ptr, // TODO: support nullable buffer #[webidl/*(default = 0, options(enforce_range = true))*/] offset: u64, #[webidl/*(options(enforce_range = true))*/] size: Option, - ) { + ) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_vertex_buffer( - &mut self.encoder.borrow_mut(), + encoder, slot, buffer.id, offset, size.and_then(NonZeroU64::new), ); + Ok(()) } #[required(1)] @@ -200,14 +245,20 @@ impl GPURenderBundleEncoder { #[webidl/*(default = 0, options(enforce_range = true))*/] first_vertex: u32, #[webidl/*(default = 0, options(enforce_range = true))*/] first_instance: u32, - ) { + ) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw( - &mut self.encoder.borrow_mut(), + encoder, vertex_count, instance_count, first_vertex, first_instance, ); + Ok(()) } #[required(1)] @@ -220,15 +271,21 @@ impl GPURenderBundleEncoder { #[webidl/*(default = 0, options(enforce_range = true))*/] base_vertex: i32, #[webidl/*(default = 0, options(enforce_range = true))*/] first_instance: u32, - ) { + ) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indexed( - &mut self.encoder.borrow_mut(), + encoder, index_count, instance_count, first_index, base_vertex, first_instance, ); + Ok(()) } #[required(2)] @@ -236,12 +293,18 @@ impl GPURenderBundleEncoder { &self, #[webidl] indirect_buffer: Ptr, #[webidl/*(options(enforce_range = true))*/] indirect_offset: u64, - ) { + ) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indirect( - &mut self.encoder.borrow_mut(), + encoder, indirect_buffer.id, indirect_offset, ); + Ok(()) } #[required(2)] @@ -249,12 +312,18 @@ impl GPURenderBundleEncoder { &self, #[webidl] indirect_buffer: Ptr, #[webidl/*(options(enforce_range = true))*/] indirect_offset: u64, - ) { + ) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indexed_indirect( - &mut self.encoder.borrow_mut(), + encoder, indirect_buffer.id, indirect_offset, ); + Ok(()) } } @@ -276,6 +345,16 @@ pub(crate) struct GPURenderBundleEncoderDescriptor { pub stencil_read_only: bool, } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +enum SetBindGroupError { + #[class(inherit)] + #[error(transparent)] + WebIDL(#[from] WebIdlError), + #[class(inherit)] + #[error(transparent)] + Other(#[from] JsErrorBox), +} + pub struct GPURenderBundle { pub id: wgpu_core::id::RenderBundleId, pub label: String, diff --git a/ext/webgpu/wrap/render_pass.rs b/ext/webgpu/wrap/render_pass.rs index 7b8d198c8a..2bb84b2c60 100644 --- a/ext/webgpu/wrap/render_pass.rs +++ b/ext/webgpu/wrap/render_pass.rs @@ -10,6 +10,7 @@ use deno_core::v8; use deno_core::webidl::IntOptions; use deno_core::webidl::Nullable; use deno_core::webidl::WebIdlConverter; +use deno_core::webidl::WebIdlError; use deno_core::GarbageCollected; use deno_core::WebIDL; @@ -195,7 +196,7 @@ impl GPURenderPassEncoder { dynamic_offsets: v8::Local<'a, v8::Value>, dynamic_offsets_data_start: v8::Local<'a, v8::Value>, dynamic_offsets_data_length: v8::Local<'a, v8::Value>, - ) { + ) -> Result<(), WebIdlError> { const PREFIX: &str = "Failed to execute 'setBindGroup' on 'GPUComputePassEncoder'"; let offsets = @@ -209,8 +210,7 @@ impl GPURenderPassEncoder { clamp: false, enforce_range: true, }, - ) - .unwrap(); // TODO: dont unwrap err + )?; let len = u32::convert( scope, dynamic_offsets, @@ -220,8 +220,7 @@ impl GPURenderPassEncoder { clamp: false, enforce_range: true, }, - ) - .unwrap(); // TODO: dont unwrap err + )?; // TODO @@ -236,8 +235,7 @@ impl GPURenderPassEncoder { clamp: false, enforce_range: true, }, - ) - .unwrap() // TODO: dont unwrap err + )? .unwrap_or_default() }; @@ -251,6 +249,8 @@ impl GPURenderPassEncoder { ) .err(); self.error_handler.push_error(err); + + Ok(()) } fn set_pipeline( diff --git a/ext/webgpu/wrap/webidl.rs b/ext/webgpu/wrap/webidl.rs index 8a3909b7d0..3997dd658c 100644 --- a/ext/webgpu/wrap/webidl.rs +++ b/ext/webgpu/wrap/webidl.rs @@ -504,7 +504,7 @@ pub fn feature_names_to_features( } pub fn features_to_feature_names( - features: &wgpu_types::Features, + features: wgpu_types::Features, ) -> HashSet { use GPUFeatureName::*; let mut return_features = HashSet::new();