Skip to main content

script_webgpu/
gpuexternaltexture.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::cell::Cell;
6use std::marker::PhantomData;
7use std::rc::Rc;
8
9use dom_struct::dom_struct;
10use euclid::default::Size2D;
11use js::context::JSContext;
12use log::warn;
13use malloc_size_of_derive::MallocSizeOf;
14use pixels::Snapshot;
15use script_bindings::cell::DomRefCell;
16use script_bindings::codegen::GenericBindings::WebGPUBinding::{
17    GPUDeviceMethods, GPUExternalTextureDescriptor, GPUExternalTextureMethods,
18    GPUExternalTextureWrap,
19};
20use script_bindings::error::{Error, Fallible};
21use script_bindings::interfaces::PromiseHelpers;
22use script_bindings::reflector::{DomGlobalGeneric, Reflector, reflect_dom_object_with_wrap};
23use script_bindings::{DomTypes, task};
24use webgpu_traits::{
25    Features, WebGPU, WebGPUDevice, WebGPUExternalTexture, WebGPUQueue, WebGPURequest,
26    WebGPUTexture, WebGPUTextureView,
27};
28
29use crate::JSTraceable;
30use crate::dom::bindings::refcounted::Trusted;
31use crate::dom::bindings::root::DomRoot;
32use crate::dom::bindings::str::USVString;
33use crate::gpudevice::GPUDevice;
34use crate::traits::{Equivalence, WebGPUGlobalTrait, WebGPUHTMLVideoTrait, WebGPUPromise};
35
36/// Backing of GPUExternalTexture
37#[derive(JSTraceable, MallocSizeOf)]
38pub struct PlanarTexture<D: DomTypes> {
39    #[ignore_malloc_size_of = "defined in webgpu"]
40    #[no_trace]
41    channel: WebGPU,
42    #[no_trace]
43    device_id: WebGPUDevice,
44    #[no_trace]
45    queue_id: WebGPUQueue,
46    #[no_trace]
47    texture_id: WebGPUTexture,
48    #[no_trace]
49    texture_view_id: WebGPUTextureView,
50    expired: Cell<bool>,
51    #[no_trace]
52    size: Size2D<u32>,
53    #[no_trace = "PhantomData does not exist"]
54    phantom: PhantomData<D>,
55}
56
57impl<D> PlanarTexture<D>
58where
59    D: Equivalence,
60    <D::Promise as PromiseHelpers<D>>::StackRoot: WebGPUPromise<D>,
61{
62    pub fn new(channel: WebGPU, device: &GPUDevice<D>, snapshot: Snapshot) -> Self {
63        let device_id = device.id();
64        let queue_id = device.queue_id();
65        let texture_id = WebGPUTexture(
66            device
67                .global_from_reflector()
68                .global_wgpu_id_hub()
69                .create_texture_id(),
70        );
71        let texture_view_id = WebGPUTextureView(
72            device
73                .global_from_reflector()
74                .global_wgpu_id_hub()
75                .create_texture_view_id(),
76        );
77        let size = snapshot.size();
78        if let Err(error) = channel.0.send(WebGPURequest::CreatePlanarTexture {
79            device_id: device_id.0,
80            texture_id: texture_id.0,
81            texture_view_id: texture_view_id.0,
82            size,
83            format: snapshot.format(),
84        }) {
85            warn!("Failed to send CreatePlanarTexture ({error})");
86        }
87        let self_ = Self {
88            channel,
89            device_id,
90            queue_id,
91            texture_id,
92            texture_view_id,
93            size,
94            expired: Cell::new(true),
95            phantom: PhantomData,
96        };
97        self_.update(snapshot);
98        self_
99    }
100
101    pub fn size(&self) -> Size2D<u32> {
102        self.size
103    }
104
105    pub fn update(&self, snapshot: Snapshot) {
106        if !self.expired.get() {
107            return;
108        }
109        if let Err(error) = self.channel.0.send(WebGPURequest::UpdatePlanarTexture {
110            device_id: self.device_id.0,
111            queue_id: self.queue_id.0,
112            texture_id: self.texture_id.0,
113            snapshot: snapshot.to_shared(),
114        }) {
115            warn!("Failed to send UpdatePlanarTexture ({error})");
116        }
117        self.expired.set(false);
118    }
119
120    pub(crate) fn expire(&self) {
121        self.expired.set(true);
122    }
123
124    pub fn is_expired(&self) -> bool {
125        self.expired.get()
126    }
127}
128
129impl<D: DomTypes> Drop for PlanarTexture<D> {
130    fn drop(&mut self) {
131        if let Err(error) = self.channel.0.send(WebGPURequest::DropPlanarTexture(
132            self.texture_id.0,
133            self.texture_view_id.0,
134        )) {
135            warn!("Failed to send DropPlanarTexture ({error})");
136        }
137    }
138}
139
140#[derive(JSTraceable, MallocSizeOf)]
141struct DroppableGPUExternalTexture {
142    #[ignore_malloc_size_of = "defined in webgpu"]
143    #[no_trace]
144    channel: WebGPU,
145    #[no_trace]
146    external_texture: WebGPUExternalTexture,
147}
148
149impl Drop for DroppableGPUExternalTexture {
150    fn drop(&mut self) {
151        if let Err(error) = self
152            .channel
153            .0
154            .send(WebGPURequest::DropExternalTexture(self.external_texture.0))
155        {
156            warn!(
157                "Failed to send DropExternalTexture ({:?}) ({error})",
158                self.external_texture.0
159            );
160        }
161    }
162}
163
164#[dom_struct]
165pub struct GPUExternalTexture<D: DomTypes> {
166    reflector_: Reflector,
167    label: DomRefCell<USVString>,
168    #[conditional_malloc_size_of]
169    planar_texture: Option<Rc<PlanarTexture<D>>>,
170    droppable: DroppableGPUExternalTexture,
171    #[no_trace = "PhantomData does not exist"]
172    phantom: PhantomData<D>,
173}
174
175impl<D> GPUExternalTexture<D>
176where
177    D: Equivalence,
178    <D::Promise as PromiseHelpers<D>>::StackRoot: WebGPUPromise<D>,
179{
180    fn new_inherited(
181        channel: WebGPU,
182        external_texture: WebGPUExternalTexture,
183        label: USVString,
184        planar_texture: Option<Rc<PlanarTexture<D>>>,
185    ) -> GPUExternalTexture<D> {
186        Self {
187            reflector_: Reflector::new(),
188            label: DomRefCell::new(label),
189            droppable: DroppableGPUExternalTexture {
190                channel,
191                external_texture,
192            },
193            planar_texture,
194            phantom: PhantomData,
195        }
196    }
197
198    pub(crate) fn new(
199        cx: &mut JSContext,
200        global: &D::GlobalScope,
201        channel: WebGPU,
202        external_texture: WebGPUExternalTexture,
203        label: USVString,
204        planar_texture: Option<Rc<PlanarTexture<D>>>,
205    ) -> DomRoot<GPUExternalTexture<D>> {
206        reflect_dom_object_with_wrap::<D, _, _>(
207            Box::new(GPUExternalTexture::new_inherited(
208                channel,
209                external_texture,
210                label,
211                planar_texture,
212            )),
213            global,
214            cx,
215            GPUExternalTextureWrap::<D>,
216        )
217    }
218
219    pub(crate) fn expire(&self) {
220        if let Some(planar_texture) = &self.planar_texture {
221            planar_texture.expire();
222        }
223        if let Err(error) = self
224            .droppable
225            .channel
226            .0
227            .send(WebGPURequest::DestroyExternalTexture(
228                self.droppable.external_texture.0,
229            ))
230        {
231            warn!(
232                "Failed to send DestroyExternalTexture ({:?}) ({error})",
233                self.droppable.external_texture.0
234            );
235        }
236    }
237
238    /// <https://www.w3.org/TR/webgpu/#dom-gpudevice-importexternaltexture>
239    pub(crate) fn create(
240        cx: &mut JSContext,
241        device: &GPUDevice<D>,
242        descriptor: &GPUExternalTextureDescriptor<D>,
243    ) -> Fallible<DomRoot<GPUExternalTexture<D>>> {
244        let (size, planar_texture) = if device
245            .Features()
246            .wgpu_features()
247            .contains(Features::EXTERNAL_TEXTURE)
248        {
249            // 2.1 - 2.4 inside the method
250            descriptor.source.planar_video_for_webgpu(device)?
251        } else {
252            // spec assumes that this is always supported, but that is not the case in wgpu
253            return Err(Error::NotSupported(Some(
254                "ExternalTexture is not supported on this device".to_string(),
255            )));
256        };
257        // 2.5. Let result be a new GPUExternalTexture object wrapping data.
258        let device_id = device.id().0;
259        let channel = device.channel();
260        let external_texture_id = device
261            .global_from_reflector()
262            .global_wgpu_id_hub()
263            .create_external_texture_id();
264
265        if let Err(error) = channel.0.send(WebGPURequest::ImportExternalTexture {
266            device_id,
267            external_texture_id,
268            size,
269            label: descriptor.parent.label.to_string(),
270            plane0: planar_texture
271                .as_ref()
272                .map(|planar_texture| planar_texture.texture_view_id.0),
273        }) {
274            warn!("Failed to send ImportExternalTexture ({error})");
275        };
276        let result = Self::new(
277            cx,
278            &device.global_from_reflector(),
279            channel,
280            WebGPUExternalTexture(external_texture_id),
281            // 5. Set result.label to descriptor.label.
282            descriptor.parent.label.clone(),
283            planar_texture,
284        );
285        // 3. If source is an HTMLVideoElement, queue an automatic expiry task with device this and the following steps
286        let this = Trusted::new(&*result);
287
288        device
289            .global_from_reflector()
290            .queue_webgpu_task_source(task!(expire:  move || {
291                this.root().expire();
292            }));
293        // 6. Return result.
294        Ok(result)
295    }
296}
297
298impl<D: Equivalence> GPUExternalTexture<D> {
299    pub(crate) fn id(&self) -> WebGPUExternalTexture {
300        self.droppable.external_texture
301    }
302}
303
304impl<D: Equivalence> GPUExternalTextureMethods<D> for GPUExternalTexture<D> {
305    /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
306    fn Label(&self) -> USVString {
307        self.label.borrow().clone()
308    }
309
310    /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
311    fn SetLabel(&self, value: USVString) {
312        *self.label.borrow_mut() = value;
313    }
314}