mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Initial implementation of GPUComputePipeline
Added WebIDL bindings for `GPUComputePipeline`. Implemented the `createComputePipeline` function of `GPUDevice`.
This commit is contained in:
parent
a8621c4ed9
commit
9031369c19
11 changed files with 180 additions and 8 deletions
|
@ -152,8 +152,8 @@ use tendril::{StrTendril, TendrilSink};
|
||||||
use time::{Duration, Timespec, Tm};
|
use time::{Duration, Timespec, Tm};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use webgpu::{
|
use webgpu::{
|
||||||
WebGPU, WebGPUAdapter, WebGPUBindGroup, WebGPUBindGroupLayout, WebGPUBuffer, WebGPUDevice,
|
WebGPU, WebGPUAdapter, WebGPUBindGroup, WebGPUBindGroupLayout, WebGPUBuffer,
|
||||||
WebGPUPipelineLayout, WebGPUShaderModule,
|
WebGPUComputePipeline, WebGPUDevice, WebGPUPipelineLayout, WebGPUShaderModule,
|
||||||
};
|
};
|
||||||
use webrender_api::{DocumentId, ImageKey};
|
use webrender_api::{DocumentId, ImageKey};
|
||||||
use webvr_traits::{WebVRGamepadData, WebVRGamepadHand, WebVRGamepadState};
|
use webvr_traits::{WebVRGamepadData, WebVRGamepadHand, WebVRGamepadState};
|
||||||
|
@ -535,6 +535,7 @@ unsafe_no_jsmanaged_fields!(WebGPUDevice);
|
||||||
unsafe_no_jsmanaged_fields!(WebGPUBuffer);
|
unsafe_no_jsmanaged_fields!(WebGPUBuffer);
|
||||||
unsafe_no_jsmanaged_fields!(WebGPUBindGroup);
|
unsafe_no_jsmanaged_fields!(WebGPUBindGroup);
|
||||||
unsafe_no_jsmanaged_fields!(WebGPUBindGroupLayout);
|
unsafe_no_jsmanaged_fields!(WebGPUBindGroupLayout);
|
||||||
|
unsafe_no_jsmanaged_fields!(WebGPUComputePipeline);
|
||||||
unsafe_no_jsmanaged_fields!(WebGPUPipelineLayout);
|
unsafe_no_jsmanaged_fields!(WebGPUPipelineLayout);
|
||||||
unsafe_no_jsmanaged_fields!(WebGPUShaderModule);
|
unsafe_no_jsmanaged_fields!(WebGPUShaderModule);
|
||||||
unsafe_no_jsmanaged_fields!(GPUBufferState);
|
unsafe_no_jsmanaged_fields!(GPUBufferState);
|
||||||
|
|
|
@ -99,8 +99,8 @@ use time::{get_time, Timespec};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use webgpu::wgpu::{
|
use webgpu::wgpu::{
|
||||||
id::{
|
id::{
|
||||||
AdapterId, BindGroupId, BindGroupLayoutId, BufferId, DeviceId, PipelineLayoutId,
|
AdapterId, BindGroupId, BindGroupLayoutId, BufferId, ComputePipelineId, DeviceId,
|
||||||
ShaderModuleId,
|
PipelineLayoutId, ShaderModuleId,
|
||||||
},
|
},
|
||||||
Backend,
|
Backend,
|
||||||
};
|
};
|
||||||
|
@ -2137,6 +2137,11 @@ impl GlobalScope {
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.create_shader_module_id(backend)
|
.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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn timestamp_in_ms(time: Timespec) -> u64 {
|
fn timestamp_in_ms(time: Timespec) -> u64 {
|
||||||
|
|
55
components/script/dom/gpucomputepipeline.rs
Normal file
55
components/script/dom/gpucomputepipeline.rs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/* 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::GPUComputePipelineBinding::{
|
||||||
|
GPUComputePipelineBinding, GPUComputePipelineMethods,
|
||||||
|
};
|
||||||
|
use crate::dom::bindings::reflector::reflect_dom_object;
|
||||||
|
use crate::dom::bindings::reflector::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::WebGPUComputePipeline;
|
||||||
|
|
||||||
|
#[dom_struct]
|
||||||
|
pub struct GPUComputePipeline {
|
||||||
|
reflector_: Reflector,
|
||||||
|
label: DomRefCell<Option<DOMString>>,
|
||||||
|
compute_pipeline: WebGPUComputePipeline,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GPUComputePipeline {
|
||||||
|
fn new_inherited(compute_pipeline: WebGPUComputePipeline) -> GPUComputePipeline {
|
||||||
|
Self {
|
||||||
|
reflector_: Reflector::new(),
|
||||||
|
label: DomRefCell::new(None),
|
||||||
|
compute_pipeline,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(
|
||||||
|
global: &GlobalScope,
|
||||||
|
compute_pipeline: WebGPUComputePipeline,
|
||||||
|
) -> DomRoot<GPUComputePipeline> {
|
||||||
|
reflect_dom_object(
|
||||||
|
Box::new(GPUComputePipeline::new_inherited(compute_pipeline)),
|
||||||
|
global,
|
||||||
|
GPUComputePipelineBinding::Wrap,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GPUComputePipelineMethods for GPUComputePipeline {
|
||||||
|
/// 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ use crate::dom::bindings::codegen::Bindings::GPUBindGroupLayoutBinding::{
|
||||||
GPUBindGroupLayoutBindings, GPUBindGroupLayoutDescriptor, GPUBindingType,
|
GPUBindGroupLayoutBindings, GPUBindGroupLayoutDescriptor, GPUBindingType,
|
||||||
};
|
};
|
||||||
use crate::dom::bindings::codegen::Bindings::GPUBufferBinding::GPUBufferDescriptor;
|
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, GPUDeviceMethods};
|
||||||
use crate::dom::bindings::codegen::Bindings::GPUPipelineLayoutBinding::GPUPipelineLayoutDescriptor;
|
use crate::dom::bindings::codegen::Bindings::GPUPipelineLayoutBinding::GPUPipelineLayoutDescriptor;
|
||||||
use crate::dom::bindings::codegen::Bindings::GPUShaderModuleBinding::GPUShaderModuleDescriptor;
|
use crate::dom::bindings::codegen::Bindings::GPUShaderModuleBinding::GPUShaderModuleDescriptor;
|
||||||
|
@ -25,6 +26,7 @@ use crate::dom::gpuadapter::GPUAdapter;
|
||||||
use crate::dom::gpubindgroup::GPUBindGroup;
|
use crate::dom::gpubindgroup::GPUBindGroup;
|
||||||
use crate::dom::gpubindgrouplayout::GPUBindGroupLayout;
|
use crate::dom::gpubindgrouplayout::GPUBindGroupLayout;
|
||||||
use crate::dom::gpubuffer::{GPUBuffer, GPUBufferState};
|
use crate::dom::gpubuffer::{GPUBuffer, GPUBufferState};
|
||||||
|
use crate::dom::gpucomputepipeline::GPUComputePipeline;
|
||||||
use crate::dom::gpupipelinelayout::GPUPipelineLayout;
|
use crate::dom::gpupipelinelayout::GPUPipelineLayout;
|
||||||
use crate::dom::gpushadermodule::GPUShaderModule;
|
use crate::dom::gpushadermodule::GPUShaderModule;
|
||||||
use crate::script_runtime::JSContext as SafeJSContext;
|
use crate::script_runtime::JSContext as SafeJSContext;
|
||||||
|
@ -559,4 +561,32 @@ impl GPUDeviceMethods for GPUDevice {
|
||||||
let shader_module = receiver.recv().unwrap();
|
let shader_module = receiver.recv().unwrap();
|
||||||
GPUShaderModule::new(&self.global(), shader_module)
|
GPUShaderModule::new(&self.global(), shader_module)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// https://gpuweb.github.io/gpuweb/#dom-gpudevice-createcomputepipeline
|
||||||
|
fn CreateComputePipeline(
|
||||||
|
&self,
|
||||||
|
descriptor: &GPUComputePipelineDescriptor,
|
||||||
|
) -> DomRoot<GPUComputePipeline> {
|
||||||
|
let pipeline = descriptor.parent.layout.id();
|
||||||
|
let program = descriptor.computeStage.module.id();
|
||||||
|
let entry_point = descriptor.computeStage.entryPoint.to_string();
|
||||||
|
let id = self
|
||||||
|
.global()
|
||||||
|
.wgpu_create_compute_pipeline_id(self.device.0.backend());
|
||||||
|
let (sender, receiver) = ipc::channel().unwrap();
|
||||||
|
self.channel
|
||||||
|
.0
|
||||||
|
.send(WebGPURequest::CreateComputePipeline(
|
||||||
|
sender,
|
||||||
|
self.device,
|
||||||
|
id,
|
||||||
|
pipeline.0,
|
||||||
|
program.0,
|
||||||
|
entry_point,
|
||||||
|
))
|
||||||
|
.expect("Failed to create WebGPU ComputePipeline");
|
||||||
|
|
||||||
|
let compute_pipeline = receiver.recv().unwrap();
|
||||||
|
GPUComputePipeline::new(&self.global(), compute_pipeline)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,12 @@ impl GPUPipelineLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl GPUPipelineLayout {
|
||||||
|
pub fn id(&self) -> WebGPUPipelineLayout {
|
||||||
|
self.pipeline_layout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl GPUPipelineLayoutMethods for GPUPipelineLayout {
|
impl GPUPipelineLayoutMethods for GPUPipelineLayout {
|
||||||
/// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label
|
/// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label
|
||||||
fn GetLabel(&self) -> Option<DOMString> {
|
fn GetLabel(&self) -> Option<DOMString> {
|
||||||
|
|
|
@ -41,6 +41,12 @@ impl GPUShaderModule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl GPUShaderModule {
|
||||||
|
pub fn id(&self) -> WebGPUShaderModule {
|
||||||
|
self.shader_module
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl GPUShaderModuleMethods for GPUShaderModule {
|
impl GPUShaderModuleMethods for GPUShaderModule {
|
||||||
/// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label
|
/// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label
|
||||||
fn GetLabel(&self) -> Option<DOMString> {
|
fn GetLabel(&self) -> Option<DOMString> {
|
||||||
|
|
|
@ -6,8 +6,8 @@ use smallvec::SmallVec;
|
||||||
use webgpu::wgpu::{
|
use webgpu::wgpu::{
|
||||||
hub::IdentityManager,
|
hub::IdentityManager,
|
||||||
id::{
|
id::{
|
||||||
AdapterId, BindGroupId, BindGroupLayoutId, BufferId, DeviceId, PipelineLayoutId,
|
AdapterId, BindGroupId, BindGroupLayoutId, BufferId, ComputePipelineId, DeviceId,
|
||||||
ShaderModuleId,
|
PipelineLayoutId, ShaderModuleId,
|
||||||
},
|
},
|
||||||
Backend,
|
Backend,
|
||||||
};
|
};
|
||||||
|
@ -19,6 +19,7 @@ pub struct IdentityHub {
|
||||||
buffers: IdentityManager,
|
buffers: IdentityManager,
|
||||||
bind_groups: IdentityManager,
|
bind_groups: IdentityManager,
|
||||||
bind_group_layouts: IdentityManager,
|
bind_group_layouts: IdentityManager,
|
||||||
|
compute_pipelines: IdentityManager,
|
||||||
pipeline_layouts: IdentityManager,
|
pipeline_layouts: IdentityManager,
|
||||||
shader_modules: IdentityManager,
|
shader_modules: IdentityManager,
|
||||||
backend: Backend,
|
backend: Backend,
|
||||||
|
@ -32,6 +33,7 @@ impl IdentityHub {
|
||||||
buffers: IdentityManager::default(),
|
buffers: IdentityManager::default(),
|
||||||
bind_groups: IdentityManager::default(),
|
bind_groups: IdentityManager::default(),
|
||||||
bind_group_layouts: IdentityManager::default(),
|
bind_group_layouts: IdentityManager::default(),
|
||||||
|
compute_pipelines: IdentityManager::default(),
|
||||||
pipeline_layouts: IdentityManager::default(),
|
pipeline_layouts: IdentityManager::default(),
|
||||||
shader_modules: IdentityManager::default(),
|
shader_modules: IdentityManager::default(),
|
||||||
backend,
|
backend,
|
||||||
|
@ -58,6 +60,10 @@ impl IdentityHub {
|
||||||
self.bind_group_layouts.alloc(self.backend)
|
self.bind_group_layouts.alloc(self.backend)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_compute_pipeline_id(&mut self) -> ComputePipelineId {
|
||||||
|
self.compute_pipelines.alloc(self.backend)
|
||||||
|
}
|
||||||
|
|
||||||
fn create_pipeline_layout_id(&mut self) -> PipelineLayoutId {
|
fn create_pipeline_layout_id(&mut self) -> PipelineLayoutId {
|
||||||
self.pipeline_layouts.alloc(self.backend)
|
self.pipeline_layouts.alloc(self.backend)
|
||||||
}
|
}
|
||||||
|
@ -149,6 +155,10 @@ impl Identities {
|
||||||
self.select(backend).create_bind_group_layout_id()
|
self.select(backend).create_bind_group_layout_id()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_compute_pipeline_id(&mut self, backend: Backend) -> ComputePipelineId {
|
||||||
|
self.select(backend).create_compute_pipeline_id()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn create_pipeline_layout_id(&mut self, backend: Backend) -> PipelineLayoutId {
|
pub fn create_pipeline_layout_id(&mut self, backend: Backend) -> PipelineLayoutId {
|
||||||
self.select(backend).create_pipeline_layout_id()
|
self.select(backend).create_pipeline_layout_id()
|
||||||
}
|
}
|
||||||
|
|
|
@ -322,6 +322,7 @@ pub mod gpubindgroup;
|
||||||
pub mod gpubindgrouplayout;
|
pub mod gpubindgrouplayout;
|
||||||
pub mod gpubuffer;
|
pub mod gpubuffer;
|
||||||
pub mod gpubufferusage;
|
pub mod gpubufferusage;
|
||||||
|
pub mod gpucomputepipeline;
|
||||||
pub mod gpudevice;
|
pub mod gpudevice;
|
||||||
pub mod gpupipelinelayout;
|
pub mod gpupipelinelayout;
|
||||||
pub mod gpushadermodule;
|
pub mod gpushadermodule;
|
||||||
|
|
22
components/script/dom/webidls/GPUComputePipeline.webidl
Normal file
22
components/script/dom/webidls/GPUComputePipeline.webidl
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/* 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/#gpucomputepipeline
|
||||||
|
[Exposed=(Window, DedicatedWorker), Serializable, Pref="dom.webgpu.enabled"]
|
||||||
|
interface GPUComputePipeline {
|
||||||
|
};
|
||||||
|
GPUComputePipeline includes GPUObjectBase;
|
||||||
|
|
||||||
|
dictionary GPUPipelineDescriptorBase : GPUObjectDescriptorBase {
|
||||||
|
required GPUPipelineLayout layout;
|
||||||
|
};
|
||||||
|
|
||||||
|
dictionary GPUProgrammableStageDescriptor {
|
||||||
|
required GPUShaderModule module;
|
||||||
|
required DOMString entryPoint;
|
||||||
|
};
|
||||||
|
|
||||||
|
dictionary GPUComputePipelineDescriptor : GPUPipelineDescriptorBase {
|
||||||
|
required GPUProgrammableStageDescriptor computeStage;
|
||||||
|
};
|
|
@ -20,8 +20,8 @@ interface GPUDevice : EventTarget {
|
||||||
GPUBindGroup createBindGroup(GPUBindGroupDescriptor descriptor);
|
GPUBindGroup createBindGroup(GPUBindGroupDescriptor descriptor);
|
||||||
|
|
||||||
GPUShaderModule createShaderModule(GPUShaderModuleDescriptor descriptor);
|
GPUShaderModule createShaderModule(GPUShaderModuleDescriptor descriptor);
|
||||||
/*GPUComputePipeline createComputePipeline(GPUComputePipelineDescriptor descriptor);
|
GPUComputePipeline createComputePipeline(GPUComputePipelineDescriptor descriptor);
|
||||||
GPURenderPipeline createRenderPipeline(GPURenderPipelineDescriptor descriptor);
|
/*GPURenderPipeline createRenderPipeline(GPURenderPipelineDescriptor descriptor);
|
||||||
|
|
||||||
GPUCommandEncoder createCommandEncoder(optional GPUCommandEncoderDescriptor descriptor = {});
|
GPUCommandEncoder createCommandEncoder(optional GPUCommandEncoderDescriptor descriptor = {});
|
||||||
GPURenderBundleEncoder createRenderBundleEncoder(GPURenderBundleEncoderDescriptor descriptor);
|
GPURenderBundleEncoder createRenderBundleEncoder(GPURenderBundleEncoderDescriptor descriptor);
|
||||||
|
|
|
@ -59,6 +59,14 @@ pub enum WebGPURequest {
|
||||||
wgpu::id::BindGroupLayoutId,
|
wgpu::id::BindGroupLayoutId,
|
||||||
Vec<wgpu::binding_model::BindGroupLayoutBinding>,
|
Vec<wgpu::binding_model::BindGroupLayoutBinding>,
|
||||||
),
|
),
|
||||||
|
CreateComputePipeline(
|
||||||
|
IpcSender<WebGPUComputePipeline>,
|
||||||
|
WebGPUDevice,
|
||||||
|
wgpu::id::ComputePipelineId,
|
||||||
|
wgpu::id::PipelineLayoutId,
|
||||||
|
wgpu::id::ShaderModuleId,
|
||||||
|
String,
|
||||||
|
),
|
||||||
CreatePipelineLayout(
|
CreatePipelineLayout(
|
||||||
IpcSender<WebGPUPipelineLayout>,
|
IpcSender<WebGPUPipelineLayout>,
|
||||||
WebGPUDevice,
|
WebGPUDevice,
|
||||||
|
@ -310,6 +318,33 @@ impl WGPU {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
WebGPURequest::CreateComputePipeline(
|
||||||
|
sender,
|
||||||
|
device,
|
||||||
|
id,
|
||||||
|
layout,
|
||||||
|
program,
|
||||||
|
entry,
|
||||||
|
) => {
|
||||||
|
let global = &self.global;
|
||||||
|
let entry_point = std::ffi::CString::new(entry).unwrap();
|
||||||
|
let descriptor = wgpu_core::pipeline::ComputePipelineDescriptor {
|
||||||
|
layout,
|
||||||
|
compute_stage: wgpu_core::pipeline::ProgrammableStageDescriptor {
|
||||||
|
module: program,
|
||||||
|
entry_point: entry_point.as_ptr(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let cp_id = gfx_select!(id => global.device_create_compute_pipeline(device.0, &descriptor, id));
|
||||||
|
let compute_pipeline = WebGPUComputePipeline(cp_id);
|
||||||
|
|
||||||
|
if let Err(e) = sender.send(compute_pipeline) {
|
||||||
|
warn!(
|
||||||
|
"Failed to send response to WebGPURequest::CreateComputePipeline ({})",
|
||||||
|
e
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
WebGPURequest::Exit(sender) => {
|
WebGPURequest::Exit(sender) => {
|
||||||
self.deinit();
|
self.deinit();
|
||||||
if let Err(e) = sender.send(()) {
|
if let Err(e) = sender.send(()) {
|
||||||
|
@ -342,5 +377,6 @@ webgpu_resource!(WebGPUDevice, wgpu::id::DeviceId);
|
||||||
webgpu_resource!(WebGPUBuffer, wgpu::id::BufferId);
|
webgpu_resource!(WebGPUBuffer, wgpu::id::BufferId);
|
||||||
webgpu_resource!(WebGPUBindGroup, wgpu::id::BindGroupId);
|
webgpu_resource!(WebGPUBindGroup, wgpu::id::BindGroupId);
|
||||||
webgpu_resource!(WebGPUBindGroupLayout, wgpu::id::BindGroupLayoutId);
|
webgpu_resource!(WebGPUBindGroupLayout, wgpu::id::BindGroupLayoutId);
|
||||||
|
webgpu_resource!(WebGPUComputePipeline, wgpu::id::ComputePipelineId);
|
||||||
webgpu_resource!(WebGPUPipelineLayout, wgpu::id::PipelineLayoutId);
|
webgpu_resource!(WebGPUPipelineLayout, wgpu::id::PipelineLayoutId);
|
||||||
webgpu_resource!(WebGPUShaderModule, wgpu::id::ShaderModuleId);
|
webgpu_resource!(WebGPUShaderModule, wgpu::id::ShaderModuleId);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue