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