Initial implementation of GPUCommandEncoder

Added WebIDL bindings for `GPUCommandEncoder`, `GPUCommandBuffer`, `GPUComputePassEncoder`, `GPUProgrammablePassEncoder`.
Implemented the `beginComputePass`, `copyBufferToBuffer` and `finish` functions of `GPUCommandEncoder`.
Implemented the `createCommandEncoder` function of `GPUDevice`.
This commit is contained in:
Zakor Gyula 2020-01-14 08:54:08 +01:00 committed by Istvan Miklos
parent 9031369c19
commit 4facd3d4d2
14 changed files with 443 additions and 13 deletions

View file

@ -153,7 +153,8 @@ use time::{Duration, Timespec, Tm};
use uuid::Uuid;
use webgpu::{
WebGPU, WebGPUAdapter, WebGPUBindGroup, WebGPUBindGroupLayout, WebGPUBuffer,
WebGPUComputePipeline, WebGPUDevice, WebGPUPipelineLayout, WebGPUShaderModule,
WebGPUCommandBuffer, WebGPUCommandEncoder, WebGPUComputePipeline, WebGPUDevice,
WebGPUPipelineLayout, WebGPUShaderModule,
};
use webrender_api::{DocumentId, ImageKey};
use webvr_traits::{WebVRGamepadData, WebVRGamepadHand, WebVRGamepadState};
@ -531,13 +532,16 @@ unsafe_no_jsmanaged_fields!(RefCell<Option<WebGPU>>);
unsafe_no_jsmanaged_fields!(RefCell<Identities>);
unsafe_no_jsmanaged_fields!(WebGPU);
unsafe_no_jsmanaged_fields!(WebGPUAdapter);
unsafe_no_jsmanaged_fields!(WebGPUDevice);
unsafe_no_jsmanaged_fields!(WebGPUBuffer);
unsafe_no_jsmanaged_fields!(WebGPUBindGroup);
unsafe_no_jsmanaged_fields!(WebGPUBindGroupLayout);
unsafe_no_jsmanaged_fields!(WebGPUComputePipeline);
unsafe_no_jsmanaged_fields!(WebGPUPipelineLayout);
unsafe_no_jsmanaged_fields!(WebGPUShaderModule);
unsafe_no_jsmanaged_fields!(WebGPUCommandBuffer);
unsafe_no_jsmanaged_fields!(WebGPUCommandEncoder);
unsafe_no_jsmanaged_fields!(WebGPUDevice);
unsafe_no_jsmanaged_fields!(webgpu::wgpu::command::RawPass);
unsafe_no_jsmanaged_fields!(GPUBufferState);
unsafe_no_jsmanaged_fields!(WebXRSwapChainId);
unsafe_no_jsmanaged_fields!(MediaList);

View file

