refactor: object wrap WebGPU (#27665)

Fixes #25874
Fixes #26760
Fixes #24288
Fixes #24798
Fixes #25627
Fixes #25915
Fixes #26769
This commit is contained in:
Leo Kettmeir 2025-02-12 14:45:41 +01:00 committed by GitHub
parent 7a112643f5
commit 7253820764
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
39 changed files with 7138 additions and 11466 deletions

View file

@ -1,144 +1,165 @@
// Copyright 2018-2025 the Deno authors. MIT license.
use std::borrow::Cow;
use std::rc::Rc;
use deno_core::error::ResourceError;
use deno_core::cppgc::Ptr;
use deno_core::op2;
use deno_core::OpState;
use deno_core::Resource;
use deno_core::ResourceId;
use serde::Deserialize;
use deno_core::GarbageCollected;
use deno_core::WebIDL;
use deno_error::JsErrorBox;
use super::error::WebGpuResult;
use crate::command_encoder::WebGpuCommandBuffer;
use crate::buffer::GPUBuffer;
use crate::command_buffer::GPUCommandBuffer;
use crate::texture::GPUTexture;
use crate::texture::GPUTextureAspect;
use crate::webidl::GPUExtent3D;
use crate::webidl::GPUOrigin3D;
use crate::Instance;
pub struct WebGpuQueue(pub Instance, pub wgpu_core::id::QueueId);
impl Resource for WebGpuQueue {
fn name(&self) -> Cow<str> {
"webGPUQueue".into()
}
pub struct GPUQueue {
pub instance: Instance,
pub error_handler: super::error::ErrorHandler,
fn close(self: Rc<Self>) {
gfx_select!(self.1 => self.0.queue_drop(self.1));
pub label: String,
pub id: wgpu_core::id::QueueId,
}
impl Drop for GPUQueue {
fn drop(&mut self) {
self.instance.queue_drop(self.id);
}
}
impl GarbageCollected for GPUQueue {}
#[op2]
#[serde]
pub fn op_webgpu_queue_submit(
state: &mut OpState,
#[smi] queue_rid: ResourceId,
#[serde] command_buffers: Vec<ResourceId>,
) -> Result<WebGpuResult, ResourceError> {
let instance = state.borrow::<Instance>();
let queue_resource = state.resource_table.get::<WebGpuQueue>(queue_rid)?;
let queue = queue_resource.1;
let ids = command_buffers
.iter()
.map(|rid| {
let buffer_resource =
state.resource_table.get::<WebGpuCommandBuffer>(*rid)?;
let mut id = buffer_resource.1.borrow_mut();
Ok(id.take().unwrap())
})
.collect::<Result<Vec<_>, ResourceError>>()?;
let maybe_err =
gfx_select!(queue => instance.queue_submit(queue, &ids)).err();
for rid in command_buffers {
let resource = state.resource_table.take::<WebGpuCommandBuffer>(rid)?;
resource.close();
impl GPUQueue {
#[getter]
#[string]
fn label(&self) -> String {
self.label.clone()
}
#[setter]
#[string]
fn label(&self, #[webidl] _label: String) {
// TODO(@crowlKats): no-op, needs wpgu to implement changing the label
}
Ok(WebGpuResult::maybe_err(maybe_err))
#[required(1)]
fn submit(
&self,
#[webidl] command_buffers: Vec<Ptr<GPUCommandBuffer>>,
) -> Result<(), JsErrorBox> {
let ids = command_buffers
.into_iter()
.enumerate()
.map(|(i, cb)| {
if cb.consumed.set(()).is_err() {
Err(JsErrorBox::type_error(format!(
"The command buffer at position {i} has already been submitted."
)))
} else {
Ok(cb.id)
}
})
.collect::<Result<Vec<_>, _>>()?;
let err = self.instance.queue_submit(self.id, &ids).err();
if let Some((_, err)) = err {
self.error_handler.push_error(Some(err));
}
Ok(())
}
#[async_method]
async fn on_submitted_work_done(&self) -> Result<(), JsErrorBox> {
Err(JsErrorBox::generic(
"This operation is currently not supported",
))
}
#[required(3)]
fn write_buffer(
&self,
#[webidl] buffer: Ptr<GPUBuffer>,
#[webidl(options(enforce_range = true))] buffer_offset: u64,
#[anybuffer] buf: &[u8],
#[webidl(default = 0, options(enforce_range = true))] data_offset: u64,
#[webidl(options(enforce_range = true))] size: Option<u64>,
) {
let data = match size {
Some(size) => {
&buf[(data_offset as usize)..((data_offset + size) as usize)]
}
None => &buf[(data_offset as usize)..],
};
let err = self
.instance
.queue_write_buffer(self.id, buffer.id, buffer_offset, data)
.err();
self.error_handler.push_error(err);
}
#[required(4)]
fn write_texture(
&self,
#[webidl] destination: GPUTexelCopyTextureInfo,
#[anybuffer] buf: &[u8],
#[webidl] data_layout: GPUTexelCopyBufferLayout,
#[webidl] size: GPUExtent3D,
) {
let destination = wgpu_core::command::TexelCopyTextureInfo {
texture: destination.texture.id,
mip_level: destination.mip_level,
origin: destination.origin.into(),
aspect: destination.aspect.into(),
};
let data_layout = wgpu_types::TexelCopyBufferLayout {
offset: data_layout.offset,
bytes_per_row: data_layout.bytes_per_row,
rows_per_image: data_layout.rows_per_image,
};
let err = self
.instance
.queue_write_texture(
self.id,
&destination,
buf,
&data_layout,
&size.into(),
)
.err();
self.error_handler.push_error(err);
}
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GpuImageDataLayout {
#[derive(WebIDL)]
#[webidl(dictionary)]
pub(crate) struct GPUTexelCopyTextureInfo {
pub texture: Ptr<GPUTexture>,
#[webidl(default = 0)]
#[options(enforce_range = true)]
pub mip_level: u32,
#[webidl(default = Default::default())]
pub origin: GPUOrigin3D,
#[webidl(default = GPUTextureAspect::All)]
pub aspect: GPUTextureAspect,
}
#[derive(WebIDL)]
#[webidl(dictionary)]
struct GPUTexelCopyBufferLayout {
#[webidl(default = 0)]
#[options(enforce_range = true)]
offset: u64,
#[options(enforce_range = true)]
bytes_per_row: Option<u32>,
#[options(enforce_range = true)]
rows_per_image: Option<u32>,
}
impl From<GpuImageDataLayout> for wgpu_types::ImageDataLayout {
fn from(layout: GpuImageDataLayout) -> Self {
wgpu_types::ImageDataLayout {
offset: layout.offset,
bytes_per_row: layout.bytes_per_row,
rows_per_image: layout.rows_per_image,
}
}
}
#[op2]
#[serde]
pub fn op_webgpu_write_buffer(
state: &mut OpState,
#[smi] queue_rid: ResourceId,
#[smi] buffer: ResourceId,
#[number] buffer_offset: u64,
#[number] data_offset: usize,
#[number] size: Option<usize>,
#[buffer] buf: &[u8],
) -> Result<WebGpuResult, ResourceError> {
let instance = state.borrow::<Instance>();
let buffer_resource = state
.resource_table
.get::<super::buffer::WebGpuBuffer>(buffer)?;
let buffer = buffer_resource.1;
let queue_resource = state.resource_table.get::<WebGpuQueue>(queue_rid)?;
let queue = queue_resource.1;
let data = match size {
Some(size) => &buf[data_offset..(data_offset + size)],
None => &buf[data_offset..],
};
let maybe_err = gfx_select!(queue => instance.queue_write_buffer(
queue,
buffer,
buffer_offset,
data
))
.err();
Ok(WebGpuResult::maybe_err(maybe_err))
}
#[op2]
#[serde]
pub fn op_webgpu_write_texture(
state: &mut OpState,
#[smi] queue_rid: ResourceId,
#[serde] destination: super::command_encoder::GpuImageCopyTexture,
#[serde] data_layout: GpuImageDataLayout,
#[serde] size: wgpu_types::Extent3d,
#[buffer] buf: &[u8],
) -> Result<WebGpuResult, ResourceError> {
let instance = state.borrow::<Instance>();
let texture_resource = state
.resource_table
.get::<super::texture::WebGpuTexture>(destination.texture)?;
let queue_resource = state.resource_table.get::<WebGpuQueue>(queue_rid)?;
let queue = queue_resource.1;
let destination = wgpu_core::command::ImageCopyTexture {
texture: texture_resource.id,
mip_level: destination.mip_level,
origin: destination.origin,
aspect: destination.aspect,
};
let data_layout = data_layout.into();
gfx_ok!(queue => instance.queue_write_texture(
queue,
&destination,
buf,
&data_layout,
&size
))
}