mirror of
https://github.com/denoland/deno.git
synced 2025-10-01 22:51:14 +00:00
feat: WebGPU API (#7977)
Co-authored-by: Luca Casonato <lucacasonato@yahoo.com>
This commit is contained in:
parent
dbdbe7a1cf
commit
7cd14f97c9
42 changed files with 15302 additions and 1 deletions
|
@ -1204,6 +1204,7 @@
|
|||
window.removeEventListener = EventTarget.prototype.removeEventListener;
|
||||
window.__bootstrap = (window.__bootstrap || {});
|
||||
window.__bootstrap.eventTarget = {
|
||||
EventTarget,
|
||||
setEventTargetData,
|
||||
};
|
||||
window.__bootstrap.event = {
|
||||
|
|
4
op_crates/web/internal.d.ts
vendored
4
op_crates/web/internal.d.ts
vendored
|
@ -270,6 +270,10 @@ declare namespace globalThis {
|
|||
): (v: any, opts: ValueConverterOpts) => any;
|
||||
}
|
||||
|
||||
declare var eventTarget: {
|
||||
EventTarget: typeof EventTarget;
|
||||
};
|
||||
|
||||
declare var url: {
|
||||
URLSearchParams: typeof URLSearchParams;
|
||||
};
|
||||
|
|
5048
op_crates/webgpu/01_webgpu.js
Normal file
5048
op_crates/webgpu/01_webgpu.js
Normal file
File diff suppressed because it is too large
Load diff
1800
op_crates/webgpu/02_idl_types.js
Normal file
1800
op_crates/webgpu/02_idl_types.js
Normal file
File diff suppressed because it is too large
Load diff
21
op_crates/webgpu/Cargo.toml
Normal file
21
op_crates/webgpu/Cargo.toml
Normal file
|
@ -0,0 +1,21 @@
|
|||
# Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
[package]
|
||||
name = "deno_webgpu"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
description = "provides webgpu Web API to deno_core"
|
||||
authors = ["the Deno authors"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/denoland/deno"
|
||||
|
||||
[lib]
|
||||
path = "lib.rs"
|
||||
|
||||
[dependencies]
|
||||
deno_core = { version = "0.79.0", path = "../../core" }
|
||||
tokio = { version = "1.1.1", features = ["full"] }
|
||||
serde = { version = "1.0.123", features = ["derive"] }
|
||||
wgpu-core = { version = "0.7.0", features = ["trace"] }
|
||||
wgpu-types = "0.7.0"
|
35
op_crates/webgpu/README.md
Normal file
35
op_crates/webgpu/README.md
Normal file
|
@ -0,0 +1,35 @@
|
|||
# deno_webgpu
|
||||
|
||||
This op crate implements the WebGPU API as defined in
|
||||
https://gpuweb.github.io/gpuweb/ in Deno. The implementation targets the spec
|
||||
draft as of February 22, 2021. The spec is still very much in flux. This op
|
||||
crate tries to stay up to date with the spec, but is constrained by the features
|
||||
implemented in our GPU backend library [wgpu](https://github.com/gfx-rs/wgpu).
|
||||
|
||||
The spec is still very bare bones, and is still missing many details. As the
|
||||
spec becomes more concrete, we will implement to follow the spec more closely.
|
||||
|
||||
In addition, setting the `DENO_WEBGPU_TRACE` environmental variable will output
|
||||
a
|
||||
[wgpu trace](https://github.com/gfx-rs/wgpu/wiki/Debugging-wgpu-Applications#tracing-infrastructure)
|
||||
to the specified directory.
|
||||
|
||||
For testing this op crate will make use of the WebGPU conformance tests suite,
|
||||
running through our WPT runner. This will be used to validate implementation
|
||||
conformance.
|
||||
|
||||
GitHub CI doesn't run with GPUs, so testing relies on software like DX WARP &
|
||||
Vulkan lavapipe. Currently only using DX WARP works, so tests are only run on
|
||||
Windows.
|
||||
|
||||
## Links
|
||||
|
||||
Specification: https://gpuweb.github.io/gpuweb/
|
||||
|
||||
Design documents: https://github.com/gpuweb/gpuweb/tree/main/design
|
||||
|
||||
Conformance tests suite: https://github.com/gpuweb/cts
|
||||
|
||||
WebGPU examples for Deno: https://github.com/crowlKats/webgpu-examples
|
||||
|
||||
wgpu-users matrix channel: https://matrix.to/#/#wgpu-users:matrix.org
|
362
op_crates/webgpu/binding.rs
Normal file
362
op_crates/webgpu/binding.rs
Normal file
|
@ -0,0 +1,362 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_core::error::bad_resource_id;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json::json;
|
||||
use deno_core::serde_json::Value;
|
||||
use deno_core::ZeroCopyBuf;
|
||||
use deno_core::{OpState, Resource};
|
||||
use serde::Deserialize;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use super::error::WebGPUError;
|
||||
|
||||
pub(crate) struct WebGPUBindGroupLayout(
|
||||
pub(crate) wgpu_core::id::BindGroupLayoutId,
|
||||
);
|
||||
impl Resource for WebGPUBindGroupLayout {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUBindGroupLayout".into()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct WebGPUBindGroup(pub(crate) wgpu_core::id::BindGroupId);
|
||||
impl Resource for WebGPUBindGroup {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUBindGroup".into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GPUBufferBindingLayout {
|
||||
#[serde(rename = "type")]
|
||||
kind: Option<String>,
|
||||
has_dynamic_offset: Option<bool>,
|
||||
min_binding_size: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GPUSamplerBindingLayout {
|
||||
#[serde(rename = "type")]
|
||||
kind: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GPUTextureBindingLayout {
|
||||
sample_type: Option<String>,
|
||||
view_dimension: Option<String>,
|
||||
multisampled: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GPUStorageTextureBindingLayout {
|
||||
access: String,
|
||||
format: String,
|
||||
view_dimension: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GPUBindGroupLayoutEntry {
|
||||
binding: u32,
|
||||
visibility: u32,
|
||||
buffer: Option<GPUBufferBindingLayout>,
|
||||
sampler: Option<GPUSamplerBindingLayout>,
|
||||
texture: Option<GPUTextureBindingLayout>,
|
||||
storage_texture: Option<GPUStorageTextureBindingLayout>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateBindGroupLayoutArgs {
|
||||
device_rid: u32,
|
||||
label: Option<String>,
|
||||
entries: Vec<GPUBindGroupLayoutEntry>,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_create_bind_group_layout(
|
||||
state: &mut OpState,
|
||||
args: CreateBindGroupLayoutArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let device_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGPUDevice>(args.device_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let device = device_resource.0;
|
||||
|
||||
let mut entries = vec![];
|
||||
|
||||
for entry in &args.entries {
|
||||
entries.push(wgpu_types::BindGroupLayoutEntry {
|
||||
binding: entry.binding,
|
||||
visibility: wgpu_types::ShaderStage::from_bits(entry.visibility).unwrap(),
|
||||
ty: if let Some(buffer) = &entry.buffer {
|
||||
wgpu_types::BindingType::Buffer {
|
||||
ty: match &buffer.kind {
|
||||
Some(kind) => match kind.as_str() {
|
||||
"uniform" => wgpu_types::BufferBindingType::Uniform,
|
||||
"storage" => {
|
||||
wgpu_types::BufferBindingType::Storage { read_only: false }
|
||||
}
|
||||
"read-only-storage" => {
|
||||
wgpu_types::BufferBindingType::Storage { read_only: true }
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
None => wgpu_types::BufferBindingType::Uniform,
|
||||
},
|
||||
has_dynamic_offset: buffer.has_dynamic_offset.unwrap_or(false),
|
||||
min_binding_size: if let Some(min_binding_size) =
|
||||
buffer.min_binding_size
|
||||
{
|
||||
std::num::NonZeroU64::new(min_binding_size)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
}
|
||||
} else if let Some(sampler) = &entry.sampler {
|
||||
match &sampler.kind {
|
||||
Some(kind) => match kind.as_str() {
|
||||
"filtering" => wgpu_types::BindingType::Sampler {
|
||||
filtering: true,
|
||||
comparison: false,
|
||||
},
|
||||
"non-filtering" => wgpu_types::BindingType::Sampler {
|
||||
filtering: false,
|
||||
comparison: false,
|
||||
},
|
||||
"comparison" => wgpu_types::BindingType::Sampler {
|
||||
filtering: false,
|
||||
comparison: true,
|
||||
},
|
||||
_ => unreachable!(),
|
||||
},
|
||||
None => wgpu_types::BindingType::Sampler {
|
||||
filtering: true,
|
||||
comparison: false,
|
||||
},
|
||||
}
|
||||
} else if let Some(texture) = &entry.texture {
|
||||
wgpu_types::BindingType::Texture {
|
||||
sample_type: match &texture.sample_type {
|
||||
Some(sample_type) => match sample_type.as_str() {
|
||||
"float" => {
|
||||
wgpu_types::TextureSampleType::Float { filterable: true }
|
||||
}
|
||||
"unfilterable-float" => {
|
||||
wgpu_types::TextureSampleType::Float { filterable: false }
|
||||
}
|
||||
"depth" => wgpu_types::TextureSampleType::Depth,
|
||||
"sint" => wgpu_types::TextureSampleType::Sint,
|
||||
"uint" => wgpu_types::TextureSampleType::Uint,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
None => wgpu_types::TextureSampleType::Float { filterable: true },
|
||||
},
|
||||
view_dimension: match &texture.view_dimension {
|
||||
Some(view_dimension) => {
|
||||
super::texture::serialize_dimension(view_dimension)
|
||||
}
|
||||
None => wgpu_types::TextureViewDimension::D2,
|
||||
},
|
||||
multisampled: texture.multisampled.unwrap_or(false),
|
||||
}
|
||||
} else if let Some(storage_texture) = &entry.storage_texture {
|
||||
wgpu_types::BindingType::StorageTexture {
|
||||
access: match storage_texture.access.as_str() {
|
||||
"read-only" => wgpu_types::StorageTextureAccess::ReadOnly,
|
||||
"write-only" => wgpu_types::StorageTextureAccess::WriteOnly,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
format: super::texture::serialize_texture_format(
|
||||
&storage_texture.format,
|
||||
)?,
|
||||
view_dimension: match &storage_texture.view_dimension {
|
||||
Some(view_dimension) => {
|
||||
super::texture::serialize_dimension(view_dimension)
|
||||
}
|
||||
None => wgpu_types::TextureViewDimension::D2,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
},
|
||||
count: None, // native-only
|
||||
});
|
||||
}
|
||||
|
||||
let descriptor = wgpu_core::binding_model::BindGroupLayoutDescriptor {
|
||||
label: args.label.map(Cow::from),
|
||||
entries: Cow::from(entries),
|
||||
};
|
||||
|
||||
let (bind_group_layout, maybe_err) = gfx_select!(device => instance.device_create_bind_group_layout(
|
||||
device,
|
||||
&descriptor,
|
||||
std::marker::PhantomData
|
||||
));
|
||||
|
||||
let rid = state
|
||||
.resource_table
|
||||
.add(WebGPUBindGroupLayout(bind_group_layout));
|
||||
|
||||
Ok(json!({
|
||||
"rid": rid,
|
||||
"err": maybe_err.map(WebGPUError::from)
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreatePipelineLayoutArgs {
|
||||
device_rid: u32,
|
||||
label: Option<String>,
|
||||
bind_group_layouts: Vec<u32>,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_create_pipeline_layout(
|
||||
state: &mut OpState,
|
||||
args: CreatePipelineLayoutArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let device_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGPUDevice>(args.device_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let device = device_resource.0;
|
||||
|
||||
let mut bind_group_layouts = vec![];
|
||||
|
||||
for rid in &args.bind_group_layouts {
|
||||
let bind_group_layout = state
|
||||
.resource_table
|
||||
.get::<WebGPUBindGroupLayout>(*rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
bind_group_layouts.push(bind_group_layout.0);
|
||||
}
|
||||
|
||||
let descriptor = wgpu_core::binding_model::PipelineLayoutDescriptor {
|
||||
label: args.label.map(Cow::from),
|
||||
bind_group_layouts: Cow::from(bind_group_layouts),
|
||||
push_constant_ranges: Default::default(),
|
||||
};
|
||||
|
||||
let (pipeline_layout, maybe_err) = gfx_select!(device => instance.device_create_pipeline_layout(
|
||||
device,
|
||||
&descriptor,
|
||||
std::marker::PhantomData
|
||||
));
|
||||
|
||||
let rid = state
|
||||
.resource_table
|
||||
.add(super::pipeline::WebGPUPipelineLayout(pipeline_layout));
|
||||
|
||||
Ok(json!({
|
||||
"rid": rid,
|
||||
"err": maybe_err.map(WebGPUError::from)
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GPUBindGroupEntry {
|
||||
binding: u32,
|
||||
kind: String,
|
||||
resource: u32,
|
||||
offset: Option<u64>,
|
||||
size: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateBindGroupArgs {
|
||||
device_rid: u32,
|
||||
label: Option<String>,
|
||||
layout: u32,
|
||||
entries: Vec<GPUBindGroupEntry>,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_create_bind_group(
|
||||
state: &mut OpState,
|
||||
args: CreateBindGroupArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let device_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGPUDevice>(args.device_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let device = device_resource.0;
|
||||
|
||||
let mut entries = vec![];
|
||||
|
||||
for entry in &args.entries {
|
||||
let e = wgpu_core::binding_model::BindGroupEntry {
|
||||
binding: entry.binding,
|
||||
resource: match entry.kind.as_str() {
|
||||
"GPUSampler" => {
|
||||
let sampler_resource = state
|
||||
.resource_table
|
||||
.get::<super::sampler::WebGPUSampler>(entry.resource)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
wgpu_core::binding_model::BindingResource::Sampler(sampler_resource.0)
|
||||
}
|
||||
"GPUTextureView" => {
|
||||
let texture_view_resource = state
|
||||
.resource_table
|
||||
.get::<super::texture::WebGPUTextureView>(entry.resource)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
wgpu_core::binding_model::BindingResource::TextureView(
|
||||
texture_view_resource.0,
|
||||
)
|
||||
}
|
||||
"GPUBufferBinding" => {
|
||||
let buffer_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGPUBuffer>(entry.resource)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
wgpu_core::binding_model::BindingResource::Buffer(
|
||||
wgpu_core::binding_model::BufferBinding {
|
||||
buffer_id: buffer_resource.0,
|
||||
offset: entry.offset.unwrap_or(0),
|
||||
size: std::num::NonZeroU64::new(entry.size.unwrap_or(0)),
|
||||
},
|
||||
)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
};
|
||||
entries.push(e);
|
||||
}
|
||||
|
||||
let bind_group_layout = state
|
||||
.resource_table
|
||||
.get::<WebGPUBindGroupLayout>(args.layout)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
let descriptor = wgpu_core::binding_model::BindGroupDescriptor {
|
||||
label: args.label.map(Cow::from),
|
||||
layout: bind_group_layout.0,
|
||||
entries: Cow::from(entries),
|
||||
};
|
||||
|
||||
let (bind_group, maybe_err) = gfx_select!(device => instance.device_create_bind_group(
|
||||
device,
|
||||
&descriptor,
|
||||
std::marker::PhantomData
|
||||
));
|
||||
|
||||
let rid = state.resource_table.add(WebGPUBindGroup(bind_group));
|
||||
|
||||
Ok(json!({
|
||||
"rid": rid,
|
||||
"err": maybe_err.map(WebGPUError::from)
|
||||
}))
|
||||
}
|
243
op_crates/webgpu/buffer.rs
Normal file
243
op_crates/webgpu/buffer.rs
Normal file
|
@ -0,0 +1,243 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_core::error::bad_resource_id;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::futures::channel::oneshot;
|
||||
use deno_core::serde_json::json;
|
||||
use deno_core::serde_json::Value;
|
||||
use deno_core::OpState;
|
||||
use deno_core::ZeroCopyBuf;
|
||||
use deno_core::{BufVec, Resource};
|
||||
use serde::Deserialize;
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
|
||||
use super::error::DOMExceptionOperationError;
|
||||
use super::error::WebGPUError;
|
||||
|
||||
pub(crate) struct WebGPUBuffer(pub(crate) wgpu_core::id::BufferId);
|
||||
impl Resource for WebGPUBuffer {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUBuffer".into()
|
||||
}
|
||||
}
|
||||
|
||||
struct WebGPUBufferMapped(*mut u8, usize);
|
||||
impl Resource for WebGPUBufferMapped {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUBufferMapped".into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateBufferArgs {
|
||||
device_rid: u32,
|
||||
label: Option<String>,
|
||||
size: u64,
|
||||
usage: u32,
|
||||
mapped_at_creation: Option<bool>,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_create_buffer(
|
||||
state: &mut OpState,
|
||||
args: CreateBufferArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let device_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGPUDevice>(args.device_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let device = device_resource.0;
|
||||
|
||||
let descriptor = wgpu_core::resource::BufferDescriptor {
|
||||
label: args.label.map(Cow::from),
|
||||
size: args.size,
|
||||
usage: wgpu_types::BufferUsage::from_bits(args.usage).unwrap(),
|
||||
mapped_at_creation: args.mapped_at_creation.unwrap_or(false),
|
||||
};
|
||||
|
||||
let (buffer, maybe_err) = gfx_select!(device => instance.device_create_buffer(
|
||||
device,
|
||||
&descriptor,
|
||||
std::marker::PhantomData
|
||||
));
|
||||
|
||||
let rid = state.resource_table.add(WebGPUBuffer(buffer));
|
||||
|
||||
Ok(json!({
|
||||
"rid": rid,
|
||||
"err": maybe_err.map(WebGPUError::from)
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BufferGetMapAsyncArgs {
|
||||
buffer_rid: u32,
|
||||
device_rid: u32,
|
||||
mode: u32,
|
||||
offset: u64,
|
||||
size: u64,
|
||||
}
|
||||
|
||||
pub async fn op_webgpu_buffer_get_map_async(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
args: BufferGetMapAsyncArgs,
|
||||
_bufs: BufVec,
|
||||
) -> Result<Value, AnyError> {
|
||||
let (sender, receiver) = oneshot::channel::<Result<(), AnyError>>();
|
||||
|
||||
let device;
|
||||
{
|
||||
let state_ = state.borrow();
|
||||
let instance = state_.borrow::<super::Instance>();
|
||||
let buffer_resource = state_
|
||||
.resource_table
|
||||
.get::<WebGPUBuffer>(args.buffer_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let buffer = buffer_resource.0;
|
||||
let device_resource = state_
|
||||
.resource_table
|
||||
.get::<super::WebGPUDevice>(args.device_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
device = device_resource.0;
|
||||
|
||||
let boxed_sender = Box::new(sender);
|
||||
let sender_ptr = Box::into_raw(boxed_sender) as *mut u8;
|
||||
|
||||
extern "C" fn buffer_map_future_wrapper(
|
||||
status: wgpu_core::resource::BufferMapAsyncStatus,
|
||||
user_data: *mut u8,
|
||||
) {
|
||||
let sender_ptr = user_data as *mut oneshot::Sender<Result<(), AnyError>>;
|
||||
let boxed_sender = unsafe { Box::from_raw(sender_ptr) };
|
||||
boxed_sender
|
||||
.send(match status {
|
||||
wgpu_core::resource::BufferMapAsyncStatus::Success => Ok(()),
|
||||
_ => unreachable!(), // TODO
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// TODO(lucacasonato): error handling
|
||||
gfx_select!(buffer => instance.buffer_map_async(
|
||||
buffer,
|
||||
args.offset..(args.offset + args.size),
|
||||
wgpu_core::resource::BufferMapOperation {
|
||||
host: match args.mode {
|
||||
1 => wgpu_core::device::HostMap::Read,
|
||||
2 => wgpu_core::device::HostMap::Write,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
callback: buffer_map_future_wrapper,
|
||||
user_data: sender_ptr,
|
||||
}
|
||||
))?;
|
||||
}
|
||||
|
||||
let done = Rc::new(RefCell::new(false));
|
||||
let done_ = done.clone();
|
||||
let device_poll_fut = async move {
|
||||
while !*done.borrow() {
|
||||
{
|
||||
let state = state.borrow();
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
gfx_select!(device => instance.device_poll(device, false)).unwrap()
|
||||
}
|
||||
tokio::time::sleep(Duration::from_millis(10)).await;
|
||||
}
|
||||
Ok::<(), AnyError>(())
|
||||
};
|
||||
|
||||
let receiver_fut = async move {
|
||||
receiver.await??;
|
||||
let mut done = done_.borrow_mut();
|
||||
*done = true;
|
||||
Ok::<(), AnyError>(())
|
||||
};
|
||||
|
||||
tokio::try_join!(device_poll_fut, receiver_fut)?;
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BufferGetMappedRangeArgs {
|
||||
buffer_rid: u32,
|
||||
offset: u64,
|
||||
size: u64,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_buffer_get_mapped_range(
|
||||
state: &mut OpState,
|
||||
args: BufferGetMappedRangeArgs,
|
||||
zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let buffer_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUBuffer>(args.buffer_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let buffer = buffer_resource.0;
|
||||
|
||||
let slice_pointer = gfx_select!(buffer => instance.buffer_get_mapped_range(
|
||||
buffer,
|
||||
args.offset,
|
||||
std::num::NonZeroU64::new(args.size)
|
||||
))
|
||||
.map_err(|e| DOMExceptionOperationError::new(&e.to_string()))?;
|
||||
|
||||
let slice = unsafe {
|
||||
std::slice::from_raw_parts_mut(slice_pointer, args.size as usize)
|
||||
};
|
||||
zero_copy[0].copy_from_slice(slice);
|
||||
|
||||
let rid = state
|
||||
.resource_table
|
||||
.add(WebGPUBufferMapped(slice_pointer, args.size as usize));
|
||||
|
||||
Ok(json!({
|
||||
"rid": rid,
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BufferUnmapArgs {
|
||||
buffer_rid: u32,
|
||||
mapped_rid: u32,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_buffer_unmap(
|
||||
state: &mut OpState,
|
||||
args: BufferUnmapArgs,
|
||||
zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let mapped_resource = state
|
||||
.resource_table
|
||||
.take::<WebGPUBufferMapped>(args.mapped_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let buffer_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUBuffer>(args.buffer_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let buffer = buffer_resource.0;
|
||||
|
||||
let slice_pointer = mapped_resource.0;
|
||||
let size = mapped_resource.1;
|
||||
|
||||
if let Some(buffer) = zero_copy.get(0) {
|
||||
let slice = unsafe { std::slice::from_raw_parts_mut(slice_pointer, size) };
|
||||
slice.copy_from_slice(&buffer);
|
||||
}
|
||||
|
||||
let maybe_err = gfx_select!(buffer => instance.buffer_unmap(buffer)).err();
|
||||
|
||||
Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
|
||||
}
|
465
op_crates/webgpu/bundle.rs
Normal file
465
op_crates/webgpu/bundle.rs
Normal file
|
@ -0,0 +1,465 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_core::error::bad_resource_id;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json::json;
|
||||
use deno_core::serde_json::Value;
|
||||
use deno_core::ZeroCopyBuf;
|
||||
use deno_core::{OpState, Resource};
|
||||
use serde::Deserialize;
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::error::WebGPUError;
|
||||
use super::texture::serialize_texture_format;
|
||||
|
||||
struct WebGPURenderBundleEncoder(
|
||||
RefCell<wgpu_core::command::RenderBundleEncoder>,
|
||||
);
|
||||
impl Resource for WebGPURenderBundleEncoder {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPURenderBundleEncoder".into()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct WebGPURenderBundle(pub(crate) wgpu_core::id::RenderBundleId);
|
||||
impl Resource for WebGPURenderBundle {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPURenderBundle".into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateRenderBundleEncoderArgs {
|
||||
device_rid: u32,
|
||||
label: Option<String>,
|
||||
color_formats: Vec<String>,
|
||||
depth_stencil_format: Option<String>,
|
||||
sample_count: Option<u32>,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_create_render_bundle_encoder(
|
||||
state: &mut OpState,
|
||||
args: CreateRenderBundleEncoderArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let device_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGPUDevice>(args.device_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let device = device_resource.0;
|
||||
|
||||
let mut color_formats = vec![];
|
||||
|
||||
for format in &args.color_formats {
|
||||
color_formats.push(serialize_texture_format(format)?);
|
||||
}
|
||||
|
||||
let descriptor = wgpu_core::command::RenderBundleEncoderDescriptor {
|
||||
label: args.label.map(Cow::from),
|
||||
color_formats: Cow::from(color_formats),
|
||||
depth_stencil_format: args
|
||||
.depth_stencil_format
|
||||
.map(|s| serialize_texture_format(&s))
|
||||
.transpose()?,
|
||||
sample_count: args.sample_count.unwrap_or(1),
|
||||
};
|
||||
|
||||
let res =
|
||||
wgpu_core::command::RenderBundleEncoder::new(&descriptor, device, None);
|
||||
let (render_bundle_encoder, maybe_err) = match res {
|
||||
Ok(encoder) => (encoder, None),
|
||||
Err(e) => (
|
||||
wgpu_core::command::RenderBundleEncoder::dummy(device),
|
||||
Some(e),
|
||||
),
|
||||
};
|
||||
|
||||
let rid = state
|
||||
.resource_table
|
||||
.add(WebGPURenderBundleEncoder(RefCell::new(
|
||||
render_bundle_encoder,
|
||||
)));
|
||||
|
||||
Ok(json!({
|
||||
"rid": rid,
|
||||
"err": maybe_err.map(WebGPUError::from),
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderBundleEncoderFinishArgs {
|
||||
render_bundle_encoder_rid: u32,
|
||||
label: Option<String>,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_bundle_encoder_finish(
|
||||
state: &mut OpState,
|
||||
args: RenderBundleEncoderFinishArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let render_bundle_encoder_resource = state
|
||||
.resource_table
|
||||
.take::<WebGPURenderBundleEncoder>(args.render_bundle_encoder_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let render_bundle_encoder = Rc::try_unwrap(render_bundle_encoder_resource)
|
||||
.ok()
|
||||
.expect("unwrapping render_bundle_encoder_resource should succeed")
|
||||
.0
|
||||
.into_inner();
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
|
||||
let (render_bundle, maybe_err) = gfx_select!(render_bundle_encoder.parent() => instance.render_bundle_encoder_finish(
|
||||
render_bundle_encoder,
|
||||
&wgpu_core::command::RenderBundleDescriptor {
|
||||
label: args.label.map(Cow::from),
|
||||
},
|
||||
std::marker::PhantomData
|
||||
));
|
||||
|
||||
let rid = state.resource_table.add(WebGPURenderBundle(render_bundle));
|
||||
|
||||
Ok(json!({
|
||||
"rid": rid,
|
||||
"err": maybe_err.map(WebGPUError::from)
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderBundleEncoderSetBindGroupArgs {
|
||||
render_bundle_encoder_rid: u32,
|
||||
index: u32,
|
||||
bind_group: u32,
|
||||
dynamic_offsets_data: Option<Vec<u32>>,
|
||||
dynamic_offsets_data_start: usize,
|
||||
dynamic_offsets_data_length: usize,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_bundle_encoder_set_bind_group(
|
||||
state: &mut OpState,
|
||||
args: RenderBundleEncoderSetBindGroupArgs,
|
||||
zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let bind_group_resource = state
|
||||
.resource_table
|
||||
.get::<super::binding::WebGPUBindGroup>(args.bind_group)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let render_bundle_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderBundleEncoder>(args.render_bundle_encoder_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
// I know this might look like it can be easily deduplicated, but it can not
|
||||
// be due to the lifetime of the args.dynamic_offsets_data slice. Because we
|
||||
// need to use a raw pointer here the slice can be freed before the pointer
|
||||
// is used in wgpu_render_pass_set_bind_group. See
|
||||
// https://matrix.to/#/!XFRnMvAfptAHthwBCx:matrix.org/$HgrlhD-Me1DwsGb8UdMu2Hqubgks8s7ILwWRwigOUAg
|
||||
match args.dynamic_offsets_data {
|
||||
Some(data) => unsafe {
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_bind_group(
|
||||
&mut render_bundle_encoder_resource.0.borrow_mut(),
|
||||
args.index,
|
||||
bind_group_resource.0,
|
||||
data.as_slice().as_ptr(),
|
||||
args.dynamic_offsets_data_length,
|
||||
);
|
||||
},
|
||||
None => {
|
||||
let (prefix, data, suffix) = unsafe { zero_copy[0].align_to::<u32>() };
|
||||
assert!(prefix.is_empty());
|
||||
assert!(suffix.is_empty());
|
||||
unsafe {
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_bind_group(
|
||||
&mut render_bundle_encoder_resource.0.borrow_mut(),
|
||||
args.index,
|
||||
bind_group_resource.0,
|
||||
data[args.dynamic_offsets_data_start..].as_ptr(),
|
||||
args.dynamic_offsets_data_length,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderBundleEncoderPushDebugGroupArgs {
|
||||
render_bundle_encoder_rid: u32,
|
||||
group_label: String,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_bundle_encoder_push_debug_group(
|
||||
state: &mut OpState,
|
||||
args: RenderBundleEncoderPushDebugGroupArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let render_bundle_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderBundleEncoder>(args.render_bundle_encoder_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
unsafe {
|
||||
let label = std::ffi::CString::new(args.group_label).unwrap();
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_push_debug_group(
|
||||
&mut render_bundle_encoder_resource.0.borrow_mut(),
|
||||
label.as_ptr(),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderBundleEncoderPopDebugGroupArgs {
|
||||
render_bundle_encoder_rid: u32,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_bundle_encoder_pop_debug_group(
|
||||
state: &mut OpState,
|
||||
args: RenderBundleEncoderPopDebugGroupArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let render_bundle_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderBundleEncoder>(args.render_bundle_encoder_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
unsafe {
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_pop_debug_group(
|
||||
&mut render_bundle_encoder_resource.0.borrow_mut(),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderBundleEncoderInsertDebugMarkerArgs {
|
||||
render_bundle_encoder_rid: u32,
|
||||
marker_label: String,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_bundle_encoder_insert_debug_marker(
|
||||
state: &mut OpState,
|
||||
args: RenderBundleEncoderInsertDebugMarkerArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let render_bundle_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderBundleEncoder>(args.render_bundle_encoder_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
unsafe {
|
||||
let label = std::ffi::CString::new(args.marker_label).unwrap();
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_insert_debug_marker(
|
||||
&mut render_bundle_encoder_resource.0.borrow_mut(),
|
||||
label.as_ptr(),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderBundleEncoderSetPipelineArgs {
|
||||
render_bundle_encoder_rid: u32,
|
||||
pipeline: u32,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_bundle_encoder_set_pipeline(
|
||||
state: &mut OpState,
|
||||
args: RenderBundleEncoderSetPipelineArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let render_pipeline_resource = state
|
||||
.resource_table
|
||||
.get::<super::pipeline::WebGPURenderPipeline>(args.pipeline)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let render_bundle_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderBundleEncoder>(args.render_bundle_encoder_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_pipeline(
|
||||
&mut render_bundle_encoder_resource.0.borrow_mut(),
|
||||
render_pipeline_resource.0,
|
||||
);
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderBundleEncoderSetIndexBufferArgs {
|
||||
render_bundle_encoder_rid: u32,
|
||||
buffer: u32,
|
||||
index_format: String,
|
||||
offset: u64,
|
||||
size: u64,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_bundle_encoder_set_index_buffer(
|
||||
state: &mut OpState,
|
||||
args: RenderBundleEncoderSetIndexBufferArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let buffer_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGPUBuffer>(args.buffer)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let render_bundle_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderBundleEncoder>(args.render_bundle_encoder_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
render_bundle_encoder_resource
|
||||
.0
|
||||
.borrow_mut()
|
||||
.set_index_buffer(
|
||||
buffer_resource.0,
|
||||
super::pipeline::serialize_index_format(args.index_format),
|
||||
args.offset,
|
||||
std::num::NonZeroU64::new(args.size),
|
||||
);
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderBundleEncoderSetVertexBufferArgs {
|
||||
render_bundle_encoder_rid: u32,
|
||||
slot: u32,
|
||||
buffer: u32,
|
||||
offset: u64,
|
||||
size: u64,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_bundle_encoder_set_vertex_buffer(
|
||||
state: &mut OpState,
|
||||
args: RenderBundleEncoderSetVertexBufferArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let buffer_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGPUBuffer>(args.buffer)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let render_bundle_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderBundleEncoder>(args.render_bundle_encoder_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_vertex_buffer(
|
||||
&mut render_bundle_encoder_resource.0.borrow_mut(),
|
||||
args.slot,
|
||||
buffer_resource.0,
|
||||
args.offset,
|
||||
std::num::NonZeroU64::new(args.size),
|
||||
);
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderBundleEncoderDrawArgs {
|
||||
render_bundle_encoder_rid: u32,
|
||||
vertex_count: u32,
|
||||
instance_count: u32,
|
||||
first_vertex: u32,
|
||||
first_instance: u32,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_bundle_encoder_draw(
|
||||
state: &mut OpState,
|
||||
args: RenderBundleEncoderDrawArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let render_bundle_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderBundleEncoder>(args.render_bundle_encoder_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw(
|
||||
&mut render_bundle_encoder_resource.0.borrow_mut(),
|
||||
args.vertex_count,
|
||||
args.instance_count,
|
||||
args.first_vertex,
|
||||
args.first_instance,
|
||||
);
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderBundleEncoderDrawIndexedArgs {
|
||||
render_bundle_encoder_rid: u32,
|
||||
index_count: u32,
|
||||
instance_count: u32,
|
||||
first_index: u32,
|
||||
base_vertex: i32,
|
||||
first_instance: u32,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_bundle_encoder_draw_indexed(
|
||||
state: &mut OpState,
|
||||
args: RenderBundleEncoderDrawIndexedArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let render_bundle_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderBundleEncoder>(args.render_bundle_encoder_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indexed(
|
||||
&mut render_bundle_encoder_resource.0.borrow_mut(),
|
||||
args.index_count,
|
||||
args.instance_count,
|
||||
args.first_index,
|
||||
args.base_vertex,
|
||||
args.first_instance,
|
||||
);
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderBundleEncoderDrawIndirectArgs {
|
||||
render_bundle_encoder_rid: u32,
|
||||
indirect_buffer: u32,
|
||||
indirect_offset: u64,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_bundle_encoder_draw_indirect(
|
||||
state: &mut OpState,
|
||||
args: RenderBundleEncoderDrawIndirectArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let buffer_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGPUBuffer>(args.indirect_buffer)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let render_bundle_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderBundleEncoder>(args.render_bundle_encoder_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indirect(
|
||||
&mut render_bundle_encoder_resource.0.borrow_mut(),
|
||||
buffer_resource.0,
|
||||
args.indirect_offset,
|
||||
);
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
734
op_crates/webgpu/command_encoder.rs
Normal file
734
op_crates/webgpu/command_encoder.rs
Normal file
|
@ -0,0 +1,734 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_core::error::bad_resource_id;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json::json;
|
||||
use deno_core::serde_json::Value;
|
||||
use deno_core::ZeroCopyBuf;
|
||||
use deno_core::{OpState, Resource};
|
||||
use serde::Deserialize;
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
|
||||
use super::error::WebGPUError;
|
||||
|
||||
pub(crate) struct WebGPUCommandEncoder(
|
||||
pub(crate) wgpu_core::id::CommandEncoderId,
|
||||
);
|
||||
impl Resource for WebGPUCommandEncoder {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUCommandEncoder".into()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct WebGPUCommandBuffer(
|
||||
pub(crate) wgpu_core::id::CommandBufferId,
|
||||
);
|
||||
impl Resource for WebGPUCommandBuffer {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUCommandBuffer".into()
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_store_op(store_op: String) -> wgpu_core::command::StoreOp {
|
||||
match store_op.as_str() {
|
||||
"store" => wgpu_core::command::StoreOp::Store,
|
||||
"clear" => wgpu_core::command::StoreOp::Clear,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateCommandEncoderArgs {
|
||||
device_rid: u32,
|
||||
label: Option<String>,
|
||||
_measure_execution_time: Option<bool>, // not yet implemented
|
||||
}
|
||||
|
||||
pub fn op_webgpu_create_command_encoder(
|
||||
state: &mut OpState,
|
||||
args: CreateCommandEncoderArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let device_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGPUDevice>(args.device_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let device = device_resource.0;
|
||||
|
||||
let descriptor = wgpu_types::CommandEncoderDescriptor {
|
||||
label: args.label.map(Cow::from),
|
||||
};
|
||||
|
||||
let (command_encoder, maybe_err) = gfx_select!(device => instance.device_create_command_encoder(
|
||||
device,
|
||||
&descriptor,
|
||||
std::marker::PhantomData
|
||||
));
|
||||
|
||||
let rid = state
|
||||
.resource_table
|
||||
.add(WebGPUCommandEncoder(command_encoder));
|
||||
|
||||
Ok(json!({
|
||||
"rid": rid,
|
||||
"err": maybe_err.map(WebGPUError::from),
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GPURenderPassColorAttachment {
|
||||
view: u32,
|
||||
resolve_target: Option<u32>,
|
||||
load_op: String,
|
||||
load_value: Option<super::render_pass::GPUColor>,
|
||||
store_op: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GPURenderPassDepthStencilAttachment {
|
||||
view: u32,
|
||||
depth_load_op: String,
|
||||
depth_load_value: Option<f32>,
|
||||
depth_store_op: String,
|
||||
depth_read_only: Option<bool>,
|
||||
stencil_load_op: String,
|
||||
stencil_load_value: Option<u32>,
|
||||
stencil_store_op: String,
|
||||
stencil_read_only: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CommandEncoderBeginRenderPassArgs {
|
||||
command_encoder_rid: u32,
|
||||
label: Option<String>,
|
||||
color_attachments: Vec<GPURenderPassColorAttachment>,
|
||||
depth_stencil_attachment: Option<GPURenderPassDepthStencilAttachment>,
|
||||
_occlusion_query_set: Option<u32>, // not yet implemented
|
||||
}
|
||||
|
||||
pub fn op_webgpu_command_encoder_begin_render_pass(
|
||||
state: &mut OpState,
|
||||
args: CommandEncoderBeginRenderPassArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let command_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUCommandEncoder>(args.command_encoder_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
let mut color_attachments = vec![];
|
||||
|
||||
for color_attachment in args.color_attachments {
|
||||
let texture_view_resource = state
|
||||
.resource_table
|
||||
.get::<super::texture::WebGPUTextureView>(color_attachment.view)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
let attachment = wgpu_core::command::ColorAttachmentDescriptor {
|
||||
attachment: texture_view_resource.0,
|
||||
resolve_target: color_attachment
|
||||
.resolve_target
|
||||
.map(|rid| {
|
||||
state
|
||||
.resource_table
|
||||
.get::<super::texture::WebGPUTextureView>(rid)
|
||||
.ok_or_else(bad_resource_id)
|
||||
})
|
||||
.transpose()?
|
||||
.map(|texture| texture.0),
|
||||
channel: match color_attachment.load_op.as_str() {
|
||||
"load" => wgpu_core::command::PassChannel {
|
||||
load_op: wgpu_core::command::LoadOp::Load,
|
||||
store_op: color_attachment
|
||||
.store_op
|
||||
.map_or(wgpu_core::command::StoreOp::Store, serialize_store_op),
|
||||
clear_value: Default::default(),
|
||||
read_only: false,
|
||||
},
|
||||
"clear" => {
|
||||
let color = color_attachment.load_value.unwrap();
|
||||
wgpu_core::command::PassChannel {
|
||||
load_op: wgpu_core::command::LoadOp::Clear,
|
||||
store_op: color_attachment
|
||||
.store_op
|
||||
.map_or(wgpu_core::command::StoreOp::Store, serialize_store_op),
|
||||
clear_value: wgpu_types::Color {
|
||||
r: color.r,
|
||||
g: color.g,
|
||||
b: color.b,
|
||||
a: color.a,
|
||||
},
|
||||
read_only: false,
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
};
|
||||
|
||||
color_attachments.push(attachment)
|
||||
}
|
||||
|
||||
let mut depth_stencil_attachment = None;
|
||||
|
||||
if let Some(attachment) = args.depth_stencil_attachment {
|
||||
let texture_view_resource = state
|
||||
.resource_table
|
||||
.get::<super::texture::WebGPUTextureView>(attachment.view)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
depth_stencil_attachment =
|
||||
Some(wgpu_core::command::DepthStencilAttachmentDescriptor {
|
||||
attachment: texture_view_resource.0,
|
||||
depth: match attachment.depth_load_op.as_str() {
|
||||
"load" => wgpu_core::command::PassChannel {
|
||||
load_op: wgpu_core::command::LoadOp::Load,
|
||||
store_op: serialize_store_op(attachment.depth_store_op),
|
||||
clear_value: 0.0,
|
||||
read_only: attachment.depth_read_only.unwrap_or(false),
|
||||
},
|
||||
"clear" => wgpu_core::command::PassChannel {
|
||||
load_op: wgpu_core::command::LoadOp::Clear,
|
||||
store_op: serialize_store_op(attachment.depth_store_op),
|
||||
clear_value: attachment.depth_load_value.unwrap(),
|
||||
read_only: attachment.depth_read_only.unwrap_or(false),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
},
|
||||
stencil: match attachment.stencil_load_op.as_str() {
|
||||
"load" => wgpu_core::command::PassChannel {
|
||||
load_op: wgpu_core::command::LoadOp::Load,
|
||||
store_op: serialize_store_op(attachment.stencil_store_op),
|
||||
clear_value: 0,
|
||||
read_only: attachment.stencil_read_only.unwrap_or(false),
|
||||
},
|
||||
"clear" => wgpu_core::command::PassChannel {
|
||||
load_op: wgpu_core::command::LoadOp::Clear,
|
||||
store_op: serialize_store_op(attachment.stencil_store_op),
|
||||
clear_value: attachment.stencil_load_value.unwrap(),
|
||||
read_only: attachment.stencil_read_only.unwrap_or(false),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
let descriptor = wgpu_core::command::RenderPassDescriptor {
|
||||
label: args.label.map(Cow::from),
|
||||
color_attachments: Cow::from(color_attachments),
|
||||
depth_stencil_attachment: depth_stencil_attachment.as_ref(),
|
||||
};
|
||||
|
||||
let render_pass = wgpu_core::command::RenderPass::new(
|
||||
command_encoder_resource.0,
|
||||
&descriptor,
|
||||
);
|
||||
|
||||
let rid = state
|
||||
.resource_table
|
||||
.add(super::render_pass::WebGPURenderPass(RefCell::new(
|
||||
render_pass,
|
||||
)));
|
||||
|
||||
Ok(json!({
|
||||
"rid": rid,
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CommandEncoderBeginComputePassArgs {
|
||||
command_encoder_rid: u32,
|
||||
label: Option<String>,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_command_encoder_begin_compute_pass(
|
||||
state: &mut OpState,
|
||||
args: CommandEncoderBeginComputePassArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let command_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUCommandEncoder>(args.command_encoder_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
let descriptor = wgpu_core::command::ComputePassDescriptor {
|
||||
label: args.label.map(Cow::from),
|
||||
};
|
||||
|
||||
let compute_pass = wgpu_core::command::ComputePass::new(
|
||||
command_encoder_resource.0,
|
||||
&descriptor,
|
||||
);
|
||||
|
||||
let rid = state
|
||||
.resource_table
|
||||
.add(super::compute_pass::WebGPUComputePass(RefCell::new(
|
||||
compute_pass,
|
||||
)));
|
||||
|
||||
Ok(json!({
|
||||
"rid": rid,
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CommandEncoderCopyBufferToBufferArgs {
|
||||
command_encoder_rid: u32,
|
||||
source: u32,
|
||||
source_offset: u64,
|
||||
destination: u32,
|
||||
destination_offset: u64,
|
||||
size: u64,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_command_encoder_copy_buffer_to_buffer(
|
||||
state: &mut OpState,
|
||||
args: CommandEncoderCopyBufferToBufferArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let command_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUCommandEncoder>(args.command_encoder_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let command_encoder = command_encoder_resource.0;
|
||||
let source_buffer_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGPUBuffer>(args.source)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let source_buffer = source_buffer_resource.0;
|
||||
let destination_buffer_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGPUBuffer>(args.destination)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let destination_buffer = destination_buffer_resource.0;
|
||||
|
||||
let maybe_err = gfx_select!(command_encoder => instance.command_encoder_copy_buffer_to_buffer(
|
||||
command_encoder,
|
||||
source_buffer,
|
||||
args.source_offset,
|
||||
destination_buffer,
|
||||
args.destination_offset,
|
||||
args.size
|
||||
)).err();
|
||||
|
||||
Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GPUImageCopyBuffer {
|
||||
buffer: u32,
|
||||
offset: Option<u64>,
|
||||
bytes_per_row: Option<u32>,
|
||||
rows_per_image: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GPUOrigin3D {
|
||||
pub x: Option<u32>,
|
||||
pub y: Option<u32>,
|
||||
pub z: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GPUImageCopyTexture {
|
||||
pub texture: u32,
|
||||
pub mip_level: Option<u32>,
|
||||
pub origin: Option<GPUOrigin3D>,
|
||||
pub _aspect: Option<String>, // not yet implemented
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CommandEncoderCopyBufferToTextureArgs {
|
||||
command_encoder_rid: u32,
|
||||
source: GPUImageCopyBuffer,
|
||||
destination: GPUImageCopyTexture,
|
||||
copy_size: super::texture::GPUExtent3D,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_command_encoder_copy_buffer_to_texture(
|
||||
state: &mut OpState,
|
||||
args: CommandEncoderCopyBufferToTextureArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let command_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUCommandEncoder>(args.command_encoder_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let command_encoder = command_encoder_resource.0;
|
||||
let source_buffer_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGPUBuffer>(args.source.buffer)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let destination_texture_resource = state
|
||||
.resource_table
|
||||
.get::<super::texture::WebGPUTexture>(args.destination.texture)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
let source = wgpu_core::command::BufferCopyView {
|
||||
buffer: source_buffer_resource.0,
|
||||
layout: wgpu_types::TextureDataLayout {
|
||||
offset: args.source.offset.unwrap_or(0),
|
||||
bytes_per_row: args.source.bytes_per_row.unwrap_or(0),
|
||||
rows_per_image: args.source.rows_per_image.unwrap_or(0),
|
||||
},
|
||||
};
|
||||
let destination = wgpu_core::command::TextureCopyView {
|
||||
texture: destination_texture_resource.0,
|
||||
mip_level: args.destination.mip_level.unwrap_or(0),
|
||||
origin: args
|
||||
.destination
|
||||
.origin
|
||||
.map_or(Default::default(), |origin| wgpu_types::Origin3d {
|
||||
x: origin.x.unwrap_or(0),
|
||||
y: origin.y.unwrap_or(0),
|
||||
z: origin.z.unwrap_or(0),
|
||||
}),
|
||||
};
|
||||
let maybe_err = gfx_select!(command_encoder => instance.command_encoder_copy_buffer_to_texture(
|
||||
command_encoder,
|
||||
&source,
|
||||
&destination,
|
||||
&wgpu_types::Extent3d {
|
||||
width: args.copy_size.width.unwrap_or(1),
|
||||
height: args.copy_size.height.unwrap_or(1),
|
||||
depth: args.copy_size.depth.unwrap_or(1),
|
||||
}
|
||||
)).err();
|
||||
|
||||
Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CommandEncoderCopyTextureToBufferArgs {
|
||||
command_encoder_rid: u32,
|
||||
source: GPUImageCopyTexture,
|
||||
destination: GPUImageCopyBuffer,
|
||||
copy_size: super::texture::GPUExtent3D,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_command_encoder_copy_texture_to_buffer(
|
||||
state: &mut OpState,
|
||||
args: CommandEncoderCopyTextureToBufferArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let command_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUCommandEncoder>(args.command_encoder_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let command_encoder = command_encoder_resource.0;
|
||||
let source_texture_resource = state
|
||||
.resource_table
|
||||
.get::<super::texture::WebGPUTexture>(args.source.texture)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let destination_buffer_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGPUBuffer>(args.destination.buffer)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
let source = wgpu_core::command::TextureCopyView {
|
||||
texture: source_texture_resource.0,
|
||||
mip_level: args.source.mip_level.unwrap_or(0),
|
||||
origin: args.source.origin.map_or(Default::default(), |origin| {
|
||||
wgpu_types::Origin3d {
|
||||
x: origin.x.unwrap_or(0),
|
||||
y: origin.y.unwrap_or(0),
|
||||
z: origin.z.unwrap_or(0),
|
||||
}
|
||||
}),
|
||||
};
|
||||
let destination = wgpu_core::command::BufferCopyView {
|
||||
buffer: destination_buffer_resource.0,
|
||||
layout: wgpu_types::TextureDataLayout {
|
||||
offset: args.destination.offset.unwrap_or(0),
|
||||
bytes_per_row: args.destination.bytes_per_row.unwrap_or(0),
|
||||
rows_per_image: args.destination.rows_per_image.unwrap_or(0),
|
||||
},
|
||||
};
|
||||
let maybe_err = gfx_select!(command_encoder => instance.command_encoder_copy_texture_to_buffer(
|
||||
command_encoder,
|
||||
&source,
|
||||
&destination,
|
||||
&wgpu_types::Extent3d {
|
||||
width: args.copy_size.width.unwrap_or(1),
|
||||
height: args.copy_size.height.unwrap_or(1),
|
||||
depth: args.copy_size.depth.unwrap_or(1),
|
||||
}
|
||||
)).err();
|
||||
|
||||
Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CommandEncoderCopyTextureToTextureArgs {
|
||||
command_encoder_rid: u32,
|
||||
source: GPUImageCopyTexture,
|
||||
destination: GPUImageCopyTexture,
|
||||
copy_size: super::texture::GPUExtent3D,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_command_encoder_copy_texture_to_texture(
|
||||
state: &mut OpState,
|
||||
args: CommandEncoderCopyTextureToTextureArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let command_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUCommandEncoder>(args.command_encoder_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let command_encoder = command_encoder_resource.0;
|
||||
let source_texture_resource = state
|
||||
.resource_table
|
||||
.get::<super::texture::WebGPUTexture>(args.source.texture)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let destination_texture_resource = state
|
||||
.resource_table
|
||||
.get::<super::texture::WebGPUTexture>(args.destination.texture)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
let source = wgpu_core::command::TextureCopyView {
|
||||
texture: source_texture_resource.0,
|
||||
mip_level: args.source.mip_level.unwrap_or(0),
|
||||
origin: args.source.origin.map_or(Default::default(), |origin| {
|
||||
wgpu_types::Origin3d {
|
||||
x: origin.x.unwrap_or(0),
|
||||
y: origin.y.unwrap_or(0),
|
||||
z: origin.z.unwrap_or(0),
|
||||
}
|
||||
}),
|
||||
};
|
||||
let destination = wgpu_core::command::TextureCopyView {
|
||||
texture: destination_texture_resource.0,
|
||||
mip_level: args.destination.mip_level.unwrap_or(0),
|
||||
origin: args
|
||||
.destination
|
||||
.origin
|
||||
.map_or(Default::default(), |origin| wgpu_types::Origin3d {
|
||||
x: origin.x.unwrap_or(0),
|
||||
y: origin.y.unwrap_or(0),
|
||||
z: origin.z.unwrap_or(0),
|
||||
}),
|
||||
};
|
||||
let maybe_err = gfx_select!(command_encoder => instance.command_encoder_copy_texture_to_texture(
|
||||
command_encoder,
|
||||
&source,
|
||||
&destination,
|
||||
&wgpu_types::Extent3d {
|
||||
width: args.copy_size.width.unwrap_or(1),
|
||||
height: args.copy_size.height.unwrap_or(1),
|
||||
depth: args.copy_size.depth.unwrap_or(1),
|
||||
}
|
||||
)).err();
|
||||
|
||||
Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CommandEncoderPushDebugGroupArgs {
|
||||
command_encoder_rid: u32,
|
||||
group_label: String,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_command_encoder_push_debug_group(
|
||||
state: &mut OpState,
|
||||
args: CommandEncoderPushDebugGroupArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let command_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUCommandEncoder>(args.command_encoder_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let command_encoder = command_encoder_resource.0;
|
||||
|
||||
let maybe_err = gfx_select!(command_encoder => instance
|
||||
.command_encoder_push_debug_group(command_encoder, &args.group_label))
|
||||
.err();
|
||||
|
||||
Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CommandEncoderPopDebugGroupArgs {
|
||||
command_encoder_rid: u32,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_command_encoder_pop_debug_group(
|
||||
state: &mut OpState,
|
||||
args: CommandEncoderPopDebugGroupArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let command_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUCommandEncoder>(args.command_encoder_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let command_encoder = command_encoder_resource.0;
|
||||
|
||||
let maybe_err = gfx_select!(command_encoder => instance.command_encoder_pop_debug_group(command_encoder)).err();
|
||||
|
||||
Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CommandEncoderInsertDebugMarkerArgs {
|
||||
command_encoder_rid: u32,
|
||||
marker_label: String,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_command_encoder_insert_debug_marker(
|
||||
state: &mut OpState,
|
||||
args: CommandEncoderInsertDebugMarkerArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let command_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUCommandEncoder>(args.command_encoder_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let command_encoder = command_encoder_resource.0;
|
||||
|
||||
let maybe_err = gfx_select!(command_encoder => instance.command_encoder_insert_debug_marker(
|
||||
command_encoder,
|
||||
&args.marker_label
|
||||
)).err();
|
||||
|
||||
Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CommandEncoderWriteTimestampArgs {
|
||||
command_encoder_rid: u32,
|
||||
query_set: u32,
|
||||
query_index: u32,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_command_encoder_write_timestamp(
|
||||
state: &mut OpState,
|
||||
args: CommandEncoderWriteTimestampArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let command_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUCommandEncoder>(args.command_encoder_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let command_encoder = command_encoder_resource.0;
|
||||
let query_set_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGPUQuerySet>(args.query_set)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
let maybe_err =
|
||||
gfx_select!(command_encoder => instance.command_encoder_write_timestamp(
|
||||
command_encoder,
|
||||
query_set_resource.0,
|
||||
args.query_index
|
||||
))
|
||||
.err();
|
||||
|
||||
Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CommandEncoderResolveQuerySetArgs {
|
||||
command_encoder_rid: u32,
|
||||
query_set: u32,
|
||||
first_query: u32,
|
||||
query_count: u32,
|
||||
destination: u32,
|
||||
destination_offset: u64,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_command_encoder_resolve_query_set(
|
||||
state: &mut OpState,
|
||||
args: CommandEncoderResolveQuerySetArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let command_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUCommandEncoder>(args.command_encoder_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let command_encoder = command_encoder_resource.0;
|
||||
let query_set_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGPUQuerySet>(args.query_set)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let destination_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGPUBuffer>(args.destination)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
let maybe_err =
|
||||
gfx_select!(command_encoder => instance.command_encoder_resolve_query_set(
|
||||
command_encoder,
|
||||
query_set_resource.0,
|
||||
args.first_query,
|
||||
args.query_count,
|
||||
destination_resource.0,
|
||||
args.destination_offset
|
||||
))
|
||||
.err();
|
||||
|
||||
Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CommandEncoderFinishArgs {
|
||||
command_encoder_rid: u32,
|
||||
label: Option<String>,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_command_encoder_finish(
|
||||
state: &mut OpState,
|
||||
args: CommandEncoderFinishArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let command_encoder_resource = state
|
||||
.resource_table
|
||||
.take::<WebGPUCommandEncoder>(args.command_encoder_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let command_encoder = command_encoder_resource.0;
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
|
||||
let descriptor = wgpu_types::CommandBufferDescriptor {
|
||||
label: args.label.map(Cow::from),
|
||||
};
|
||||
|
||||
let (command_buffer, maybe_err) = gfx_select!(command_encoder => instance.command_encoder_finish(
|
||||
command_encoder,
|
||||
&descriptor
|
||||
));
|
||||
|
||||
let rid = state
|
||||
.resource_table
|
||||
.add(WebGPUCommandBuffer(command_buffer));
|
||||
|
||||
Ok(json!({
|
||||
"rid": rid,
|
||||
"err": maybe_err.map(WebGPUError::from)
|
||||
}))
|
||||
}
|
365
op_crates/webgpu/compute_pass.rs
Normal file
365
op_crates/webgpu/compute_pass.rs
Normal file
|
@ -0,0 +1,365 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_core::error::bad_resource_id;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json::json;
|
||||
use deno_core::serde_json::Value;
|
||||
use deno_core::ZeroCopyBuf;
|
||||
use deno_core::{OpState, Resource};
|
||||
use serde::Deserialize;
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
|
||||
use super::error::WebGPUError;
|
||||
|
||||
pub(crate) struct WebGPUComputePass(
|
||||
pub(crate) RefCell<wgpu_core::command::ComputePass>,
|
||||
);
|
||||
impl Resource for WebGPUComputePass {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUComputePass".into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ComputePassSetPipelineArgs {
|
||||
compute_pass_rid: u32,
|
||||
pipeline: u32,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_compute_pass_set_pipeline(
|
||||
state: &mut OpState,
|
||||
args: ComputePassSetPipelineArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let compute_pipeline_resource = state
|
||||
.resource_table
|
||||
.get::<super::pipeline::WebGPUComputePipeline>(args.pipeline)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let compute_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUComputePass>(args.compute_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
wgpu_core::command::compute_ffi::wgpu_compute_pass_set_pipeline(
|
||||
&mut compute_pass_resource.0.borrow_mut(),
|
||||
compute_pipeline_resource.0,
|
||||
);
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ComputePassDispatchArgs {
|
||||
compute_pass_rid: u32,
|
||||
x: u32,
|
||||
y: u32,
|
||||
z: u32,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_compute_pass_dispatch(
|
||||
state: &mut OpState,
|
||||
args: ComputePassDispatchArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let compute_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUComputePass>(args.compute_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
wgpu_core::command::compute_ffi::wgpu_compute_pass_dispatch(
|
||||
&mut compute_pass_resource.0.borrow_mut(),
|
||||
args.x,
|
||||
args.y,
|
||||
args.z,
|
||||
);
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ComputePassDispatchIndirectArgs {
|
||||
compute_pass_rid: u32,
|
||||
indirect_buffer: u32,
|
||||
indirect_offset: u64,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_compute_pass_dispatch_indirect(
|
||||
state: &mut OpState,
|
||||
args: ComputePassDispatchIndirectArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let buffer_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGPUBuffer>(args.indirect_buffer)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let compute_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUComputePass>(args.compute_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
wgpu_core::command::compute_ffi::wgpu_compute_pass_dispatch_indirect(
|
||||
&mut compute_pass_resource.0.borrow_mut(),
|
||||
buffer_resource.0,
|
||||
args.indirect_offset,
|
||||
);
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ComputePassBeginPipelineStatisticsQueryArgs {
|
||||
compute_pass_rid: u32,
|
||||
query_set: u32,
|
||||
query_index: u32,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_compute_pass_begin_pipeline_statistics_query(
|
||||
state: &mut OpState,
|
||||
args: ComputePassBeginPipelineStatisticsQueryArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let compute_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUComputePass>(args.compute_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let query_set_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGPUQuerySet>(args.query_set)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
unsafe {
|
||||
wgpu_core::command::compute_ffi::wgpu_compute_pass_begin_pipeline_statistics_query(
|
||||
&mut compute_pass_resource.0.borrow_mut(),
|
||||
query_set_resource.0,
|
||||
args.query_index,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ComputePassEndPipelineStatisticsQueryArgs {
|
||||
compute_pass_rid: u32,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_compute_pass_end_pipeline_statistics_query(
|
||||
state: &mut OpState,
|
||||
args: ComputePassEndPipelineStatisticsQueryArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let compute_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUComputePass>(args.compute_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
unsafe {
|
||||
wgpu_core::command::compute_ffi::wgpu_compute_pass_end_pipeline_statistics_query(
|
||||
&mut compute_pass_resource.0.borrow_mut(),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ComputePassWriteTimestampArgs {
|
||||
compute_pass_rid: u32,
|
||||
query_set: u32,
|
||||
query_index: u32,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_compute_pass_write_timestamp(
|
||||
state: &mut OpState,
|
||||
args: ComputePassWriteTimestampArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let compute_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUComputePass>(args.compute_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let query_set_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGPUQuerySet>(args.query_set)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
unsafe {
|
||||
wgpu_core::command::compute_ffi::wgpu_compute_pass_write_timestamp(
|
||||
&mut compute_pass_resource.0.borrow_mut(),
|
||||
query_set_resource.0,
|
||||
args.query_index,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ComputePassEndPassArgs {
|
||||
command_encoder_rid: u32,
|
||||
compute_pass_rid: u32,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_compute_pass_end_pass(
|
||||
state: &mut OpState,
|
||||
args: ComputePassEndPassArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let command_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<super::command_encoder::WebGPUCommandEncoder>(
|
||||
args.command_encoder_rid,
|
||||
)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let command_encoder = command_encoder_resource.0;
|
||||
let compute_pass_resource = state
|
||||
.resource_table
|
||||
.take::<WebGPUComputePass>(args.compute_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let compute_pass = &compute_pass_resource.0.borrow();
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
|
||||
let maybe_err =
|
||||
gfx_select!(command_encoder => instance.command_encoder_run_compute_pass(
|
||||
command_encoder,
|
||||
compute_pass
|
||||
))
|
||||
.err();
|
||||
|
||||
Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ComputePassSetBindGroupArgs {
|
||||
compute_pass_rid: u32,
|
||||
index: u32,
|
||||
bind_group: u32,
|
||||
dynamic_offsets_data: Option<Vec<u32>>,
|
||||
dynamic_offsets_data_start: usize,
|
||||
dynamic_offsets_data_length: usize,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_compute_pass_set_bind_group(
|
||||
state: &mut OpState,
|
||||
args: ComputePassSetBindGroupArgs,
|
||||
zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let bind_group_resource = state
|
||||
.resource_table
|
||||
.get::<super::binding::WebGPUBindGroup>(args.bind_group)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let compute_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUComputePass>(args.compute_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
unsafe {
|
||||
wgpu_core::command::compute_ffi::wgpu_compute_pass_set_bind_group(
|
||||
&mut compute_pass_resource.0.borrow_mut(),
|
||||
args.index,
|
||||
bind_group_resource.0,
|
||||
match args.dynamic_offsets_data {
|
||||
Some(data) => data.as_ptr(),
|
||||
None => {
|
||||
let (prefix, data, suffix) = zero_copy[0].align_to::<u32>();
|
||||
assert!(prefix.is_empty());
|
||||
assert!(suffix.is_empty());
|
||||
data[args.dynamic_offsets_data_start..].as_ptr()
|
||||
}
|
||||
},
|
||||
args.dynamic_offsets_data_length,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ComputePassPushDebugGroupArgs {
|
||||
compute_pass_rid: u32,
|
||||
group_label: String,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_compute_pass_push_debug_group(
|
||||
state: &mut OpState,
|
||||
args: ComputePassPushDebugGroupArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let compute_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUComputePass>(args.compute_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
unsafe {
|
||||
let label = std::ffi::CString::new(args.group_label).unwrap();
|
||||
wgpu_core::command::compute_ffi::wgpu_compute_pass_push_debug_group(
|
||||
&mut compute_pass_resource.0.borrow_mut(),
|
||||
label.as_ptr(),
|
||||
0, // wgpu#975
|
||||
);
|
||||
}
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ComputePassPopDebugGroupArgs {
|
||||
compute_pass_rid: u32,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_compute_pass_pop_debug_group(
|
||||
state: &mut OpState,
|
||||
args: ComputePassPopDebugGroupArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let compute_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUComputePass>(args.compute_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
wgpu_core::command::compute_ffi::wgpu_compute_pass_pop_debug_group(
|
||||
&mut compute_pass_resource.0.borrow_mut(),
|
||||
);
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ComputePassInsertDebugMarkerArgs {
|
||||
compute_pass_rid: u32,
|
||||
marker_label: String,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_compute_pass_insert_debug_marker(
|
||||
state: &mut OpState,
|
||||
args: ComputePassInsertDebugMarkerArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let compute_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUComputePass>(args.compute_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
unsafe {
|
||||
let label = std::ffi::CString::new(args.marker_label).unwrap();
|
||||
wgpu_core::command::compute_ffi::wgpu_compute_pass_insert_debug_marker(
|
||||
&mut compute_pass_resource.0.borrow_mut(),
|
||||
label.as_ptr(),
|
||||
0, // wgpu#975
|
||||
);
|
||||
}
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
252
op_crates/webgpu/error.rs
Normal file
252
op_crates/webgpu/error.rs
Normal file
|
@ -0,0 +1,252 @@
|
|||
use deno_core::error::AnyError;
|
||||
use serde::Serialize;
|
||||
use std::fmt;
|
||||
use wgpu_core::binding_model::CreateBindGroupError;
|
||||
use wgpu_core::binding_model::CreateBindGroupLayoutError;
|
||||
use wgpu_core::binding_model::CreatePipelineLayoutError;
|
||||
use wgpu_core::binding_model::GetBindGroupLayoutError;
|
||||
use wgpu_core::command::CommandAllocatorError;
|
||||
use wgpu_core::command::CommandEncoderError;
|
||||
use wgpu_core::command::ComputePassError;
|
||||
use wgpu_core::command::CopyError;
|
||||
use wgpu_core::command::CreateRenderBundleError;
|
||||
use wgpu_core::command::QueryError;
|
||||
use wgpu_core::command::RenderBundleError;
|
||||
use wgpu_core::command::RenderPassError;
|
||||
use wgpu_core::device::queue::QueueSubmitError;
|
||||
use wgpu_core::device::queue::QueueWriteError;
|
||||
use wgpu_core::device::DeviceError;
|
||||
use wgpu_core::pipeline::CreateComputePipelineError;
|
||||
use wgpu_core::pipeline::CreateRenderPipelineError;
|
||||
use wgpu_core::pipeline::CreateShaderModuleError;
|
||||
use wgpu_core::resource::BufferAccessError;
|
||||
use wgpu_core::resource::CreateBufferError;
|
||||
use wgpu_core::resource::CreateQuerySetError;
|
||||
use wgpu_core::resource::CreateSamplerError;
|
||||
use wgpu_core::resource::CreateTextureError;
|
||||
use wgpu_core::resource::CreateTextureViewError;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(tag = "type", content = "value")]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum WebGPUError {
|
||||
Lost,
|
||||
OutOfMemory,
|
||||
Validation(String),
|
||||
}
|
||||
|
||||
impl From<CreateBufferError> for WebGPUError {
|
||||
fn from(err: CreateBufferError) -> Self {
|
||||
match err {
|
||||
CreateBufferError::Device(err) => err.into(),
|
||||
CreateBufferError::AccessError(err) => err.into(),
|
||||
err => WebGPUError::Validation(err.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DeviceError> for WebGPUError {
|
||||
fn from(err: DeviceError) -> Self {
|
||||
match err {
|
||||
DeviceError::Lost => WebGPUError::Lost,
|
||||
DeviceError::OutOfMemory => WebGPUError::OutOfMemory,
|
||||
DeviceError::Invalid => WebGPUError::Validation(err.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BufferAccessError> for WebGPUError {
|
||||
fn from(err: BufferAccessError) -> Self {
|
||||
match err {
|
||||
BufferAccessError::Device(err) => err.into(),
|
||||
err => WebGPUError::Validation(err.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CreateBindGroupLayoutError> for WebGPUError {
|
||||
fn from(err: CreateBindGroupLayoutError) -> Self {
|
||||
match err {
|
||||
CreateBindGroupLayoutError::Device(err) => err.into(),
|
||||
err => WebGPUError::Validation(err.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CreatePipelineLayoutError> for WebGPUError {
|
||||
fn from(err: CreatePipelineLayoutError) -> Self {
|
||||
match err {
|
||||
CreatePipelineLayoutError::Device(err) => err.into(),
|
||||
err => WebGPUError::Validation(err.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CreateBindGroupError> for WebGPUError {
|
||||
fn from(err: CreateBindGroupError) -> Self {
|
||||
match err {
|
||||
CreateBindGroupError::Device(err) => err.into(),
|
||||
err => WebGPUError::Validation(err.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RenderBundleError> for WebGPUError {
|
||||
fn from(err: RenderBundleError) -> Self {
|
||||
WebGPUError::Validation(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CreateRenderBundleError> for WebGPUError {
|
||||
fn from(err: CreateRenderBundleError) -> Self {
|
||||
WebGPUError::Validation(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CommandAllocatorError> for WebGPUError {
|
||||
fn from(err: CommandAllocatorError) -> Self {
|
||||
match err {
|
||||
CommandAllocatorError::Device(err) => err.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CopyError> for WebGPUError {
|
||||
fn from(err: CopyError) -> Self {
|
||||
WebGPUError::Validation(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CommandEncoderError> for WebGPUError {
|
||||
fn from(err: CommandEncoderError) -> Self {
|
||||
WebGPUError::Validation(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<QueryError> for WebGPUError {
|
||||
fn from(err: QueryError) -> Self {
|
||||
WebGPUError::Validation(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ComputePassError> for WebGPUError {
|
||||
fn from(err: ComputePassError) -> Self {
|
||||
WebGPUError::Validation(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CreateComputePipelineError> for WebGPUError {
|
||||
fn from(err: CreateComputePipelineError) -> Self {
|
||||
match err {
|
||||
CreateComputePipelineError::Device(err) => err.into(),
|
||||
err => WebGPUError::Validation(err.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GetBindGroupLayoutError> for WebGPUError {
|
||||
fn from(err: GetBindGroupLayoutError) -> Self {
|
||||
WebGPUError::Validation(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CreateRenderPipelineError> for WebGPUError {
|
||||
fn from(err: CreateRenderPipelineError) -> Self {
|
||||
match err {
|
||||
CreateRenderPipelineError::Device(err) => err.into(),
|
||||
err => WebGPUError::Validation(err.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RenderPassError> for WebGPUError {
|
||||
fn from(err: RenderPassError) -> Self {
|
||||
WebGPUError::Validation(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CreateSamplerError> for WebGPUError {
|
||||
fn from(err: CreateSamplerError) -> Self {
|
||||
match err {
|
||||
CreateSamplerError::Device(err) => err.into(),
|
||||
err => WebGPUError::Validation(err.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CreateShaderModuleError> for WebGPUError {
|
||||
fn from(err: CreateShaderModuleError) -> Self {
|
||||
match err {
|
||||
CreateShaderModuleError::Device(err) => err.into(),
|
||||
err => WebGPUError::Validation(err.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CreateTextureError> for WebGPUError {
|
||||
fn from(err: CreateTextureError) -> Self {
|
||||
match err {
|
||||
CreateTextureError::Device(err) => err.into(),
|
||||
err => WebGPUError::Validation(err.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CreateTextureViewError> for WebGPUError {
|
||||
fn from(err: CreateTextureViewError) -> Self {
|
||||
WebGPUError::Validation(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CreateQuerySetError> for WebGPUError {
|
||||
fn from(err: CreateQuerySetError) -> Self {
|
||||
match err {
|
||||
CreateQuerySetError::Device(err) => err.into(),
|
||||
err => WebGPUError::Validation(err.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<QueueSubmitError> for WebGPUError {
|
||||
fn from(err: QueueSubmitError) -> Self {
|
||||
match err {
|
||||
QueueSubmitError::Queue(err) => err.into(),
|
||||
err => WebGPUError::Validation(err.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<QueueWriteError> for WebGPUError {
|
||||
fn from(err: QueueWriteError) -> Self {
|
||||
match err {
|
||||
QueueWriteError::Queue(err) => err.into(),
|
||||
err => WebGPUError::Validation(err.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DOMExceptionOperationError {
|
||||
pub msg: String,
|
||||
}
|
||||
|
||||
impl DOMExceptionOperationError {
|
||||
pub fn new(msg: &str) -> Self {
|
||||
DOMExceptionOperationError {
|
||||
msg: msg.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for DOMExceptionOperationError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.pad(&self.msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for DOMExceptionOperationError {}
|
||||
|
||||
pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> {
|
||||
e.downcast_ref::<DOMExceptionOperationError>()
|
||||
.map(|_| "DOMExceptionOperationError")
|
||||
}
|
1126
op_crates/webgpu/lib.deno_webgpu.d.ts
vendored
Normal file
1126
op_crates/webgpu/lib.deno_webgpu.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load diff
541
op_crates/webgpu/lib.rs
Normal file
541
op_crates/webgpu/lib.rs
Normal file
|
@ -0,0 +1,541 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
#![deny(warnings)]
|
||||
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::error::{bad_resource_id, not_supported};
|
||||
use deno_core::serde_json::json;
|
||||
use deno_core::serde_json::Value;
|
||||
use deno_core::OpState;
|
||||
use deno_core::ZeroCopyBuf;
|
||||
use deno_core::{BufVec, Resource};
|
||||
use serde::Deserialize;
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
pub use wgpu_core;
|
||||
pub use wgpu_types;
|
||||
|
||||
use error::DOMExceptionOperationError;
|
||||
use error::WebGPUError;
|
||||
|
||||
#[macro_use]
|
||||
mod macros {
|
||||
macro_rules! gfx_select {
|
||||
($id:expr => $global:ident.$method:ident( $($param:expr),* )) => {
|
||||
match $id.backend() {
|
||||
#[cfg(all(not(target_arch = "wasm32"), not(any(target_os = "ios", target_os = "macos"))))]
|
||||
wgpu_types::Backend::Vulkan => $global.$method::<wgpu_core::backend::Vulkan>( $($param),* ),
|
||||
#[cfg(all(not(target_arch = "wasm32"), any(target_os = "ios", target_os = "macos")))]
|
||||
wgpu_types::Backend::Metal => $global.$method::<wgpu_core::backend::Metal>( $($param),* ),
|
||||
#[cfg(all(not(target_arch = "wasm32"), windows))]
|
||||
wgpu_types::Backend::Dx12 => $global.$method::<wgpu_core::backend::Dx12>( $($param),* ),
|
||||
#[cfg(all(not(target_arch = "wasm32"), windows))]
|
||||
wgpu_types::Backend::Dx11 => $global.$method::<wgpu_core::backend::Dx11>( $($param),* ),
|
||||
#[cfg(any(target_arch = "wasm32", all(unix, not(any(target_os = "ios", target_os = "macos")))))]
|
||||
wgpu_types::Backend::Gl => $global.$method::<wgpu_core::backend::Gl>( $($param),+ ),
|
||||
other => panic!("Unexpected backend {:?}", other),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub mod binding;
|
||||
pub mod buffer;
|
||||
pub mod bundle;
|
||||
pub mod command_encoder;
|
||||
pub mod compute_pass;
|
||||
pub mod error;
|
||||
pub mod pipeline;
|
||||
pub mod queue;
|
||||
pub mod render_pass;
|
||||
pub mod sampler;
|
||||
pub mod shader;
|
||||
pub mod texture;
|
||||
|
||||
pub struct Unstable(pub bool);
|
||||
|
||||
fn check_unstable(state: &OpState, api_name: &str) {
|
||||
let unstable = state.borrow::<Unstable>();
|
||||
|
||||
if !unstable.0 {
|
||||
eprintln!(
|
||||
"Unstable API '{}'. The --unstable flag must be provided.",
|
||||
api_name
|
||||
);
|
||||
std::process::exit(70);
|
||||
}
|
||||
}
|
||||
|
||||
type Instance = wgpu_core::hub::Global<wgpu_core::hub::IdentityManagerFactory>;
|
||||
|
||||
struct WebGPUAdapter(wgpu_core::id::AdapterId);
|
||||
impl Resource for WebGPUAdapter {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUAdapter".into()
|
||||
}
|
||||
}
|
||||
|
||||
struct WebGPUDevice(wgpu_core::id::DeviceId);
|
||||
impl Resource for WebGPUDevice {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUDevice".into()
|
||||
}
|
||||
}
|
||||
|
||||
struct WebGPUQuerySet(wgpu_core::id::QuerySetId);
|
||||
impl Resource for WebGPUQuerySet {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUQuerySet".into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute this crates' JS source files.
|
||||
pub fn init(isolate: &mut deno_core::JsRuntime) {
|
||||
let files = vec![
|
||||
(
|
||||
"deno:op_crates/webgpu/01_webgpu.js",
|
||||
include_str!("01_webgpu.js"),
|
||||
),
|
||||
(
|
||||
"deno:op_crates/webgpu/02_idl_types.js",
|
||||
include_str!("02_idl_types.js"),
|
||||
),
|
||||
];
|
||||
for (url, source_code) in files {
|
||||
isolate.execute(url, source_code).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_declaration() -> PathBuf {
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_webgpu.d.ts")
|
||||
}
|
||||
|
||||
fn deserialize_features(features: &wgpu_types::Features) -> Vec<&str> {
|
||||
let mut return_features: Vec<&str> = vec![];
|
||||
|
||||
if features.contains(wgpu_types::Features::DEPTH_CLAMPING) {
|
||||
return_features.push("depth-clamping");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::PIPELINE_STATISTICS_QUERY) {
|
||||
return_features.push("pipeline-statistics-query");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_BC) {
|
||||
return_features.push("texture-compression-bc");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::TIMESTAMP_QUERY) {
|
||||
return_features.push("timestamp-query");
|
||||
}
|
||||
|
||||
// extended from spec
|
||||
if features.contains(wgpu_types::Features::MAPPABLE_PRIMARY_BUFFERS) {
|
||||
return_features.push("mappable-primary-buffers");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::SAMPLED_TEXTURE_BINDING_ARRAY) {
|
||||
return_features.push("sampled-texture-binding-array");
|
||||
}
|
||||
if features
|
||||
.contains(wgpu_types::Features::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING)
|
||||
{
|
||||
return_features.push("sampled-texture-array-dynamic-indexing");
|
||||
}
|
||||
if features
|
||||
.contains(wgpu_types::Features::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING)
|
||||
{
|
||||
return_features.push("sampled-texture-array-non-uniform-indexing");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::UNSIZED_BINDING_ARRAY) {
|
||||
return_features.push("unsized-binding-array");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::MULTI_DRAW_INDIRECT) {
|
||||
return_features.push("multi-draw-indirect");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::MULTI_DRAW_INDIRECT_COUNT) {
|
||||
return_features.push("multi-draw-indirect-count");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::PUSH_CONSTANTS) {
|
||||
return_features.push("push-constants");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_BORDER) {
|
||||
return_features.push("address-mode-clamp-to-border");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::NON_FILL_POLYGON_MODE) {
|
||||
return_features.push("non-fill-polygon-mode");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ETC2) {
|
||||
return_features.push("texture-compression-etc2");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ASTC_LDR) {
|
||||
return_features.push("texture-compression-astc-ldr");
|
||||
}
|
||||
if features
|
||||
.contains(wgpu_types::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES)
|
||||
{
|
||||
return_features.push("texture-adapter-specific-format-features");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::SHADER_FLOAT64) {
|
||||
return_features.push("shader-float64");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::VERTEX_ATTRIBUTE_64BIT) {
|
||||
return_features.push("vertex-attribute-64bit");
|
||||
}
|
||||
|
||||
return_features
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RequestAdapterArgs {
|
||||
power_preference: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn op_webgpu_request_adapter(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
args: RequestAdapterArgs,
|
||||
_bufs: BufVec,
|
||||
) -> Result<Value, AnyError> {
|
||||
let mut state = state.borrow_mut();
|
||||
check_unstable(&state, "navigator.gpu.requestAdapter");
|
||||
let instance = state.borrow::<Instance>();
|
||||
|
||||
let descriptor = wgpu_core::instance::RequestAdapterOptions {
|
||||
power_preference: match args.power_preference {
|
||||
Some(power_preference) => match power_preference.as_str() {
|
||||
"low-power" => wgpu_types::PowerPreference::LowPower,
|
||||
"high-performance" => wgpu_types::PowerPreference::HighPerformance,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
None => Default::default(),
|
||||
},
|
||||
compatible_surface: None, // windowless
|
||||
};
|
||||
let res = instance.request_adapter(
|
||||
&descriptor,
|
||||
wgpu_core::instance::AdapterInputs::Mask(
|
||||
wgpu_types::BackendBit::PRIMARY,
|
||||
|_| std::marker::PhantomData,
|
||||
),
|
||||
);
|
||||
|
||||
let adapter = match res {
|
||||
Ok(adapter) => adapter,
|
||||
Err(err) => {
|
||||
return Ok(json!({
|
||||
"err": err.to_string()
|
||||
}))
|
||||
}
|
||||
};
|
||||
let name = gfx_select!(adapter => instance.adapter_get_info(adapter))?.name;
|
||||
let adapter_features =
|
||||
gfx_select!(adapter => instance.adapter_features(adapter))?;
|
||||
let features = deserialize_features(&adapter_features);
|
||||
let adapter_limits =
|
||||
gfx_select!(adapter => instance.adapter_limits(adapter))?;
|
||||
|
||||
let limits = json!({
|
||||
"maxBindGroups": adapter_limits.max_bind_groups,
|
||||
"maxDynamicUniformBuffersPerPipelineLayout": adapter_limits.max_dynamic_uniform_buffers_per_pipeline_layout,
|
||||
"maxDynamicStorageBuffersPerPipelineLayout": adapter_limits.max_dynamic_storage_buffers_per_pipeline_layout,
|
||||
"maxSampledTexturesPerShaderStage": adapter_limits.max_sampled_textures_per_shader_stage,
|
||||
"maxSamplersPerShaderStage": adapter_limits.max_samplers_per_shader_stage,
|
||||
"maxStorageBuffersPerShaderStage": adapter_limits.max_storage_buffers_per_shader_stage,
|
||||
"maxStorageTexturesPerShaderStage": adapter_limits.max_storage_textures_per_shader_stage,
|
||||
"maxUniformBuffersPerShaderStage": adapter_limits.max_uniform_buffers_per_shader_stage,
|
||||
"maxUniformBufferBindingSize": adapter_limits.max_uniform_buffer_binding_size
|
||||
});
|
||||
|
||||
let rid = state.resource_table.add(WebGPUAdapter(adapter));
|
||||
|
||||
Ok(json!({
|
||||
"rid": rid,
|
||||
"name": name,
|
||||
"features": features,
|
||||
"limits": limits
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GPULimits {
|
||||
_max_texture_dimension1d: Option<u32>,
|
||||
_max_texture_dimension2d: Option<u32>,
|
||||
_max_texture_dimension3d: Option<u32>,
|
||||
_max_texture_array_layers: Option<u32>,
|
||||
max_bind_groups: Option<u32>,
|
||||
max_dynamic_uniform_buffers_per_pipeline_layout: Option<u32>,
|
||||
max_dynamic_storage_buffers_per_pipeline_layout: Option<u32>,
|
||||
max_sampled_textures_per_shader_stage: Option<u32>,
|
||||
max_samplers_per_shader_stage: Option<u32>,
|
||||
max_storage_buffers_per_shader_stage: Option<u32>,
|
||||
max_storage_textures_per_shader_stage: Option<u32>,
|
||||
max_uniform_buffers_per_shader_stage: Option<u32>,
|
||||
max_uniform_buffer_binding_size: Option<u32>,
|
||||
_max_storage_buffer_binding_size: Option<u32>,
|
||||
_max_vertex_buffers: Option<u32>,
|
||||
_max_vertex_attributes: Option<u32>,
|
||||
_max_vertex_buffer_array_stride: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RequestDeviceArgs {
|
||||
adapter_rid: u32,
|
||||
label: Option<String>,
|
||||
non_guaranteed_features: Option<Vec<String>>,
|
||||
non_guaranteed_limits: Option<GPULimits>,
|
||||
}
|
||||
|
||||
pub async fn op_webgpu_request_device(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
args: RequestDeviceArgs,
|
||||
_bufs: BufVec,
|
||||
) -> Result<Value, AnyError> {
|
||||
let mut state = state.borrow_mut();
|
||||
let adapter_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUAdapter>(args.adapter_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let adapter = adapter_resource.0;
|
||||
let instance = state.borrow::<Instance>();
|
||||
|
||||
let mut features: wgpu_types::Features = wgpu_types::Features::empty();
|
||||
|
||||
if let Some(passed_features) = args.non_guaranteed_features {
|
||||
if passed_features.contains(&"depth-clamping".to_string()) {
|
||||
features.set(wgpu_types::Features::DEPTH_CLAMPING, true);
|
||||
}
|
||||
if passed_features.contains(&"pipeline-statistics-query".to_string()) {
|
||||
features.set(wgpu_types::Features::PIPELINE_STATISTICS_QUERY, true);
|
||||
}
|
||||
if passed_features.contains(&"texture-compression-bc".to_string()) {
|
||||
features.set(wgpu_types::Features::TEXTURE_COMPRESSION_BC, true);
|
||||
}
|
||||
if passed_features.contains(&"timestamp-query".to_string()) {
|
||||
features.set(wgpu_types::Features::TIMESTAMP_QUERY, true);
|
||||
}
|
||||
|
||||
// extended from spec
|
||||
if passed_features.contains(&"mappable-primary-buffers".to_string()) {
|
||||
features.set(wgpu_types::Features::MAPPABLE_PRIMARY_BUFFERS, true);
|
||||
}
|
||||
if passed_features.contains(&"sampled-texture-binding-array".to_string()) {
|
||||
features.set(wgpu_types::Features::SAMPLED_TEXTURE_BINDING_ARRAY, true);
|
||||
}
|
||||
if passed_features
|
||||
.contains(&"sampled-texture-array-dynamic-indexing".to_string())
|
||||
{
|
||||
features.set(
|
||||
wgpu_types::Features::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING,
|
||||
true,
|
||||
);
|
||||
}
|
||||
if passed_features
|
||||
.contains(&"sampled-texture-array-non-uniform-indexing".to_string())
|
||||
{
|
||||
features.set(
|
||||
wgpu_types::Features::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
|
||||
true,
|
||||
);
|
||||
}
|
||||
if passed_features.contains(&"unsized-binding-array".to_string()) {
|
||||
features.set(wgpu_types::Features::UNSIZED_BINDING_ARRAY, true);
|
||||
}
|
||||
if passed_features.contains(&"multi-draw-indirect".to_string()) {
|
||||
features.set(wgpu_types::Features::MULTI_DRAW_INDIRECT, true);
|
||||
}
|
||||
if passed_features.contains(&"multi-draw-indirect-count".to_string()) {
|
||||
features.set(wgpu_types::Features::MULTI_DRAW_INDIRECT_COUNT, true);
|
||||
}
|
||||
if passed_features.contains(&"push-constants".to_string()) {
|
||||
features.set(wgpu_types::Features::PUSH_CONSTANTS, true);
|
||||
}
|
||||
if passed_features.contains(&"address-mode-clamp-to-border".to_string()) {
|
||||
features.set(wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_BORDER, true);
|
||||
}
|
||||
if passed_features.contains(&"non-fill-polygon-mode".to_string()) {
|
||||
features.set(wgpu_types::Features::NON_FILL_POLYGON_MODE, true);
|
||||
}
|
||||
if passed_features.contains(&"texture-compression-etc2".to_string()) {
|
||||
features.set(wgpu_types::Features::TEXTURE_COMPRESSION_ETC2, true);
|
||||
}
|
||||
if passed_features.contains(&"texture-compression-astc-ldr".to_string()) {
|
||||
features.set(wgpu_types::Features::TEXTURE_COMPRESSION_ASTC_LDR, true);
|
||||
}
|
||||
if passed_features
|
||||
.contains(&"texture-adapter-specific-format-features".to_string())
|
||||
{
|
||||
features.set(
|
||||
wgpu_types::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,
|
||||
true,
|
||||
);
|
||||
}
|
||||
if passed_features.contains(&"shader-float64".to_string()) {
|
||||
features.set(wgpu_types::Features::SHADER_FLOAT64, true);
|
||||
}
|
||||
if passed_features.contains(&"vertex-attribute-64bit".to_string()) {
|
||||
features.set(wgpu_types::Features::VERTEX_ATTRIBUTE_64BIT, true);
|
||||
}
|
||||
}
|
||||
|
||||
let descriptor = wgpu_types::DeviceDescriptor {
|
||||
label: args.label.map(Cow::from),
|
||||
features,
|
||||
limits: args
|
||||
.non_guaranteed_limits
|
||||
.map_or(Default::default(), |limits| wgpu_types::Limits {
|
||||
max_bind_groups: limits.max_bind_groups.unwrap_or(4),
|
||||
max_dynamic_uniform_buffers_per_pipeline_layout: limits
|
||||
.max_dynamic_uniform_buffers_per_pipeline_layout
|
||||
.unwrap_or(8),
|
||||
max_dynamic_storage_buffers_per_pipeline_layout: limits
|
||||
.max_dynamic_storage_buffers_per_pipeline_layout
|
||||
.unwrap_or(4),
|
||||
max_sampled_textures_per_shader_stage: limits
|
||||
.max_sampled_textures_per_shader_stage
|
||||
.unwrap_or(16),
|
||||
max_samplers_per_shader_stage: limits
|
||||
.max_samplers_per_shader_stage
|
||||
.unwrap_or(16),
|
||||
max_storage_buffers_per_shader_stage: limits
|
||||
.max_storage_buffers_per_shader_stage
|
||||
.unwrap_or(4),
|
||||
max_storage_textures_per_shader_stage: limits
|
||||
.max_storage_textures_per_shader_stage
|
||||
.unwrap_or(4),
|
||||
max_uniform_buffers_per_shader_stage: limits
|
||||
.max_uniform_buffers_per_shader_stage
|
||||
.unwrap_or(12),
|
||||
max_uniform_buffer_binding_size: limits
|
||||
.max_uniform_buffer_binding_size
|
||||
.unwrap_or(16384),
|
||||
max_push_constant_size: 0,
|
||||
}),
|
||||
};
|
||||
|
||||
let (device, maybe_err) = gfx_select!(adapter => instance.adapter_request_device(
|
||||
adapter,
|
||||
&descriptor,
|
||||
std::env::var("DENO_WEBGPU_TRACE").ok().as_ref().map(std::path::Path::new),
|
||||
std::marker::PhantomData
|
||||
));
|
||||
if let Some(err) = maybe_err {
|
||||
return Err(DOMExceptionOperationError::new(&err.to_string()).into());
|
||||
}
|
||||
|
||||
let device_features =
|
||||
gfx_select!(device => instance.device_features(device))?;
|
||||
let features = deserialize_features(&device_features);
|
||||
let limits = gfx_select!(device => instance.device_limits(device))?;
|
||||
let json_limits = json!({
|
||||
"maxBindGroups": limits.max_bind_groups,
|
||||
"maxDynamicUniformBuffersPerPipelineLayout": limits.max_dynamic_uniform_buffers_per_pipeline_layout,
|
||||
"maxDynamicStorageBuffersPerPipelineLayout": limits.max_dynamic_storage_buffers_per_pipeline_layout,
|
||||
"maxSampledTexturesPerShaderStage": limits.max_sampled_textures_per_shader_stage,
|
||||
"maxSamplersPerShaderStage": limits.max_samplers_per_shader_stage,
|
||||
"maxStorageBuffersPerShaderStage": limits.max_storage_buffers_per_shader_stage,
|
||||
"maxStorageTexturesPerShaderStage": limits.max_storage_textures_per_shader_stage,
|
||||
"maxUniformBuffersPerShaderStage": limits.max_uniform_buffers_per_shader_stage,
|
||||
"maxUniformBufferBindingSize": limits.max_uniform_buffer_binding_size,
|
||||
});
|
||||
|
||||
let rid = state.resource_table.add(WebGPUDevice(device));
|
||||
|
||||
Ok(json!({
|
||||
"rid": rid,
|
||||
"features": features,
|
||||
"limits": json_limits,
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateQuerySetArgs {
|
||||
device_rid: u32,
|
||||
_label: Option<String>, // not yet implemented
|
||||
#[serde(rename = "type")]
|
||||
kind: String,
|
||||
count: u32,
|
||||
pipeline_statistics: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_create_query_set(
|
||||
state: &mut OpState,
|
||||
args: CreateQuerySetArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let device_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUDevice>(args.device_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let device = device_resource.0;
|
||||
let instance = &state.borrow::<Instance>();
|
||||
|
||||
let descriptor = wgpu_types::QuerySetDescriptor {
|
||||
ty: match args.kind.as_str() {
|
||||
"pipeline-statistics" => {
|
||||
let mut pipeline_statistics_names =
|
||||
wgpu_types::PipelineStatisticsTypes::empty();
|
||||
|
||||
if let Some(pipeline_statistics) = args.pipeline_statistics {
|
||||
if pipeline_statistics
|
||||
.contains(&"vertex-shader-invocations".to_string())
|
||||
{
|
||||
pipeline_statistics_names.set(
|
||||
wgpu_types::PipelineStatisticsTypes::VERTEX_SHADER_INVOCATIONS,
|
||||
true,
|
||||
);
|
||||
}
|
||||
if pipeline_statistics.contains(&"clipper-invocations".to_string()) {
|
||||
pipeline_statistics_names.set(
|
||||
wgpu_types::PipelineStatisticsTypes::CLIPPER_INVOCATIONS,
|
||||
true,
|
||||
);
|
||||
}
|
||||
if pipeline_statistics.contains(&"clipper-primitives-out".to_string())
|
||||
{
|
||||
pipeline_statistics_names.set(
|
||||
wgpu_types::PipelineStatisticsTypes::CLIPPER_PRIMITIVES_OUT,
|
||||
true,
|
||||
);
|
||||
}
|
||||
if pipeline_statistics
|
||||
.contains(&"fragment-shader-invocations".to_string())
|
||||
{
|
||||
pipeline_statistics_names.set(
|
||||
wgpu_types::PipelineStatisticsTypes::FRAGMENT_SHADER_INVOCATIONS,
|
||||
true,
|
||||
);
|
||||
}
|
||||
if pipeline_statistics
|
||||
.contains(&"compute-shader-invocations".to_string())
|
||||
{
|
||||
pipeline_statistics_names.set(
|
||||
wgpu_types::PipelineStatisticsTypes::COMPUTE_SHADER_INVOCATIONS,
|
||||
true,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
wgpu_types::QueryType::PipelineStatistics(pipeline_statistics_names)
|
||||
}
|
||||
"occlusion" => return Err(not_supported()),
|
||||
"timestamp" => wgpu_types::QueryType::Timestamp,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
count: args.count,
|
||||
};
|
||||
|
||||
let (query_set, maybe_err) = gfx_select!(device => instance.device_create_query_set(
|
||||
device,
|
||||
&descriptor,
|
||||
std::marker::PhantomData
|
||||
));
|
||||
|
||||
let rid = state.resource_table.add(WebGPUQuerySet(query_set));
|
||||
|
||||
Ok(json!({
|
||||
"rid": rid,
|
||||
"err": maybe_err.map(WebGPUError::from),
|
||||
}))
|
||||
}
|
643
op_crates/webgpu/pipeline.rs
Normal file
643
op_crates/webgpu/pipeline.rs
Normal file
|
@ -0,0 +1,643 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_core::error::bad_resource_id;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json::json;
|
||||
use deno_core::serde_json::Value;
|
||||
use deno_core::ZeroCopyBuf;
|
||||
use deno_core::{OpState, Resource};
|
||||
use serde::Deserialize;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use super::error::WebGPUError;
|
||||
|
||||
pub(crate) struct WebGPUPipelineLayout(
|
||||
pub(crate) wgpu_core::id::PipelineLayoutId,
|
||||
);
|
||||
impl Resource for WebGPUPipelineLayout {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUPipelineLayout".into()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct WebGPUComputePipeline(
|
||||
pub(crate) wgpu_core::id::ComputePipelineId,
|
||||
);
|
||||
impl Resource for WebGPUComputePipeline {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUComputePipeline".into()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct WebGPURenderPipeline(
|
||||
pub(crate) wgpu_core::id::RenderPipelineId,
|
||||
);
|
||||
impl Resource for WebGPURenderPipeline {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPURenderPipeline".into()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn serialize_index_format(format: String) -> wgpu_types::IndexFormat {
|
||||
match format.as_str() {
|
||||
"uint16" => wgpu_types::IndexFormat::Uint16,
|
||||
"uint32" => wgpu_types::IndexFormat::Uint32,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_stencil_operation(
|
||||
operation: &str,
|
||||
) -> wgpu_types::StencilOperation {
|
||||
match operation {
|
||||
"keep" => wgpu_types::StencilOperation::Keep,
|
||||
"zero" => wgpu_types::StencilOperation::Zero,
|
||||
"replace" => wgpu_types::StencilOperation::Replace,
|
||||
"invert" => wgpu_types::StencilOperation::Invert,
|
||||
"increment-clamp" => wgpu_types::StencilOperation::IncrementClamp,
|
||||
"decrement-clamp" => wgpu_types::StencilOperation::DecrementClamp,
|
||||
"increment-wrap" => wgpu_types::StencilOperation::IncrementWrap,
|
||||
"decrement-wrap" => wgpu_types::StencilOperation::DecrementWrap,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_stencil_face_state(
|
||||
state: GPUStencilFaceState,
|
||||
) -> wgpu_types::StencilFaceState {
|
||||
wgpu_types::StencilFaceState {
|
||||
compare: state
|
||||
.compare
|
||||
.as_ref()
|
||||
.map_or(wgpu_types::CompareFunction::Always, |op| {
|
||||
super::sampler::serialize_compare_function(op)
|
||||
}),
|
||||
fail_op: state
|
||||
.fail_op
|
||||
.as_ref()
|
||||
.map_or(wgpu_types::StencilOperation::Keep, |op| {
|
||||
serialize_stencil_operation(op)
|
||||
}),
|
||||
depth_fail_op: state
|
||||
.depth_fail_op
|
||||
.as_ref()
|
||||
.map_or(wgpu_types::StencilOperation::Keep, |op| {
|
||||
serialize_stencil_operation(op)
|
||||
}),
|
||||
pass_op: state
|
||||
.pass_op
|
||||
.as_ref()
|
||||
.map_or(wgpu_types::StencilOperation::Keep, |op| {
|
||||
serialize_stencil_operation(op)
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_blend_factor(blend_factor: &str) -> wgpu_types::BlendFactor {
|
||||
match blend_factor {
|
||||
"zero" => wgpu_types::BlendFactor::Zero,
|
||||
"one" => wgpu_types::BlendFactor::One,
|
||||
"src-color" => wgpu_types::BlendFactor::SrcColor,
|
||||
"one-minus-src-color" => wgpu_types::BlendFactor::OneMinusSrcColor,
|
||||
"src-alpha" => wgpu_types::BlendFactor::SrcAlpha,
|
||||
"one-minus-src-alpha" => wgpu_types::BlendFactor::OneMinusSrcAlpha,
|
||||
"dst-color" => wgpu_types::BlendFactor::DstColor,
|
||||
"one-minus-dst-color" => wgpu_types::BlendFactor::OneMinusDstColor,
|
||||
"dst-alpha" => wgpu_types::BlendFactor::DstAlpha,
|
||||
"one-minus-dst-alpha" => wgpu_types::BlendFactor::OneMinusDstAlpha,
|
||||
"src-alpha-saturated" => wgpu_types::BlendFactor::SrcAlphaSaturated,
|
||||
"blend-color" => wgpu_types::BlendFactor::BlendColor,
|
||||
"one-minus-blend-color" => wgpu_types::BlendFactor::OneMinusBlendColor,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_blend_component(
|
||||
blend: GPUBlendComponent,
|
||||
) -> wgpu_types::BlendState {
|
||||
wgpu_types::BlendState {
|
||||
src_factor: blend
|
||||
.src_factor
|
||||
.as_ref()
|
||||
.map_or(wgpu_types::BlendFactor::One, |factor| {
|
||||
serialize_blend_factor(factor)
|
||||
}),
|
||||
dst_factor: blend
|
||||
.dst_factor
|
||||
.as_ref()
|
||||
.map_or(wgpu_types::BlendFactor::Zero, |factor| {
|
||||
serialize_blend_factor(factor)
|
||||
}),
|
||||
operation: match &blend.operation {
|
||||
Some(operation) => match operation.as_str() {
|
||||
"add" => wgpu_types::BlendOperation::Add,
|
||||
"subtract" => wgpu_types::BlendOperation::Subtract,
|
||||
"reverse-subtract" => wgpu_types::BlendOperation::ReverseSubtract,
|
||||
"min" => wgpu_types::BlendOperation::Min,
|
||||
"max" => wgpu_types::BlendOperation::Max,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
None => wgpu_types::BlendOperation::Add,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GPUProgrammableStage {
|
||||
module: u32,
|
||||
entry_point: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateComputePipelineArgs {
|
||||
device_rid: u32,
|
||||
label: Option<String>,
|
||||
layout: Option<u32>,
|
||||
compute: GPUProgrammableStage,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_create_compute_pipeline(
|
||||
state: &mut OpState,
|
||||
args: CreateComputePipelineArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let device_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGPUDevice>(args.device_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let device = device_resource.0;
|
||||
|
||||
let pipeline_layout = if let Some(rid) = args.layout {
|
||||
let id = state
|
||||
.resource_table
|
||||
.get::<WebGPUPipelineLayout>(rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
Some(id.0)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let compute_shader_module_resource = state
|
||||
.resource_table
|
||||
.get::<super::shader::WebGPUShaderModule>(args.compute.module)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
let descriptor = wgpu_core::pipeline::ComputePipelineDescriptor {
|
||||
label: args.label.map(Cow::from),
|
||||
layout: pipeline_layout,
|
||||
stage: wgpu_core::pipeline::ProgrammableStageDescriptor {
|
||||
module: compute_shader_module_resource.0,
|
||||
entry_point: Cow::from(args.compute.entry_point),
|
||||
},
|
||||
};
|
||||
let implicit_pipelines = match args.layout {
|
||||
Some(_) => None,
|
||||
None => Some(wgpu_core::device::ImplicitPipelineIds {
|
||||
root_id: std::marker::PhantomData,
|
||||
group_ids: &[std::marker::PhantomData; wgpu_core::MAX_BIND_GROUPS],
|
||||
}),
|
||||
};
|
||||
|
||||
let (compute_pipeline, _, maybe_err) = gfx_select!(device => instance.device_create_compute_pipeline(
|
||||
device,
|
||||
&descriptor,
|
||||
std::marker::PhantomData,
|
||||
implicit_pipelines
|
||||
));
|
||||
|
||||
let rid = state
|
||||
.resource_table
|
||||
.add(WebGPUComputePipeline(compute_pipeline));
|
||||
|
||||
Ok(json!({
|
||||
"rid": rid,
|
||||
"err": maybe_err.map(WebGPUError::from),
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ComputePipelineGetBindGroupLayoutArgs {
|
||||
compute_pipeline_rid: u32,
|
||||
index: u32,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_compute_pipeline_get_bind_group_layout(
|
||||
state: &mut OpState,
|
||||
args: ComputePipelineGetBindGroupLayoutArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let compute_pipeline_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUComputePipeline>(args.compute_pipeline_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let compute_pipeline = compute_pipeline_resource.0;
|
||||
|
||||
let (bind_group_layout, maybe_err) = gfx_select!(compute_pipeline => instance.compute_pipeline_get_bind_group_layout(compute_pipeline, args.index, std::marker::PhantomData));
|
||||
|
||||
let label = gfx_select!(bind_group_layout => instance.bind_group_layout_label(bind_group_layout));
|
||||
|
||||
let rid = state
|
||||
.resource_table
|
||||
.add(super::binding::WebGPUBindGroupLayout(bind_group_layout));
|
||||
|
||||
Ok(json!({
|
||||
"rid": rid,
|
||||
"label": label,
|
||||
"err": maybe_err.map(WebGPUError::from)
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GPUPrimitiveState {
|
||||
topology: Option<String>,
|
||||
strip_index_format: Option<String>,
|
||||
front_face: Option<String>,
|
||||
cull_mode: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GPUBlendComponent {
|
||||
src_factor: Option<String>,
|
||||
dst_factor: Option<String>,
|
||||
operation: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GPUBlendState {
|
||||
color: GPUBlendComponent,
|
||||
alpha: GPUBlendComponent,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GPUColorTargetState {
|
||||
format: String,
|
||||
blend: Option<GPUBlendState>,
|
||||
write_mask: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GPUStencilFaceState {
|
||||
compare: Option<String>,
|
||||
fail_op: Option<String>,
|
||||
depth_fail_op: Option<String>,
|
||||
pass_op: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GPUDepthStencilState {
|
||||
format: String,
|
||||
depth_write_enabled: Option<bool>,
|
||||
depth_compare: Option<String>,
|
||||
stencil_front: Option<GPUStencilFaceState>,
|
||||
stencil_back: Option<GPUStencilFaceState>,
|
||||
stencil_read_mask: Option<u32>,
|
||||
stencil_write_mask: Option<u32>,
|
||||
depth_bias: Option<i32>,
|
||||
depth_bias_slope_scale: Option<f32>,
|
||||
depth_bias_clamp: Option<f32>,
|
||||
clamp_depth: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GPUVertexAttribute {
|
||||
format: String,
|
||||
offset: u64,
|
||||
shader_location: u32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GPUVertexBufferLayout {
|
||||
array_stride: u64,
|
||||
step_mode: Option<String>,
|
||||
attributes: Vec<GPUVertexAttribute>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GPUVertexState {
|
||||
module: u32,
|
||||
entry_point: String,
|
||||
buffers: Option<Vec<Option<GPUVertexBufferLayout>>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GPUMultisampleState {
|
||||
count: Option<u32>,
|
||||
mask: Option<u64>, // against spec, but future proof
|
||||
alpha_to_coverage_enabled: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GPUFragmentState {
|
||||
targets: Vec<GPUColorTargetState>,
|
||||
module: u32,
|
||||
entry_point: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateRenderPipelineArgs {
|
||||
device_rid: u32,
|
||||
label: Option<String>,
|
||||
layout: Option<u32>,
|
||||
vertex: GPUVertexState,
|
||||
primitive: Option<GPUPrimitiveState>,
|
||||
depth_stencil: Option<GPUDepthStencilState>,
|
||||
multisample: Option<GPUMultisampleState>,
|
||||
fragment: Option<GPUFragmentState>,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_create_render_pipeline(
|
||||
state: &mut OpState,
|
||||
args: CreateRenderPipelineArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let device_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGPUDevice>(args.device_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let device = device_resource.0;
|
||||
|
||||
let layout = if let Some(rid) = args.layout {
|
||||
let pipeline_layout_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUPipelineLayout>(rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
Some(pipeline_layout_resource.0)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let vertex_shader_module_resource = state
|
||||
.resource_table
|
||||
.get::<super::shader::WebGPUShaderModule>(args.vertex.module)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
let descriptor = wgpu_core::pipeline::RenderPipelineDescriptor {
|
||||
label: args.label.map(Cow::from),
|
||||
layout,
|
||||
vertex: wgpu_core::pipeline::VertexState {
|
||||
stage: wgpu_core::pipeline::ProgrammableStageDescriptor {
|
||||
module: vertex_shader_module_resource.0,
|
||||
entry_point: Cow::from(args.vertex.entry_point),
|
||||
},
|
||||
buffers: Cow::from(if let Some(buffers) = args.vertex.buffers {
|
||||
let mut return_buffers = vec![];
|
||||
for buffer in buffers {
|
||||
if let Some(buffer) = buffer {
|
||||
return_buffers.push(wgpu_core::pipeline::VertexBufferLayout {
|
||||
array_stride: buffer.array_stride,
|
||||
step_mode: match buffer.step_mode {
|
||||
Some(step_mode) => match step_mode.as_str() {
|
||||
"vertex" => wgpu_types::InputStepMode::Vertex,
|
||||
"instance" => wgpu_types::InputStepMode::Instance,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
None => wgpu_types::InputStepMode::Vertex,
|
||||
},
|
||||
attributes: Cow::from(
|
||||
buffer
|
||||
.attributes
|
||||
.iter()
|
||||
.map(|attribute| wgpu_types::VertexAttribute {
|
||||
format: match attribute.format.as_str() {
|
||||
"uchar2" => wgpu_types::VertexFormat::Uchar2,
|
||||
"uchar4" => wgpu_types::VertexFormat::Uchar4,
|
||||
"char2" => wgpu_types::VertexFormat::Char2,
|
||||
"char4" => wgpu_types::VertexFormat::Char4,
|
||||
"uchar2norm" => wgpu_types::VertexFormat::Uchar2Norm,
|
||||
"uchar4norm" => wgpu_types::VertexFormat::Uchar4,
|
||||
"char2norm" => wgpu_types::VertexFormat::Char2Norm,
|
||||
"char4norm" => wgpu_types::VertexFormat::Char4Norm,
|
||||
"ushort2" => wgpu_types::VertexFormat::Ushort2,
|
||||
"ushort4" => wgpu_types::VertexFormat::Ushort4,
|
||||
"short2" => wgpu_types::VertexFormat::Short2,
|
||||
"short4" => wgpu_types::VertexFormat::Short4,
|
||||
"ushort2norm" => wgpu_types::VertexFormat::Ushort2Norm,
|
||||
"ushort4norm" => wgpu_types::VertexFormat::Ushort4Norm,
|
||||
"short2norm" => wgpu_types::VertexFormat::Short2Norm,
|
||||
"short4norm" => wgpu_types::VertexFormat::Short4Norm,
|
||||
"half2" => wgpu_types::VertexFormat::Half2,
|
||||
"half4" => wgpu_types::VertexFormat::Half4,
|
||||
"float" => wgpu_types::VertexFormat::Float,
|
||||
"float2" => wgpu_types::VertexFormat::Float2,
|
||||
"float3" => wgpu_types::VertexFormat::Float3,
|
||||
"float4" => wgpu_types::VertexFormat::Float4,
|
||||
"uint" => wgpu_types::VertexFormat::Uint,
|
||||
"uint2" => wgpu_types::VertexFormat::Uint2,
|
||||
"uint3" => wgpu_types::VertexFormat::Uint3,
|
||||
"uint4" => wgpu_types::VertexFormat::Uint4,
|
||||
"int" => wgpu_types::VertexFormat::Int,
|
||||
"int2" => wgpu_types::VertexFormat::Int2,
|
||||
"int3" => wgpu_types::VertexFormat::Int3,
|
||||
"int4" => wgpu_types::VertexFormat::Int4,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
offset: attribute.offset,
|
||||
shader_location: attribute.shader_location,
|
||||
})
|
||||
.collect::<Vec<wgpu_types::VertexAttribute>>(),
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
return_buffers
|
||||
} else {
|
||||
vec![]
|
||||
}),
|
||||
},
|
||||
primitive: args.primitive.map_or(Default::default(), |primitive| {
|
||||
wgpu_types::PrimitiveState {
|
||||
topology: match primitive.topology {
|
||||
Some(topology) => match topology.as_str() {
|
||||
"point-list" => wgpu_types::PrimitiveTopology::PointList,
|
||||
"line-list" => wgpu_types::PrimitiveTopology::LineList,
|
||||
"line-strip" => wgpu_types::PrimitiveTopology::LineStrip,
|
||||
"triangle-list" => wgpu_types::PrimitiveTopology::TriangleList,
|
||||
"triangle-strip" => wgpu_types::PrimitiveTopology::TriangleStrip,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
None => wgpu_types::PrimitiveTopology::TriangleList,
|
||||
},
|
||||
strip_index_format: primitive
|
||||
.strip_index_format
|
||||
.map(serialize_index_format),
|
||||
front_face: match primitive.front_face {
|
||||
Some(front_face) => match front_face.as_str() {
|
||||
"ccw" => wgpu_types::FrontFace::Ccw,
|
||||
"cw" => wgpu_types::FrontFace::Cw,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
None => wgpu_types::FrontFace::Ccw,
|
||||
},
|
||||
cull_mode: match primitive.cull_mode {
|
||||
Some(cull_mode) => match cull_mode.as_str() {
|
||||
"none" => wgpu_types::CullMode::None,
|
||||
"front" => wgpu_types::CullMode::Front,
|
||||
"back" => wgpu_types::CullMode::Back,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
None => wgpu_types::CullMode::None,
|
||||
},
|
||||
polygon_mode: Default::default(), // native-only
|
||||
}
|
||||
}),
|
||||
depth_stencil: args.depth_stencil.map(|depth_stencil| {
|
||||
wgpu_types::DepthStencilState {
|
||||
format: super::texture::serialize_texture_format(&depth_stencil.format)
|
||||
.unwrap(),
|
||||
depth_write_enabled: depth_stencil.depth_write_enabled.unwrap_or(false),
|
||||
depth_compare: match depth_stencil.depth_compare {
|
||||
Some(depth_compare) => {
|
||||
super::sampler::serialize_compare_function(&depth_compare)
|
||||
}
|
||||
None => wgpu_types::CompareFunction::Always,
|
||||
},
|
||||
stencil: wgpu_types::StencilState {
|
||||
front: depth_stencil
|
||||
.stencil_front
|
||||
.map_or(Default::default(), serialize_stencil_face_state),
|
||||
back: depth_stencil
|
||||
.stencil_back
|
||||
.map_or(Default::default(), serialize_stencil_face_state),
|
||||
read_mask: depth_stencil.stencil_read_mask.unwrap_or(0xFFFFFFFF),
|
||||
write_mask: depth_stencil.stencil_write_mask.unwrap_or(0xFFFFFFFF),
|
||||
},
|
||||
bias: wgpu_types::DepthBiasState {
|
||||
constant: depth_stencil.depth_bias.unwrap_or(0),
|
||||
slope_scale: depth_stencil.depth_bias_slope_scale.unwrap_or(0.0),
|
||||
clamp: depth_stencil.depth_bias_clamp.unwrap_or(0.0),
|
||||
},
|
||||
clamp_depth: depth_stencil.clamp_depth.unwrap_or(false),
|
||||
}
|
||||
}),
|
||||
multisample: args.multisample.map_or(Default::default(), |multisample| {
|
||||
wgpu_types::MultisampleState {
|
||||
count: multisample.count.unwrap_or(1),
|
||||
mask: multisample.mask.unwrap_or(0xFFFFFFFF),
|
||||
alpha_to_coverage_enabled: multisample
|
||||
.alpha_to_coverage_enabled
|
||||
.unwrap_or(false),
|
||||
}
|
||||
}),
|
||||
fragment: args.fragment.map(|fragment| {
|
||||
let fragment_shader_module_resource = state
|
||||
.resource_table
|
||||
.get::<super::shader::WebGPUShaderModule>(fragment.module)
|
||||
.ok_or_else(bad_resource_id)
|
||||
.unwrap();
|
||||
|
||||
wgpu_core::pipeline::FragmentState {
|
||||
stage: wgpu_core::pipeline::ProgrammableStageDescriptor {
|
||||
module: fragment_shader_module_resource.0,
|
||||
entry_point: Cow::from(fragment.entry_point),
|
||||
},
|
||||
targets: Cow::from(
|
||||
fragment
|
||||
.targets
|
||||
.iter()
|
||||
.map(|target| {
|
||||
let blends = target.blend.clone().map(|blend| {
|
||||
(
|
||||
serialize_blend_component(blend.alpha),
|
||||
serialize_blend_component(blend.color),
|
||||
)
|
||||
});
|
||||
|
||||
wgpu_types::ColorTargetState {
|
||||
format: super::texture::serialize_texture_format(
|
||||
&target.format,
|
||||
)
|
||||
.unwrap(),
|
||||
alpha_blend: blends
|
||||
.clone()
|
||||
.map_or(Default::default(), |states| states.0),
|
||||
color_blend: blends
|
||||
.map_or(Default::default(), |states| states.1),
|
||||
write_mask: target
|
||||
.write_mask
|
||||
.map_or(Default::default(), |mask| {
|
||||
wgpu_types::ColorWrite::from_bits(mask).unwrap()
|
||||
}),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<wgpu_types::ColorTargetState>>(),
|
||||
),
|
||||
}
|
||||
}),
|
||||
};
|
||||
|
||||
let implicit_pipelines = match args.layout {
|
||||
Some(_) => None,
|
||||
None => Some(wgpu_core::device::ImplicitPipelineIds {
|
||||
root_id: std::marker::PhantomData,
|
||||
group_ids: &[std::marker::PhantomData; wgpu_core::MAX_BIND_GROUPS],
|
||||
}),
|
||||
};
|
||||
|
||||
let (render_pipeline, _, maybe_err) = gfx_select!(device => instance.device_create_render_pipeline(
|
||||
device,
|
||||
&descriptor,
|
||||
std::marker::PhantomData,
|
||||
implicit_pipelines
|
||||
));
|
||||
|
||||
let rid = state
|
||||
.resource_table
|
||||
.add(WebGPURenderPipeline(render_pipeline));
|
||||
|
||||
Ok(json!({
|
||||
"rid": rid,
|
||||
"err": maybe_err.map(WebGPUError::from)
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderPipelineGetBindGroupLayoutArgs {
|
||||
render_pipeline_rid: u32,
|
||||
index: u32,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_pipeline_get_bind_group_layout(
|
||||
state: &mut OpState,
|
||||
args: RenderPipelineGetBindGroupLayoutArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let render_pipeline_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderPipeline>(args.render_pipeline_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let render_pipeline = render_pipeline_resource.0;
|
||||
|
||||
let (bind_group_layout, maybe_err) = gfx_select!(render_pipeline => instance.render_pipeline_get_bind_group_layout(render_pipeline, args.index, std::marker::PhantomData));
|
||||
|
||||
let label = gfx_select!(bind_group_layout => instance.bind_group_layout_label(bind_group_layout));
|
||||
|
||||
let rid = state
|
||||
.resource_table
|
||||
.add(super::binding::WebGPUBindGroupLayout(bind_group_layout));
|
||||
|
||||
Ok(json!({
|
||||
"rid": rid,
|
||||
"label": label,
|
||||
"err": maybe_err.map(WebGPUError::from),
|
||||
}))
|
||||
}
|
157
op_crates/webgpu/queue.rs
Normal file
157
op_crates/webgpu/queue.rs
Normal file
|
@ -0,0 +1,157 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_core::error::bad_resource_id;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json::json;
|
||||
use deno_core::serde_json::Value;
|
||||
use deno_core::OpState;
|
||||
use deno_core::ZeroCopyBuf;
|
||||
use serde::Deserialize;
|
||||
|
||||
use super::error::WebGPUError;
|
||||
|
||||
type WebGPUQueue = super::WebGPUDevice;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct QueueSubmitArgs {
|
||||
queue_rid: u32,
|
||||
command_buffers: Vec<u32>,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_queue_submit(
|
||||
state: &mut OpState,
|
||||
args: QueueSubmitArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let queue_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUQueue>(args.queue_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let queue = queue_resource.0;
|
||||
|
||||
let mut ids = vec![];
|
||||
|
||||
for rid in args.command_buffers {
|
||||
let buffer_resource = state
|
||||
.resource_table
|
||||
.get::<super::command_encoder::WebGPUCommandBuffer>(rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
ids.push(buffer_resource.0);
|
||||
}
|
||||
|
||||
let maybe_err =
|
||||
gfx_select!(queue => instance.queue_submit(queue, &ids)).err();
|
||||
|
||||
Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GPUImageDataLayout {
|
||||
offset: Option<u64>,
|
||||
bytes_per_row: Option<u32>,
|
||||
rows_per_image: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct QueueWriteBufferArgs {
|
||||
queue_rid: u32,
|
||||
buffer: u32,
|
||||
buffer_offset: u64,
|
||||
data_offset: usize,
|
||||
size: Option<usize>,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_write_buffer(
|
||||
state: &mut OpState,
|
||||
args: QueueWriteBufferArgs,
|
||||
zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let buffer_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGPUBuffer>(args.buffer)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let buffer = buffer_resource.0;
|
||||
let queue_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUQueue>(args.queue_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let queue = queue_resource.0;
|
||||
|
||||
let data = match args.size {
|
||||
Some(size) => &zero_copy[0][args.data_offset..(args.data_offset + size)],
|
||||
None => &zero_copy[0][args.data_offset..],
|
||||
};
|
||||
let maybe_err = gfx_select!(queue => instance.queue_write_buffer(
|
||||
queue,
|
||||
buffer,
|
||||
args.buffer_offset,
|
||||
data
|
||||
))
|
||||
.err();
|
||||
|
||||
Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct QueueWriteTextureArgs {
|
||||
queue_rid: u32,
|
||||
destination: super::command_encoder::GPUImageCopyTexture,
|
||||
data_layout: GPUImageDataLayout,
|
||||
size: super::texture::GPUExtent3D,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_write_texture(
|
||||
state: &mut OpState,
|
||||
args: QueueWriteTextureArgs,
|
||||
zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let texture_resource = state
|
||||
.resource_table
|
||||
.get::<super::texture::WebGPUTexture>(args.destination.texture)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let queue_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUQueue>(args.queue_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let queue = queue_resource.0;
|
||||
|
||||
let destination = wgpu_core::command::TextureCopyView {
|
||||
texture: texture_resource.0,
|
||||
mip_level: args.destination.mip_level.unwrap_or(0),
|
||||
origin: args
|
||||
.destination
|
||||
.origin
|
||||
.map_or(Default::default(), |origin| wgpu_types::Origin3d {
|
||||
x: origin.x.unwrap_or(0),
|
||||
y: origin.y.unwrap_or(0),
|
||||
z: origin.z.unwrap_or(0),
|
||||
}),
|
||||
};
|
||||
let data_layout = wgpu_types::TextureDataLayout {
|
||||
offset: args.data_layout.offset.unwrap_or(0),
|
||||
bytes_per_row: args.data_layout.bytes_per_row.unwrap_or(0),
|
||||
rows_per_image: args.data_layout.rows_per_image.unwrap_or(0),
|
||||
};
|
||||
|
||||
let maybe_err = gfx_select!(queue => instance.queue_write_texture(
|
||||
queue,
|
||||
&destination,
|
||||
&*zero_copy[0],
|
||||
&data_layout,
|
||||
&wgpu_types::Extent3d {
|
||||
width: args.size.width.unwrap_or(1),
|
||||
height: args.size.height.unwrap_or(1),
|
||||
depth: args.size.depth.unwrap_or(1),
|
||||
}
|
||||
))
|
||||
.err();
|
||||
|
||||
Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
|
||||
}
|
676
op_crates/webgpu/render_pass.rs
Normal file
676
op_crates/webgpu/render_pass.rs
Normal file
|
@ -0,0 +1,676 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_core::error::bad_resource_id;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json::json;
|
||||
use deno_core::serde_json::Value;
|
||||
use deno_core::ZeroCopyBuf;
|
||||
use deno_core::{OpState, Resource};
|
||||
use serde::Deserialize;
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
|
||||
use super::error::WebGPUError;
|
||||
|
||||
pub(crate) struct WebGPURenderPass(
|
||||
pub(crate) RefCell<wgpu_core::command::RenderPass>,
|
||||
);
|
||||
impl Resource for WebGPURenderPass {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPURenderPass".into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderPassSetViewportArgs {
|
||||
render_pass_rid: u32,
|
||||
x: f32,
|
||||
y: f32,
|
||||
width: f32,
|
||||
height: f32,
|
||||
min_depth: f32,
|
||||
max_depth: f32,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_pass_set_viewport(
|
||||
state: &mut OpState,
|
||||
args: RenderPassSetViewportArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderPass>(args.render_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
wgpu_core::command::render_ffi::wgpu_render_pass_set_viewport(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
args.x,
|
||||
args.y,
|
||||
args.width,
|
||||
args.height,
|
||||
args.min_depth,
|
||||
args.max_depth,
|
||||
);
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderPassSetScissorRectArgs {
|
||||
render_pass_rid: u32,
|
||||
x: u32,
|
||||
y: u32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_pass_set_scissor_rect(
|
||||
state: &mut OpState,
|
||||
args: RenderPassSetScissorRectArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderPass>(args.render_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
wgpu_core::command::render_ffi::wgpu_render_pass_set_scissor_rect(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
args.x,
|
||||
args.y,
|
||||
args.width,
|
||||
args.height,
|
||||
);
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GPUColor {
|
||||
pub r: f64,
|
||||
pub g: f64,
|
||||
pub b: f64,
|
||||
pub a: f64,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderPassSetBlendColorArgs {
|
||||
render_pass_rid: u32,
|
||||
color: GPUColor,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_pass_set_blend_color(
|
||||
state: &mut OpState,
|
||||
args: RenderPassSetBlendColorArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderPass>(args.render_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
wgpu_core::command::render_ffi::wgpu_render_pass_set_blend_color(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
&wgpu_types::Color {
|
||||
r: args.color.r,
|
||||
g: args.color.g,
|
||||
b: args.color.b,
|
||||
a: args.color.a,
|
||||
},
|
||||
);
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderPassSetStencilReferenceArgs {
|
||||
render_pass_rid: u32,
|
||||
reference: u32,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_pass_set_stencil_reference(
|
||||
state: &mut OpState,
|
||||
args: RenderPassSetStencilReferenceArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderPass>(args.render_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
wgpu_core::command::render_ffi::wgpu_render_pass_set_stencil_reference(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
args.reference,
|
||||
);
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderPassBeginPipelineStatisticsQueryArgs {
|
||||
render_pass_rid: u32,
|
||||
query_set: u32,
|
||||
query_index: u32,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_pass_begin_pipeline_statistics_query(
|
||||
state: &mut OpState,
|
||||
args: RenderPassBeginPipelineStatisticsQueryArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderPass>(args.render_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let query_set_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGPUQuerySet>(args.query_set)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
unsafe {
|
||||
wgpu_core::command::render_ffi::wgpu_render_pass_begin_pipeline_statistics_query(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
query_set_resource.0,
|
||||
args.query_index,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderPassEndPipelineStatisticsQueryArgs {
|
||||
render_pass_rid: u32,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_pass_end_pipeline_statistics_query(
|
||||
state: &mut OpState,
|
||||
args: RenderPassEndPipelineStatisticsQueryArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderPass>(args.render_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
unsafe {
|
||||
wgpu_core::command::render_ffi::wgpu_render_pass_end_pipeline_statistics_query(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderPassWriteTimestampArgs {
|
||||
render_pass_rid: u32,
|
||||
query_set: u32,
|
||||
query_index: u32,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_pass_write_timestamp(
|
||||
state: &mut OpState,
|
||||
args: RenderPassWriteTimestampArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderPass>(args.render_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let query_set_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGPUQuerySet>(args.query_set)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
unsafe {
|
||||
wgpu_core::command::render_ffi::wgpu_render_pass_write_timestamp(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
query_set_resource.0,
|
||||
args.query_index,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderPassExecuteBundlesArgs {
|
||||
render_pass_rid: u32,
|
||||
bundles: Vec<u32>,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_pass_execute_bundles(
|
||||
state: &mut OpState,
|
||||
args: RenderPassExecuteBundlesArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let mut render_bundle_ids = vec![];
|
||||
|
||||
for rid in &args.bundles {
|
||||
let render_bundle_resource = state
|
||||
.resource_table
|
||||
.get::<super::bundle::WebGPURenderBundle>(*rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
render_bundle_ids.push(render_bundle_resource.0);
|
||||
}
|
||||
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderPass>(args.render_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
unsafe {
|
||||
wgpu_core::command::render_ffi::wgpu_render_pass_execute_bundles(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
render_bundle_ids.as_ptr(),
|
||||
args.bundles.len(),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderPassEndPassArgs {
|
||||
command_encoder_rid: u32,
|
||||
render_pass_rid: u32,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_pass_end_pass(
|
||||
state: &mut OpState,
|
||||
args: RenderPassEndPassArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let command_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<super::command_encoder::WebGPUCommandEncoder>(
|
||||
args.command_encoder_rid,
|
||||
)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let command_encoder = command_encoder_resource.0;
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.take::<WebGPURenderPass>(args.render_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let render_pass = &render_pass_resource.0.borrow();
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
|
||||
let maybe_err = gfx_select!(command_encoder => instance.command_encoder_run_render_pass(command_encoder, render_pass)).err();
|
||||
|
||||
Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderPassSetBindGroupArgs {
|
||||
render_pass_rid: u32,
|
||||
index: u32,
|
||||
bind_group: u32,
|
||||
dynamic_offsets_data: Option<Vec<u32>>,
|
||||
dynamic_offsets_data_start: usize,
|
||||
dynamic_offsets_data_length: usize,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_pass_set_bind_group(
|
||||
state: &mut OpState,
|
||||
args: RenderPassSetBindGroupArgs,
|
||||
zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let bind_group_resource = state
|
||||
.resource_table
|
||||
.get::<super::binding::WebGPUBindGroup>(args.bind_group)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderPass>(args.render_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
// I know this might look like it can be easily deduplicated, but it can not
|
||||
// be due to the lifetime of the args.dynamic_offsets_data slice. Because we
|
||||
// need to use a raw pointer here the slice can be freed before the pointer
|
||||
// is used in wgpu_render_pass_set_bind_group. See
|
||||
// https://matrix.to/#/!XFRnMvAfptAHthwBCx:matrix.org/$HgrlhD-Me1DwsGb8UdMu2Hqubgks8s7ILwWRwigOUAg
|
||||
match args.dynamic_offsets_data {
|
||||
Some(data) => unsafe {
|
||||
wgpu_core::command::render_ffi::wgpu_render_pass_set_bind_group(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
args.index,
|
||||
bind_group_resource.0,
|
||||
data.as_slice().as_ptr(),
|
||||
args.dynamic_offsets_data_length,
|
||||
);
|
||||
},
|
||||
None => {
|
||||
let (prefix, data, suffix) = unsafe { zero_copy[0].align_to::<u32>() };
|
||||
assert!(prefix.is_empty());
|
||||
assert!(suffix.is_empty());
|
||||
unsafe {
|
||||
wgpu_core::command::render_ffi::wgpu_render_pass_set_bind_group(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
args.index,
|
||||
bind_group_resource.0,
|
||||
data[args.dynamic_offsets_data_start..].as_ptr(),
|
||||
args.dynamic_offsets_data_length,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderPassPushDebugGroupArgs {
|
||||
render_pass_rid: u32,
|
||||
group_label: String,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_pass_push_debug_group(
|
||||
state: &mut OpState,
|
||||
args: RenderPassPushDebugGroupArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderPass>(args.render_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
unsafe {
|
||||
let label = std::ffi::CString::new(args.group_label).unwrap();
|
||||
wgpu_core::command::render_ffi::wgpu_render_pass_push_debug_group(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
label.as_ptr(),
|
||||
0, // wgpu#975
|
||||
);
|
||||
}
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderPassPopDebugGroupArgs {
|
||||
render_pass_rid: u32,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_pass_pop_debug_group(
|
||||
state: &mut OpState,
|
||||
args: RenderPassPopDebugGroupArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderPass>(args.render_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
wgpu_core::command::render_ffi::wgpu_render_pass_pop_debug_group(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
);
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderPassInsertDebugMarkerArgs {
|
||||
render_pass_rid: u32,
|
||||
marker_label: String,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_pass_insert_debug_marker(
|
||||
state: &mut OpState,
|
||||
args: RenderPassInsertDebugMarkerArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderPass>(args.render_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
unsafe {
|
||||
let label = std::ffi::CString::new(args.marker_label).unwrap();
|
||||
wgpu_core::command::render_ffi::wgpu_render_pass_insert_debug_marker(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
label.as_ptr(),
|
||||
0, // wgpu#975
|
||||
);
|
||||
}
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderPassSetPipelineArgs {
|
||||
render_pass_rid: u32,
|
||||
pipeline: u32,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_pass_set_pipeline(
|
||||
state: &mut OpState,
|
||||
args: RenderPassSetPipelineArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let render_pipeline_resource = state
|
||||
.resource_table
|
||||
.get::<super::pipeline::WebGPURenderPipeline>(args.pipeline)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderPass>(args.render_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
wgpu_core::command::render_ffi::wgpu_render_pass_set_pipeline(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
render_pipeline_resource.0,
|
||||
);
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderPassSetIndexBufferArgs {
|
||||
render_pass_rid: u32,
|
||||
buffer: u32,
|
||||
index_format: String,
|
||||
offset: u64,
|
||||
size: u64,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_pass_set_index_buffer(
|
||||
state: &mut OpState,
|
||||
args: RenderPassSetIndexBufferArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let buffer_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGPUBuffer>(args.buffer)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderPass>(args.render_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
render_pass_resource.0.borrow_mut().set_index_buffer(
|
||||
buffer_resource.0,
|
||||
super::pipeline::serialize_index_format(args.index_format),
|
||||
args.offset,
|
||||
std::num::NonZeroU64::new(args.size),
|
||||
);
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderPassSetVertexBufferArgs {
|
||||
render_pass_rid: u32,
|
||||
slot: u32,
|
||||
buffer: u32,
|
||||
offset: u64,
|
||||
size: u64,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_pass_set_vertex_buffer(
|
||||
state: &mut OpState,
|
||||
args: RenderPassSetVertexBufferArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let buffer_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGPUBuffer>(args.buffer)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderPass>(args.render_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
wgpu_core::command::render_ffi::wgpu_render_pass_set_vertex_buffer(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
args.slot,
|
||||
buffer_resource.0,
|
||||
args.offset,
|
||||
std::num::NonZeroU64::new(args.size),
|
||||
);
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderPassDrawArgs {
|
||||
render_pass_rid: u32,
|
||||
vertex_count: u32,
|
||||
instance_count: u32,
|
||||
first_vertex: u32,
|
||||
first_instance: u32,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_pass_draw(
|
||||
state: &mut OpState,
|
||||
args: RenderPassDrawArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderPass>(args.render_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
wgpu_core::command::render_ffi::wgpu_render_pass_draw(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
args.vertex_count,
|
||||
args.instance_count,
|
||||
args.first_vertex,
|
||||
args.first_instance,
|
||||
);
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderPassDrawIndexedArgs {
|
||||
render_pass_rid: u32,
|
||||
index_count: u32,
|
||||
instance_count: u32,
|
||||
first_index: u32,
|
||||
base_vertex: i32,
|
||||
first_instance: u32,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_pass_draw_indexed(
|
||||
state: &mut OpState,
|
||||
args: RenderPassDrawIndexedArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderPass>(args.render_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
wgpu_core::command::render_ffi::wgpu_render_pass_draw_indexed(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
args.index_count,
|
||||
args.instance_count,
|
||||
args.first_index,
|
||||
args.base_vertex,
|
||||
args.first_instance,
|
||||
);
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderPassDrawIndirectArgs {
|
||||
render_pass_rid: u32,
|
||||
indirect_buffer: u32,
|
||||
indirect_offset: u64,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_pass_draw_indirect(
|
||||
state: &mut OpState,
|
||||
args: RenderPassDrawIndirectArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let buffer_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGPUBuffer>(args.indirect_buffer)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderPass>(args.render_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
wgpu_core::command::render_ffi::wgpu_render_pass_draw_indirect(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
buffer_resource.0,
|
||||
args.indirect_offset,
|
||||
);
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderPassDrawIndexedIndirectArgs {
|
||||
render_pass_rid: u32,
|
||||
indirect_buffer: u32,
|
||||
indirect_offset: u64,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_render_pass_draw_indexed_indirect(
|
||||
state: &mut OpState,
|
||||
args: RenderPassDrawIndexedIndirectArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let buffer_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGPUBuffer>(args.indirect_buffer)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPURenderPass>(args.render_pass_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
wgpu_core::command::render_ffi::wgpu_render_pass_draw_indexed_indirect(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
buffer_resource.0,
|
||||
args.indirect_offset,
|
||||
);
|
||||
|
||||
Ok(json!({}))
|
||||
}
|
129
op_crates/webgpu/sampler.rs
Normal file
129
op_crates/webgpu/sampler.rs
Normal file
|
@ -0,0 +1,129 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_core::error::bad_resource_id;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json::json;
|
||||
use deno_core::serde_json::Value;
|
||||
use deno_core::ZeroCopyBuf;
|
||||
use deno_core::{OpState, Resource};
|
||||
use serde::Deserialize;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use super::error::WebGPUError;
|
||||
|
||||
pub(crate) struct WebGPUSampler(pub(crate) wgpu_core::id::SamplerId);
|
||||
impl Resource for WebGPUSampler {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUSampler".into()
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_address_mode(
|
||||
address_mode: Option<String>,
|
||||
) -> wgpu_types::AddressMode {
|
||||
match address_mode {
|
||||
Some(address_mode) => match address_mode.as_str() {
|
||||
"clamp-to-edge" => wgpu_types::AddressMode::ClampToEdge,
|
||||
"repeat" => wgpu_types::AddressMode::Repeat,
|
||||
"mirror-repeat" => wgpu_types::AddressMode::MirrorRepeat,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
None => wgpu_types::AddressMode::ClampToEdge,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_filter_mode(
|
||||
filter_mode: Option<String>,
|
||||
) -> wgpu_types::FilterMode {
|
||||
match filter_mode {
|
||||
Some(filter_mode) => match filter_mode.as_str() {
|
||||
"nearest" => wgpu_types::FilterMode::Nearest,
|
||||
"linear" => wgpu_types::FilterMode::Linear,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
None => wgpu_types::FilterMode::Nearest,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn serialize_compare_function(
|
||||
compare: &str,
|
||||
) -> wgpu_types::CompareFunction {
|
||||
match compare {
|
||||
"never" => wgpu_types::CompareFunction::Never,
|
||||
"less" => wgpu_types::CompareFunction::Less,
|
||||
"equal" => wgpu_types::CompareFunction::Equal,
|
||||
"less-equal" => wgpu_types::CompareFunction::LessEqual,
|
||||
"greater" => wgpu_types::CompareFunction::Greater,
|
||||
"not-equal" => wgpu_types::CompareFunction::NotEqual,
|
||||
"greater-equal" => wgpu_types::CompareFunction::GreaterEqual,
|
||||
"always" => wgpu_types::CompareFunction::Always,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateSamplerArgs {
|
||||
device_rid: u32,
|
||||
label: Option<String>,
|
||||
address_mode_u: Option<String>,
|
||||
address_mode_v: Option<String>,
|
||||
address_mode_w: Option<String>,
|
||||
mag_filter: Option<String>,
|
||||
min_filter: Option<String>,
|
||||
mipmap_filter: Option<String>,
|
||||
lod_min_clamp: Option<f32>,
|
||||
lod_max_clamp: Option<f32>,
|
||||
compare: Option<String>,
|
||||
max_anisotropy: Option<u8>,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_create_sampler(
|
||||
state: &mut OpState,
|
||||
args: CreateSamplerArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let device_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGPUDevice>(args.device_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let device = device_resource.0;
|
||||
|
||||
let descriptor = wgpu_core::resource::SamplerDescriptor {
|
||||
label: args.label.map(Cow::from),
|
||||
address_modes: [
|
||||
serialize_address_mode(args.address_mode_u),
|
||||
serialize_address_mode(args.address_mode_v),
|
||||
serialize_address_mode(args.address_mode_w),
|
||||
],
|
||||
mag_filter: serialize_filter_mode(args.mag_filter),
|
||||
min_filter: serialize_filter_mode(args.min_filter),
|
||||
mipmap_filter: serialize_filter_mode(args.mipmap_filter),
|
||||
lod_min_clamp: args.lod_min_clamp.unwrap_or(0.0),
|
||||
lod_max_clamp: args.lod_max_clamp.unwrap_or(
|
||||
wgpu_core::resource::SamplerDescriptor::default().lod_max_clamp,
|
||||
),
|
||||
compare: args
|
||||
.compare
|
||||
.as_ref()
|
||||
.map(|compare| serialize_compare_function(compare)),
|
||||
anisotropy_clamp: std::num::NonZeroU8::new(
|
||||
args.max_anisotropy.unwrap_or(0),
|
||||
),
|
||||
border_color: None, // native-only
|
||||
};
|
||||
|
||||
let (sampler, maybe_err) = gfx_select!(device => instance.device_create_sampler(
|
||||
device,
|
||||
&descriptor,
|
||||
std::marker::PhantomData
|
||||
));
|
||||
|
||||
let rid = state.resource_table.add(WebGPUSampler(sampler));
|
||||
|
||||
Ok(json!({
|
||||
"rid": rid,
|
||||
"err": maybe_err.map(WebGPUError::from)
|
||||
}))
|
||||
}
|
77
op_crates/webgpu/shader.rs
Normal file
77
op_crates/webgpu/shader.rs
Normal file
|
@ -0,0 +1,77 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_core::error::bad_resource_id;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json::json;
|
||||
use deno_core::serde_json::Value;
|
||||
use deno_core::ZeroCopyBuf;
|
||||
use deno_core::{OpState, Resource};
|
||||
use serde::Deserialize;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use super::error::WebGPUError;
|
||||
|
||||
pub(crate) struct WebGPUShaderModule(pub(crate) wgpu_core::id::ShaderModuleId);
|
||||
impl Resource for WebGPUShaderModule {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUShaderModule".into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateShaderModuleArgs {
|
||||
device_rid: u32,
|
||||
label: Option<String>,
|
||||
code: Option<String>,
|
||||
_source_map: Option<()>, // not yet implemented
|
||||
}
|
||||
|
||||
pub fn op_webgpu_create_shader_module(
|
||||
state: &mut OpState,
|
||||
args: CreateShaderModuleArgs,
|
||||
zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let device_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGPUDevice>(args.device_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let device = device_resource.0;
|
||||
|
||||
let source = match args.code {
|
||||
Some(code) => {
|
||||
wgpu_core::pipeline::ShaderModuleSource::Wgsl(Cow::from(code))
|
||||
}
|
||||
None => wgpu_core::pipeline::ShaderModuleSource::SpirV(Cow::from(unsafe {
|
||||
let (prefix, data, suffix) = zero_copy[0].align_to::<u32>();
|
||||
assert!(prefix.is_empty());
|
||||
assert!(suffix.is_empty());
|
||||
data
|
||||
})),
|
||||
};
|
||||
|
||||
let mut flags = wgpu_types::ShaderFlags::default();
|
||||
flags.set(wgpu_types::ShaderFlags::VALIDATION, true);
|
||||
#[cfg(all(target_os = "macos", target_arch = "x86_64"))]
|
||||
flags.set(wgpu_types::ShaderFlags::EXPERIMENTAL_TRANSLATION, true);
|
||||
|
||||
let descriptor = wgpu_core::pipeline::ShaderModuleDescriptor {
|
||||
label: args.label.map(Cow::from),
|
||||
flags,
|
||||
};
|
||||
|
||||
let (shader_module, maybe_err) = gfx_select!(device => instance.device_create_shader_module(
|
||||
device,
|
||||
&descriptor,
|
||||
source,
|
||||
std::marker::PhantomData
|
||||
));
|
||||
|
||||
let rid = state.resource_table.add(WebGPUShaderModule(shader_module));
|
||||
|
||||
Ok(json!({
|
||||
"rid": rid,
|
||||
"err": maybe_err.map(WebGPUError::from)
|
||||
}))
|
||||
}
|
256
op_crates/webgpu/texture.rs
Normal file
256
op_crates/webgpu/texture.rs
Normal file
|
@ -0,0 +1,256 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::error::{bad_resource_id, not_supported};
|
||||
use deno_core::serde_json::json;
|
||||
use deno_core::serde_json::Value;
|
||||
use deno_core::ZeroCopyBuf;
|
||||
use deno_core::{OpState, Resource};
|
||||
use serde::Deserialize;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use super::error::WebGPUError;
|
||||
pub(crate) struct WebGPUTexture(pub(crate) wgpu_core::id::TextureId);
|
||||
impl Resource for WebGPUTexture {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUTexture".into()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct WebGPUTextureView(pub(crate) wgpu_core::id::TextureViewId);
|
||||
impl Resource for WebGPUTextureView {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUTextureView".into()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn serialize_texture_format(
|
||||
format: &str,
|
||||
) -> Result<wgpu_types::TextureFormat, AnyError> {
|
||||
Ok(match format {
|
||||
// 8-bit formats
|
||||
"r8unorm" => wgpu_types::TextureFormat::R8Unorm,
|
||||
"r8snorm" => wgpu_types::TextureFormat::R8Snorm,
|
||||
"r8uint" => wgpu_types::TextureFormat::R8Uint,
|
||||
"r8sint" => wgpu_types::TextureFormat::R8Sint,
|
||||
|
||||
// 16-bit formats
|
||||
"r16uint" => wgpu_types::TextureFormat::R16Uint,
|
||||
"r16sint" => wgpu_types::TextureFormat::R16Sint,
|
||||
"r16float" => wgpu_types::TextureFormat::R16Float,
|
||||
"rg8unorm" => wgpu_types::TextureFormat::Rg8Unorm,
|
||||
"rg8snorm" => wgpu_types::TextureFormat::Rg8Snorm,
|
||||
"rg8uint" => wgpu_types::TextureFormat::Rg8Uint,
|
||||
"rg8sint" => wgpu_types::TextureFormat::Rg8Sint,
|
||||
|
||||
// 32-bit formats
|
||||
"r32uint" => wgpu_types::TextureFormat::R32Uint,
|
||||
"r32sint" => wgpu_types::TextureFormat::R32Sint,
|
||||
"r32float" => wgpu_types::TextureFormat::R32Float,
|
||||
"rg16uint" => wgpu_types::TextureFormat::Rg16Uint,
|
||||
"rg16sint" => wgpu_types::TextureFormat::Rg16Sint,
|
||||
"rg16float" => wgpu_types::TextureFormat::Rg16Float,
|
||||
"rgba8unorm" => wgpu_types::TextureFormat::Rgba8Unorm,
|
||||
"rgba8unorm-srgb" => wgpu_types::TextureFormat::Rgba8UnormSrgb,
|
||||
"rgba8snorm" => wgpu_types::TextureFormat::Rgba8Snorm,
|
||||
"rgba8uint" => wgpu_types::TextureFormat::Rgba8Uint,
|
||||
"rgba8sint" => wgpu_types::TextureFormat::Rgba8Sint,
|
||||
"bgra8unorm" => wgpu_types::TextureFormat::Bgra8Unorm,
|
||||
"bgra8unorm-srgb" => wgpu_types::TextureFormat::Bgra8UnormSrgb,
|
||||
// Packed 32-bit formats
|
||||
"rgb9e5ufloat" => return Err(not_supported()), // wgpu#967
|
||||
"rgb10a2unorm" => wgpu_types::TextureFormat::Rgb10a2Unorm,
|
||||
"rg11b10ufloat" => wgpu_types::TextureFormat::Rg11b10Float,
|
||||
|
||||
// 64-bit formats
|
||||
"rg32uint" => wgpu_types::TextureFormat::Rg32Uint,
|
||||
"rg32sint" => wgpu_types::TextureFormat::Rg32Sint,
|
||||
"rg32float" => wgpu_types::TextureFormat::Rg32Float,
|
||||
"rgba16uint" => wgpu_types::TextureFormat::Rgba16Uint,
|
||||
"rgba16sint" => wgpu_types::TextureFormat::Rgba16Sint,
|
||||
"rgba16float" => wgpu_types::TextureFormat::Rgba16Float,
|
||||
|
||||
// 128-bit formats
|
||||
"rgba32uint" => wgpu_types::TextureFormat::Rgba32Uint,
|
||||
"rgba32sint" => wgpu_types::TextureFormat::Rgba32Sint,
|
||||
"rgba32float" => wgpu_types::TextureFormat::Rgba32Float,
|
||||
|
||||
// Depth and stencil formats
|
||||
"stencil8" => return Err(not_supported()), // wgpu#967
|
||||
"depth16unorm" => return Err(not_supported()), // wgpu#967
|
||||
"depth24plus" => wgpu_types::TextureFormat::Depth24Plus,
|
||||
"depth24plus-stencil8" => wgpu_types::TextureFormat::Depth24PlusStencil8,
|
||||
"depth32float" => wgpu_types::TextureFormat::Depth32Float,
|
||||
|
||||
// BC compressed formats usable if "texture-compression-bc" is both
|
||||
// supported by the device/user agent and enabled in requestDevice.
|
||||
"bc1-rgba-unorm" => wgpu_types::TextureFormat::Bc1RgbaUnorm,
|
||||
"bc1-rgba-unorm-srgb" => wgpu_types::TextureFormat::Bc1RgbaUnormSrgb,
|
||||
"bc2-rgba-unorm" => wgpu_types::TextureFormat::Bc2RgbaUnorm,
|
||||
"bc2-rgba-unorm-srgb" => wgpu_types::TextureFormat::Bc2RgbaUnormSrgb,
|
||||
"bc3-rgba-unorm" => wgpu_types::TextureFormat::Bc3RgbaUnorm,
|
||||
"bc3-rgba-unorm-srgb" => wgpu_types::TextureFormat::Bc3RgbaUnormSrgb,
|
||||
"bc4-r-unorm" => wgpu_types::TextureFormat::Bc4RUnorm,
|
||||
"bc4-r-snorm" => wgpu_types::TextureFormat::Bc4RSnorm,
|
||||
"bc5-rg-unorm" => wgpu_types::TextureFormat::Bc5RgUnorm,
|
||||
"bc5-rg-snorm" => wgpu_types::TextureFormat::Bc5RgSnorm,
|
||||
"bc6h-rgb-ufloat" => wgpu_types::TextureFormat::Bc6hRgbUfloat,
|
||||
"bc6h-rgb-float" => wgpu_types::TextureFormat::Bc6hRgbSfloat, // wgpu#967
|
||||
"bc7-rgba-unorm" => wgpu_types::TextureFormat::Bc7RgbaUnorm,
|
||||
"bc7-rgba-unorm-srgb" => wgpu_types::TextureFormat::Bc7RgbaUnormSrgb,
|
||||
|
||||
// "depth24unorm-stencil8" extension
|
||||
"depth24unorm-stencil8" => return Err(not_supported()), // wgpu#967
|
||||
|
||||
// "depth32float-stencil8" extension
|
||||
"depth32float-stencil8" => return Err(not_supported()), // wgpu#967
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn serialize_dimension(
|
||||
dimension: &str,
|
||||
) -> wgpu_types::TextureViewDimension {
|
||||
match dimension {
|
||||
"1d" => wgpu_types::TextureViewDimension::D1,
|
||||
"2d" => wgpu_types::TextureViewDimension::D2,
|
||||
"2d-array" => wgpu_types::TextureViewDimension::D2Array,
|
||||
"cube" => wgpu_types::TextureViewDimension::Cube,
|
||||
"cube-array" => wgpu_types::TextureViewDimension::CubeArray,
|
||||
"3d" => wgpu_types::TextureViewDimension::D3,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GPUExtent3D {
|
||||
pub width: Option<u32>,
|
||||
pub height: Option<u32>,
|
||||
pub depth: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateTextureArgs {
|
||||
device_rid: u32,
|
||||
label: Option<String>,
|
||||
size: GPUExtent3D,
|
||||
mip_level_count: Option<u32>,
|
||||
sample_count: Option<u32>,
|
||||
dimension: Option<String>,
|
||||
format: String,
|
||||
usage: u32,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_create_texture(
|
||||
state: &mut OpState,
|
||||
args: CreateTextureArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let device_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGPUDevice>(args.device_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let device = device_resource.0;
|
||||
|
||||
let descriptor = wgpu_core::resource::TextureDescriptor {
|
||||
label: args.label.map(Cow::from),
|
||||
size: wgpu_types::Extent3d {
|
||||
width: args.size.width.unwrap_or(1),
|
||||
height: args.size.height.unwrap_or(1),
|
||||
depth: args.size.depth.unwrap_or(1),
|
||||
},
|
||||
mip_level_count: args.mip_level_count.unwrap_or(1),
|
||||
sample_count: args.sample_count.unwrap_or(1),
|
||||
dimension: match args.dimension {
|
||||
Some(dimension) => match dimension.as_str() {
|
||||
"1d" => wgpu_types::TextureDimension::D1,
|
||||
"2d" => wgpu_types::TextureDimension::D2,
|
||||
"3d" => wgpu_types::TextureDimension::D3,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
None => wgpu_types::TextureDimension::D2,
|
||||
},
|
||||
format: serialize_texture_format(&args.format)?,
|
||||
usage: wgpu_types::TextureUsage::from_bits(args.usage).unwrap(),
|
||||
};
|
||||
|
||||
let (texture, maybe_err) = gfx_select!(device => instance.device_create_texture(
|
||||
device,
|
||||
&descriptor,
|
||||
std::marker::PhantomData
|
||||
));
|
||||
|
||||
let rid = state.resource_table.add(WebGPUTexture(texture));
|
||||
|
||||
Ok(json!({
|
||||
"rid": rid,
|
||||
"err": maybe_err.map(WebGPUError::from)
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateTextureViewArgs {
|
||||
texture_rid: u32,
|
||||
label: Option<String>,
|
||||
format: Option<String>,
|
||||
dimension: Option<String>,
|
||||
aspect: Option<String>,
|
||||
base_mip_level: Option<u32>,
|
||||
mip_level_count: Option<u32>,
|
||||
base_array_layer: Option<u32>,
|
||||
array_layer_count: Option<u32>,
|
||||
}
|
||||
|
||||
pub fn op_webgpu_create_texture_view(
|
||||
state: &mut OpState,
|
||||
args: CreateTextureViewArgs,
|
||||
_zero_copy: &mut [ZeroCopyBuf],
|
||||
) -> Result<Value, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let texture_resource = state
|
||||
.resource_table
|
||||
.get::<WebGPUTexture>(args.texture_rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
let texture = texture_resource.0;
|
||||
|
||||
let descriptor = wgpu_core::resource::TextureViewDescriptor {
|
||||
label: args.label.map(Cow::from),
|
||||
format: args
|
||||
.format
|
||||
.map(|s| serialize_texture_format(&s))
|
||||
.transpose()?,
|
||||
dimension: args.dimension.map(|s| serialize_dimension(&s)),
|
||||
aspect: match args.aspect {
|
||||
Some(aspect) => match aspect.as_str() {
|
||||
"all" => wgpu_types::TextureAspect::All,
|
||||
"stencil-only" => wgpu_types::TextureAspect::StencilOnly,
|
||||
"depth-only" => wgpu_types::TextureAspect::DepthOnly,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
None => wgpu_types::TextureAspect::All,
|
||||
},
|
||||
base_mip_level: args.base_mip_level.unwrap_or(0),
|
||||
level_count: std::num::NonZeroU32::new(args.mip_level_count.unwrap_or(0)),
|
||||
base_array_layer: args.base_array_layer.unwrap_or(0),
|
||||
array_layer_count: std::num::NonZeroU32::new(
|
||||
args.array_layer_count.unwrap_or(0),
|
||||
),
|
||||
};
|
||||
|
||||
let (texture_view, maybe_err) = gfx_select!(texture => instance.texture_create_view(
|
||||
texture,
|
||||
&descriptor,
|
||||
std::marker::PhantomData
|
||||
));
|
||||
|
||||
let rid = state.resource_table.add(WebGPUTextureView(texture_view));
|
||||
|
||||
Ok(json!({
|
||||
"rid": rid,
|
||||
"err": maybe_err.map(WebGPUError::from)
|
||||
}))
|
||||
}
|
1023
op_crates/webgpu/webgpu.idl
Normal file
1023
op_crates/webgpu/webgpu.idl
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue