1use 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#[derive(JSTraceable, MallocSizeOf)]
38pub struct PlanarTexture<D: DomTypes> {
39 #[no_trace]
40 channel: WebGPU,
41 #[no_trace]
42 device_id: WebGPUDevice,
43 #[no_trace]
44 queue_id: WebGPUQueue,
45 #[no_trace]
46 texture_id: WebGPUTexture,
47 #[no_trace]
48 texture_view_id: WebGPUTextureView,
49 expired: Cell<bool>,
50 #[no_trace]
51 size: Size2D<u32>,
52 #[no_trace = "PhantomData does not exist"]
53 phantom: PhantomData<D>,
54}
55
56impl<D> PlanarTexture<D>
57where
58 D: Equivalence,
59 <D::Promise as PromiseHelpers<D>>::StackRoot: WebGPUPromise<D>,
60{
61 pub fn new(channel: WebGPU, device: &GPUDevice<D>, snapshot: Snapshot) -> Self {
62 let device_id = device.id();
63 let queue_id = device.queue_id();
64 let texture_id = WebGPUTexture(
65 device
66 .global_from_reflector()
67 .global_wgpu_id_hub()
68 .create_texture_id(),
69 );
70 let texture_view_id = WebGPUTextureView(
71 device
72 .global_from_reflector()
73 .global_wgpu_id_hub()
74 .create_texture_view_id(),
75 );
76 let size = snapshot.size();
77 if let Err(error) = channel.0.send(WebGPURequest::CreatePlanarTexture {
78 device_id: device_id.0,
79 texture_id: texture_id.0,
80 texture_view_id: texture_view_id.0,
81 size,
82 format: snapshot.format(),
83 }) {
84 warn!("Failed to send CreatePlanarTexture ({error})");
85 }
86 let self_ = Self {
87 channel,
88 device_id,
89 queue_id,
90 texture_id,
91 texture_view_id,
92 size,
93 expired: Cell::new(true),
94 phantom: PhantomData,
95 };
96 self_.update(snapshot);
97 self_
98 }
99
100 pub fn size(&self) -> Size2D<u32> {
101 self.size
102 }
103
104 pub fn update(&self, snapshot: Snapshot) {
105 if !self.expired.get() {
106 return;
107 }
108 if let Err(error) = self.channel.0.send(WebGPURequest::UpdatePlanarTexture {
109 device_id: self.device_id.0,
110 queue_id: self.queue_id.0,
111 texture_id: self.texture_id.0,
112 snapshot: snapshot.into_shared(),
113 }) {
114 warn!("Failed to send UpdatePlanarTexture ({error})");
115 }
116 self.expired.set(false);
117 }
118
119 pub(crate) fn expire(&self) {
120 self.expired.set(true);
121 }
122
123 pub fn is_expired(&self) -> bool {
124 self.expired.get()
125 }
126}
127
128impl<D: DomTypes> Drop for PlanarTexture<D> {
129 fn drop(&mut self) {
130 if let Err(error) = self.channel.0.send(WebGPURequest::DropPlanarTexture(
131 self.texture_id.0,
132 self.texture_view_id.0,
133 )) {
134 warn!("Failed to send DropPlanarTexture ({error})");
135 }
136 }
137}
138
139#[derive(JSTraceable, MallocSizeOf)]
140struct DroppableGPUExternalTexture {
141 #[no_trace]
142 channel: WebGPU,
143 #[no_trace]
144 external_texture: WebGPUExternalTexture,
145}
146
147impl Drop for DroppableGPUExternalTexture {
148 fn drop(&mut self) {
149 if let Err(error) = self
150 .channel
151 .0
152 .send(WebGPURequest::DropExternalTexture(self.external_texture.0))
153 {
154 warn!(
155 "Failed to send DropExternalTexture ({:?}) ({error})",
156 self.external_texture.0
157 );
158 }
159 }
160}
161
162#[dom_struct]
163pub struct GPUExternalTexture<D: DomTypes> {
164 reflector_: Reflector,
165 label: DomRefCell<USVString>,
166 #[conditional_malloc_size_of]
167 planar_texture: Option<Rc<PlanarTexture<D>>>,
168 droppable: DroppableGPUExternalTexture,
169 #[no_trace = "PhantomData does not exist"]
170 phantom: PhantomData<D>,
171}
172
173impl<D> GPUExternalTexture<D>
174where
175 D: Equivalence,
176 <D::Promise as PromiseHelpers<D>>::StackRoot: WebGPUPromise<D>,
177{
178 fn new_inherited(
179 channel: WebGPU,
180 external_texture: WebGPUExternalTexture,
181 label: USVString,
182 planar_texture: Option<Rc<PlanarTexture<D>>>,
183 ) -> GPUExternalTexture<D> {
184 Self {
185 reflector_: Reflector::new(),
186 label: DomRefCell::new(label),
187 droppable: DroppableGPUExternalTexture {
188 channel,
189 external_texture,
190 },
191 planar_texture,
192 phantom: PhantomData,
193 }
194 }
195
196 pub(crate) fn new(
197 cx: &mut JSContext,
198 global: &D::GlobalScope,
199 channel: WebGPU,
200 external_texture: WebGPUExternalTexture,
201 label: USVString,
202 planar_texture: Option<Rc<PlanarTexture<D>>>,
203 ) -> DomRoot<GPUExternalTexture<D>> {
204 reflect_dom_object_with_wrap::<D, _, _>(
205 cx,
206 Box::new(GPUExternalTexture::new_inherited(
207 channel,
208 external_texture,
209 label,
210 planar_texture,
211 )),
212 global,
213 GPUExternalTextureWrap::<D>,
214 )
215 }
216
217 pub(crate) fn expire(&self) {
218 if let Some(planar_texture) = &self.planar_texture {
219 planar_texture.expire();
220 }
221 if let Err(error) = self
222 .droppable
223 .channel
224 .0
225 .send(WebGPURequest::DestroyExternalTexture(
226 self.droppable.external_texture.0,
227 ))
228 {
229 warn!(
230 "Failed to send DestroyExternalTexture ({:?}) ({error})",
231 self.droppable.external_texture.0
232 );
233 }
234 }
235
236 pub(crate) fn create(
238 cx: &mut JSContext,
239 device: &GPUDevice<D>,
240 descriptor: &GPUExternalTextureDescriptor<D>,
241 ) -> Fallible<DomRoot<GPUExternalTexture<D>>> {
242 let (size, planar_texture) = if device
243 .Features()
244 .wgpu_features()
245 .contains(Features::EXTERNAL_TEXTURE)
246 {
247 descriptor.source.planar_video_for_webgpu(device)?
249 } else {
250 return Err(Error::NotSupported(Some(
252 "ExternalTexture is not supported on this device".to_string(),
253 )));
254 };
255 let device_id = device.id().0;
257 let channel = device.channel();
258 let external_texture_id = device
259 .global_from_reflector()
260 .global_wgpu_id_hub()
261 .create_external_texture_id();
262
263 if let Err(error) = channel.0.send(WebGPURequest::ImportExternalTexture {
264 device_id,
265 external_texture_id,
266 size,
267 label: descriptor.parent.label.to_string(),
268 plane0: planar_texture
269 .as_ref()
270 .map(|planar_texture| planar_texture.texture_view_id.0),
271 }) {
272 warn!("Failed to send ImportExternalTexture ({error})");
273 };
274 let result = Self::new(
275 cx,
276 &device.global_from_reflector(),
277 channel,
278 WebGPUExternalTexture(external_texture_id),
279 descriptor.parent.label.clone(),
281 planar_texture,
282 );
283 let this = Trusted::new(&*result);
285
286 device
287 .global_from_reflector()
288 .queue_webgpu_task_source(task!(expire: move || {
289 this.root().expire();
290 }));
291 Ok(result)
293 }
294}
295
296impl<D: Equivalence> GPUExternalTexture<D> {
297 pub(crate) fn id(&self) -> WebGPUExternalTexture {
298 self.droppable.external_texture
299 }
300}
301
302impl<D: Equivalence> GPUExternalTextureMethods<D> for GPUExternalTexture<D> {
303 fn Label(&self) -> USVString {
305 self.label.borrow().clone()
306 }
307
308 fn SetLabel(&self, value: USVString) {
310 *self.label.borrow_mut() = value;
311 }
312}