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