@ -99,8 +99,8 @@ use time::{get_time, Timespec};
use uuid::Uuid;
use webgpu::wgpu::{
id::{
AdapterId, BindGroupId, BindGroupLayoutId, BufferId, ComputePipelineId, DeviceId,
PipelineLayoutId, ShaderModuleId,
AdapterId, BindGroupId, BindGroupLayoutId, BufferId, CommandEncoderId, ComputePipelineId,
DeviceId, PipelineLayoutId, ShaderModuleId,
},
Backend,
};
@ -2137,11 +2137,18 @@ impl GlobalScope {
.borrow_mut()
.create_shader_module_id(backend)
}
pub fn wgpu_create_compute_pipeline_id(&self, backend: Backend) -> ComputePipelineId {
self.gpu_id_hub
.borrow_mut()
.create_compute_pipeline_id(backend)
}
pub fn wgpu_create_command_encoder_id(&self, backend: Backend) -> CommandEncoderId {
self.gpu_id_hub
.borrow_mut()
.create_command_encoder_id(backend)
}
}
fn timestamp_in_ms(time: Timespec) -> u64 {

View file

@ -0,0 +1,58 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::GPUCommandBufferBinding::{
self, GPUCommandBufferMethods,
};
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::globalscope::GlobalScope;
use dom_struct::dom_struct;
use webgpu::{WebGPU, WebGPUCommandBuffer};
#[dom_struct]
pub struct GPUCommandBuffer {
reflector_: Reflector,
#[ignore_malloc_size_of = "defined in webgpu"]
channel: WebGPU,
label: DomRefCell<Option<DOMString>>,
command_buffer: WebGPUCommandBuffer,
}
impl GPUCommandBuffer {
pub fn new_inherited(channel: WebGPU, command_buffer: WebGPUCommandBuffer) -> GPUCommandBuffer {
GPUCommandBuffer {
channel,
reflector_: Reflector::new(),
label: DomRefCell::new(None),
command_buffer,
}
}
pub fn new(
global: &GlobalScope,
channel: WebGPU,
command_buffer: WebGPUCommandBuffer,
) -> DomRoot<GPUCommandBuffer> {
reflect_dom_object(
Box::new(GPUCommandBuffer::new_inherited(channel, command_buffer)),
global,
GPUCommandBufferBinding::Wrap,
)
}
}
impl GPUCommandBufferMethods for GPUCommandBuffer {
/// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label
fn GetLabel(&self) -> Option<DOMString> {
self.label.borrow().clone()
}
/// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label
fn SetLabel(&self, value: Option<DOMString>) {
*self.label.borrow_mut() = value;
}
}

View file

@ -0,0 +1,114 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::GPUCommandEncoderBinding::{
self, GPUCommandBufferDescriptor, GPUCommandEncoderMethods, GPUComputePassDescriptor,
};
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::globalscope::GlobalScope;
use crate::dom::gpubuffer::GPUBuffer;
use crate::dom::gpucommandbuffer::GPUCommandBuffer;
use crate::dom::gpucomputepassencoder::GPUComputePassEncoder;
use dom_struct::dom_struct;
use ipc_channel::ipc;
use webgpu::{wgpu::command::RawPass, WebGPU, WebGPUCommandEncoder, WebGPURequest};
#[dom_struct]
pub struct GPUCommandEncoder {
reflector_: Reflector,
#[ignore_malloc_size_of = "defined in webgpu"]
channel: WebGPU,
label: DomRefCell<Option<DOMString>>,
encoder: WebGPUCommandEncoder,
}
impl GPUCommandEncoder {
pub fn new_inherited(channel: WebGPU, encoder: WebGPUCommandEncoder) -> GPUCommandEncoder {
GPUCommandEncoder {
channel,
reflector_: Reflector::new(),
label: DomRefCell::new(None),
encoder,
}
}
pub fn new(
global: &GlobalScope,
channel: WebGPU,
encoder: WebGPUCommandEncoder,
) -> DomRoot<GPUCommandEncoder> {
reflect_dom_object(
Box::new(GPUCommandEncoder::new_inherited(channel, encoder)),
global,
GPUCommandEncoderBinding::Wrap,
)
}
}
impl GPUCommandEncoderMethods for GPUCommandEncoder {
/// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label
fn GetLabel(&self) -> Option<DOMString> {
self.label.borrow().clone()
}
/// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label
fn SetLabel(&self, value: Option<DOMString>) {
*self.label.borrow_mut() = value;
}
/// https://gpuweb.github.io/gpuweb/#dom-gpucommandencoder-begincomputepass
fn BeginComputePass(
&self,
_descriptor: &GPUComputePassDescriptor,
) -> DomRoot<GPUComputePassEncoder> {
GPUComputePassEncoder::new(
&self.global(),
self.channel.clone(),
RawPass::new_compute(self.encoder.0),
)
}
/// https://gpuweb.github.io/gpuweb/#dom-gpucommandencoder-copybuffertobuffer
fn CopyBufferToBuffer(
&self,
source: &GPUBuffer,
source_offset: u64,
destination: &GPUBuffer,
destination_offset: u64,
size: u64,
) {
self.channel
.0
.send(WebGPURequest::CopyBufferToBuffer(
self.encoder.0,
source.id().0,
source_offset,
destination.id().0,
destination_offset,
size,
))
.expect("Failed to send CopyBufferToBuffer");
}
/// https://gpuweb.github.io/gpuweb/#dom-gpucommandencoder-finish
fn Finish(&self, _descriptor: &GPUCommandBufferDescriptor) -> DomRoot<GPUCommandBuffer> {
let (sender, receiver) = ipc::channel().unwrap();
self.channel
.0
.send(WebGPURequest::CommandEncoderFinish(
sender,
self.encoder.0,
// TODO(zakorgy): We should use `_descriptor` here after it's not empty
// and the underlying wgpu-core struct is serializable
))
.expect("Failed to send CopyBufferToBuffer");
let buffer = receiver.recv().unwrap();
GPUCommandBuffer::new(&self.global(), self.channel.clone(), buffer)
}
}

View file

@ -0,0 +1,59 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::GPUComputePassEncoderBinding::{
self, GPUComputePassEncoderMethods,
};
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::globalscope::GlobalScope;
use dom_struct::dom_struct;
use webgpu::{wgpu::command::RawPass, WebGPU};
#[dom_struct]
pub struct GPUComputePassEncoder {
reflector_: Reflector,
#[ignore_malloc_size_of = "defined in webgpu"]
channel: WebGPU,
label: DomRefCell<Option<DOMString>>,
#[ignore_malloc_size_of = "defined in wgpu-core"]
pass: RawPass,
}
impl GPUComputePassEncoder {
pub fn new_inherited(channel: WebGPU, pass: RawPass) -> GPUComputePassEncoder {
GPUComputePassEncoder {
channel,
reflector_: Reflector::new(),
label: DomRefCell::new(None),
pass,
}
}
pub fn new(
global: &GlobalScope,
channel: WebGPU,
pass: RawPass,
) -> DomRoot<GPUComputePassEncoder> {
reflect_dom_object(
Box::new(GPUComputePassEncoder::new_inherited(channel, pass)),
global,
GPUComputePassEncoderBinding::Wrap,
)
}
}
impl GPUComputePassEncoderMethods for GPUComputePassEncoder {
/// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label
fn GetLabel(&self) -> Option<DOMString> {
self.label.borrow().clone()
}
/// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label
fn SetLabel(&self, value: Option<DOMString>) {
*self.label.borrow_mut() = value;
}
}

View file

@ -12,7 +12,9 @@ use crate::dom::bindings::codegen::Bindings::GPUBindGroupLayoutBinding::{
};
use crate::dom::bindings::codegen::Bindings::GPUBufferBinding::GPUBufferDescriptor;
use crate::dom::bindings::codegen::Bindings::GPUComputePipelineBinding::GPUComputePipelineDescriptor;
use crate::dom::bindings::codegen::Bindings::GPUDeviceBinding::{self, GPUDeviceMethods};
use crate::dom::bindings::codegen::Bindings::GPUDeviceBinding::{
self, GPUCommandEncoderDescriptor, GPUDeviceMethods,
};
use crate::dom::bindings::codegen::Bindings::GPUPipelineLayoutBinding::GPUPipelineLayoutDescriptor;
use crate::dom::bindings::codegen::Bindings::GPUShaderModuleBinding::GPUShaderModuleDescriptor;
use crate::dom::bindings::codegen::UnionTypes::Uint32ArrayOrString::{String, Uint32Array};
@ -26,6 +28,7 @@ use crate::dom::gpuadapter::GPUAdapter;
use crate::dom::gpubindgroup::GPUBindGroup;
use crate::dom::gpubindgrouplayout::GPUBindGroupLayout;
use crate::dom::gpubuffer::{GPUBuffer, GPUBufferState};
use crate::dom::gpucommandencoder::GPUCommandEncoder;
use crate::dom::gpucomputepipeline::GPUComputePipeline;
use crate::dom::gpupipelinelayout::GPUPipelineLayout;
use crate::dom::gpushadermodule::GPUShaderModule;
@ -589,4 +592,21 @@ impl GPUDeviceMethods for GPUDevice {
let compute_pipeline = receiver.recv().unwrap();
GPUComputePipeline::new(&self.global(), compute_pipeline)
}
/// https://gpuweb.github.io/gpuweb/#dom-gpudevice-createcommandencoder
fn CreateCommandEncoder(
&self,
_descriptor: &GPUCommandEncoderDescriptor,
) -> DomRoot<GPUCommandEncoder> {
let (sender, receiver) = ipc::channel().unwrap();
let id = self
.global()
.wgpu_create_command_encoder_id(self.device.0.backend());
self.channel
.0
.send(WebGPURequest::CreateCommandEncoder(sender, self.device, id))
.expect("Failed to create WebGPU command encoder");
let encoder = receiver.recv().unwrap();
GPUCommandEncoder::new(&self.global(), self.channel.clone(), encoder)
}
}

