Skip to main content

script_webgpu/
gpupipelinelayout.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 std::borrow::Cow;
6use std::marker::PhantomData;
7
8use dom_struct::dom_struct;
9use js::context::{JSContext, NoGC};
10use log::warn;
11use malloc_size_of_derive::MallocSizeOf;
12use script_bindings::DomTypes;
13use script_bindings::cell::DomRefCell;
14use script_bindings::codegen::GenericBindings::WebGPUBinding::{
15    GPUPipelineLayoutDescriptor, GPUPipelineLayoutMethods, GPUPipelineLayoutWrap,
16};
17use script_bindings::interfaces::PromiseHelpers;
18use script_bindings::reflector::{DomGlobalGeneric, Reflector, reflect_dom_object_with_wrap};
19use webgpu_traits::{
20    PipelineLayoutDescriptor, WebGPU, WebGPUBindGroupLayout, WebGPUPipelineLayout, WebGPURequest,
21};
22
23use crate::JSTraceable;
24use crate::dom::bindings::root::DomRoot;
25use crate::dom::bindings::str::USVString;
26use crate::gpuconvert::WebGPUConvert;
27use crate::traits::{Equivalence, WebGPUGlobalTrait, WebGPUPromise};
28
29#[derive(MallocSizeOf)]
30struct DroppableGPUPipelineLayout {
31    channel: WebGPU,
32    pipeline_layout: WebGPUPipelineLayout,
33}
34
35impl Drop for DroppableGPUPipelineLayout {
36    fn drop(&mut self) {
37        if let Err(e) = self
38            .channel
39            .0
40            .send(WebGPURequest::DropPipelineLayout(self.pipeline_layout.0))
41        {
42            warn!(
43                "Failed to send DropPipelineLayout ({:?}) ({})",
44                self.pipeline_layout.0, e
45            );
46        }
47    }
48}
49
50#[dom_struct]
51pub struct GPUPipelineLayout<D: DomTypes> {
52    reflector_: Reflector,
53    label: DomRefCell<USVString>,
54    #[no_trace]
55    bind_group_layouts: Vec<WebGPUBindGroupLayout>,
56    #[no_trace]
57    droppable: DroppableGPUPipelineLayout,
58    #[no_trace = "PhantomData does not exist"]
59    phantom: PhantomData<D>,
60}
61
62impl<D> GPUPipelineLayout<D>
63where
64    D: Equivalence,
65{
66    fn new_inherited(
67        channel: WebGPU,
68        pipeline_layout: WebGPUPipelineLayout,
69        label: USVString,
70        bgls: Vec<WebGPUBindGroupLayout>,
71    ) -> Self {
72        Self {
73            reflector_: Reflector::new(),
74            label: DomRefCell::new(label),
75            bind_group_layouts: bgls,
76            droppable: DroppableGPUPipelineLayout {
77                channel,
78                pipeline_layout,
79            },
80            phantom: PhantomData,
81        }
82    }
83
84    pub(crate) fn new(
85        cx: &mut JSContext,
86        global: &D::GlobalScope,
87        channel: WebGPU,
88        pipeline_layout: WebGPUPipelineLayout,
89        label: USVString,
90        bgls: Vec<WebGPUBindGroupLayout>,
91    ) -> DomRoot<Self> {
92        reflect_dom_object_with_wrap::<D, _, _>(
93            cx,
94            Box::new(GPUPipelineLayout::new_inherited(
95                channel,
96                pipeline_layout,
97                label,
98                bgls,
99            )),
100            global,
101            GPUPipelineLayoutWrap::<D>,
102        )
103    }
104}
105
106impl<D> GPUPipelineLayout<D>
107where
108    D: Equivalence,
109    <D::Promise as PromiseHelpers<D>>::StackRoot: WebGPUPromise<D>,
110{
111    pub(crate) fn id(&self) -> WebGPUPipelineLayout {
112        self.droppable.pipeline_layout
113    }
114
115    #[expect(unused)]
116    fn bind_group_layouts(&self) -> Vec<WebGPUBindGroupLayout> {
117        self.bind_group_layouts.clone()
118    }
119
120    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createpipelinelayout>
121    pub(crate) fn create(
122        cx: &mut JSContext,
123        device: &D::GPUDevice,
124        descriptor: &GPUPipelineLayoutDescriptor<D>,
125    ) -> DomRoot<GPUPipelineLayout<D>> {
126        let bgls = descriptor
127            .bindGroupLayouts
128            .iter()
129            .map(|each| each.id())
130            .collect::<Vec<_>>();
131
132        let desc = PipelineLayoutDescriptor {
133            label: (&descriptor.parent).convert(),
134            // TODO(sagudev): this needs webidl sync
135            bind_group_layouts: Cow::Owned(bgls.iter().map(|l| Some(l.0)).collect::<Vec<_>>()),
136            immediate_size: 0,
137        };
138
139        let pipeline_layout_id = device
140            .global_from_reflector()
141            .global_wgpu_id_hub()
142            .create_pipeline_layout_id();
143        device
144            .channel()
145            .0
146            .send(WebGPURequest::CreatePipelineLayout {
147                device_id: device.id().0,
148                pipeline_layout_id,
149                descriptor: desc,
150            })
151            .expect("Failed to create WebGPU PipelineLayout");
152
153        let pipeline_layout = WebGPUPipelineLayout(pipeline_layout_id);
154        GPUPipelineLayout::new(
155            cx,
156            &*device.global_from_reflector(),
157            device.channel(),
158            pipeline_layout,
159            descriptor.parent.label.clone(),
160            bgls,
161        )
162    }
163}
164
165impl<D: DomTypes> GPUPipelineLayoutMethods<D> for GPUPipelineLayout<D> {
166    /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
167    fn Label(&self) -> USVString {
168        self.label.borrow().clone()
169    }
170
171    /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
172    fn SetLabel(&self, no_gc: &NoGC, value: USVString) {
173        *self.label.safe_borrow_mut(no_gc) = value;
174    }
175}