Skip to main content

script_webgpu/
gpushadermodule.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use dom_struct::dom_struct;
6use js::context::{JSContext, NoGC};
7use js::realm::CurrentRealm;
8use log::warn;
9use malloc_size_of_derive::MallocSizeOf;
10use script_bindings::DomTypes;
11use script_bindings::cell::DomRefCell;
12use script_bindings::codegen::GenericBindings::WebGPUBinding::{
13    GPUShaderModuleDescriptor, GPUShaderModuleMethods, GPUShaderModuleWrap,
14};
15use script_bindings::interfaces::{
16    HeapTracedPromiseHelpers, PromiseHelpers, StackRootPromiseHelpers,
17};
18use script_bindings::reflector::{DomGlobalGeneric, Reflector, reflect_dom_object_with_wrap};
19use script_bindings::routed_promise::RoutedPromiseListener;
20use webgpu_traits::{ShaderCompilationInfo, WebGPU, WebGPURequest, WebGPUShaderModule};
21
22use crate::JSTraceable;
23use crate::dom::bindings::root::DomRoot;
24use crate::dom::bindings::str::USVString;
25use crate::gpucompilationinfo::GPUCompilationInfo;
26use crate::traits::{Equivalence, WebGPUGlobalTrait, WebGPUPromise, WebGPUPromiseCallbackTrait};
27
28#[derive(JSTraceable, MallocSizeOf)]
29struct DroppableGPUShaderModule {
30    #[no_trace]
31    channel: WebGPU,
32    #[no_trace]
33    shader_module: WebGPUShaderModule,
34}
35
36impl Drop for DroppableGPUShaderModule {
37    fn drop(&mut self) {
38        if let Err(e) = self
39            .channel
40            .0
41            .send(WebGPURequest::DropShaderModule(self.shader_module.0))
42        {
43            warn!(
44                "Failed to send DropShaderModule ({:?}) ({})",
45                self.shader_module.0, e
46            );
47        }
48    }
49}
50
51#[dom_struct]
52pub struct GPUShaderModule<D: DomTypes> {
53    reflector_: Reflector,
54    label: DomRefCell<USVString>,
55    compilation_info_promise: <D::Promise as PromiseHelpers<D>>::HeapTraced,
56    droppable: DroppableGPUShaderModule,
57}
58
59impl<D: Equivalence> GPUShaderModule<D> {
60    fn new_inherited(
61        channel: WebGPU,
62        shader_module: WebGPUShaderModule,
63        label: USVString,
64        promise: &<D::Promise as PromiseHelpers<D>>::StackRoot,
65    ) -> Self {
66        Self {
67            reflector_: Reflector::new(),
68            label: DomRefCell::new(label),
69            compilation_info_promise: promise.to_traced(),
70            droppable: DroppableGPUShaderModule {
71                channel,
72                shader_module,
73            },
74        }
75    }
76
77    pub(crate) fn new(
78        cx: &mut JSContext,
79        global: &D::GlobalScope,
80        channel: WebGPU,
81        shader_module: WebGPUShaderModule,
82        label: USVString,
83        promise: &<D::Promise as PromiseHelpers<D>>::StackRoot,
84    ) -> DomRoot<Self> {
85        reflect_dom_object_with_wrap::<D, _, _>(
86            cx,
87            Box::new(GPUShaderModule::new_inherited(
88                channel,
89                shader_module,
90                label,
91                promise,
92            )),
93            global,
94            GPUShaderModuleWrap::<D>,
95        )
96    }
97}
98
99impl<D> GPUShaderModule<D>
100where
101    D: Equivalence,
102    <D::Promise as PromiseHelpers<D>>::StackRoot: WebGPUPromise<D>,
103{
104    pub(crate) fn id(&self) -> WebGPUShaderModule {
105        self.droppable.shader_module
106    }
107
108    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createshadermodule>
109    pub(crate) fn create(
110        cx: &mut CurrentRealm<'_>,
111        device: &D::GPUDevice,
112        descriptor: &GPUShaderModuleDescriptor,
113    ) -> DomRoot<GPUShaderModule<D>> {
114        let program_id = device
115            .global_from_reflector()
116            .global_wgpu_id_hub()
117            .create_shader_module_id();
118        let promise = D::Promise::new_in_realm(cx);
119        let shader_module = GPUShaderModule::new(
120            cx,
121            &*device.global_from_reflector(),
122            device.channel(),
123            WebGPUShaderModule(program_id),
124            descriptor.parent.label.clone(),
125            &promise,
126        );
127        let callback = promise.callback_promise_dom_manipulation_task_source(&*shader_module);
128        device
129            .channel()
130            .0
131            .send(WebGPURequest::CreateShaderModule {
132                device_id: device.id().0,
133                program_id,
134                program: descriptor.code.0.clone(),
135                label: None,
136                callback,
137            })
138            .expect("Failed to create WebGPU ShaderModule");
139        shader_module
140    }
141}
142
143impl<D: DomTypes> GPUShaderModuleMethods<D> for GPUShaderModule<D> {
144    /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
145    fn Label(&self) -> USVString {
146        self.label.borrow().clone()
147    }
148
149    /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
150    fn SetLabel(&self, no_gc: &NoGC, value: USVString) {
151        *self.label.safe_borrow_mut(no_gc) = value;
152    }
153
154    /// <https://gpuweb.github.io/gpuweb/#dom-gpushadermodule-getcompilationinfo>
155    fn GetCompilationInfo(&self, cx: &JSContext) -> <D::Promise as PromiseHelpers<D>>::StackRoot {
156        self.compilation_info_promise.root(cx)
157    }
158}
159
160impl<D: Equivalence> RoutedPromiseListener<D, Option<ShaderCompilationInfo>> for GPUShaderModule<D>
161where
162    Self: DomGlobalGeneric<D>,
163{
164    fn handle_response(
165        &self,
166        cx: &mut js::context::JSContext,
167        response: Option<ShaderCompilationInfo>,
168        promise: &<D::Promise as PromiseHelpers<D>>::StackRoot,
169    ) {
170        let info = GPUCompilationInfo::<D>::from(cx, &self.global_from_reflector(), response);
171        promise.resolve_native(cx, &info);
172    }
173}