View file

@ -6,8 +6,8 @@ use smallvec::SmallVec;
use webgpu::wgpu::{
hub::IdentityManager,
id::{
AdapterId, BindGroupId, BindGroupLayoutId, BufferId, ComputePipelineId, DeviceId,
PipelineLayoutId, ShaderModuleId,
AdapterId, BindGroupId, BindGroupLayoutId, BufferId, CommandEncoderId, ComputePipelineId,
DeviceId, PipelineLayoutId, ShaderModuleId,
},
Backend,
};
@ -22,6 +22,7 @@ pub struct IdentityHub {
compute_pipelines: IdentityManager,
pipeline_layouts: IdentityManager,
shader_modules: IdentityManager,
command_encoders: IdentityManager,
backend: Backend,
}
@ -36,6 +37,7 @@ impl IdentityHub {
compute_pipelines: IdentityManager::default(),
pipeline_layouts: IdentityManager::default(),
shader_modules: IdentityManager::default(),
command_encoders: IdentityManager::default(),
backend,
}
}
@ -71,6 +73,10 @@ impl IdentityHub {
fn create_shader_module_id(&mut self) -> ShaderModuleId {
self.shader_modules.alloc(self.backend)
}
pub fn create_command_encoder_id(&mut self) -> CommandEncoderId {
self.command_encoders.alloc(self.backend)
}
}
#[derive(Debug)]
@ -166,4 +172,8 @@ impl Identities {
pub fn create_shader_module_id(&mut self, backend: Backend) -> ShaderModuleId {
self.select(backend).create_shader_module_id()
}
pub fn create_command_encoder_id(&mut self, backend: Backend) -> CommandEncoderId {
self.select(backend).create_command_encoder_id()
}
}

View file

@ -322,6 +322,9 @@ pub mod gpubindgroup;
pub mod gpubindgrouplayout;
pub mod gpubuffer;
pub mod gpubufferusage;
pub mod gpucommandbuffer;
pub mod gpucommandencoder;
pub mod gpucomputepassencoder;
pub mod gpucomputepipeline;
pub mod gpudevice;
pub mod gpupipelinelayout;

View file

@ -0,0 +1,9 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
// https://gpuweb.github.io/gpuweb/#gpucommandbuffer
[Exposed=(Window, DedicatedWorker), Serializable, Pref="dom.webgpu.enabled"]
interface GPUCommandBuffer {
};
GPUCommandBuffer includes GPUObjectBase;

View file

@ -0,0 +1,45 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
// https://gpuweb.github.io/gpuweb/#gpucommandencoder
[Exposed=(Window, DedicatedWorker), Serializable, Pref="dom.webgpu.enabled"]
interface GPUCommandEncoder {
// GPURenderPassEncoder beginRenderPass(GPURenderPassDescriptor descriptor);
GPUComputePassEncoder beginComputePass(optional GPUComputePassDescriptor descriptor = {});
void copyBufferToBuffer(
GPUBuffer source,
GPUBufferSize sourceOffset,
GPUBuffer destination,
GPUBufferSize destinationOffset,
GPUBufferSize size);
// void copyBufferToTexture(
// GPUBufferCopyView source,
// GPUTextureCopyView destination,
// GPUExtent3D copySize);
// void copyTextureToBuffer(
// GPUTextureCopyView source,
// GPUBufferCopyView destination,
// GPUExtent3D copySize);
// void copyTextureToTexture(
// GPUTextureCopyView source,
// GPUTextureCopyView destination,
// GPUExtent3D copySize);
// void pushDebugGroup(DOMString groupLabel);
// void popDebugGroup();
// void insertDebugMarker(DOMString markerLabel);
GPUCommandBuffer finish(optional GPUCommandBufferDescriptor descriptor = {});
};
GPUCommandEncoder includes GPUObjectBase;
dictionary GPUComputePassDescriptor : GPUObjectDescriptorBase {
};
dictionary GPUCommandBufferDescriptor : GPUObjectDescriptorBase {
};

