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 #[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 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 descriptor.source.planar_video_for_webgpu(device)?
251 } else {
252 return Err(Error::NotSupported(Some(
254 "ExternalTexture is not supported on this device".to_string(),
255 )));
256 };
257 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 descriptor.parent.label.clone(),
283 planar_texture,
284 );
285 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 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 fn Label(&self) -> USVString {
307 self.label.borrow().clone()
308 }
309
310 fn SetLabel(&self, value: USVString) {
312 *self.label.borrow_mut() = value;
313 }
314}