Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cts_runner/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ webgpu:api,validation,render_pass,render_pass_descriptor:attachments,*
webgpu:api,validation,render_pass,render_pass_descriptor:color_attachments,*
webgpu:api,validation,render_pass,render_pass_descriptor:resolveTarget,*
webgpu:api,validation,render_pass,resolve:resolve_attachment:*
webgpu:api,validation,render_pipeline,depth_stencil_state:stencil_write:*
webgpu:api,validation,resource_usages,buffer,in_pass_encoder:*
// FAIL: 2 other cases in resource_usages,texture,in_pass_encoder. https://github.com/gfx-rs/wgpu/issues/3126
webgpu:api,validation,resource_usages,texture,in_pass_encoder:scope,*
Expand Down
17 changes: 9 additions & 8 deletions deno_webgpu/01_webgpu.js
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,14 @@ ObjectDefineProperty(GPUQuerySetPrototype, privateCustomInspect, {

// Converters

webidl.converters["GPUPipelineErrorReason"] = webidl.createEnumConverter(
"GPUPipelineErrorReason",
[
"validation",
"internal",
],
);

webidl.converters["GPUPipelineErrorInit"] = webidl.createDictionaryConverter(
"GPUPipelineErrorInit",
[
Expand All @@ -852,14 +860,6 @@ webidl.converters["GPUPipelineErrorInit"] = webidl.createDictionaryConverter(
],
);

webidl.converters["GPUPipelineErrorReason"] = webidl.createEnumConverter(
"GPUPipelineErrorReason",
[
"validation",
"internal",
],
);

webidl.converters["GPUError"] = webidl.converters.any /* put union here! */;

const dictMembersGPUUncapturedErrorEventInit = [
Expand Down Expand Up @@ -892,6 +892,7 @@ function initGPU() {
webidl.brand,
setEventTargetData,
GPUUncapturedErrorEvent,
GPUPipelineError,
);
}
}
Expand Down
4 changes: 2 additions & 2 deletions deno_webgpu/bind_group_layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use deno_core::WebIDL;

use crate::error::GPUGenericError;
use crate::texture::GPUTextureViewDimension;
use crate::webidl::GPUShaderStageFlags;
use crate::Instance;

pub struct GPUBindGroupLayout {
Expand Down Expand Up @@ -63,8 +64,7 @@ pub(crate) struct GPUBindGroupLayoutDescriptor {
pub(crate) struct GPUBindGroupLayoutEntry {
#[options(enforce_range = true)]
pub binding: u32,
#[options(enforce_range = true)]
pub visibility: u32,
pub visibility: GPUShaderStageFlags,
pub buffer: Option<GPUBufferBindingLayout>,
pub sampler: Option<GPUSamplerBindingLayout>,
pub texture: Option<GPUTextureBindingLayout>,
Expand Down
171 changes: 103 additions & 68 deletions deno_webgpu/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ use crate::adapter::GPUAdapterInfo;
use crate::adapter::GPUSupportedFeatures;
use crate::adapter::GPUSupportedLimits;
use crate::command_encoder::GPUCommandEncoder;
use crate::error::{GPUError, GPUGenericError};
use crate::error::make_pipeline_error;
use crate::error::{GPUError, GPUGenericError, GPUPipelineErrorReason};
use crate::query_set::GPUQuerySet;
use crate::render_bundle::GPURenderBundleEncoder;
use crate::render_pipeline::GPURenderPipeline;
Expand Down Expand Up @@ -219,8 +220,7 @@ impl GPUDevice {
sample_count: descriptor.sample_count,
dimension: descriptor.dimension.clone().into(),
format: descriptor.format.clone().into(),
usage: wgpu_types::TextureUsages::from_bits(descriptor.usage)
.ok_or_else(|| JsErrorBox::type_error("usage is not valid"))?,
usage: descriptor.usage.into(),
view_formats: descriptor
.view_formats
.into_iter()
Expand Down Expand Up @@ -338,8 +338,7 @@ impl GPUDevice {

entries.push(wgpu_types::BindGroupLayoutEntry {
binding: entry.binding,
visibility: wgpu_types::ShaderStages::from_bits(entry.visibility)
.ok_or_else(|| JsErrorBox::type_error("usage is not valid"))?,
visibility: entry.visibility.into(),
ty,
count: None, // native-only
});
Expand Down Expand Up @@ -498,36 +497,74 @@ impl GPUDevice {
&self,
#[webidl] descriptor: super::compute_pipeline::GPUComputePipelineDescriptor,
) -> GPUComputePipeline {
self.new_compute_pipeline(descriptor)
let (pipeline, err) = self.new_compute_pipeline(descriptor);
self.error_handler.push_error(err);
pipeline
}

#[required(1)]
#[cppgc]
fn create_render_pipeline(
&self,
#[webidl] descriptor: super::render_pipeline::GPURenderPipelineDescriptor,
) -> Result<GPURenderPipeline, JsErrorBox> {
self.new_render_pipeline(descriptor)
) -> GPURenderPipeline {
let (pipeline, err) = self.new_render_pipeline(descriptor);
self.error_handler.push_error(err);
pipeline
}

#[async_method]
#[async_method(fake)]
#[required(1)]
#[cppgc]
async fn create_compute_pipeline_async(
#[global]
fn create_compute_pipeline_async(
&self,
scope: &mut v8::HandleScope,
#[webidl] descriptor: super::compute_pipeline::GPUComputePipelineDescriptor,
) -> GPUComputePipeline {
self.new_compute_pipeline(descriptor)
) -> v8::Global<v8::Promise> {
let resolver = v8::PromiseResolver::new(scope).unwrap();
let promise = resolver.get_promise(scope);

let (pipeline, err) = self.new_compute_pipeline(descriptor);
if let Some(err) = err {
let err = make_pipeline_error(
scope,
GPUPipelineErrorReason::Validation,
&err.to_string(),
);
resolver.reject(scope, err.into());
} else {
let val = make_cppgc_object(scope, pipeline).into();
resolver.resolve(scope, val);
}
v8::Global::new(scope, promise)
}

#[async_method]
#[async_method(fake)]
#[required(1)]
#[cppgc]
async fn create_render_pipeline_async(
#[global]
fn create_render_pipeline_async(
&self,
scope: &mut v8::HandleScope,
#[webidl] descriptor: super::render_pipeline::GPURenderPipelineDescriptor,
) -> Result<GPURenderPipeline, JsErrorBox> {
self.new_render_pipeline(descriptor)
) -> v8::Global<v8::Promise> {
let resolver = v8::PromiseResolver::new(scope).unwrap();
let promise = resolver.get_promise(scope);

let (pipeline, err) = self.new_render_pipeline(descriptor);
if let Some(err) = err {
let err = make_pipeline_error(
scope,
GPUPipelineErrorReason::Validation,
&err.to_string(),
);
resolver.reject(scope, err.into());
} else {
let val = make_cppgc_object(scope, pipeline).into();
resolver.resolve(scope, val);
}
v8::Global::new(scope, promise)
}

fn create_command_encoder<'a>(
Expand Down Expand Up @@ -739,7 +776,10 @@ impl GPUDevice {
fn new_compute_pipeline(
&self,
descriptor: super::compute_pipeline::GPUComputePipelineDescriptor,
) -> GPUComputePipeline {
) -> (
GPUComputePipeline,
Option<wgpu_core::pipeline::CreateComputePipelineError>,
) {
let wgpu_descriptor = wgpu_core::pipeline::ComputePipelineDescriptor {
label: crate::transform_label(descriptor.label.clone()),
layout: descriptor.layout.into(),
Expand All @@ -758,20 +798,24 @@ impl GPUDevice {
None,
);

self.error_handler.push_error(err);

GPUComputePipeline {
instance: self.instance.clone(),
error_handler: self.error_handler.clone(),
id,
label: descriptor.label.clone(),
}
(
GPUComputePipeline {
instance: self.instance.clone(),
error_handler: self.error_handler.clone(),
id,
label: descriptor.label.clone(),
},
err,
)
}

fn new_render_pipeline(
&self,
descriptor: super::render_pipeline::GPURenderPipelineDescriptor,
) -> Result<GPURenderPipeline, JsErrorBox> {
) -> (
GPURenderPipeline,
Option<wgpu_core::pipeline::CreateRenderPipelineError>,
) {
let vertex = wgpu_core::pipeline::VertexState {
stage: ProgrammableStageDescriptor {
module: descriptor.vertex.module.id,
Expand Down Expand Up @@ -866,10 +910,10 @@ impl GPUDevice {
.alpha_to_coverage_enabled,
};

let fragment = descriptor
.fragment
.map(|fragment| {
Ok::<_, JsErrorBox>(wgpu_core::pipeline::FragmentState {
let fragment =
descriptor
.fragment
.map(|fragment| wgpu_core::pipeline::FragmentState {
stage: ProgrammableStageDescriptor {
module: fragment.module.id,
entry_point: fragment.entry_point.map(Into::into),
Expand All @@ -881,38 +925,28 @@ impl GPUDevice {
.targets
.into_iter()
.map(|target| {
target
.into_option()
.map(|target| {
Ok(wgpu_types::ColorTargetState {
format: target.format.into(),
blend: target.blend.map(|blend| wgpu_types::BlendState {
color: wgpu_types::BlendComponent {
src_factor: blend.color.src_factor.into(),
dst_factor: blend.color.dst_factor.into(),
operation: blend.color.operation.into(),
},
alpha: wgpu_types::BlendComponent {
src_factor: blend.alpha.src_factor.into(),
dst_factor: blend.alpha.dst_factor.into(),
operation: blend.alpha.operation.into(),
},
}),
write_mask: wgpu_types::ColorWrites::from_bits(
target.write_mask,
)
.ok_or_else(|| {
JsErrorBox::type_error("usage is not valid")
})?,
})
})
.transpose()
target.into_option().map(|target| {
wgpu_types::ColorTargetState {
format: target.format.into(),
blend: target.blend.map(|blend| wgpu_types::BlendState {
color: wgpu_types::BlendComponent {
src_factor: blend.color.src_factor.into(),
dst_factor: blend.color.dst_factor.into(),
operation: blend.color.operation.into(),
},
alpha: wgpu_types::BlendComponent {
src_factor: blend.alpha.src_factor.into(),
dst_factor: blend.alpha.dst_factor.into(),
operation: blend.alpha.operation.into(),
},
}),
write_mask: target.write_mask.into(),
}
})
})
.collect::<Result<_, JsErrorBox>>()?,
.collect(),
),
})
})
.transpose()?;
});

let wgpu_descriptor = wgpu_core::pipeline::RenderPipelineDescriptor {
label: crate::transform_label(descriptor.label.clone()),
Expand All @@ -932,14 +966,15 @@ impl GPUDevice {
None,
);

self.error_handler.push_error(err);

Ok(GPURenderPipeline {
instance: self.instance.clone(),
error_handler: self.error_handler.clone(),
id,
label: descriptor.label,
})
(
GPURenderPipeline {
instance: self.instance.clone(),
error_handler: self.error_handler.clone(),
id,
label: descriptor.label,
},
err,
)
}
}

Expand Down
40 changes: 40 additions & 0 deletions deno_webgpu/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,3 +379,43 @@ pub enum GPUGenericError {
#[error("Illegal constructor")]
InvalidConstructor,
}

pub enum GPUPipelineErrorReason {
Validation,
#[expect(dead_code)]
Internal,
}

impl Display for GPUPipelineErrorReason {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Validation => f.write_str("validation"),
Self::Internal => f.write_str("internal"),
}
}
}

pub(crate) fn make_pipeline_error<'a>(
scope: &mut v8::HandleScope<'a>,
reason: GPUPipelineErrorReason,
message: &str,
) -> v8::Local<'a, v8::Object> {
let state = JsRuntime::op_state_from(scope);
let class = state
.borrow()
.borrow::<crate::PipelineErrorClass>()
.0
.clone();
let constructor =
v8::Local::<v8::Function>::try_from(v8::Local::new(scope, class)).unwrap();
let message_str = v8::String::new(scope, message).unwrap();
let reason_str = v8::String::new(scope, &reason.to_string()).unwrap();

let options = v8::Object::new(scope);
let key = v8::String::new(scope, "reason").unwrap();
options.set(scope, key.into(), reason_str.into());

constructor
.new_instance(scope, &[message_str.into(), options.into()])
.unwrap()
}
13 changes: 11 additions & 2 deletions deno_webgpu/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,21 @@ pub fn op_create_gpu(
scope: &mut v8::HandleScope,
webidl_brand: v8::Local<v8::Value>,
set_event_target_data: v8::Local<v8::Value>,
error_event_class: v8::Local<v8::Value>,
uncaptured_error_event_class: v8::Local<v8::Value>,
pipeline_error_class: v8::Local<v8::Value>,
) -> GPU {
state.put(EventTargetSetup {
brand: v8::Global::new(scope, webidl_brand),
set_event_target_data: v8::Global::new(scope, set_event_target_data),
});
state.put(ErrorEventClass(v8::Global::new(scope, error_event_class)));
state.put(ErrorEventClass(v8::Global::new(
scope,
uncaptured_error_event_class,
)));
state.put(PipelineErrorClass(v8::Global::new(
scope,
pipeline_error_class,
)));
GPU
}

Expand All @@ -129,6 +137,7 @@ struct EventTargetSetup {
set_event_target_data: v8::Global<v8::Value>,
}
struct ErrorEventClass(v8::Global<v8::Value>);
struct PipelineErrorClass(v8::Global<v8::Value>);

pub struct GPU;

Expand Down
Loading