1use std::cell::Cell;
6use std::rc::Rc;
7
8use dom_struct::dom_struct;
9use euclid::default::Size2D;
10use js::context::JSContext;
11use pixels::Snapshot;
12use script_bindings::cell::DomRefCell;
13use script_bindings::codegen::GenericBindings::WebGPUBinding::GPUDeviceMethods as _;
14use script_bindings::error::Fallible;
15use script_bindings::reflector::{Reflector, reflect_dom_object};
16use script_webgpu::traits::GPUExternalTextureTrait;
17use webgpu_traits::{
18 WebGPU, WebGPUDevice, WebGPUExternalTexture, WebGPUQueue, WebGPURequest, WebGPUTexture,
19 WebGPUTextureView,
20};
21use wgpu_types::Features;
22
23use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
24 GPUExternalTextureDescriptor, GPUExternalTextureMethods,
25};
26use crate::dom::bindings::error::Error;
27use crate::dom::bindings::refcounted::Trusted;
28use crate::dom::bindings::reflector::DomGlobal as _;
29use crate::dom::bindings::root::DomRoot;
30use crate::dom::bindings::str::USVString;
31use crate::dom::globalscope::GlobalScope;
32use crate::dom::gpudevice::GPUDevice;
33
34#[derive(JSTraceable, MallocSizeOf)]
36pub(crate) struct PlanarTexture {
37 #[ignore_malloc_size_of = "defined in webgpu"]
38 #[no_trace]
39 channel: WebGPU,
40 #[no_trace]
41 device_id: WebGPUDevice,
42 #[no_trace]
43 queue_id: WebGPUQueue,
44 #[no_trace]
45 texture_id: WebGPUTexture,
46 #[no_trace]
47 texture_view_id: WebGPUTextureView,
48 expired: Cell<bool>,
49 #[no_trace]
50 size: Size2D<u32>,
51}
52
53impl PlanarTexture {
54 pub(crate) fn new(channel: WebGPU, device: &GPUDevice, snapshot: Snapshot) -> Self {
55 let device_id = device.id();
56 let queue_id = device.queue_id();
57 let texture_id = WebGPUTexture(device.global().wgpu_id_hub().create_texture_id());
58 let texture_view_id =
59 WebGPUTextureView(device.global().wgpu_id_hub().create_texture_view_id());
60 let size = snapshot.size();
61 if let Err(error) = channel.0.send(WebGPURequest::CreatePlanarTexture {
62 device_id: device_id.0,
63 texture_id: texture_id.0,
64 texture_view_id: texture_view_id.0,
65 size,
66 format: snapshot.format(),
67 }) {
68 warn!("Failed to send CreatePlanarTexture ({error})");
69 }
70 let self_ = Self {
71 channel,
72 device_id,
73 queue_id,
74 texture_id,
75 texture_view_id,
76 size,
77 expired: Cell::new(true),
78 };
79 self_.update(snapshot);
80 self_
81 }
82
83 pub(crate) fn size(&self) -> Size2D<u32> {
84 self.size
85 }
86
87 pub(crate) fn update(&self, snapshot: Snapshot) {
88 if !self.expired.get() {
89 return;
90 }
91 if let Err(error) = self.channel.0.send(WebGPURequest::UpdatePlanarTexture {
92 device_id: self.device_id.0,
93 queue_id: self.queue_id.0,
94 texture_id: self.texture_id.0,
95 snapshot: snapshot.to_shared(),
96 }) {
97 warn!("Failed to send UpdatePlanarTexture ({error})");
98 }
99 self.expired.set(false);
100 }
101
102 pub(crate) fn expire(&self) {
103 self.expired.set(true);
104 }
105
106 pub(crate) fn is_expired(&self) -> bool {
107 self.expired.get()
108 }
109}
110
111impl Drop for PlanarTexture {
112 fn drop(&mut self) {
113 if let Err(error) = self.channel.0.send(WebGPURequest::DropPlanarTexture(
114 self.texture_id.0,
115 self.texture_view_id.0,
116 )) {
117 warn!("Failed to send DropPlanarTexture ({error})");
118 }
119 }
120}
121
122#[derive(JSTraceable, MallocSizeOf)]
123struct DroppableGPUExternalTexture {
124 #[ignore_malloc_size_of = "defined in webgpu"]
125 #[no_trace]
126 channel: WebGPU,
127 #[no_trace]
128 external_texture: WebGPUExternalTexture,
129}
130
131impl Drop for DroppableGPUExternalTexture {
132 fn drop(&mut self) {
133 if let Err(error) = self
134 .channel
135 .0
136 .send(WebGPURequest::DropExternalTexture(self.external_texture.0))
137 {
138 warn!(
139 "Failed to send DropExternalTexture ({:?}) ({error})",
140 self.external_texture.0
141 );
142 }
143 }
144}
145
146#[dom_struct]
147pub(crate) struct GPUExternalTexture {
148 reflector_: Reflector,
149 label: DomRefCell<USVString>,
150 #[conditional_malloc_size_of]
151 planar_texture: Option<Rc<PlanarTexture>>,
152 droppable: DroppableGPUExternalTexture,
153}
154
155impl GPUExternalTexture {
156 fn new_inherited(
157 channel: WebGPU,
158 external_texture: WebGPUExternalTexture,
159 label: USVString,
160 planar_texture: Option<Rc<PlanarTexture>>,
161 ) -> GPUExternalTexture {
162 Self {
163 reflector_: Reflector::new(),
164 label: DomRefCell::new(label),
165 droppable: DroppableGPUExternalTexture {
166 channel,
167 external_texture,
168 },
169 planar_texture,
170 }
171 }
172
173 pub(crate) fn new(
174 cx: &mut JSContext,
175 global: &GlobalScope,
176 channel: WebGPU,
177 external_texture: WebGPUExternalTexture,
178 label: USVString,
179 planar_texture: Option<Rc<PlanarTexture>>,
180 ) -> DomRoot<GPUExternalTexture> {
181 reflect_dom_object(
182 cx,
183 Box::new(GPUExternalTexture::new_inherited(
184 channel,
185 external_texture,
186 label,
187 planar_texture,
188 )),
189 global,
190 )
191 }
192
193 pub(crate) fn expire(&self) {
194 if let Some(planar_texture) = &self.planar_texture {
195 planar_texture.expire();
196 }
197 if let Err(error) = self
198 .droppable
199 .channel
200 .0
201 .send(WebGPURequest::DestroyExternalTexture(
202 self.droppable.external_texture.0,
203 ))
204 {
205 warn!(
206 "Failed to send DestroyExternalTexture ({:?}) ({error})",
207 self.droppable.external_texture.0
208 );
209 }
210 }
211
212 pub(crate) fn create(
214 cx: &mut JSContext,
215 device: &super::gpudevice::GPUDevice,
216 descriptor: &GPUExternalTextureDescriptor,
217 ) -> Fallible<DomRoot<GPUExternalTexture>> {
218 let (size, planar_texture) = if device
219 .Features()
220 .wgpu_features()
221 .contains(Features::EXTERNAL_TEXTURE)
222 {
223 descriptor.source.planar_video_for_webgpu(device)?
225 } else {
226 return Err(Error::NotSupported(Some(
228 "ExternalTexture is not supported on this device".to_string(),
229 )));
230 };
231 let device_id = device.id().0;
233 let channel = device.channel();
234 let external_texture_id = device.global().wgpu_id_hub().create_external_texture_id();
235
236 if let Err(error) = channel.0.send(WebGPURequest::ImportExternalTexture {
237 device_id,
238 external_texture_id,
239 size,
240 label: descriptor.parent.label.to_string(),
241 plane0: planar_texture
242 .as_ref()
243 .map(|planar_texture| planar_texture.texture_view_id.0),
244 }) {
245 warn!("Failed to send ImportExternalTexture ({error})");
246 };
247 let result = Self::new(
248 cx,
249 &device.global(),
250 channel,
251 WebGPUExternalTexture(external_texture_id),
252 descriptor.parent.label.clone(),
254 planar_texture,
255 );
256 let this = Trusted::new(&*result);
258 device
259 .global()
260 .task_manager()
261 .webgpu_task_source()
262 .queue(task!(expire: move || {
263 this.root().expire();
264 }));
265
266 Ok(result)
268 }
269}
270
271impl GPUExternalTexture {
272 pub(crate) fn id(&self) -> WebGPUExternalTexture {
273 self.droppable.external_texture
274 }
275}
276
277impl GPUExternalTextureMethods<crate::DomTypeHolder> for GPUExternalTexture {
278 fn Label(&self) -> USVString {
280 self.label.borrow().clone()
281 }
282
283 fn SetLabel(&self, value: USVString) {
285 *self.label.borrow_mut() = value;
286 }
287}
288
289impl GPUExternalTextureTrait for GPUExternalTexture {
290 fn id(&self) -> WebGPUExternalTexture {
291 self.id()
292 }
293}