View file

@ -0,0 +1,15 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
// https://gpuweb.github.io/gpuweb/#gpucomputepassencoder
[Exposed=(Window, DedicatedWorker), Serializable, Pref="dom.webgpu.enabled"]
interface GPUComputePassEncoder {
// void setPipeline(GPUComputePipeline pipeline);
// void dispatch(unsigned long x, optional unsigned long y = 1, optional unsigned long z = 1);
// void dispatchIndirect(GPUBuffer indirectBuffer, GPUBufferSize indirectOffset);
// void endPass();
};
GPUComputePassEncoder includes GPUObjectBase;
GPUComputePassEncoder includes GPUProgrammablePassEncoder;

View file

@ -11,9 +11,9 @@ interface GPUDevice : EventTarget {
GPUBuffer createBuffer(GPUBufferDescriptor descriptor);
GPUMappedBuffer createBufferMapped(GPUBufferDescriptor descriptor);
//Promise<GPUMappedBuffer> createBufferMappedAsync(GPUBufferDescriptor descriptor);
//GPUTexture createTexture(GPUTextureDescriptor descriptor);
//GPUSampler createSampler(optional GPUSamplerDescriptor descriptor = {});
// Promise<GPUMappedBuffer> createBufferMappedAsync(GPUBufferDescriptor descriptor);
// GPUTexture createTexture(GPUTextureDescriptor descriptor);
// GPUSampler createSampler(optional GPUSamplerDescriptor descriptor = {});
GPUBindGroupLayout createBindGroupLayout(GPUBindGroupLayoutDescriptor descriptor);
GPUPipelineLayout createPipelineLayout(GPUPipelineLayoutDescriptor descriptor);
@ -21,11 +21,14 @@ interface GPUDevice : EventTarget {
GPUShaderModule createShaderModule(GPUShaderModuleDescriptor descriptor);
GPUComputePipeline createComputePipeline(GPUComputePipelineDescriptor descriptor);
/*GPURenderPipeline createRenderPipeline(GPURenderPipelineDescriptor descriptor);
// GPURenderPipeline createRenderPipeline(GPURenderPipelineDescriptor descriptor);
GPUCommandEncoder createCommandEncoder(optional GPUCommandEncoderDescriptor descriptor = {});
GPURenderBundleEncoder createRenderBundleEncoder(GPURenderBundleEncoderDescriptor descriptor);
// GPURenderBundleEncoder createRenderBundleEncoder(GPURenderBundleEncoderDescriptor descriptor);
GPUQueue getQueue();*/
// GPUQueue getQueue();
};
GPUDevice includes GPUObjectBase;
dictionary GPUCommandEncoderDescriptor : GPUObjectDescriptorBase {
};

View file

@ -0,0 +1,19 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
// https://gpuweb.github.io/gpuweb/#gpuprogrammablepassencoder
[Exposed=(Window, DedicatedWorker)]
interface mixin GPUProgrammablePassEncoder {
// void setBindGroup(unsigned long index, GPUBindGroup bindGroup,
// optional sequence<unsigned long> dynamicOffsets = []);
// void setBindGroup(unsigned long index, GPUBindGroup bindGroup,
// Uint32Array dynamicOffsetsData,
// unsigned long long dynamicOffsetsDataStart,
// unsigned long long dynamicOffsetsDataLength);
// void pushDebugGroup(DOMString groupLabel);
// void popDebugGroup();
// void insertDebugMarker(DOMString markerLabel);
};

View file

@ -81,6 +81,27 @@ pub enum WebGPURequest {
),
UnmapBuffer(WebGPUBuffer),
DestroyBuffer(WebGPUBuffer),
CreateCommandEncoder(
IpcSender<WebGPUCommandEncoder>,
WebGPUDevice,
// TODO(zakorgy): Serialize CommandEncoderDescriptor in wgpu-core
// wgpu::command::CommandEncoderDescriptor,
wgpu::id::CommandEncoderId,
),
CopyBufferToBuffer(
wgpu::id::CommandEncoderId,
wgpu::id::BufferId,
wgpu::BufferAddress,
wgpu::id::BufferId,
wgpu::BufferAddress,
wgpu::BufferAddress,
),
CommandEncoderFinish(
IpcSender<WebGPUCommandBuffer>,
wgpu::id::CommandEncoderId,
// TODO(zakorgy): Serialize CommandBufferDescriptor in wgpu-core
// wgpu::command::CommandBufferDescriptor,
),
}
#[derive(Clone, Debug, Deserialize, Serialize)]
@ -345,6 +366,47 @@ impl WGPU {
)
}
},
WebGPURequest::CreateCommandEncoder(sender, device, id) => {
let global = &self.global;
let id = gfx_select!(id => global.device_create_command_encoder(device.0, &Default::default(), id));
if let Err(e) = sender.send(WebGPUCommandEncoder(id)) {
warn!(
"Failed to send response to WebGPURequest::CreateCommandEncoder ({})",
e
)
}
},
WebGPURequest::CopyBufferToBuffer(
command_encoder_id,
source,
source_offset,
destination,
destination_offset,
size,
) => {
let global = &self.global;
let _ = gfx_select!(command_encoder_id => global.command_encoder_copy_buffer_to_buffer(
command_encoder_id,
source,
source_offset,
destination,
destination_offset,
size
));
},
WebGPURequest::CommandEncoderFinish(sender, command_encoder_id) => {
let global = &self.global;
let command_buffer_id = gfx_select!(command_encoder_id => global.command_encoder_finish(
command_encoder_id,
&wgpu::command::CommandBufferDescriptor::default()
));
if let Err(e) = sender.send(WebGPUCommandBuffer(command_buffer_id)) {
warn!(
"Failed to send response to WebGPURequest::CommandEncoderFinish ({})",
e
)
}
},
WebGPURequest::Exit(sender) => {
self.deinit();
if let Err(e) = sender.send(()) {
@ -380,3 +442,5 @@ webgpu_resource!(WebGPUBindGroupLayout, wgpu::id::BindGroupLayoutId);
webgpu_resource!(WebGPUComputePipeline, wgpu::id::ComputePipelineId);
webgpu_resource!(WebGPUPipelineLayout, wgpu::id::PipelineLayoutId);
webgpu_resource!(WebGPUShaderModule, wgpu::id::ShaderModuleId);
webgpu_resource!(WebGPUCommandEncoder, wgpu::id::CommandEncoderId);
webgpu_resource!(WebGPUCommandBuffer, wgpu::id::CommandBufferId);