Skip to main content

webgl/
webgl_thread.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#![expect(unsafe_code)]
5use std::borrow::Cow;
6use std::collections::HashMap;
7use std::collections::hash_map::Entry;
8use std::num::NonZeroU32;
9use std::rc::Rc;
10use std::sync::Arc;
11use std::thread::JoinHandle;
12use std::{slice, thread};
13
14use bitflags::bitflags;
15use byteorder::{ByteOrder, NativeEndian, WriteBytesExt};
16use euclid::default::Size2D;
17use glow::{
18    self as gl, ActiveTransformFeedback, Context as Gl, HasContext, NativeTransformFeedback,
19    NativeUniformLocation, NativeVertexArray, PixelUnpackData, ShaderPrecisionFormat,
20    bytes_per_type,
21};
22use half::f16;
23use itertools::Itertools;
24use log::{debug, error, trace, warn};
25use paint_api::{
26    CrossProcessPaintApi, PainterSurfmanDetailsMap, SerializableImageData,
27    WebRenderExternalImageIdManager, WebRenderImageHandlerType,
28};
29use parking_lot::RwLock;
30use pixels::{self, PixelFormat, SnapshotAlphaMode, unmultiply_inplace};
31use rustc_hash::FxHashMap;
32use servo_base::Epoch;
33use servo_base::generic_channel::{
34    GenericReceiver, GenericSender, GenericSharedMemory, RoutedReceiver,
35};
36use servo_base::id::PainterId;
37use servo_canvas_traits::webgl;
38#[cfg(feature = "webxr")]
39use servo_canvas_traits::webgl::WebXRCommand;
40use servo_canvas_traits::webgl::{
41    ActiveAttribInfo, ActiveUniformBlockInfo, ActiveUniformInfo, AlphaTreatment,
42    GLContextAttributes, GLLimits, GlType, InternalFormatIntVec, ProgramLinkInfo, TexDataType,
43    TexFormat, WebGLBufferId, WebGLChan, WebGLCommand, WebGLCommandBacktrace, WebGLContextId,
44    WebGLCreateContextResult, WebGLFramebufferBindingRequest, WebGLFramebufferId, WebGLMsg,
45    WebGLMsgSender, WebGLProgramId, WebGLQueryId, WebGLRenderbufferId, WebGLSLVersion,
46    WebGLSamplerId, WebGLShaderId, WebGLSyncId, WebGLTextureId, WebGLVersion, WebGLVertexArrayId,
47    YAxisTreatment,
48};
49use surfman::chains::{PreserveBuffer, SwapChains, SwapChainsAPI};
50use surfman::{
51    self, Context, ContextAttributeFlags, ContextAttributes, Device, GLVersion, SurfaceAccess,
52    SurfaceInfo, SurfaceType,
53};
54use webrender_api::units::DeviceIntSize;
55use webrender_api::{
56    ExternalImageData, ExternalImageId, ExternalImageType, ImageBufferKind, ImageDescriptor,
57    ImageDescriptorFlags, ImageFormat, ImageKey,
58};
59
60use crate::webgl_limits::GLLimitsDetect;
61#[cfg(feature = "webxr")]
62use crate::webxr::{WebXRBridge, WebXRBridgeInit};
63
64type GLint = i32;
65
66fn native_uniform_location(location: i32) -> Option<NativeUniformLocation> {
67    location.try_into().ok().map(NativeUniformLocation)
68}
69
70/// A map which tracks whether a given WebGL context is "busy" ie whether WebRender has
71/// currently taken a surface from its [`SwapChain`] for rendering purposes. Contexts will
72/// only be deleted once no WebRender instance is using it for rendering. This ensures
73/// that all Surfman `Surface`s can be released properly on the [`WebGLThread`].
74pub type WebGLContextBusyMap = Arc<RwLock<HashMap<WebGLContextId, usize>>>;
75
76pub(crate) struct GLContextData {
77    pub(crate) ctx: Context,
78    pub(crate) gl: Rc<glow::Context>,
79    device: Rc<Device>,
80    state: GLState,
81    attributes: GLContextAttributes,
82    /// The context should be removed, but the [`WebGLThread`] is currently waiting on
83    /// WebRender to finish rendering to the context in order to delete it.
84    marked_for_deletion: bool,
85}
86
87#[derive(Debug)]
88pub struct GLState {
89    webgl_version: WebGLVersion,
90    _gl_version: GLVersion,
91    requested_flags: ContextAttributeFlags,
92    // This is the WebGL view of the color mask
93    // The GL view may be different: if the GL context supports alpha
94    // but the WebGL context doesn't, then color_write_mask.3 might be true
95    // but the GL color write mask is false.
96    color_write_mask: [bool; 4],
97    clear_color: (f32, f32, f32, f32),
98    scissor_test_enabled: bool,
99    // The WebGL view of the stencil write mask (see comment re `color_write_mask`)
100    stencil_write_mask: (u32, u32),
101    // Kept here rather than read back: glGetIntegerv cannot return a full GLuint.
102    stencil_value_mask: (u32, u32),
103    stencil_test_enabled: bool,
104    stencil_clear_value: i32,
105    // The WebGL view of the depth write mask (see comment re `color_write_mask`)
106    depth_write_mask: bool,
107    depth_test_enabled: bool,
108    depth_clear_value: f64,
109    // True when the default framebuffer is bound to DRAW_FRAMEBUFFER
110    drawing_to_default_framebuffer: bool,
111    default_vao: Option<NativeVertexArray>,
112}
113
114impl GLState {
115    // Are we faking having no alpha / depth / stencil?
116    fn fake_no_alpha(&self) -> bool {
117        self.drawing_to_default_framebuffer &
118            !self.requested_flags.contains(ContextAttributeFlags::ALPHA)
119    }
120
121    fn fake_no_depth(&self) -> bool {
122        self.drawing_to_default_framebuffer &
123            !self.requested_flags.contains(ContextAttributeFlags::DEPTH)
124    }
125
126    fn fake_no_stencil(&self) -> bool {
127        self.drawing_to_default_framebuffer &
128            !self
129                .requested_flags
130                .contains(ContextAttributeFlags::STENCIL)
131    }
132
133    // We maintain invariants between the GLState object and the GL state.
134    fn restore_invariant(&self, gl: &Gl) {
135        self.restore_clear_invariant(gl);
136        self.restore_scissor_invariant(gl);
137        self.restore_alpha_invariant(gl);
138        self.restore_depth_invariant(gl);
139        self.restore_stencil_invariant(gl);
140    }
141
142    fn restore_clear_invariant(&self, gl: &Gl) {
143        let (r, g, b, a) = self.clear_color;
144        unsafe {
145            gl.clear_color(r, g, b, a);
146            gl.clear_depth(self.depth_clear_value);
147            gl.clear_stencil(self.stencil_clear_value);
148        }
149    }
150
151    fn restore_scissor_invariant(&self, gl: &Gl) {
152        if self.scissor_test_enabled {
153            unsafe { gl.enable(gl::SCISSOR_TEST) };
154        } else {
155            unsafe { gl.disable(gl::SCISSOR_TEST) };
156        }
157    }
158
159    fn restore_alpha_invariant(&self, gl: &Gl) {
160        let [r, g, b, a] = self.color_write_mask;
161        if self.fake_no_alpha() {
162            unsafe { gl.color_mask(r, g, b, false) };
163        } else {
164            unsafe { gl.color_mask(r, g, b, a) };
165        }
166    }
167
168    fn restore_depth_invariant(&self, gl: &Gl) {
169        unsafe {
170            if self.fake_no_depth() {
171                gl.depth_mask(false);
172                gl.disable(gl::DEPTH_TEST);
173            } else {
174                gl.depth_mask(self.depth_write_mask);
175                if self.depth_test_enabled {
176                    gl.enable(gl::DEPTH_TEST);
177                } else {
178                    gl.disable(gl::DEPTH_TEST);
179                }
180            }
181        }
182    }
183
184    fn restore_stencil_invariant(&self, gl: &Gl) {
185        unsafe {
186            if self.fake_no_stencil() {
187                gl.stencil_mask(0);
188                gl.disable(gl::STENCIL_TEST);
189            } else {
190                let (f, b) = self.stencil_write_mask;
191                gl.stencil_mask_separate(gl::FRONT, f);
192                gl.stencil_mask_separate(gl::BACK, b);
193                if self.stencil_test_enabled {
194                    gl.enable(gl::STENCIL_TEST);
195                } else {
196                    gl.disable(gl::STENCIL_TEST);
197                }
198            }
199        }
200    }
201}
202
203impl Default for GLState {
204    fn default() -> GLState {
205        GLState {
206            _gl_version: GLVersion { major: 1, minor: 0 },
207            webgl_version: WebGLVersion::WebGL1,
208            requested_flags: ContextAttributeFlags::empty(),
209            color_write_mask: [true, true, true, true],
210            clear_color: (0., 0., 0., 0.),
211            scissor_test_enabled: false,
212            // GL's initial stencil writemask is all ones.
213            stencil_write_mask: (u32::MAX, u32::MAX),
214            stencil_value_mask: (u32::MAX, u32::MAX),
215            stencil_test_enabled: false,
216            stencil_clear_value: 0,
217            depth_write_mask: true,
218            depth_test_enabled: false,
219            depth_clear_value: 1.,
220            default_vao: None,
221            drawing_to_default_framebuffer: true,
222        }
223    }
224}
225
226/// A WebGLThread manages the life cycle and message multiplexing of
227/// a set of WebGLContexts living in the same thread.
228pub(crate) struct WebGLThread {
229    /// The GPU device.
230    device_map: HashMap<PainterId, Rc<Device>>,
231    /// Channel used to generate/update or delete `ImageKey`s.
232    paint_api: CrossProcessPaintApi,
233    /// Map of live WebGLContexts.
234    contexts: FxHashMap<WebGLContextId, GLContextData>,
235    /// Cached information for WebGLContexts.
236    cached_context_info: FxHashMap<WebGLContextId, WebGLContextInfo>,
237    /// Current bound context.
238    bound_context_id: Option<WebGLContextId>,
239    /// A [`WebRenderExternalImageIdManager`] used to generate new [`ExternalImageId`]s for our
240    /// WebGL contexts.
241    external_image_id_manager: WebRenderExternalImageIdManager,
242    /// The receiver that will be used for processing WebGL messages.
243    receiver: RoutedReceiver<WebGLMsg>,
244    /// The receiver that should be used to send WebGL messages for processing.
245    sender: GenericSender<WebGLMsg>,
246    /// The swap chains used by webrender
247    webrender_swap_chains: SwapChains<WebGLContextId, Device>,
248    /// The per-painter details of the underlying surfman connection.
249    painter_surfman_details_map: PainterSurfmanDetailsMap,
250    /// A usage map used to delay the deletion of WebGL contexts until all WebRender
251    /// rendering is finished, so that any existing `Surface`s can be properly released.
252    busy_webgl_context_map: WebGLContextBusyMap,
253
254    #[cfg(feature = "webxr")]
255    /// The bridge to WebXR
256    pub webxr_bridge: Option<WebXRBridge>,
257}
258
259/// The data required to initialize an instance of the WebGLThread type.
260pub(crate) struct WebGLThreadInit {
261    pub paint_api: CrossProcessPaintApi,
262    pub external_image_id_manager: WebRenderExternalImageIdManager,
263    pub sender: GenericSender<WebGLMsg>,
264    pub receiver: GenericReceiver<WebGLMsg>,
265    pub webrender_swap_chains: SwapChains<WebGLContextId, Device>,
266    pub painter_surfman_details_map: PainterSurfmanDetailsMap,
267    pub busy_webgl_context_map: WebGLContextBusyMap,
268    #[cfg(feature = "webxr")]
269    pub webxr_init: WebXRBridgeInit,
270}
271
272// A size at which it should be safe to create GL contexts
273const SAFE_VIEWPORT_DIMS: [u32; 2] = [1024, 1024];
274
275impl WebGLThread {
276    /// Create a new instance of WebGLThread.
277    pub(crate) fn new(
278        WebGLThreadInit {
279            paint_api,
280            external_image_id_manager: external_images,
281            sender,
282            receiver,
283            webrender_swap_chains,
284            painter_surfman_details_map,
285            busy_webgl_context_map,
286            #[cfg(feature = "webxr")]
287            webxr_init,
288        }: WebGLThreadInit,
289    ) -> Self {
290        WebGLThread {
291            device_map: Default::default(),
292            paint_api,
293            contexts: Default::default(),
294            cached_context_info: Default::default(),
295            bound_context_id: None,
296            external_image_id_manager: external_images,
297            sender,
298            receiver: receiver.route_preserving_errors(),
299            webrender_swap_chains,
300            painter_surfman_details_map,
301            busy_webgl_context_map,
302            #[cfg(feature = "webxr")]
303            webxr_bridge: Some(WebXRBridge::new(webxr_init)),
304        }
305    }
306
307    /// Perform all initialization required to run an instance of WebGLThread
308    /// in parallel on its own dedicated thread.
309    pub(crate) fn run_on_own_thread(init: WebGLThreadInit) -> JoinHandle<()> {
310        thread::Builder::new()
311            .name("WebGL".to_owned())
312            .spawn(move || {
313                let mut data = WebGLThread::new(init);
314                data.process();
315            })
316            .expect("Thread spawning failed")
317    }
318
319    fn process(&mut self) {
320        let webgl_chan = WebGLChan(self.sender.clone());
321        while let Ok(Ok(msg)) = self.receiver.recv() {
322            let exit = self.handle_msg(msg, &webgl_chan);
323            if exit {
324                break;
325            }
326        }
327    }
328
329    /// Enable GL_POINT_SPRITE and GL_PROGRAM_POINT_SIZE on desktop OpenGL.
330    ///
331    /// FIXME(nox): Should probably be done by surfman.
332    /// FIXME(sagudev): Do we even need to do this?
333    fn ensure_point_sprite_and_program_point_size_enabled(gl_context_data: &GLContextData) {
334        // Points sprites are enabled by default in OpenGL 3.2 core
335        // and in GLES.
336        if gl_context_data.gl.version().is_embedded {
337            return;
338        }
339
340        // Rather than doing version detection, it does not hurt to enable GL_POINT_SPRITE and
341        // PROGRAM_POINT_SIZE always.
342        const GL_POINT_SPRITE: u32 = 0x8861;
343        unsafe { gl_context_data.gl.enable(GL_POINT_SPRITE) };
344        let error = unsafe { gl_context_data.gl.get_error() };
345        if error != 0 {
346            warn!("Error enabling GL point sprites: {error}");
347        }
348
349        unsafe { gl_context_data.gl.enable(gl::PROGRAM_POINT_SIZE) };
350        let error = unsafe { gl_context_data.gl.get_error() };
351        if error != 0 {
352            warn!("Error enabling GL program point size: {error}");
353        }
354    }
355
356    /// Handles a generic WebGLMsg message
357    fn handle_msg(&mut self, msg: WebGLMsg, webgl_chan: &WebGLChan) -> bool {
358        trace!("processing {:?}", msg);
359        match msg {
360            WebGLMsg::CreateContext(painter_id, version, size, attributes, result_sender) => {
361                let result = self.create_webgl_context(painter_id, version, size, attributes);
362
363                result_sender
364                    .send(result.map(|(id, limits)| {
365                        let data = self
366                            .make_current_if_needed(id)
367                            .expect("WebGLContext not found");
368
369                        Self::ensure_point_sprite_and_program_point_size_enabled(data);
370
371                        let glsl_version = Self::get_glsl_version(&data.gl);
372                        let api_type = if data.gl.version().is_embedded {
373                            GlType::Gles
374                        } else {
375                            GlType::Gl
376                        };
377
378                        WebGLCreateContextResult {
379                            sender: WebGLMsgSender::new(id, webgl_chan.clone()),
380                            limits,
381                            glsl_version,
382                            api_type,
383                        }
384                    }))
385                    .unwrap();
386            },
387            WebGLMsg::SetImageKey(ctx_id, image_key) => {
388                self.handle_set_image_key(ctx_id, image_key);
389            },
390            WebGLMsg::ResizeContext(ctx_id, size, sender) => {
391                let _ = sender.send(self.resize_webgl_context(ctx_id, size));
392            },
393            WebGLMsg::RemoveContext(ctx_id) => {
394                self.remove_webgl_context(ctx_id);
395            },
396            WebGLMsg::WebGLCommand(ctx_id, command, backtrace) => {
397                self.handle_webgl_command(ctx_id, command, backtrace);
398            },
399            WebGLMsg::WebXRCommand(_command) => {
400                #[cfg(feature = "webxr")]
401                self.handle_webxr_command(_command);
402            },
403            WebGLMsg::SwapBuffers(swap_ids, canvas_epoch, sent_time) => {
404                self.handle_swap_buffers(canvas_epoch, swap_ids, sent_time);
405            },
406            WebGLMsg::FinishedRenderingToContext(context_id) => {
407                self.handle_finished_rendering_to_context(context_id);
408            },
409            WebGLMsg::ClearPainterResources(painter_id, sender) => {
410                self.device_map.remove(&painter_id);
411                if let Err(error) = sender.send(()) {
412                    warn!("Failed to send response to WebGLMsg::ClearPainterResources ({error})");
413                }
414            },
415            WebGLMsg::Exit => {
416                // Call remove_context functions in order to correctly delete WebRender image keys.
417                let context_ids: Vec<WebGLContextId> = self.contexts.keys().copied().collect();
418                for id in context_ids {
419                    self.remove_webgl_context(id);
420                }
421                return true;
422            },
423        }
424
425        false
426    }
427
428    pub(crate) fn get_or_create_device_for_painter(
429        &mut self,
430        painter_id: PainterId,
431    ) -> Result<Rc<Device>, String> {
432        let entry = self.device_map.entry(painter_id);
433        if let Entry::Occupied(entry) = entry {
434            return Ok(entry.get().clone());
435        }
436
437        // This can happen if the Webview was dropped while one of its ScriptThreads
438        // is still issuing asynchronous commands to the WebGL thread.
439        let Some(surfman_details) = self.painter_surfman_details_map.get(painter_id) else {
440            return Err(format!("No PainterSurfmanDetails found for {painter_id:?}"));
441        };
442
443        // Gracefully handle failure to create a device.
444        let Ok(device) = surfman_details
445            .connection
446            .create_device(&surfman_details.adapter)
447        else {
448            return Err("Could not open WebGL device".into());
449        };
450
451        Ok(entry.or_insert(Rc::new(device)).clone())
452    }
453
454    #[cfg(feature = "webxr")]
455    /// Handles a WebXR message
456    fn handle_webxr_command(&mut self, command: WebXRCommand) {
457        trace!("processing {:?}", command);
458        // Take `webxr_bridge` from the `WebGLThread` in order to avoid a double mutable borrow.
459        let Some(mut webxr_bridge) = self.webxr_bridge.take() else {
460            return;
461        };
462        match command {
463            WebXRCommand::CreateLayerManager(sender) => {
464                let result = webxr_bridge.create_layer_manager(self);
465                let _ = sender.send(result);
466            },
467            WebXRCommand::DestroyLayerManager(manager_id) => {
468                webxr_bridge.destroy_layer_manager(manager_id);
469            },
470            WebXRCommand::CreateLayer(manager_id, context_id, layer_init, sender) => {
471                let result = webxr_bridge.create_layer(manager_id, self, context_id, layer_init);
472                let _ = sender.send(result);
473            },
474            WebXRCommand::DestroyLayer(manager_id, context_id, layer_id) => {
475                webxr_bridge.destroy_layer(manager_id, self, context_id, layer_id);
476            },
477            WebXRCommand::BeginFrame(manager_id, layers, sender) => {
478                let result = webxr_bridge.begin_frame(manager_id, self, &layers[..]);
479                let _ = sender.send(result);
480            },
481            WebXRCommand::EndFrame(manager_id, layers, sender) => {
482                let result = webxr_bridge.end_frame(manager_id, self, &layers[..]);
483                let _ = sender.send(result);
484            },
485        }
486
487        self.webxr_bridge.replace(webxr_bridge);
488    }
489
490    fn device_for_context(&self, context_id: WebGLContextId) -> Rc<Device> {
491        self.maybe_device_for_context(context_id)
492            .expect("Should be called with a valid WebGLContextId")
493    }
494
495    /// A function like `Self::device_for_context`, except that it does not panic if the context
496    /// cannot be found. This is useful for WebXR, which might try to access WebGL contexts after
497    /// they have been cleaned up.
498    pub(crate) fn maybe_device_for_context(
499        &self,
500        context_id: WebGLContextId,
501    ) -> Option<Rc<Device>> {
502        self.contexts
503            .get(&context_id)
504            .map(|context| context.device.clone())
505    }
506
507    /// Handles a WebGLCommand for a specific WebGLContext
508    fn handle_webgl_command(
509        &mut self,
510        context_id: WebGLContextId,
511        command: WebGLCommand,
512        backtrace: WebGLCommandBacktrace,
513    ) {
514        if self.cached_context_info.get_mut(&context_id).is_none() {
515            return;
516        }
517        let data = self.make_current_if_needed_mut(context_id);
518        if let Some(data) = data {
519            WebGLImpl::apply(
520                &data.device,
521                &data.ctx,
522                &data.gl,
523                &mut data.state,
524                &data.attributes,
525                command,
526                backtrace,
527            );
528        }
529    }
530
531    /// Creates a new WebGLContext
532    fn create_webgl_context(
533        &mut self,
534        painter_id: PainterId,
535        webgl_version: WebGLVersion,
536        requested_size: Size2D<u32>,
537        attributes: GLContextAttributes,
538    ) -> Result<(WebGLContextId, webgl::GLLimits), String> {
539        debug!(
540            "WebGLThread::create_webgl_context({:?}, {:?}, {:?})",
541            webgl_version, requested_size, attributes
542        );
543
544        // Creating a new GLContext may make the current bound context_id dirty.
545        // Clear it to ensure that  make_current() is called in subsequent commands.
546        self.bound_context_id = None;
547
548        // This can happen if the Webview was dropped while one of its ScriptThreads
549        // is still issuing asynchronous commands to the WebGL thread.
550        let Some(painter_surfman_details) = self.painter_surfman_details_map.get(painter_id) else {
551            return Err(format!(
552                "PainterSurfmanDetails not found for {painter_id:?}"
553            ));
554        };
555
556        let api_type = match painter_surfman_details.connection.gl_api() {
557            surfman::GLApi::GL => GlType::Gl,
558            surfman::GLApi::GLES => GlType::Gles,
559        };
560
561        let requested_flags =
562            attributes.to_surfman_context_attribute_flags(webgl_version, api_type);
563        // Some GL implementations seem to only allow famebuffers
564        // to have alpha, depth and stencil if their creating context does.
565        // WebGL requires all contexts to be able to create framebuffers with
566        // alpha, depth and stencil. So we always create a context with them,
567        // and fake not having them if requested.
568        let flags = requested_flags |
569            ContextAttributeFlags::ALPHA |
570            ContextAttributeFlags::DEPTH |
571            ContextAttributeFlags::STENCIL;
572        let context_attributes = &ContextAttributes {
573            version: webgl_version.to_surfman_version(api_type),
574            flags,
575        };
576
577        let device = self.get_or_create_device_for_painter(painter_id)?;
578        let context_descriptor = device
579            .create_context_descriptor(context_attributes)
580            .map_err(|err| format!("Failed to create context descriptor: {:?}", err))?;
581
582        let safe_size = Size2D::new(
583            requested_size.width.min(SAFE_VIEWPORT_DIMS[0]).max(1),
584            requested_size.height.min(SAFE_VIEWPORT_DIMS[1]).max(1),
585        );
586        let surface_type = SurfaceType::Generic {
587            size: safe_size.to_i32(),
588        };
589        let surface_access = self.surface_access();
590
591        let mut ctx = device
592            .create_context(&context_descriptor, None)
593            .map_err(|err| format!("Failed to create the GL context: {:?}", err))?;
594        let surface = device
595            .create_surface(&ctx, surface_access, surface_type)
596            .map_err(|err| format!("Failed to create the initial surface: {:?}", err))?;
597        device
598            .bind_surface_to_context(&mut ctx, surface)
599            .map_err(|err| format!("Failed to bind initial surface: {:?}", err))?;
600        // https://github.com/pcwalton/surfman/issues/7
601        device
602            .make_context_current(&ctx)
603            .map_err(|err| format!("Failed to make new context current: {:?}", err))?;
604
605        let context_id = WebGLContextId(
606            self.external_image_id_manager
607                .next_id(WebRenderImageHandlerType::WebGl)
608                .0,
609        );
610
611        self.webrender_swap_chains
612            .create_attached_swap_chain(context_id, &*device, &mut ctx, surface_access)
613            .map_err(|err| format!("Failed to create swap chain: {:?}", err))?;
614
615        let Some(swap_chain) = self.webrender_swap_chains.get(context_id) else {
616            return Err("Failed to get the swap chain".into());
617        };
618
619        debug!(
620            "Created webgl context {:?}/{:?}",
621            context_id,
622            device.context_id(&ctx),
623        );
624
625        let gl = unsafe {
626            Rc::new(match api_type {
627                GlType::Gl => glow::Context::from_loader_function(|symbol_name| {
628                    device.get_proc_address(&ctx, symbol_name)
629                }),
630                GlType::Gles => glow::Context::from_loader_function(|symbol_name| {
631                    device.get_proc_address(&ctx, symbol_name)
632                }),
633            })
634        };
635
636        let limits = GLLimits::detect(&gl, webgl_version);
637
638        let size = clamp_viewport(&gl, requested_size);
639        debug_assert_eq!(unsafe { gl.get_error() }, gl::NO_ERROR);
640
641        if safe_size != size {
642            debug!("Resizing swap chain from {:?} to {:?}", safe_size, size);
643            swap_chain
644                .resize(&device, &mut ctx, size.to_i32())
645                .map_err(|err| format!("Failed to resize swap chain: {:?}", err))?;
646        }
647
648        let descriptor = device.context_descriptor(&ctx);
649        let descriptor_attributes = device.context_descriptor_attributes(&descriptor);
650        let gl_version = descriptor_attributes.version;
651        let has_alpha = requested_flags.contains(ContextAttributeFlags::ALPHA);
652
653        device.make_context_current(&ctx).unwrap();
654        let framebuffer = device
655            .context_surface_info(&ctx)
656            .map_err(|err| format!("Failed to get context surface info: {:?}", err))?
657            .ok_or_else(|| "Failed to get context surface info".to_string())?
658            .framebuffer_object;
659
660        unsafe {
661            gl.bind_framebuffer(gl::FRAMEBUFFER, framebuffer);
662            gl.viewport(0, 0, size.width as i32, size.height as i32);
663            gl.scissor(0, 0, size.width as i32, size.height as i32);
664            gl.clear_color(0., 0., 0., !has_alpha as u32 as f32);
665            gl.clear_depth(1.);
666            gl.clear_stencil(0);
667            gl.clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT);
668            gl.clear_color(0., 0., 0., 0.);
669            debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
670        }
671
672        let default_vao = if let Some(vao) = WebGLImpl::create_vertex_array(&gl) {
673            unsafe { gl.bind_vertex_array(Some(vao.glow())) }
674            Some(vao.glow())
675        } else {
676            None
677        };
678        debug_assert_eq!(unsafe { gl.get_error() }, gl::NO_ERROR);
679
680        let state = GLState {
681            _gl_version: gl_version,
682            webgl_version,
683            requested_flags,
684            default_vao,
685            ..Default::default()
686        };
687        debug!("Created state {:?}", state);
688
689        state.restore_invariant(&gl);
690        debug_assert_eq!(unsafe { gl.get_error() }, gl::NO_ERROR);
691
692        self.contexts.insert(
693            context_id,
694            GLContextData {
695                ctx,
696                device,
697                gl,
698                state,
699                attributes,
700                marked_for_deletion: false,
701            },
702        );
703
704        self.cached_context_info.insert(
705            context_id,
706            WebGLContextInfo {
707                image_key: None,
708                size: size.to_i32(),
709                alpha: has_alpha,
710            },
711        );
712
713        Ok((context_id, limits))
714    }
715
716    /// Resizes a WebGLContext
717    fn resize_webgl_context(
718        &mut self,
719        context_id: WebGLContextId,
720        requested_size: Size2D<u32>,
721    ) -> Result<(), String> {
722        self.make_current_if_needed(context_id);
723
724        let Some(data) = self.contexts.get_mut(&context_id) else {
725            return Err("Missing WebGL context!".into());
726        };
727
728        let size = clamp_viewport(&data.gl, requested_size);
729        debug_assert_eq!(unsafe { data.gl.get_error() }, gl::NO_ERROR);
730
731        // Check to see if any of the current framebuffer bindings are the surface we're about to
732        // throw out. If so, we'll have to reset them after destroying the surface.
733        let framebuffer_rebinding_info =
734            FramebufferRebindingInfo::detect(&data.device, &data.ctx, &data.gl);
735
736        // Resize the swap chains
737        if let Some(swap_chain) = self.webrender_swap_chains.get(context_id) {
738            let alpha = data
739                .state
740                .requested_flags
741                .contains(ContextAttributeFlags::ALPHA);
742            let clear_color = [0.0, 0.0, 0.0, !alpha as i32 as f32];
743            swap_chain
744                .resize(&data.device, &mut data.ctx, size.to_i32())
745                .map_err(|err| format!("Failed to resize swap chain: {:?}", err))?;
746            swap_chain
747                .clear_surface(&data.device, &mut data.ctx, &data.gl, clear_color)
748                .map_err(|err| format!("Failed to clear resized swap chain: {:?}", err))?;
749        } else {
750            error!("Failed to find swap chain");
751        }
752
753        // Reset framebuffer bindings as appropriate.
754        framebuffer_rebinding_info.apply(&data.device, &data.ctx, &data.gl);
755        debug_assert_eq!(unsafe { data.gl.get_error() }, gl::NO_ERROR);
756
757        let has_alpha = data
758            .state
759            .requested_flags
760            .contains(ContextAttributeFlags::ALPHA);
761        self.update_webrender_image_for_context(context_id, size.to_i32(), has_alpha, None);
762
763        Ok(())
764    }
765
766    /// Note that rendering has finished in WebRender for this context. If the context
767    /// is marked for deletion, it will now be deleted.
768    fn handle_finished_rendering_to_context(&mut self, context_id: WebGLContextId) {
769        let marked_for_deletion = self
770            .contexts
771            .get(&context_id)
772            .is_some_and(|context_data| context_data.marked_for_deletion);
773        if marked_for_deletion {
774            self.remove_webgl_context(context_id);
775        }
776    }
777
778    /// Removes a WebGLContext and releases attached resources.
779    fn remove_webgl_context(&mut self, context_id: WebGLContextId) {
780        {
781            let mut busy_webgl_context_map = self.busy_webgl_context_map.write();
782            let entry = busy_webgl_context_map.entry(context_id);
783            match entry {
784                Entry::Vacant(..) => {},
785                Entry::Occupied(occupied_entry) if *occupied_entry.get() > 0 => {
786                    // WebRender is in the process of rendering this WebGL context, so wait until it
787                    // finishes in order to release it.
788                    if let Some(context_data) = self.contexts.get_mut(&context_id) {
789                        context_data.marked_for_deletion = true;
790                    }
791                    return;
792                },
793                Entry::Occupied(occupied_entry) => {
794                    occupied_entry.remove();
795                },
796            }
797        }
798
799        // Release webrender image keys.
800        if let Some(image_key) = self
801            .cached_context_info
802            .remove(&context_id)
803            .and_then(|info| info.image_key)
804        {
805            self.paint_api.delete_image(image_key);
806        }
807
808        if !self.contexts.contains_key(&context_id) {
809            return;
810        };
811
812        // We need to make the context current so its resources can be disposed of.
813        self.make_current_if_needed(context_id);
814
815        // Destroy WebXR layers associated with this context
816        #[cfg(feature = "webxr")]
817        {
818            // We must temporarily take the WebXRBridge, as we are passing self to a
819            // method on the bridge.
820            let mut webxr_bridge = self.webxr_bridge.take();
821            if let Some(webxr_bridge) = &mut webxr_bridge {
822                webxr_bridge.destroy_all_layers(self, context_id.into());
823            }
824            self.webxr_bridge = webxr_bridge;
825        }
826
827        // Release GL context.
828        let Some(mut data) = self.contexts.remove(&context_id) else {
829            return;
830        };
831
832        // Destroy the swap chains
833        self.webrender_swap_chains
834            .destroy(context_id, &data.device, &mut data.ctx)
835            .unwrap();
836
837        // Destroy the context
838        data.device.destroy_context(&mut data.ctx).unwrap();
839
840        // Removing a GLContext may make the current bound context_id dirty.
841        self.bound_context_id = None;
842    }
843
844    fn handle_swap_buffers(
845        &mut self,
846        canvas_epoch: Option<Epoch>,
847        context_ids: Vec<WebGLContextId>,
848        _sent_time: u64,
849    ) {
850        debug!("handle_swap_buffers()");
851        for context_id in context_ids {
852            self.make_current_if_needed(context_id)
853                .expect("Where's the GL data?");
854
855            let data = self
856                .contexts
857                .get_mut(&context_id)
858                .expect("Missing WebGL context");
859
860            // Some surfman backends fail to create the next surface while a GL error is pending.
861            let pending_error = unsafe { data.gl.get_error() };
862            debug_assert_eq!(pending_error, gl::NO_ERROR);
863            if pending_error != gl::NO_ERROR {
864                warn!("Discarding GL error {pending_error:#x} before swapping {context_id:?}");
865            }
866
867            // Check to see if any of the current framebuffer bindings are the surface we're about
868            // to swap out. If so, we'll have to reset them after destroying the surface.
869            let framebuffer_rebinding_info =
870                FramebufferRebindingInfo::detect(&data.device, &data.ctx, &data.gl);
871            debug_assert_eq!(unsafe { data.gl.get_error() }, gl::NO_ERROR);
872
873            debug!("Getting swap chain for {:?}", context_id);
874            let swap_chain = self
875                .webrender_swap_chains
876                .get(context_id)
877                .expect("Where's the swap chain?");
878
879            debug!("Swapping {:?}", context_id);
880            swap_chain
881                .swap_buffers(
882                    &data.device,
883                    &mut data.ctx,
884                    if data.attributes.preserve_drawing_buffer {
885                        PreserveBuffer::Yes(&data.gl)
886                    } else {
887                        PreserveBuffer::No
888                    },
889                )
890                .unwrap();
891            debug_assert_eq!(unsafe { data.gl.get_error() }, gl::NO_ERROR);
892
893            if !data.attributes.preserve_drawing_buffer {
894                debug!("Clearing {:?}", context_id);
895                let alpha = data
896                    .state
897                    .requested_flags
898                    .contains(ContextAttributeFlags::ALPHA);
899                let clear_color = [0.0, 0.0, 0.0, !alpha as i32 as f32];
900                swap_chain
901                    .clear_surface(&data.device, &mut data.ctx, &data.gl, clear_color)
902                    .unwrap();
903                debug_assert_eq!(unsafe { data.gl.get_error() }, gl::NO_ERROR);
904            }
905
906            // Rebind framebuffers as appropriate.
907            debug!("Rebinding {:?}", context_id);
908            framebuffer_rebinding_info.apply(&data.device, &data.ctx, &data.gl);
909            debug_assert_eq!(unsafe { data.gl.get_error() }, gl::NO_ERROR);
910
911            let SurfaceInfo {
912                size,
913                framebuffer_object,
914                id,
915                ..
916            } = data
917                .device
918                .context_surface_info(&data.ctx)
919                .unwrap()
920                .unwrap();
921            debug!(
922                "... rebound framebuffer {:?}, new back buffer surface is {:?}",
923                framebuffer_object, id
924            );
925
926            let has_alpha = data
927                .state
928                .requested_flags
929                .contains(ContextAttributeFlags::ALPHA);
930            self.update_webrender_image_for_context(context_id, size, has_alpha, canvas_epoch);
931        }
932    }
933
934    /// Which access mode to use
935    fn surface_access(&self) -> SurfaceAccess {
936        SurfaceAccess::GPUOnly
937    }
938
939    /// Gets a reference to a Context for a given WebGLContextId and makes it current if required.
940    pub(crate) fn make_current_if_needed(
941        &mut self,
942        context_id: WebGLContextId,
943    ) -> Option<&GLContextData> {
944        let data = self.contexts.get(&context_id);
945
946        if let Some(data) = data &&
947            Some(context_id) != self.bound_context_id
948        {
949            data.device.make_context_current(&data.ctx).unwrap();
950            self.bound_context_id = Some(context_id);
951        }
952
953        data
954    }
955
956    /// Gets a mutable reference to a GLContextWrapper for a WebGLContextId and makes it current if required.
957    pub(crate) fn make_current_if_needed_mut(
958        &mut self,
959        context_id: WebGLContextId,
960    ) -> Option<&mut GLContextData> {
961        let data = self.contexts.get_mut(&context_id);
962        if let Some(ref data) = data &&
963            Some(context_id) != self.bound_context_id
964        {
965            data.device.make_context_current(&data.ctx).unwrap();
966            self.bound_context_id = Some(context_id);
967        }
968
969        data
970    }
971
972    /// Tell WebRender to invalidate any cached tiles for a given `WebGLContextId`
973    /// when the underlying surface has changed e.g due to resize or buffer swap
974    fn update_webrender_image_for_context(
975        &mut self,
976        context_id: WebGLContextId,
977        size: Size2D<i32>,
978        has_alpha: bool,
979        canvas_epoch: Option<Epoch>,
980    ) {
981        let image_data = self.external_image_data(context_id);
982        let info = self.cached_context_info.get_mut(&context_id).unwrap();
983        info.size = size;
984        info.alpha = has_alpha;
985
986        if let Some(image_key) = info.image_key {
987            self.paint_api.update_image(
988                image_key,
989                info.image_descriptor(),
990                image_data,
991                canvas_epoch,
992            );
993        }
994    }
995
996    /// Helper function to create a `ImageData::External` instance.
997    fn external_image_data(&self, context_id: WebGLContextId) -> SerializableImageData {
998        // TODO(pcwalton): Add `GL_TEXTURE_EXTERNAL_OES`?
999        let device = self.device_for_context(context_id);
1000        let image_buffer_kind = match device.surface_gl_texture_target() {
1001            gl::TEXTURE_RECTANGLE => ImageBufferKind::TextureRect,
1002            _ => ImageBufferKind::Texture2D,
1003        };
1004
1005        let data = ExternalImageData {
1006            id: ExternalImageId(context_id.0),
1007            channel_index: 0,
1008            image_type: ExternalImageType::TextureHandle(image_buffer_kind),
1009            normalized_uvs: false,
1010        };
1011        SerializableImageData::External(data)
1012    }
1013
1014    /// Gets the GLSL Version supported by a GLContext.
1015    fn get_glsl_version(gl: &Gl) -> WebGLSLVersion {
1016        let version = unsafe { gl.get_parameter_string(gl::SHADING_LANGUAGE_VERSION) };
1017        // Fomat used by SHADING_LANGUAGE_VERSION query : major.minor[.release] [vendor info]
1018        let mut values = version.split(&['.', ' '][..]);
1019        let major = values
1020            .next()
1021            .and_then(|v| v.parse::<u32>().ok())
1022            .unwrap_or(1);
1023        let minor = values
1024            .next()
1025            .and_then(|v| v.parse::<u32>().ok())
1026            .unwrap_or(20);
1027
1028        WebGLSLVersion { major, minor }
1029    }
1030
1031    fn handle_set_image_key(&mut self, context_id: WebGLContextId, image_key: ImageKey) {
1032        let external_image_data = self.external_image_data(context_id);
1033        let Some(info) = self.cached_context_info.get_mut(&context_id) else {
1034            self.paint_api.delete_image(image_key);
1035            return;
1036        };
1037
1038        if let Some(old_image_key) = info.image_key.replace(image_key) {
1039            self.paint_api.delete_image(old_image_key);
1040            return;
1041        }
1042
1043        self.paint_api.add_image(
1044            image_key,
1045            info.image_descriptor(),
1046            external_image_data,
1047            false,
1048        );
1049    }
1050}
1051
1052/// Helper struct to store cached WebGLContext information.
1053struct WebGLContextInfo {
1054    image_key: Option<ImageKey>,
1055    size: Size2D<i32>,
1056    alpha: bool,
1057}
1058
1059impl WebGLContextInfo {
1060    /// Helper function to create a `ImageDescriptor`.
1061    fn image_descriptor(&self) -> ImageDescriptor {
1062        let mut flags = ImageDescriptorFlags::empty();
1063        flags.set(ImageDescriptorFlags::IS_OPAQUE, !self.alpha);
1064        ImageDescriptor {
1065            size: DeviceIntSize::new(self.size.width, self.size.height),
1066            stride: None,
1067            format: ImageFormat::BGRA8,
1068            offset: 0,
1069            flags,
1070        }
1071    }
1072}
1073
1074/// WebGL Commands Implementation
1075pub struct WebGLImpl;
1076
1077impl WebGLImpl {
1078    pub fn apply(
1079        device: &Device,
1080        ctx: &Context,
1081        gl: &Gl,
1082        state: &mut GLState,
1083        attributes: &GLContextAttributes,
1084        command: WebGLCommand,
1085        _backtrace: WebGLCommandBacktrace,
1086    ) {
1087        // Ensure there are no pending GL errors from other parts of the pipeline.
1088        debug_assert_eq!(unsafe { gl.get_error() }, gl::NO_ERROR);
1089
1090        match command {
1091            WebGLCommand::GetContextAttributes(ref sender) => sender.send(*attributes).unwrap(),
1092            WebGLCommand::ActiveTexture(target) => unsafe { gl.active_texture(target) },
1093            WebGLCommand::AttachShader(program_id, shader_id) => unsafe {
1094                gl.attach_shader(program_id.glow(), shader_id.glow())
1095            },
1096            WebGLCommand::DetachShader(program_id, shader_id) => unsafe {
1097                gl.detach_shader(program_id.glow(), shader_id.glow())
1098            },
1099            WebGLCommand::BindAttribLocation(program_id, index, ref name) => unsafe {
1100                gl.bind_attrib_location(program_id.glow(), index, &to_name_in_compiled_shader(name))
1101            },
1102            WebGLCommand::BlendColor(r, g, b, a) => unsafe { gl.blend_color(r, g, b, a) },
1103            WebGLCommand::BlendEquation(mode) => unsafe { gl.blend_equation(mode) },
1104            WebGLCommand::BlendEquationSeparate(mode_rgb, mode_alpha) => unsafe {
1105                gl.blend_equation_separate(mode_rgb, mode_alpha)
1106            },
1107            WebGLCommand::BlendFunc(src, dest) => unsafe { gl.blend_func(src, dest) },
1108            WebGLCommand::BlendFuncSeparate(src_rgb, dest_rgb, src_alpha, dest_alpha) => unsafe {
1109                gl.blend_func_separate(src_rgb, dest_rgb, src_alpha, dest_alpha)
1110            },
1111            WebGLCommand::BufferData(buffer_type, ref receiver, usage) => unsafe {
1112                gl.buffer_data_u8_slice(buffer_type, &receiver.recv().unwrap(), usage)
1113            },
1114            WebGLCommand::BufferSubData(buffer_type, offset, ref receiver) => unsafe {
1115                gl.buffer_sub_data_u8_slice(buffer_type, offset as i32, &receiver.recv().unwrap())
1116            },
1117            WebGLCommand::CopyBufferSubData(src, dst, src_offset, dst_offset, size) => {
1118                unsafe {
1119                    gl.copy_buffer_sub_data(
1120                        src,
1121                        dst,
1122                        src_offset as i32,
1123                        dst_offset as i32,
1124                        size as i32,
1125                    )
1126                };
1127            },
1128            WebGLCommand::GetBufferSubData(buffer_type, offset, length, ref sender) => unsafe {
1129                let ptr = gl.map_buffer_range(
1130                    buffer_type,
1131                    offset as i32,
1132                    length as i32,
1133                    gl::MAP_READ_BIT,
1134                );
1135                let data: &[u8] = slice::from_raw_parts(ptr as _, length);
1136                let buffer = GenericSharedMemory::from_bytes(data);
1137                sender.send(buffer).unwrap();
1138                gl.unmap_buffer(buffer_type);
1139            },
1140            WebGLCommand::Clear(mask) => {
1141                unsafe { gl.clear(mask) };
1142            },
1143            WebGLCommand::ClearColor(r, g, b, a) => {
1144                state.clear_color = (r, g, b, a);
1145                unsafe { gl.clear_color(r, g, b, a) };
1146            },
1147            WebGLCommand::ClearDepth(depth) => {
1148                let value = depth.clamp(0., 1.) as f64;
1149                state.depth_clear_value = value;
1150                unsafe { gl.clear_depth(value) }
1151            },
1152            WebGLCommand::ClearStencil(stencil) => {
1153                state.stencil_clear_value = stencil;
1154                unsafe { gl.clear_stencil(stencil) };
1155            },
1156            WebGLCommand::ColorMask(r, g, b, a) => {
1157                state.color_write_mask = [r, g, b, a];
1158                state.restore_alpha_invariant(gl);
1159            },
1160            WebGLCommand::CopyTexImage2D(
1161                target,
1162                level,
1163                internal_format,
1164                x,
1165                y,
1166                width,
1167                height,
1168                border,
1169            ) => unsafe {
1170                gl.copy_tex_image_2d(target, level, internal_format, x, y, width, height, border)
1171            },
1172            WebGLCommand::CopyTexSubImage2D(
1173                target,
1174                level,
1175                xoffset,
1176                yoffset,
1177                x,
1178                y,
1179                width,
1180                height,
1181            ) => unsafe {
1182                gl.copy_tex_sub_image_2d(target, level, xoffset, yoffset, x, y, width, height)
1183            },
1184            WebGLCommand::CullFace(mode) => unsafe { gl.cull_face(mode) },
1185            WebGLCommand::DepthFunc(func) => unsafe { gl.depth_func(func) },
1186            WebGLCommand::DepthMask(flag) => {
1187                state.depth_write_mask = flag;
1188                state.restore_depth_invariant(gl);
1189            },
1190            WebGLCommand::DepthRange(near, far) => unsafe {
1191                gl.depth_range(near.clamp(0., 1.) as f64, far.clamp(0., 1.) as f64)
1192            },
1193            WebGLCommand::Disable(cap) => match cap {
1194                gl::SCISSOR_TEST => {
1195                    state.scissor_test_enabled = false;
1196                    state.restore_scissor_invariant(gl);
1197                },
1198                gl::DEPTH_TEST => {
1199                    state.depth_test_enabled = false;
1200                    state.restore_depth_invariant(gl);
1201                },
1202                gl::STENCIL_TEST => {
1203                    state.stencil_test_enabled = false;
1204                    state.restore_stencil_invariant(gl);
1205                },
1206                _ => unsafe { gl.disable(cap) },
1207            },
1208            WebGLCommand::Enable(cap) => match cap {
1209                gl::SCISSOR_TEST => {
1210                    state.scissor_test_enabled = true;
1211                    state.restore_scissor_invariant(gl);
1212                },
1213                gl::DEPTH_TEST => {
1214                    state.depth_test_enabled = true;
1215                    state.restore_depth_invariant(gl);
1216                },
1217                gl::STENCIL_TEST => {
1218                    state.stencil_test_enabled = true;
1219                    state.restore_stencil_invariant(gl);
1220                },
1221                _ => unsafe { gl.enable(cap) },
1222            },
1223            WebGLCommand::FramebufferRenderbuffer(target, attachment, renderbuffertarget, rb) => {
1224                let attach = |attachment| unsafe {
1225                    gl.framebuffer_renderbuffer(
1226                        target,
1227                        attachment,
1228                        renderbuffertarget,
1229                        rb.map(WebGLRenderbufferId::glow),
1230                    )
1231                };
1232                if attachment == gl::DEPTH_STENCIL_ATTACHMENT {
1233                    attach(gl::DEPTH_ATTACHMENT);
1234                    attach(gl::STENCIL_ATTACHMENT);
1235                } else {
1236                    attach(attachment);
1237                }
1238            },
1239            WebGLCommand::FramebufferTexture2D(target, attachment, textarget, texture, level) => {
1240                let attach = |attachment| unsafe {
1241                    gl.framebuffer_texture_2d(
1242                        target,
1243                        attachment,
1244                        textarget,
1245                        texture.map(WebGLTextureId::glow),
1246                        level,
1247                    )
1248                };
1249                if attachment == gl::DEPTH_STENCIL_ATTACHMENT {
1250                    attach(gl::DEPTH_ATTACHMENT);
1251                    attach(gl::STENCIL_ATTACHMENT);
1252                } else {
1253                    attach(attachment)
1254                }
1255            },
1256            WebGLCommand::FrontFace(mode) => unsafe { gl.front_face(mode) },
1257            WebGLCommand::DisableVertexAttribArray(attrib_id) => unsafe {
1258                gl.disable_vertex_attrib_array(attrib_id)
1259            },
1260            WebGLCommand::EnableVertexAttribArray(attrib_id) => unsafe {
1261                gl.enable_vertex_attrib_array(attrib_id)
1262            },
1263            WebGLCommand::Hint(name, val) => unsafe { gl.hint(name, val) },
1264            WebGLCommand::LineWidth(width) => {
1265                unsafe { gl.line_width(width) };
1266                // In OpenGL Core Profile >3.2, any non-1.0 value will generate INVALID_VALUE.
1267                if width != 1.0 {
1268                    let _ = unsafe { gl.get_error() };
1269                }
1270            },
1271            WebGLCommand::PixelStorei(name, val) => unsafe { gl.pixel_store_i32(name, val) },
1272            WebGLCommand::PolygonOffset(factor, units) => unsafe {
1273                gl.polygon_offset(factor, units)
1274            },
1275            WebGLCommand::ReadPixels(rect, format, pixel_type, ref sender) => {
1276                let len =
1277                    read_pixels_bytes_per_pixel(format, pixel_type) * rect.size.area() as usize;
1278                let mut pixels = vec![0; len];
1279                unsafe {
1280                    // We don't want any alignment padding on pixel rows.
1281                    gl.pixel_store_i32(glow::PACK_ALIGNMENT, 1);
1282                    gl.read_pixels(
1283                        rect.origin.x as i32,
1284                        rect.origin.y as i32,
1285                        rect.size.width as i32,
1286                        rect.size.height as i32,
1287                        format,
1288                        pixel_type,
1289                        glow::PixelPackData::Slice(Some(&mut pixels)),
1290                    )
1291                };
1292                let alpha_mode = match (attributes.alpha, attributes.premultiplied_alpha) {
1293                    (true, premultiplied) => SnapshotAlphaMode::Transparent { premultiplied },
1294                    (false, _) => SnapshotAlphaMode::Opaque,
1295                };
1296                sender
1297                    .send((GenericSharedMemory::from_vec(pixels), alpha_mode))
1298                    .unwrap();
1299            },
1300            WebGLCommand::ReadPixelsPP(rect, format, pixel_type, offset) => unsafe {
1301                gl.read_pixels(
1302                    rect.origin.x,
1303                    rect.origin.y,
1304                    rect.size.width,
1305                    rect.size.height,
1306                    format,
1307                    pixel_type,
1308                    glow::PixelPackData::BufferOffset(offset as u32),
1309                );
1310            },
1311            WebGLCommand::RenderbufferStorage(target, format, width, height) => unsafe {
1312                gl.renderbuffer_storage(target, format, width, height)
1313            },
1314            WebGLCommand::RenderbufferStorageMultisample(
1315                target,
1316                samples,
1317                format,
1318                width,
1319                height,
1320            ) => unsafe {
1321                gl.renderbuffer_storage_multisample(target, samples, format, width, height)
1322            },
1323            WebGLCommand::SampleCoverage(value, invert) => unsafe {
1324                gl.sample_coverage(value, invert)
1325            },
1326            WebGLCommand::Scissor(x, y, width, height) => {
1327                // FIXME(nox): Kinda unfortunate that some u32 values could
1328                // end up as negative numbers here, but I don't even think
1329                // that can happen in the real world.
1330                unsafe { gl.scissor(x, y, width as i32, height as i32) };
1331            },
1332            WebGLCommand::StencilFunc(func, ref_, mask) => {
1333                state.stencil_value_mask = (mask, mask);
1334                unsafe { gl.stencil_func(func, ref_, mask) }
1335            },
1336            WebGLCommand::StencilFuncSeparate(face, func, ref_, mask) => {
1337                if face != gl::BACK {
1338                    state.stencil_value_mask.0 = mask;
1339                }
1340                if face != gl::FRONT {
1341                    state.stencil_value_mask.1 = mask;
1342                }
1343                unsafe { gl.stencil_func_separate(face, func, ref_, mask) }
1344            },
1345            WebGLCommand::StencilMask(mask) => {
1346                state.stencil_write_mask = (mask, mask);
1347                state.restore_stencil_invariant(gl);
1348            },
1349            WebGLCommand::StencilMaskSeparate(face, mask) => {
1350                match face {
1351                    gl::FRONT => state.stencil_write_mask.0 = mask,
1352                    gl::BACK => state.stencil_write_mask.1 = mask,
1353                    gl::FRONT_AND_BACK => state.stencil_write_mask = (mask, mask),
1354                    _ => debug_assert!(
1355                        false,
1356                        "stencilMaskSeparate face {face:#x} should have been rejected by the DOM"
1357                    ),
1358                }
1359                state.restore_stencil_invariant(gl);
1360            },
1361            WebGLCommand::StencilOp(fail, zfail, zpass) => unsafe {
1362                gl.stencil_op(fail, zfail, zpass)
1363            },
1364            WebGLCommand::StencilOpSeparate(face, fail, zfail, zpass) => unsafe {
1365                gl.stencil_op_separate(face, fail, zfail, zpass)
1366            },
1367            WebGLCommand::GetRenderbufferParameter(target, pname, ref chan) => {
1368                Self::get_renderbuffer_parameter(gl, target, pname, chan)
1369            },
1370            WebGLCommand::CreateTransformFeedback(ref sender) => {
1371                let value = unsafe { gl.create_transform_feedback() }.ok();
1372                sender
1373                    .send(value.map(|ntf| ntf.0.get()).unwrap_or_default())
1374                    .unwrap()
1375            },
1376            WebGLCommand::DeleteTransformFeedback(id) => {
1377                if let Some(tf) = NonZeroU32::new(id) {
1378                    unsafe { gl.delete_transform_feedback(NativeTransformFeedback(tf)) };
1379                }
1380            },
1381            WebGLCommand::IsTransformFeedback(id, ref sender) => {
1382                let value = NonZeroU32::new(id)
1383                    .map(|id| unsafe { gl.is_transform_feedback(NativeTransformFeedback(id)) })
1384                    .unwrap_or_default();
1385                sender.send(value).unwrap()
1386            },
1387            WebGLCommand::BindTransformFeedback(target, id) => {
1388                unsafe {
1389                    gl.bind_transform_feedback(
1390                        target,
1391                        NonZeroU32::new(id).map(NativeTransformFeedback),
1392                    )
1393                };
1394            },
1395            WebGLCommand::BeginTransformFeedback(mode) => {
1396                unsafe { gl.begin_transform_feedback(mode) };
1397            },
1398            WebGLCommand::EndTransformFeedback() => {
1399                unsafe { gl.end_transform_feedback() };
1400            },
1401            WebGLCommand::PauseTransformFeedback() => {
1402                unsafe { gl.pause_transform_feedback() };
1403            },
1404            WebGLCommand::ResumeTransformFeedback() => {
1405                unsafe { gl.resume_transform_feedback() };
1406            },
1407            WebGLCommand::GetTransformFeedbackVarying(program, index, ref sender) => {
1408                let ActiveTransformFeedback { size, tftype, name } =
1409                    unsafe { gl.get_transform_feedback_varying(program.glow(), index) }.unwrap();
1410                // We need to split, because the name starts with '_u' prefix.
1411                let name = from_name_in_compiled_shader(&name);
1412                sender.send((size, tftype, name)).unwrap();
1413            },
1414            WebGLCommand::TransformFeedbackVaryings(program, ref varyings, buffer_mode) => {
1415                let varyings: Vec<String> = varyings
1416                    .iter()
1417                    .map(|varying| to_name_in_compiled_shader(varying))
1418                    .collect();
1419                let varyings_refs: Vec<&str> = varyings.iter().map(String::as_ref).collect();
1420                unsafe {
1421                    gl.transform_feedback_varyings(
1422                        program.glow(),
1423                        varyings_refs.as_slice(),
1424                        buffer_mode,
1425                    )
1426                };
1427            },
1428            WebGLCommand::GetFramebufferAttachmentParameter(
1429                target,
1430                attachment,
1431                pname,
1432                ref chan,
1433            ) => Self::get_framebuffer_attachment_parameter(gl, target, attachment, pname, chan),
1434            WebGLCommand::GetShaderPrecisionFormat(shader_type, precision_type, ref chan) => {
1435                Self::shader_precision_format(gl, shader_type, precision_type, chan)
1436            },
1437            WebGLCommand::GetExtensions(ref chan) => Self::get_extensions(gl, chan),
1438            WebGLCommand::GetFragDataLocation(program_id, ref name, ref sender) => {
1439                let location = unsafe {
1440                    gl.get_frag_data_location(program_id.glow(), &to_name_in_compiled_shader(name))
1441                };
1442                sender.send(location).unwrap();
1443            },
1444            WebGLCommand::GetUniformLocation(program_id, ref name, ref chan) => {
1445                Self::uniform_location(gl, program_id, name, chan)
1446            },
1447            WebGLCommand::GetShaderInfoLog(shader_id, ref chan) => {
1448                Self::shader_info_log(gl, shader_id, chan)
1449            },
1450            WebGLCommand::GetProgramInfoLog(program_id, ref chan) => {
1451                Self::program_info_log(gl, program_id, chan)
1452            },
1453            WebGLCommand::CompileShader(shader_id, ref source) => {
1454                Self::compile_shader(gl, shader_id, source)
1455            },
1456            WebGLCommand::CreateBuffer(ref chan) => Self::create_buffer(gl, chan),
1457            WebGLCommand::CreateFramebuffer(ref chan) => Self::create_framebuffer(gl, chan),
1458            WebGLCommand::CreateRenderbuffer(ref chan) => Self::create_renderbuffer(gl, chan),
1459            WebGLCommand::CreateTexture(ref chan) => Self::create_texture(gl, chan),
1460            WebGLCommand::CreateProgram(ref chan) => Self::create_program(gl, chan),
1461            WebGLCommand::CreateShader(shader_type, ref chan) => {
1462                Self::create_shader(gl, shader_type, chan)
1463            },
1464            WebGLCommand::DeleteBuffer(id) => unsafe { gl.delete_buffer(id.glow()) },
1465            WebGLCommand::DeleteFramebuffer(id) => unsafe { gl.delete_framebuffer(id.glow()) },
1466            WebGLCommand::DeleteRenderbuffer(id) => unsafe { gl.delete_renderbuffer(id.glow()) },
1467            WebGLCommand::DeleteTexture(id) => unsafe { gl.delete_texture(id.glow()) },
1468            WebGLCommand::DeleteProgram(id) => unsafe { gl.delete_program(id.glow()) },
1469            WebGLCommand::DeleteShader(id) => unsafe { gl.delete_shader(id.glow()) },
1470            WebGLCommand::BindBuffer(target, id) => unsafe {
1471                gl.bind_buffer(target, id.map(WebGLBufferId::glow))
1472            },
1473            WebGLCommand::BindFramebuffer(target, request) => {
1474                Self::bind_framebuffer(gl, target, request, ctx, device, state)
1475            },
1476            WebGLCommand::BindRenderbuffer(target, id) => unsafe {
1477                gl.bind_renderbuffer(target, id.map(WebGLRenderbufferId::glow))
1478            },
1479            WebGLCommand::BindTexture(target, id) => unsafe {
1480                gl.bind_texture(target, id.map(WebGLTextureId::glow))
1481            },
1482            WebGLCommand::BlitFrameBuffer(
1483                src_x0,
1484                src_y0,
1485                src_x1,
1486                src_y1,
1487                dst_x0,
1488                dst_y0,
1489                dst_x1,
1490                dst_y1,
1491                mask,
1492                filter,
1493            ) => unsafe {
1494                gl.blit_framebuffer(
1495                    src_x0, src_y0, src_x1, src_y1, dst_x0, dst_y0, dst_x1, dst_y1, mask, filter,
1496                );
1497            },
1498            WebGLCommand::Uniform1f(uniform_id, v) => unsafe {
1499                gl.uniform_1_f32(native_uniform_location(uniform_id).as_ref(), v)
1500            },
1501            WebGLCommand::Uniform1fv(uniform_id, ref v) => unsafe {
1502                gl.uniform_1_f32_slice(native_uniform_location(uniform_id).as_ref(), v)
1503            },
1504            WebGLCommand::Uniform1i(uniform_id, v) => unsafe {
1505                gl.uniform_1_i32(native_uniform_location(uniform_id).as_ref(), v)
1506            },
1507            WebGLCommand::Uniform1iv(uniform_id, ref v) => unsafe {
1508                gl.uniform_1_i32_slice(native_uniform_location(uniform_id).as_ref(), v)
1509            },
1510            WebGLCommand::Uniform1ui(uniform_id, v) => unsafe {
1511                gl.uniform_1_u32(native_uniform_location(uniform_id).as_ref(), v)
1512            },
1513            WebGLCommand::Uniform1uiv(uniform_id, ref v) => unsafe {
1514                gl.uniform_1_u32_slice(native_uniform_location(uniform_id).as_ref(), v)
1515            },
1516            WebGLCommand::Uniform2f(uniform_id, x, y) => unsafe {
1517                gl.uniform_2_f32(native_uniform_location(uniform_id).as_ref(), x, y)
1518            },
1519            WebGLCommand::Uniform2fv(uniform_id, ref v) => unsafe {
1520                gl.uniform_2_f32_slice(native_uniform_location(uniform_id).as_ref(), v)
1521            },
1522            WebGLCommand::Uniform2i(uniform_id, x, y) => unsafe {
1523                gl.uniform_2_i32(native_uniform_location(uniform_id).as_ref(), x, y)
1524            },
1525            WebGLCommand::Uniform2iv(uniform_id, ref v) => unsafe {
1526                gl.uniform_2_i32_slice(native_uniform_location(uniform_id).as_ref(), v)
1527            },
1528            WebGLCommand::Uniform2ui(uniform_id, x, y) => unsafe {
1529                gl.uniform_2_u32(native_uniform_location(uniform_id).as_ref(), x, y)
1530            },
1531            WebGLCommand::Uniform2uiv(uniform_id, ref v) => unsafe {
1532                gl.uniform_2_u32_slice(native_uniform_location(uniform_id).as_ref(), v)
1533            },
1534            WebGLCommand::Uniform3f(uniform_id, x, y, z) => unsafe {
1535                gl.uniform_3_f32(native_uniform_location(uniform_id).as_ref(), x, y, z)
1536            },
1537            WebGLCommand::Uniform3fv(uniform_id, ref v) => unsafe {
1538                gl.uniform_3_f32_slice(native_uniform_location(uniform_id).as_ref(), v)
1539            },
1540            WebGLCommand::Uniform3i(uniform_id, x, y, z) => unsafe {
1541                gl.uniform_3_i32(native_uniform_location(uniform_id).as_ref(), x, y, z)
1542            },
1543            WebGLCommand::Uniform3iv(uniform_id, ref v) => unsafe {
1544                gl.uniform_3_i32_slice(native_uniform_location(uniform_id).as_ref(), v)
1545            },
1546            WebGLCommand::Uniform3ui(uniform_id, x, y, z) => unsafe {
1547                gl.uniform_3_u32(native_uniform_location(uniform_id).as_ref(), x, y, z)
1548            },
1549            WebGLCommand::Uniform3uiv(uniform_id, ref v) => unsafe {
1550                gl.uniform_3_u32_slice(native_uniform_location(uniform_id).as_ref(), v)
1551            },
1552            WebGLCommand::Uniform4f(uniform_id, x, y, z, w) => unsafe {
1553                gl.uniform_4_f32(native_uniform_location(uniform_id).as_ref(), x, y, z, w)
1554            },
1555            WebGLCommand::Uniform4fv(uniform_id, ref v) => unsafe {
1556                gl.uniform_4_f32_slice(native_uniform_location(uniform_id).as_ref(), v)
1557            },
1558            WebGLCommand::Uniform4i(uniform_id, x, y, z, w) => unsafe {
1559                gl.uniform_4_i32(native_uniform_location(uniform_id).as_ref(), x, y, z, w)
1560            },
1561            WebGLCommand::Uniform4iv(uniform_id, ref v) => unsafe {
1562                gl.uniform_4_i32_slice(native_uniform_location(uniform_id).as_ref(), v)
1563            },
1564            WebGLCommand::Uniform4ui(uniform_id, x, y, z, w) => unsafe {
1565                gl.uniform_4_u32(native_uniform_location(uniform_id).as_ref(), x, y, z, w)
1566            },
1567            WebGLCommand::Uniform4uiv(uniform_id, ref v) => unsafe {
1568                gl.uniform_4_u32_slice(native_uniform_location(uniform_id).as_ref(), v)
1569            },
1570            WebGLCommand::UniformMatrix2fv(uniform_id, ref v) => unsafe {
1571                gl.uniform_matrix_2_f32_slice(
1572                    native_uniform_location(uniform_id).as_ref(),
1573                    false,
1574                    v,
1575                )
1576            },
1577            WebGLCommand::UniformMatrix3fv(uniform_id, ref v) => unsafe {
1578                gl.uniform_matrix_3_f32_slice(
1579                    native_uniform_location(uniform_id).as_ref(),
1580                    false,
1581                    v,
1582                )
1583            },
1584            WebGLCommand::UniformMatrix4fv(uniform_id, ref v) => unsafe {
1585                gl.uniform_matrix_4_f32_slice(
1586                    native_uniform_location(uniform_id).as_ref(),
1587                    false,
1588                    v,
1589                )
1590            },
1591            WebGLCommand::UniformMatrix3x2fv(uniform_id, ref v) => unsafe {
1592                gl.uniform_matrix_3x2_f32_slice(
1593                    native_uniform_location(uniform_id).as_ref(),
1594                    false,
1595                    v,
1596                )
1597            },
1598            WebGLCommand::UniformMatrix4x2fv(uniform_id, ref v) => unsafe {
1599                gl.uniform_matrix_4x2_f32_slice(
1600                    native_uniform_location(uniform_id).as_ref(),
1601                    false,
1602                    v,
1603                )
1604            },
1605            WebGLCommand::UniformMatrix2x3fv(uniform_id, ref v) => unsafe {
1606                gl.uniform_matrix_2x3_f32_slice(
1607                    native_uniform_location(uniform_id).as_ref(),
1608                    false,
1609                    v,
1610                )
1611            },
1612            WebGLCommand::UniformMatrix4x3fv(uniform_id, ref v) => unsafe {
1613                gl.uniform_matrix_4x3_f32_slice(
1614                    native_uniform_location(uniform_id).as_ref(),
1615                    false,
1616                    v,
1617                )
1618            },
1619            WebGLCommand::UniformMatrix2x4fv(uniform_id, ref v) => unsafe {
1620                gl.uniform_matrix_2x4_f32_slice(
1621                    native_uniform_location(uniform_id).as_ref(),
1622                    false,
1623                    v,
1624                )
1625            },
1626            WebGLCommand::UniformMatrix3x4fv(uniform_id, ref v) => unsafe {
1627                gl.uniform_matrix_3x4_f32_slice(
1628                    native_uniform_location(uniform_id).as_ref(),
1629                    false,
1630                    v,
1631                )
1632            },
1633            WebGLCommand::ValidateProgram(program_id) => unsafe {
1634                gl.validate_program(program_id.glow())
1635            },
1636            WebGLCommand::VertexAttrib(attrib_id, x, y, z, w) => unsafe {
1637                gl.vertex_attrib_4_f32(attrib_id, x, y, z, w)
1638            },
1639            WebGLCommand::VertexAttribI(attrib_id, x, y, z, w) => unsafe {
1640                gl.vertex_attrib_4_i32(attrib_id, x, y, z, w)
1641            },
1642            WebGLCommand::VertexAttribU(attrib_id, x, y, z, w) => unsafe {
1643                gl.vertex_attrib_4_u32(attrib_id, x, y, z, w)
1644            },
1645            WebGLCommand::VertexAttribPointer2f(attrib_id, size, normalized, stride, offset) => unsafe {
1646                gl.vertex_attrib_pointer_f32(
1647                    attrib_id,
1648                    size,
1649                    gl::FLOAT,
1650                    normalized,
1651                    stride,
1652                    offset as _,
1653                )
1654            },
1655            WebGLCommand::VertexAttribPointer(
1656                attrib_id,
1657                size,
1658                data_type,
1659                normalized,
1660                stride,
1661                offset,
1662            ) => unsafe {
1663                gl.vertex_attrib_pointer_f32(
1664                    attrib_id,
1665                    size,
1666                    data_type,
1667                    normalized,
1668                    stride,
1669                    offset as _,
1670                )
1671            },
1672            WebGLCommand::VertexAttribIPointer(attrib_id, size, data_type, stride, offset) => unsafe {
1673                gl.vertex_attrib_pointer_i32(attrib_id, size, data_type, stride, offset as _)
1674            },
1675            WebGLCommand::SetViewport(x, y, width, height) => unsafe {
1676                gl.viewport(x, y, width, height)
1677            },
1678            WebGLCommand::TexImage3D {
1679                target,
1680                level,
1681                internal_format,
1682                size,
1683                depth,
1684                format,
1685                data_type,
1686                effective_data_type,
1687                unpacking_alignment,
1688                alpha_treatment,
1689                y_axis_treatment,
1690                pixel_format,
1691                ref data,
1692            } => {
1693                let pixels = prepare_pixels(
1694                    internal_format,
1695                    data_type,
1696                    size,
1697                    unpacking_alignment,
1698                    alpha_treatment,
1699                    y_axis_treatment,
1700                    pixel_format,
1701                    Cow::Borrowed(data),
1702                );
1703
1704                unsafe {
1705                    gl.pixel_store_i32(gl::UNPACK_ALIGNMENT, unpacking_alignment as i32);
1706                    gl.tex_image_3d(
1707                        target,
1708                        level as i32,
1709                        internal_format.as_gl_constant() as i32,
1710                        size.width as i32,
1711                        size.height as i32,
1712                        depth as i32,
1713                        0,
1714                        format.as_gl_constant(),
1715                        effective_data_type,
1716                        PixelUnpackData::Slice(Some(&pixels)),
1717                    );
1718                }
1719            },
1720            WebGLCommand::TexImage2D {
1721                target,
1722                level,
1723                internal_format,
1724                size,
1725                format,
1726                data_type,
1727                effective_data_type,
1728                unpacking_alignment,
1729                alpha_treatment,
1730                y_axis_treatment,
1731                pixel_format,
1732                ref data,
1733            } => {
1734                let pixels = prepare_pixels(
1735                    internal_format,
1736                    data_type,
1737                    size,
1738                    unpacking_alignment,
1739                    alpha_treatment,
1740                    y_axis_treatment,
1741                    pixel_format,
1742                    Cow::Borrowed(data),
1743                );
1744
1745                unsafe {
1746                    gl.pixel_store_i32(gl::UNPACK_ALIGNMENT, unpacking_alignment as i32);
1747                    gl.tex_image_2d(
1748                        target,
1749                        level as i32,
1750                        internal_format.as_gl_constant() as i32,
1751                        size.width as i32,
1752                        size.height as i32,
1753                        0,
1754                        format.as_gl_constant(),
1755                        effective_data_type,
1756                        PixelUnpackData::Slice(Some(&pixels)),
1757                    );
1758                }
1759            },
1760            WebGLCommand::TexImage2DPBO {
1761                target,
1762                level,
1763                internal_format,
1764                size,
1765                format,
1766                effective_data_type,
1767                unpacking_alignment,
1768                offset,
1769            } => unsafe {
1770                gl.pixel_store_i32(gl::UNPACK_ALIGNMENT, unpacking_alignment as i32);
1771
1772                gl.tex_image_2d(
1773                    target,
1774                    level as i32,
1775                    internal_format.as_gl_constant() as i32,
1776                    size.width as i32,
1777                    size.height as i32,
1778                    0,
1779                    format.as_gl_constant(),
1780                    effective_data_type,
1781                    PixelUnpackData::BufferOffset(offset as u32),
1782                );
1783            },
1784            WebGLCommand::TexSubImage2D {
1785                target,
1786                level,
1787                xoffset,
1788                yoffset,
1789                size,
1790                format,
1791                data_type,
1792                effective_data_type,
1793                unpacking_alignment,
1794                alpha_treatment,
1795                y_axis_treatment,
1796                pixel_format,
1797                ref data,
1798            } => {
1799                let pixels = prepare_pixels(
1800                    format,
1801                    data_type,
1802                    size,
1803                    unpacking_alignment,
1804                    alpha_treatment,
1805                    y_axis_treatment,
1806                    pixel_format,
1807                    Cow::Borrowed(data),
1808                );
1809
1810                unsafe {
1811                    gl.pixel_store_i32(gl::UNPACK_ALIGNMENT, unpacking_alignment as i32);
1812                    gl.tex_sub_image_2d(
1813                        target,
1814                        level as i32,
1815                        xoffset,
1816                        yoffset,
1817                        size.width as i32,
1818                        size.height as i32,
1819                        format.as_gl_constant(),
1820                        effective_data_type,
1821                        glow::PixelUnpackData::Slice(Some(&pixels)),
1822                    );
1823                }
1824            },
1825            WebGLCommand::CompressedTexImage2D {
1826                target,
1827                level,
1828                internal_format,
1829                size,
1830                ref data,
1831            } => unsafe {
1832                gl.compressed_tex_image_2d(
1833                    target,
1834                    level as i32,
1835                    internal_format as i32,
1836                    size.width as i32,
1837                    size.height as i32,
1838                    0,
1839                    data.len() as i32,
1840                    data,
1841                )
1842            },
1843            WebGLCommand::CompressedTexSubImage2D {
1844                target,
1845                level,
1846                xoffset,
1847                yoffset,
1848                size,
1849                format,
1850                ref data,
1851            } => {
1852                unsafe {
1853                    gl.compressed_tex_sub_image_2d(
1854                        target,
1855                        level,
1856                        xoffset,
1857                        yoffset,
1858                        size.width as i32,
1859                        size.height as i32,
1860                        format,
1861                        glow::CompressedPixelUnpackData::Slice(data),
1862                    )
1863                };
1864            },
1865            WebGLCommand::TexStorage2D(target, levels, internal_format, width, height) => unsafe {
1866                gl.tex_storage_2d(
1867                    target,
1868                    levels as i32,
1869                    internal_format.as_gl_constant(),
1870                    width as i32,
1871                    height as i32,
1872                );
1873                // Zero-initialize the texture for security
1874                Self::zero_initialize_texture(
1875                    gl,
1876                    target,
1877                    internal_format,
1878                    width,
1879                    height,
1880                    1,
1881                    levels,
1882                );
1883            },
1884            WebGLCommand::TexStorage3D(target, levels, internal_format, width, height, depth) => unsafe {
1885                gl.tex_storage_3d(
1886                    target,
1887                    levels as i32,
1888                    internal_format.as_gl_constant(),
1889                    width as i32,
1890                    height as i32,
1891                    depth as i32,
1892                );
1893                // Zero-initialize the texture for security
1894                Self::zero_initialize_texture(
1895                    gl,
1896                    target,
1897                    internal_format,
1898                    width,
1899                    height,
1900                    depth,
1901                    levels,
1902                );
1903            },
1904            WebGLCommand::DrawingBufferWidth(ref sender) => {
1905                let size = device
1906                    .context_surface_info(ctx)
1907                    .unwrap()
1908                    .expect("Where's the front buffer?")
1909                    .size;
1910                sender.send(size.width).unwrap()
1911            },
1912            WebGLCommand::DrawingBufferHeight(ref sender) => {
1913                let size = device
1914                    .context_surface_info(ctx)
1915                    .unwrap()
1916                    .expect("Where's the front buffer?")
1917                    .size;
1918                sender.send(size.height).unwrap()
1919            },
1920            WebGLCommand::Finish(ref sender) => Self::finish(gl, sender),
1921            WebGLCommand::Flush => unsafe { gl.flush() },
1922            WebGLCommand::GenerateMipmap(target) => unsafe { gl.generate_mipmap(target) },
1923            WebGLCommand::CreateVertexArray(ref chan) => {
1924                let id = Self::create_vertex_array(gl);
1925                let _ = chan.send(id);
1926            },
1927            WebGLCommand::DeleteVertexArray(id) => {
1928                unsafe { gl.delete_vertex_array(id.glow()) };
1929            },
1930            WebGLCommand::BindVertexArray(id) => {
1931                let id = id.map(WebGLVertexArrayId::glow).or(state.default_vao);
1932                unsafe { gl.bind_vertex_array(id) }
1933            },
1934            WebGLCommand::GetParameterBool(param, ref sender) => {
1935                let value = match param {
1936                    webgl::ParameterBool::DepthWritemask => state.depth_write_mask,
1937                    _ => unsafe { gl.get_parameter_bool(param as u32) },
1938                };
1939                sender.send(value).unwrap()
1940            },
1941            WebGLCommand::FenceSync(ref sender) => {
1942                let value = unsafe { gl.fence_sync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0).unwrap() };
1943                sender.send(WebGLSyncId::from_glow(value)).unwrap();
1944            },
1945            WebGLCommand::IsSync(sync_id, ref sender) => {
1946                let value = unsafe { gl.is_sync(sync_id.glow()) };
1947                sender.send(value).unwrap();
1948            },
1949            WebGLCommand::ClientWaitSync(sync_id, flags, timeout, ref sender) => {
1950                let value = unsafe { gl.client_wait_sync(sync_id.glow(), flags, timeout as _) };
1951                sender.send(value).unwrap();
1952            },
1953            WebGLCommand::WaitSync(sync_id, flags, timeout) => {
1954                unsafe { gl.wait_sync(sync_id.glow(), flags, timeout as u64) };
1955            },
1956            WebGLCommand::GetSyncParameter(sync_id, param, ref sender) => {
1957                let value = unsafe { gl.get_sync_parameter_i32(sync_id.glow(), param) };
1958                sender.send(value as u32).unwrap();
1959            },
1960            WebGLCommand::DeleteSync(sync_id) => {
1961                unsafe { gl.delete_sync(sync_id.glow()) };
1962            },
1963            WebGLCommand::GetParameterBool4(param, ref sender) => {
1964                let value = match param {
1965                    webgl::ParameterBool4::ColorWritemask => state.color_write_mask,
1966                };
1967                sender.send(value).unwrap()
1968            },
1969            WebGLCommand::GetParameterInt(param, ref sender) => {
1970                // The core profiles used for WebGL 2 dropped the `*_BITS` queries.
1971                let webgl2 = state.webgl_version == WebGLVersion::WebGL2;
1972                let value = match param {
1973                    webgl::ParameterInt::AlphaBits if state.fake_no_alpha() => 0,
1974                    webgl::ParameterInt::DepthBits if state.fake_no_depth() => 0,
1975                    webgl::ParameterInt::StencilBits if state.fake_no_stencil() => 0,
1976                    webgl::ParameterInt::RedBits if webgl2 => Self::read_draw_framebuffer_bits(
1977                        gl,
1978                        gl::COLOR_ATTACHMENT0,
1979                        gl::FRAMEBUFFER_ATTACHMENT_RED_SIZE,
1980                    ),
1981                    webgl::ParameterInt::GreenBits if webgl2 => Self::read_draw_framebuffer_bits(
1982                        gl,
1983                        gl::COLOR_ATTACHMENT0,
1984                        gl::FRAMEBUFFER_ATTACHMENT_GREEN_SIZE,
1985                    ),
1986                    webgl::ParameterInt::BlueBits if webgl2 => Self::read_draw_framebuffer_bits(
1987                        gl,
1988                        gl::COLOR_ATTACHMENT0,
1989                        gl::FRAMEBUFFER_ATTACHMENT_BLUE_SIZE,
1990                    ),
1991                    webgl::ParameterInt::AlphaBits if webgl2 => Self::read_draw_framebuffer_bits(
1992                        gl,
1993                        gl::COLOR_ATTACHMENT0,
1994                        gl::FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE,
1995                    ),
1996                    webgl::ParameterInt::DepthBits if webgl2 => Self::read_draw_framebuffer_bits(
1997                        gl,
1998                        gl::DEPTH_ATTACHMENT,
1999                        gl::FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE,
2000                    ),
2001                    webgl::ParameterInt::StencilBits if webgl2 => Self::read_draw_framebuffer_bits(
2002                        gl,
2003                        gl::STENCIL_ATTACHMENT,
2004                        gl::FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE,
2005                    ),
2006                    _ => unsafe { gl.get_parameter_i32(param as u32) },
2007                };
2008                sender.send(value).unwrap()
2009            },
2010            WebGLCommand::GetParameterUInt(param, ref sender) => {
2011                let value = match param {
2012                    webgl::ParameterUInt::StencilWritemask => state.stencil_write_mask.0,
2013                    webgl::ParameterUInt::StencilBackWritemask => state.stencil_write_mask.1,
2014                    webgl::ParameterUInt::StencilValueMask => state.stencil_value_mask.0,
2015                    webgl::ParameterUInt::StencilBackValueMask => state.stencil_value_mask.1,
2016                };
2017                sender.send(value).unwrap()
2018            },
2019            WebGLCommand::GetParameterInt2(param, ref sender) => {
2020                let mut value = [0; 2];
2021                unsafe {
2022                    gl.get_parameter_i32_slice(param as u32, &mut value);
2023                }
2024                sender.send(value).unwrap()
2025            },
2026            WebGLCommand::GetParameterInt4(param, ref sender) => {
2027                let mut value = [0; 4];
2028                unsafe {
2029                    gl.get_parameter_i32_slice(param as u32, &mut value);
2030                }
2031                sender.send(value).unwrap()
2032            },
2033            WebGLCommand::GetParameterFloat(param, ref sender) => {
2034                let mut value = [0.];
2035                unsafe {
2036                    gl.get_parameter_f32_slice(param as u32, &mut value);
2037                }
2038                sender.send(value[0]).unwrap()
2039            },
2040            WebGLCommand::GetParameterFloat2(param, ref sender) => {
2041                let mut value = [0.; 2];
2042                unsafe {
2043                    gl.get_parameter_f32_slice(param as u32, &mut value);
2044                }
2045                sender.send(value).unwrap()
2046            },
2047            WebGLCommand::GetParameterFloat4(param, ref sender) => {
2048                let mut value = [0.; 4];
2049                unsafe {
2050                    gl.get_parameter_f32_slice(param as u32, &mut value);
2051                }
2052                sender.send(value).unwrap()
2053            },
2054            WebGLCommand::GetParameterString(param, ref sender) => {
2055                let gl_param = match param {
2056                    webgl::ParameterString::UnmaskedVendor => gl::VENDOR,
2057                    webgl::ParameterString::UnmaskedRenderer => gl::RENDERER,
2058                };
2059                let value = unsafe { gl.get_parameter_string(gl_param) };
2060                sender.send(value).unwrap()
2061            },
2062            WebGLCommand::GetProgramValidateStatus(program, ref sender) => sender
2063                .send(unsafe { gl.get_program_validate_status(program.glow()) })
2064                .unwrap(),
2065            WebGLCommand::GetProgramActiveUniforms(program, ref sender) => sender
2066                .send(unsafe { gl.get_program_parameter_i32(program.glow(), gl::ACTIVE_UNIFORMS) })
2067                .unwrap(),
2068            WebGLCommand::GetCurrentVertexAttrib(index, ref sender) => {
2069                let mut value = [0.; 4];
2070                unsafe {
2071                    gl.get_vertex_attrib_parameter_f32_slice(
2072                        index,
2073                        gl::CURRENT_VERTEX_ATTRIB,
2074                        &mut value,
2075                    );
2076                }
2077                sender.send(value).unwrap();
2078            },
2079            WebGLCommand::GetTexParameterFloat(target, param, ref sender) => {
2080                sender
2081                    .send(unsafe { gl.get_tex_parameter_f32(target, param as u32) })
2082                    .unwrap();
2083            },
2084            WebGLCommand::GetTexParameterInt(target, param, ref sender) => {
2085                sender
2086                    .send(unsafe { gl.get_tex_parameter_i32(target, param as u32) })
2087                    .unwrap();
2088            },
2089            WebGLCommand::GetTexParameterBool(target, param, ref sender) => {
2090                sender
2091                    .send(unsafe { gl.get_tex_parameter_i32(target, param as u32) } != 0)
2092                    .unwrap();
2093            },
2094            WebGLCommand::GetInternalFormatIntVec(target, internal_format, param, ref sender) => {
2095                match param {
2096                    InternalFormatIntVec::Samples => {
2097                        let mut count = [0; 1];
2098                        unsafe {
2099                            gl.get_internal_format_i32_slice(
2100                                target,
2101                                internal_format,
2102                                gl::NUM_SAMPLE_COUNTS,
2103                                &mut count,
2104                            )
2105                        };
2106                        assert!(count[0] >= 0);
2107
2108                        let mut values = vec![0; count[0] as usize];
2109                        unsafe {
2110                            gl.get_internal_format_i32_slice(
2111                                target,
2112                                internal_format,
2113                                param as u32,
2114                                &mut values,
2115                            )
2116                        };
2117                        sender.send(values).unwrap()
2118                    },
2119                }
2120            },
2121            WebGLCommand::TexParameteri(target, param, value) => unsafe {
2122                gl.tex_parameter_i32(target, param, value)
2123            },
2124            WebGLCommand::TexParameterf(target, param, value) => unsafe {
2125                gl.tex_parameter_f32(target, param, value)
2126            },
2127            WebGLCommand::LinkProgram(program_id, ref sender) => {
2128                sender.send(Self::link_program(gl, program_id)).unwrap()
2129            },
2130            WebGLCommand::UseProgram(program_id) => unsafe {
2131                gl.use_program(program_id.map(|p| p.glow()))
2132            },
2133            WebGLCommand::DrawArrays { mode, first, count } => unsafe {
2134                gl.draw_arrays(mode, first, count)
2135            },
2136            WebGLCommand::DrawArraysInstanced {
2137                mode,
2138                first,
2139                count,
2140                primcount,
2141            } => unsafe { gl.draw_arrays_instanced(mode, first, count, primcount) },
2142            WebGLCommand::DrawElements {
2143                mode,
2144                count,
2145                type_,
2146                offset,
2147            } => unsafe { gl.draw_elements(mode, count, type_, offset as _) },
2148            WebGLCommand::DrawElementsInstanced {
2149                mode,
2150                count,
2151                type_,
2152                offset,
2153                primcount,
2154            } => unsafe {
2155                gl.draw_elements_instanced(mode, count, type_, offset as i32, primcount)
2156            },
2157            WebGLCommand::VertexAttribDivisor { index, divisor } => unsafe {
2158                gl.vertex_attrib_divisor(index, divisor)
2159            },
2160            WebGLCommand::GetUniformBool(program_id, loc, ref sender) => {
2161                let mut value = [0];
2162                unsafe {
2163                    gl.get_uniform_i32(
2164                        program_id.glow(),
2165                        &NativeUniformLocation(loc as u32),
2166                        &mut value,
2167                    );
2168                }
2169                sender.send(value[0] != 0).unwrap();
2170            },
2171            WebGLCommand::GetUniformBool2(program_id, loc, ref sender) => {
2172                let mut value = [0; 2];
2173                unsafe {
2174                    gl.get_uniform_i32(
2175                        program_id.glow(),
2176                        &NativeUniformLocation(loc as u32),
2177                        &mut value,
2178                    );
2179                }
2180                let value = [value[0] != 0, value[1] != 0];
2181                sender.send(value).unwrap();
2182            },
2183            WebGLCommand::GetUniformBool3(program_id, loc, ref sender) => {
2184                let mut value = [0; 3];
2185                unsafe {
2186                    gl.get_uniform_i32(
2187                        program_id.glow(),
2188                        &NativeUniformLocation(loc as u32),
2189                        &mut value,
2190                    );
2191                }
2192                let value = [value[0] != 0, value[1] != 0, value[2] != 0];
2193                sender.send(value).unwrap();
2194            },
2195            WebGLCommand::GetUniformBool4(program_id, loc, ref sender) => {
2196                let mut value = [0; 4];
2197                unsafe {
2198                    gl.get_uniform_i32(
2199                        program_id.glow(),
2200                        &NativeUniformLocation(loc as u32),
2201                        &mut value,
2202                    );
2203                }
2204                let value = [value[0] != 0, value[1] != 0, value[2] != 0, value[3] != 0];
2205                sender.send(value).unwrap();
2206            },
2207            WebGLCommand::GetUniformInt(program_id, loc, ref sender) => {
2208                let mut value = [0];
2209                unsafe {
2210                    gl.get_uniform_i32(
2211                        program_id.glow(),
2212                        &NativeUniformLocation(loc as u32),
2213                        &mut value,
2214                    );
2215                }
2216                sender.send(value[0]).unwrap();
2217            },
2218            WebGLCommand::GetUniformInt2(program_id, loc, ref sender) => {
2219                let mut value = [0; 2];
2220                unsafe {
2221                    gl.get_uniform_i32(
2222                        program_id.glow(),
2223                        &NativeUniformLocation(loc as u32),
2224                        &mut value,
2225                    );
2226                }
2227                sender.send(value).unwrap();
2228            },
2229            WebGLCommand::GetUniformInt3(program_id, loc, ref sender) => {
2230                let mut value = [0; 3];
2231                unsafe {
2232                    gl.get_uniform_i32(
2233                        program_id.glow(),
2234                        &NativeUniformLocation(loc as u32),
2235                        &mut value,
2236                    );
2237                }
2238                sender.send(value).unwrap();
2239            },
2240            WebGLCommand::GetUniformInt4(program_id, loc, ref sender) => {
2241                let mut value = [0; 4];
2242                unsafe {
2243                    gl.get_uniform_i32(
2244                        program_id.glow(),
2245                        &NativeUniformLocation(loc as u32),
2246                        &mut value,
2247                    );
2248                }
2249                sender.send(value).unwrap();
2250            },
2251            WebGLCommand::GetUniformUint(program_id, loc, ref sender) => {
2252                let mut value = [0];
2253                unsafe {
2254                    gl.get_uniform_u32(
2255                        program_id.glow(),
2256                        &NativeUniformLocation(loc as u32),
2257                        &mut value,
2258                    );
2259                }
2260                sender.send(value[0]).unwrap();
2261            },
2262            WebGLCommand::GetUniformUint2(program_id, loc, ref sender) => {
2263                let mut value = [0; 2];
2264                unsafe {
2265                    gl.get_uniform_u32(
2266                        program_id.glow(),
2267                        &NativeUniformLocation(loc as u32),
2268                        &mut value,
2269                    );
2270                }
2271                sender.send(value).unwrap();
2272            },
2273            WebGLCommand::GetUniformUint3(program_id, loc, ref sender) => {
2274                let mut value = [0; 3];
2275                unsafe {
2276                    gl.get_uniform_u32(
2277                        program_id.glow(),
2278                        &NativeUniformLocation(loc as u32),
2279                        &mut value,
2280                    );
2281                }
2282                sender.send(value).unwrap();
2283            },
2284            WebGLCommand::GetUniformUint4(program_id, loc, ref sender) => {
2285                let mut value = [0; 4];
2286                unsafe {
2287                    gl.get_uniform_u32(
2288                        program_id.glow(),
2289                        &NativeUniformLocation(loc as u32),
2290                        &mut value,
2291                    );
2292                }
2293                sender.send(value).unwrap();
2294            },
2295            WebGLCommand::GetUniformFloat(program_id, loc, ref sender) => {
2296                let mut value = [0.];
2297                unsafe {
2298                    gl.get_uniform_f32(
2299                        program_id.glow(),
2300                        &NativeUniformLocation(loc as u32),
2301                        &mut value,
2302                    );
2303                }
2304                sender.send(value[0]).unwrap();
2305            },
2306            WebGLCommand::GetUniformFloat2(program_id, loc, ref sender) => {
2307                let mut value = [0.; 2];
2308                unsafe {
2309                    gl.get_uniform_f32(
2310                        program_id.glow(),
2311                        &NativeUniformLocation(loc as u32),
2312                        &mut value,
2313                    );
2314                }
2315                sender.send(value).unwrap();
2316            },
2317            WebGLCommand::GetUniformFloat3(program_id, loc, ref sender) => {
2318                let mut value = [0.; 3];
2319                unsafe {
2320                    gl.get_uniform_f32(
2321                        program_id.glow(),
2322                        &NativeUniformLocation(loc as u32),
2323                        &mut value,
2324                    );
2325                }
2326                sender.send(value).unwrap();
2327            },
2328            WebGLCommand::GetUniformFloat4(program_id, loc, ref sender) => {
2329                let mut value = [0.; 4];
2330                unsafe {
2331                    gl.get_uniform_f32(
2332                        program_id.glow(),
2333                        &NativeUniformLocation(loc as u32),
2334                        &mut value,
2335                    );
2336                }
2337                sender.send(value).unwrap();
2338            },
2339            WebGLCommand::GetUniformFloat9(program_id, loc, ref sender) => {
2340                let mut value = [0.; 9];
2341                unsafe {
2342                    gl.get_uniform_f32(
2343                        program_id.glow(),
2344                        &NativeUniformLocation(loc as u32),
2345                        &mut value,
2346                    );
2347                }
2348                sender.send(value).unwrap();
2349            },
2350            WebGLCommand::GetUniformFloat16(program_id, loc, ref sender) => {
2351                let mut value = [0.; 16];
2352                unsafe {
2353                    gl.get_uniform_f32(
2354                        program_id.glow(),
2355                        &NativeUniformLocation(loc as u32),
2356                        &mut value,
2357                    );
2358                }
2359                sender.send(value).unwrap();
2360            },
2361            WebGLCommand::GetUniformFloat2x3(program_id, loc, ref sender) => {
2362                let mut value = [0.; 2 * 3];
2363                unsafe {
2364                    gl.get_uniform_f32(
2365                        program_id.glow(),
2366                        &NativeUniformLocation(loc as u32),
2367                        &mut value,
2368                    );
2369                }
2370                sender.send(value).unwrap()
2371            },
2372            WebGLCommand::GetUniformFloat2x4(program_id, loc, ref sender) => {
2373                let mut value = [0.; 2 * 4];
2374                unsafe {
2375                    gl.get_uniform_f32(
2376                        program_id.glow(),
2377                        &NativeUniformLocation(loc as u32),
2378                        &mut value,
2379                    );
2380                }
2381                sender.send(value).unwrap()
2382            },
2383            WebGLCommand::GetUniformFloat3x2(program_id, loc, ref sender) => {
2384                let mut value = [0.; 3 * 2];
2385                unsafe {
2386                    gl.get_uniform_f32(
2387                        program_id.glow(),
2388                        &NativeUniformLocation(loc as u32),
2389                        &mut value,
2390                    );
2391                }
2392                sender.send(value).unwrap()
2393            },
2394            WebGLCommand::GetUniformFloat3x4(program_id, loc, ref sender) => {
2395                let mut value = [0.; 3 * 4];
2396                unsafe {
2397                    gl.get_uniform_f32(
2398                        program_id.glow(),
2399                        &NativeUniformLocation(loc as u32),
2400                        &mut value,
2401                    );
2402                }
2403                sender.send(value).unwrap()
2404            },
2405            WebGLCommand::GetUniformFloat4x2(program_id, loc, ref sender) => {
2406                let mut value = [0.; 4 * 2];
2407                unsafe {
2408                    gl.get_uniform_f32(
2409                        program_id.glow(),
2410                        &NativeUniformLocation(loc as u32),
2411                        &mut value,
2412                    );
2413                }
2414                sender.send(value).unwrap()
2415            },
2416            WebGLCommand::GetUniformFloat4x3(program_id, loc, ref sender) => {
2417                let mut value = [0.; 4 * 3];
2418                unsafe {
2419                    gl.get_uniform_f32(
2420                        program_id.glow(),
2421                        &NativeUniformLocation(loc as u32),
2422                        &mut value,
2423                    );
2424                }
2425                sender.send(value).unwrap()
2426            },
2427            WebGLCommand::GetUniformBlockIndex(program_id, ref name, ref sender) => {
2428                let name = to_name_in_compiled_shader(name);
2429                let index = unsafe { gl.get_uniform_block_index(program_id.glow(), &name) };
2430                // TODO(#34300): use Option<u32>
2431                sender.send(index.unwrap_or(gl::INVALID_INDEX)).unwrap();
2432            },
2433            WebGLCommand::GetUniformIndices(program_id, ref names, ref sender) => {
2434                let names = names
2435                    .iter()
2436                    .map(|name| to_name_in_compiled_shader(name))
2437                    .collect::<Vec<_>>();
2438                let name_strs = names.iter().map(|name| name.as_str()).collect::<Vec<_>>();
2439                let indices = unsafe {
2440                    gl.get_uniform_indices(program_id.glow(), &name_strs)
2441                        .iter()
2442                        .map(|index| index.unwrap_or(gl::INVALID_INDEX))
2443                        .collect()
2444                };
2445                sender.send(indices).unwrap();
2446            },
2447            WebGLCommand::GetActiveUniforms(program_id, ref indices, pname, ref sender) => {
2448                let results =
2449                    unsafe { gl.get_active_uniforms_parameter(program_id.glow(), indices, pname) };
2450                sender.send(results).unwrap();
2451            },
2452            WebGLCommand::GetActiveUniformBlockName(program_id, block_idx, ref sender) => {
2453                let name =
2454                    unsafe { gl.get_active_uniform_block_name(program_id.glow(), block_idx) };
2455                sender.send(name).unwrap();
2456            },
2457            WebGLCommand::GetActiveUniformBlockParameter(
2458                program_id,
2459                block_idx,
2460                pname,
2461                ref sender,
2462            ) => {
2463                let size = match pname {
2464                    gl::UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES => unsafe {
2465                        gl.get_active_uniform_block_parameter_i32(
2466                            program_id.glow(),
2467                            block_idx,
2468                            gl::UNIFORM_BLOCK_ACTIVE_UNIFORMS,
2469                        ) as usize
2470                    },
2471                    _ => 1,
2472                };
2473                let mut result = vec![0; size];
2474                unsafe {
2475                    gl.get_active_uniform_block_parameter_i32_slice(
2476                        program_id.glow(),
2477                        block_idx,
2478                        pname,
2479                        &mut result,
2480                    )
2481                };
2482                sender.send(result).unwrap();
2483            },
2484            WebGLCommand::UniformBlockBinding(program_id, block_idx, block_binding) => unsafe {
2485                gl.uniform_block_binding(program_id.glow(), block_idx, block_binding)
2486            },
2487            WebGLCommand::InitializeFramebuffer {
2488                color,
2489                depth,
2490                stencil,
2491            } => Self::initialize_framebuffer(gl, state, color, depth, stencil),
2492            WebGLCommand::BeginQuery(target, query_id) => {
2493                unsafe { gl.begin_query(target, query_id.glow()) };
2494            },
2495            WebGLCommand::EndQuery(target) => {
2496                unsafe { gl.end_query(target) };
2497            },
2498            WebGLCommand::DeleteQuery(query_id) => {
2499                unsafe { gl.delete_query(query_id.glow()) };
2500            },
2501            WebGLCommand::GenerateQuery(ref sender) => {
2502                // TODO(#34300): use Option<WebGLQueryId>
2503                let id = unsafe { gl.create_query().unwrap() };
2504                sender.send(WebGLQueryId::from_glow(id)).unwrap()
2505            },
2506            WebGLCommand::GetQueryState(ref sender, query_id, pname) => {
2507                let value = unsafe { gl.get_query_parameter_u32(query_id.glow(), pname) };
2508                sender.send(value).unwrap()
2509            },
2510            WebGLCommand::GenerateSampler(ref sender) => {
2511                let id = unsafe { gl.create_sampler().unwrap() };
2512                sender.send(WebGLSamplerId::from_glow(id)).unwrap()
2513            },
2514            WebGLCommand::DeleteSampler(sampler_id) => {
2515                unsafe { gl.delete_sampler(sampler_id.glow()) };
2516            },
2517            WebGLCommand::BindSampler(unit, sampler_id) => {
2518                unsafe { gl.bind_sampler(unit, Some(sampler_id.glow())) };
2519            },
2520            WebGLCommand::SetSamplerParameterInt(sampler_id, pname, value) => {
2521                unsafe { gl.sampler_parameter_i32(sampler_id.glow(), pname, value) };
2522            },
2523            WebGLCommand::SetSamplerParameterFloat(sampler_id, pname, value) => {
2524                unsafe { gl.sampler_parameter_f32(sampler_id.glow(), pname, value) };
2525            },
2526            WebGLCommand::GetSamplerParameterInt(sampler_id, pname, ref sender) => {
2527                let value = unsafe { gl.get_sampler_parameter_i32(sampler_id.glow(), pname) };
2528                sender.send(value).unwrap();
2529            },
2530            WebGLCommand::GetSamplerParameterFloat(sampler_id, pname, ref sender) => {
2531                let value = unsafe { gl.get_sampler_parameter_f32(sampler_id.glow(), pname) };
2532                sender.send(value).unwrap();
2533            },
2534            WebGLCommand::BindBufferBase(target, index, id) => {
2535                // https://searchfox.org/mozilla-central/rev/13b081a62d3f3e3e3120f95564529257b0bf451c/dom/canvas/WebGLContextBuffers.cpp#208-210
2536                // BindBufferBase/Range will fail (on some drivers) if the buffer name has
2537                // never been bound. (GenBuffers makes a name, but BindBuffer initializes
2538                // that name as a real buffer object)
2539                let id = id.map(WebGLBufferId::glow);
2540                unsafe {
2541                    gl.bind_buffer(target, id);
2542                    gl.bind_buffer(target, None);
2543                    gl.bind_buffer_base(target, index, id);
2544                }
2545            },
2546            WebGLCommand::BindBufferRange(target, index, id, offset, size) => {
2547                // https://searchfox.org/mozilla-central/rev/13b081a62d3f3e3e3120f95564529257b0bf451c/dom/canvas/WebGLContextBuffers.cpp#208-210
2548                // BindBufferBase/Range will fail (on some drivers) if the buffer name has
2549                // never been bound. (GenBuffers makes a name, but BindBuffer initializes
2550                // that name as a real buffer object)
2551                let id = id.map(WebGLBufferId::glow);
2552                unsafe {
2553                    gl.bind_buffer(target, id);
2554                    gl.bind_buffer(target, None);
2555                    gl.bind_buffer_range(target, index, id, offset as i32, size as i32);
2556                }
2557            },
2558            WebGLCommand::ClearBufferfv(buffer, draw_buffer, ref value) => unsafe {
2559                gl.clear_buffer_f32_slice(buffer, draw_buffer as u32, value)
2560            },
2561            WebGLCommand::ClearBufferiv(buffer, draw_buffer, ref value) => unsafe {
2562                gl.clear_buffer_i32_slice(buffer, draw_buffer as u32, value)
2563            },
2564            WebGLCommand::ClearBufferuiv(buffer, draw_buffer, ref value) => unsafe {
2565                gl.clear_buffer_u32_slice(buffer, draw_buffer as u32, value)
2566            },
2567            WebGLCommand::ClearBufferfi(buffer, draw_buffer, depth, stencil) => unsafe {
2568                gl.clear_buffer_depth_stencil(buffer, draw_buffer as u32, depth, stencil)
2569            },
2570            WebGLCommand::InvalidateFramebuffer(target, ref attachments) => unsafe {
2571                gl.invalidate_framebuffer(target, attachments)
2572            },
2573            WebGLCommand::InvalidateSubFramebuffer(target, ref attachments, x, y, w, h) => unsafe {
2574                gl.invalidate_sub_framebuffer(target, attachments, x, y, w, h)
2575            },
2576            WebGLCommand::FramebufferTextureLayer(target, attachment, tex_id, level, layer) => {
2577                let tex_id = tex_id.map(WebGLTextureId::glow);
2578                let attach = |attachment| unsafe {
2579                    gl.framebuffer_texture_layer(target, attachment, tex_id, level, layer)
2580                };
2581
2582                if attachment == gl::DEPTH_STENCIL_ATTACHMENT {
2583                    attach(gl::DEPTH_ATTACHMENT);
2584                    attach(gl::STENCIL_ATTACHMENT);
2585                } else {
2586                    attach(attachment)
2587                }
2588            },
2589            WebGLCommand::ReadBuffer(buffer) => unsafe {
2590                gl.read_buffer(Self::default_framebuffer_color_buffer(buffer, ctx, device))
2591            },
2592            WebGLCommand::DrawBuffers(ref buffers) => {
2593                let buffers: Vec<_> = buffers
2594                    .iter()
2595                    .map(|&buffer| Self::default_framebuffer_color_buffer(buffer, ctx, device))
2596                    .collect();
2597                unsafe { gl.draw_buffers(&buffers) }
2598            },
2599        }
2600
2601        // If debug asertions are enabled, then check the error state.
2602        #[cfg(debug_assertions)]
2603        {
2604            let error = unsafe { gl.get_error() };
2605            if error != gl::NO_ERROR {
2606                error!("Last GL operation failed: {:?}", command);
2607                if error == gl::INVALID_FRAMEBUFFER_OPERATION {
2608                    let framebuffer_bindings =
2609                        unsafe { gl.get_parameter_framebuffer(gl::DRAW_FRAMEBUFFER_BINDING) };
2610                    debug!(
2611                        "(thread {:?}) Current draw framebuffer binding: {:?}",
2612                        ::std::thread::current().id(),
2613                        framebuffer_bindings
2614                    );
2615                }
2616                #[cfg(feature = "webgl_backtrace")]
2617                {
2618                    error!("Backtrace from failed WebGL API:\n{}", _backtrace.backtrace);
2619                    if let Some(backtrace) = _backtrace.js_backtrace {
2620                        error!("JS backtrace from failed WebGL API:\n{}", backtrace);
2621                    }
2622                }
2623                // TODO(servo#30568) revert to panic!() once underlying bug is fixed
2624                log::warn!(
2625                    "debug assertion failed! Unexpected WebGL error: 0x{:x} ({}) [{:?}]",
2626                    error,
2627                    error,
2628                    command
2629                );
2630            }
2631        }
2632    }
2633
2634    fn tex_bytes_per_pixel(format: TexFormat, data_type: TexDataType) -> usize {
2635        match data_type {
2636            TexDataType::Float32UnsignedInt248Rev => 8,
2637            _ if data_type.components_per_element() > 1 => data_type.element_size() as usize,
2638            _ => (format.components() * data_type.element_size()) as usize,
2639        }
2640    }
2641
2642    /// Zero-initializes a texture after texStorage allocation using tex_sub_image_2d or tex_sub_image_3d.
2643    /// This is required by WebGL for security to prevent information leakage
2644    /// from uninitialized GPU memory without causing state corruption or format incompatibility.
2645    fn zero_initialize_texture(
2646        gl: &Gl,
2647        target: u32,
2648        internal_format: TexFormat,
2649        width: u32,
2650        height: u32,
2651        depth: u32,
2652        levels: u32,
2653    ) {
2654        // TODO: Zero-initialize compressed formats with compressedTexSubImage2D/3D.
2655        if internal_format.is_compressed() {
2656            return;
2657        }
2658        let Some(&data_type) = internal_format.compatible_data_types().first() else {
2659            return;
2660        };
2661        let unsized_format = internal_format.to_unsized();
2662        let bpp = Self::tex_bytes_per_pixel(unsized_format, data_type);
2663
2664        let gl_type = if data_type == TexDataType::HalfFloat {
2665            gl::HALF_FLOAT
2666        } else {
2667            data_type.as_gl_constant()
2668        };
2669
2670        // Temporarily unbind PIXEL_UNPACK_BUFFER if bound.
2671        let prev_pbo = unsafe { gl.get_parameter_i32(gl::PIXEL_UNPACK_BUFFER_BINDING) };
2672        if prev_pbo != 0 {
2673            unsafe { gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, None) };
2674        }
2675        unsafe { gl.pixel_store_i32(gl::UNPACK_ALIGNMENT, 1) };
2676
2677        // Upload in bands of rows using a buffer capped to at most 1 MiB to avoid OOM crashes on huge textures.
2678        const MAX_CHUNK_BYTES: usize = 1024 * 1024;
2679        let mut zeros = Vec::new();
2680
2681        let is_3d_target = target == gl::TEXTURE_3D || target == gl::TEXTURE_2D_ARRAY;
2682
2683        for level in 0..levels {
2684            let level_width = (width >> level).max(1);
2685            let level_height = (height >> level).max(1);
2686            let row_bytes = level_width as usize * bpp;
2687            let rows_per_chunk = (MAX_CHUNK_BYTES / row_bytes).clamp(1, level_height as usize);
2688            let chunk_bytes = rows_per_chunk * row_bytes;
2689
2690            if zeros.len() < chunk_bytes {
2691                zeros.resize(chunk_bytes, 0);
2692            }
2693
2694            let level_depth = if target == gl::TEXTURE_3D {
2695                (depth >> level).max(1)
2696            } else {
2697                depth
2698            };
2699
2700            let num_images = if target == gl::TEXTURE_CUBE_MAP {
2701                6
2702            } else {
2703                level_depth
2704            };
2705
2706            for img in 0..num_images {
2707                let mut y = 0;
2708                while y < level_height {
2709                    let rows = (level_height - y).min(rows_per_chunk as u32);
2710                    let upload_bytes = rows as usize * row_bytes;
2711                    let slice = &zeros[..upload_bytes];
2712
2713                    unsafe {
2714                        if is_3d_target {
2715                            gl.tex_sub_image_3d(
2716                                target,
2717                                level as i32,
2718                                0,
2719                                y as i32,
2720                                img as i32,
2721                                level_width as i32,
2722                                rows as i32,
2723                                1,
2724                                unsized_format.as_gl_constant(),
2725                                gl_type,
2726                                PixelUnpackData::Slice(Some(slice)),
2727                            );
2728                        } else {
2729                            let img_target = if target == gl::TEXTURE_CUBE_MAP {
2730                                gl::TEXTURE_CUBE_MAP_POSITIVE_X + img
2731                            } else {
2732                                target
2733                            };
2734                            gl.tex_sub_image_2d(
2735                                img_target,
2736                                level as i32,
2737                                0,
2738                                y as i32,
2739                                level_width as i32,
2740                                rows as i32,
2741                                unsized_format.as_gl_constant(),
2742                                gl_type,
2743                                PixelUnpackData::Slice(Some(slice)),
2744                            );
2745                        }
2746                    }
2747
2748                    y += rows;
2749                }
2750            }
2751        }
2752
2753        if prev_pbo != 0 {
2754            unsafe {
2755                gl.bind_buffer(
2756                    gl::PIXEL_UNPACK_BUFFER,
2757                    NonZeroU32::new(prev_pbo as u32).map(gl::NativeBuffer),
2758                );
2759            }
2760        }
2761    }
2762
2763    fn initialize_framebuffer(gl: &Gl, state: &GLState, color: bool, depth: bool, stencil: bool) {
2764        let bits = [
2765            (color, gl::COLOR_BUFFER_BIT),
2766            (depth, gl::DEPTH_BUFFER_BIT),
2767            (stencil, gl::STENCIL_BUFFER_BIT),
2768        ]
2769        .iter()
2770        .fold(0, |bits, &(enabled, bit)| {
2771            bits | if enabled { bit } else { 0 }
2772        });
2773
2774        unsafe {
2775            gl.disable(gl::SCISSOR_TEST);
2776            gl.color_mask(true, true, true, true);
2777            gl.clear_color(0., 0., 0., 0.);
2778            gl.depth_mask(true);
2779            gl.clear_depth(1.);
2780            gl.stencil_mask_separate(gl::FRONT, 0xFFFFFFFF);
2781            gl.stencil_mask_separate(gl::BACK, 0xFFFFFFFF);
2782            gl.clear_stencil(0);
2783            gl.clear(bits);
2784        }
2785
2786        state.restore_invariant(gl);
2787    }
2788
2789    fn link_program(gl: &Gl, program: WebGLProgramId) -> ProgramLinkInfo {
2790        unsafe { gl.link_program(program.glow()) };
2791        let linked = unsafe { gl.get_program_link_status(program.glow()) };
2792        if !linked {
2793            return ProgramLinkInfo {
2794                linked: false,
2795                active_attribs: vec![].into(),
2796                active_uniforms: vec![].into(),
2797                active_uniform_blocks: vec![].into(),
2798                transform_feedback_length: Default::default(),
2799                transform_feedback_mode: Default::default(),
2800            };
2801        }
2802        let num_active_attribs =
2803            unsafe { gl.get_program_parameter_i32(program.glow(), gl::ACTIVE_ATTRIBUTES) };
2804        let active_attribs = (0..num_active_attribs as u32)
2805            .map(|i| {
2806                let active_attribute =
2807                    unsafe { gl.get_active_attribute(program.glow(), i) }.unwrap();
2808                let name = &active_attribute.name;
2809                let location = if name.starts_with("gl_") {
2810                    None
2811                } else {
2812                    unsafe { gl.get_attrib_location(program.glow(), name) }
2813                };
2814                ActiveAttribInfo {
2815                    name: from_name_in_compiled_shader(name),
2816                    size: active_attribute.size,
2817                    type_: active_attribute.atype,
2818                    location,
2819                }
2820            })
2821            .collect::<Vec<_>>()
2822            .into();
2823
2824        let num_active_uniforms =
2825            unsafe { gl.get_program_parameter_i32(program.glow(), gl::ACTIVE_UNIFORMS) };
2826        let active_uniforms = (0..num_active_uniforms as u32)
2827            .map(|i| {
2828                let active_uniform = unsafe { gl.get_active_uniform(program.glow(), i) }.unwrap();
2829                let is_array = active_uniform.name.ends_with("[0]");
2830                let active_uniform_name = active_uniform
2831                    .name
2832                    .strip_suffix("[0]")
2833                    .unwrap_or_else(|| &active_uniform.name);
2834                ActiveUniformInfo {
2835                    base_name: from_name_in_compiled_shader(active_uniform_name).into(),
2836                    size: if is_array {
2837                        Some(active_uniform.size)
2838                    } else {
2839                        None
2840                    },
2841                    type_: active_uniform.utype,
2842                    bind_index: None,
2843                }
2844            })
2845            .collect::<Vec<_>>()
2846            .into();
2847
2848        let num_active_uniform_blocks =
2849            unsafe { gl.get_program_parameter_i32(program.glow(), gl::ACTIVE_UNIFORM_BLOCKS) };
2850        let active_uniform_blocks = (0..num_active_uniform_blocks as u32)
2851            .map(|i| {
2852                let name = unsafe { gl.get_active_uniform_block_name(program.glow(), i) };
2853                let size = unsafe {
2854                    gl.get_active_uniform_block_parameter_i32(
2855                        program.glow(),
2856                        i,
2857                        gl::UNIFORM_BLOCK_DATA_SIZE,
2858                    )
2859                };
2860                ActiveUniformBlockInfo { name, size }
2861            })
2862            .collect::<Vec<_>>()
2863            .into();
2864
2865        let transform_feedback_length = unsafe {
2866            gl.get_program_parameter_i32(program.glow(), gl::TRANSFORM_FEEDBACK_VARYINGS)
2867        };
2868        let transform_feedback_mode = unsafe {
2869            gl.get_program_parameter_i32(program.glow(), gl::TRANSFORM_FEEDBACK_BUFFER_MODE)
2870        };
2871
2872        ProgramLinkInfo {
2873            linked: true,
2874            active_attribs,
2875            active_uniforms,
2876            active_uniform_blocks,
2877            transform_feedback_length,
2878            transform_feedback_mode,
2879        }
2880    }
2881
2882    fn finish(gl: &Gl, chan: &GenericSender<()>) {
2883        unsafe { gl.finish() };
2884        chan.send(()).unwrap();
2885    }
2886
2887    fn shader_precision_format(
2888        gl: &Gl,
2889        shader_type: u32,
2890        precision_type: u32,
2891        chan: &GenericSender<(i32, i32, i32)>,
2892    ) {
2893        let ShaderPrecisionFormat {
2894            range_min,
2895            range_max,
2896            precision,
2897        } = unsafe {
2898            gl.get_shader_precision_format(shader_type, precision_type)
2899                .unwrap_or_else(|| {
2900                    ShaderPrecisionFormat::common_desktop_hardware(
2901                        precision_type,
2902                        gl.version().is_embedded,
2903                    )
2904                })
2905        };
2906        chan.send((range_min, range_max, precision)).unwrap();
2907    }
2908
2909    /// This is an implementation of `getSupportedExtensions()` from
2910    /// <https://registry.khronos.org/webgl/specs/latest/1.0/#5.14>
2911    fn get_extensions(gl: &Gl, result_sender: &GenericSender<String>) {
2912        let _ = result_sender.send(gl.supported_extensions().iter().join(" "));
2913    }
2914
2915    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6>
2916    fn get_framebuffer_attachment_parameter(
2917        gl: &Gl,
2918        target: u32,
2919        attachment: u32,
2920        pname: u32,
2921        chan: &GenericSender<i32>,
2922    ) {
2923        let parameter =
2924            unsafe { gl.get_framebuffer_attachment_parameter_i32(target, attachment, pname) };
2925        chan.send(parameter).unwrap();
2926    }
2927
2928    /// Reads one component size of a draw framebuffer attachment, or 0 when
2929    /// nothing is attached, as OpenGL ES 3.0.6 §4.4.5 defines the `*_BITS` values.
2930    fn read_draw_framebuffer_bits(gl: &Gl, attachment: u32, pname: u32) -> i32 {
2931        unsafe {
2932            let object_type = gl.get_framebuffer_attachment_parameter_i32(
2933                gl::DRAW_FRAMEBUFFER,
2934                attachment,
2935                gl::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
2936            );
2937            if object_type as u32 == gl::NONE {
2938                return 0;
2939            }
2940            gl.get_framebuffer_attachment_parameter_i32(gl::DRAW_FRAMEBUFFER, attachment, pname)
2941        }
2942    }
2943
2944    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7>
2945    fn get_renderbuffer_parameter(gl: &Gl, target: u32, pname: u32, chan: &GenericSender<i32>) {
2946        let parameter = unsafe { gl.get_renderbuffer_parameter_i32(target, pname) };
2947        chan.send(parameter).unwrap();
2948    }
2949
2950    fn uniform_location(
2951        gl: &Gl,
2952        program_id: WebGLProgramId,
2953        name: &str,
2954        chan: &GenericSender<Option<i32>>,
2955    ) {
2956        let location = unsafe {
2957            gl.get_uniform_location(program_id.glow(), &to_name_in_compiled_shader(name))
2958        };
2959        // (#34300): replace this with WebGLUniformId
2960        chan.send(location.map(|l| l.0 as i32)).unwrap();
2961    }
2962
2963    fn shader_info_log(gl: &Gl, shader_id: WebGLShaderId, chan: &GenericSender<String>) {
2964        let log = unsafe { gl.get_shader_info_log(shader_id.glow()) };
2965        chan.send(log).unwrap();
2966    }
2967
2968    fn program_info_log(gl: &Gl, program_id: WebGLProgramId, chan: &GenericSender<String>) {
2969        let log = unsafe { gl.get_program_info_log(program_id.glow()) };
2970        chan.send(log).unwrap();
2971    }
2972
2973    fn create_buffer(gl: &Gl, chan: &GenericSender<Option<WebGLBufferId>>) {
2974        let buffer = unsafe { gl.create_buffer() }
2975            .ok()
2976            .map(WebGLBufferId::from_glow);
2977        chan.send(buffer).unwrap();
2978    }
2979
2980    fn create_framebuffer(gl: &Gl, chan: &GenericSender<Option<WebGLFramebufferId>>) {
2981        let framebuffer = unsafe { gl.create_framebuffer() }
2982            .ok()
2983            .map(WebGLFramebufferId::from_glow);
2984        chan.send(framebuffer).unwrap();
2985    }
2986
2987    fn create_renderbuffer(gl: &Gl, chan: &GenericSender<Option<WebGLRenderbufferId>>) {
2988        let renderbuffer = unsafe { gl.create_renderbuffer() }
2989            .ok()
2990            .map(WebGLRenderbufferId::from_glow);
2991        chan.send(renderbuffer).unwrap();
2992    }
2993
2994    fn create_texture(gl: &Gl, chan: &GenericSender<Option<WebGLTextureId>>) {
2995        let texture = unsafe { gl.create_texture() }
2996            .ok()
2997            .map(WebGLTextureId::from_glow);
2998        chan.send(texture).unwrap();
2999    }
3000
3001    fn create_program(gl: &Gl, chan: &GenericSender<Option<WebGLProgramId>>) {
3002        let program = unsafe { gl.create_program() }
3003            .ok()
3004            .map(WebGLProgramId::from_glow);
3005        chan.send(program).unwrap();
3006    }
3007
3008    fn create_shader(gl: &Gl, shader_type: u32, chan: &GenericSender<Option<WebGLShaderId>>) {
3009        let shader = unsafe { gl.create_shader(shader_type) }
3010            .ok()
3011            .map(WebGLShaderId::from_glow);
3012        chan.send(shader).unwrap();
3013    }
3014
3015    fn create_vertex_array(gl: &Gl) -> Option<WebGLVertexArrayId> {
3016        let vao = unsafe { gl.create_vertex_array() }
3017            .ok()
3018            .map(WebGLVertexArrayId::from_glow);
3019        if vao.is_none() {
3020            let code = unsafe { gl.get_error() };
3021            warn!("Failed to create vertex array with error code {:x}", code);
3022        }
3023        vao
3024    }
3025
3026    #[inline]
3027    fn bind_framebuffer(
3028        gl: &Gl,
3029        target: u32,
3030        request: WebGLFramebufferBindingRequest,
3031        ctx: &Context,
3032        device: &Device,
3033        state: &mut GLState,
3034    ) {
3035        let id = match request {
3036            WebGLFramebufferBindingRequest::Explicit(id) => Some(id.glow()),
3037            WebGLFramebufferBindingRequest::Default => {
3038                device
3039                    .context_surface_info(ctx)
3040                    .unwrap()
3041                    .expect("No surface attached!")
3042                    .framebuffer_object
3043            },
3044        };
3045
3046        debug!("WebGLImpl::bind_framebuffer: {:?}", id);
3047        unsafe { gl.bind_framebuffer(target, id) };
3048
3049        if (target == gl::FRAMEBUFFER) || (target == gl::DRAW_FRAMEBUFFER) {
3050            state.drawing_to_default_framebuffer =
3051                request == WebGLFramebufferBindingRequest::Default;
3052            state.restore_invariant(gl);
3053        }
3054    }
3055
3056    /// Maps a WebGL default framebuffer color buffer to its GL name: when the surface is backed
3057    /// by a framebuffer object, GL rejects `BACK` and uses `COLOR_ATTACHMENT0` for the color
3058    /// buffer.
3059    fn default_framebuffer_color_buffer(buffer: u32, ctx: &Context, device: &Device) -> u32 {
3060        if buffer != gl::BACK {
3061            return buffer;
3062        }
3063        let surface_info = device
3064            .context_surface_info(ctx)
3065            .expect("WebGL commands run with their context current")
3066            .expect("WebGL contexts are created with a surface attached");
3067        match surface_info.framebuffer_object {
3068            Some(_) => gl::COLOR_ATTACHMENT0,
3069            None => gl::BACK,
3070        }
3071    }
3072
3073    #[inline]
3074    fn compile_shader(gl: &Gl, shader_id: WebGLShaderId, source: &str) {
3075        unsafe {
3076            gl.shader_source(shader_id.glow(), source);
3077            gl.compile_shader(shader_id.glow());
3078        }
3079    }
3080}
3081
3082/// ANGLE adds a `_u` prefix to variable names:
3083///
3084/// <https://chromium.googlesource.com/angle/angle/+/855d964bd0d05f6b2cb303f625506cf53d37e94f>
3085///
3086/// To avoid hard-coding this we would need to use the `sh::GetAttributes` and `sh::GetUniforms`
3087/// API to look up the `x.name` and `x.mappedName` members.
3088const ANGLE_NAME_PREFIX: &str = "_u";
3089
3090/// Adds `_u` prefix to variable names
3091fn to_name_in_compiled_shader(s: &str) -> String {
3092    map_dot_separated(s, |s, mapped| {
3093        mapped.push_str(ANGLE_NAME_PREFIX);
3094        mapped.push_str(s);
3095    })
3096}
3097
3098/// Removes `_u` prefix from variable names
3099fn from_name_in_compiled_shader(s: &str) -> String {
3100    map_dot_separated(s, |s, mapped| {
3101        mapped.push_str(if let Some(stripped) = s.strip_prefix(ANGLE_NAME_PREFIX) {
3102            stripped
3103        } else {
3104            s
3105        })
3106    })
3107}
3108
3109fn map_dot_separated<F: Fn(&str, &mut String)>(s: &str, f: F) -> String {
3110    let mut iter = s.split('.');
3111    let mut mapped = String::new();
3112    f(iter.next().unwrap(), &mut mapped);
3113    for s in iter {
3114        mapped.push('.');
3115        f(s, &mut mapped);
3116    }
3117    mapped
3118}
3119
3120#[expect(clippy::too_many_arguments)]
3121fn prepare_pixels(
3122    internal_format: TexFormat,
3123    data_type: TexDataType,
3124    size: Size2D<u32>,
3125    unpacking_alignment: u32,
3126    alpha_treatment: Option<AlphaTreatment>,
3127    y_axis_treatment: YAxisTreatment,
3128    pixel_format: Option<PixelFormat>,
3129    mut pixels: Cow<[u8]>,
3130) -> Cow<[u8]> {
3131    match alpha_treatment {
3132        Some(AlphaTreatment::Premultiply) => {
3133            if let Some(pixel_format) = pixel_format {
3134                match pixel_format {
3135                    PixelFormat::BGRA8 | PixelFormat::RGBA8 => {},
3136                    _ => unimplemented!("unsupported pixel format ({:?})", pixel_format),
3137                }
3138                premultiply_inplace(TexFormat::RGBA, TexDataType::UnsignedByte, pixels.to_mut());
3139            } else {
3140                premultiply_inplace(internal_format, data_type, pixels.to_mut());
3141            }
3142        },
3143        Some(AlphaTreatment::Unmultiply) => {
3144            assert!(pixel_format.is_some());
3145            unmultiply_inplace::<false>(pixels.to_mut());
3146        },
3147        None => {},
3148    }
3149
3150    if let Some(pixel_format) = pixel_format {
3151        pixels = image_to_tex_image_data(
3152            pixel_format,
3153            internal_format,
3154            data_type,
3155            pixels.into_owned(),
3156        )
3157        .into();
3158    }
3159
3160    if y_axis_treatment == YAxisTreatment::Flipped {
3161        // FINISHME: Consider doing premultiply and flip in a single mutable Vec.
3162        pixels = flip_pixels_y(
3163            internal_format,
3164            data_type,
3165            size.width as usize,
3166            size.height as usize,
3167            unpacking_alignment as usize,
3168            pixels.into_owned(),
3169        )
3170        .into();
3171    }
3172
3173    pixels
3174}
3175
3176/// Translates an image in rgba8 (red in the first byte) format to
3177/// the format that was requested of TexImage.
3178fn image_to_tex_image_data(
3179    pixel_format: PixelFormat,
3180    format: TexFormat,
3181    data_type: TexDataType,
3182    mut pixels: Vec<u8>,
3183) -> Vec<u8> {
3184    // hint for vector allocation sizing.
3185    let pixel_count = pixels.len() / 4;
3186
3187    match pixel_format {
3188        PixelFormat::BGRA8 => pixels::rgba8_byte_swap_colors_inplace(&mut pixels),
3189        PixelFormat::RGBA8 => {},
3190        _ => unimplemented!("unsupported pixel format ({:?})", pixel_format),
3191    }
3192
3193    match (format, data_type) {
3194        (TexFormat::RGBA, TexDataType::UnsignedByte) |
3195        (TexFormat::RGBA8, TexDataType::UnsignedByte) => pixels,
3196        (TexFormat::RGB, TexDataType::UnsignedByte) |
3197        (TexFormat::RGB8, TexDataType::UnsignedByte) => {
3198            for i in 0..pixel_count {
3199                let rgb = {
3200                    let rgb = &pixels[i * 4..i * 4 + 3];
3201                    [rgb[0], rgb[1], rgb[2]]
3202                };
3203                pixels[i * 3..i * 3 + 3].copy_from_slice(&rgb);
3204            }
3205            pixels.truncate(pixel_count * 3);
3206            pixels
3207        },
3208        (TexFormat::Alpha, TexDataType::UnsignedByte) => {
3209            for i in 0..pixel_count {
3210                let p = pixels[i * 4 + 3];
3211                pixels[i] = p;
3212            }
3213            pixels.truncate(pixel_count);
3214            pixels
3215        },
3216        (TexFormat::Luminance, TexDataType::UnsignedByte) => {
3217            for i in 0..pixel_count {
3218                let p = pixels[i * 4];
3219                pixels[i] = p;
3220            }
3221            pixels.truncate(pixel_count);
3222            pixels
3223        },
3224        (TexFormat::LuminanceAlpha, TexDataType::UnsignedByte) => {
3225            for i in 0..pixel_count {
3226                let (lum, a) = {
3227                    let rgba = &pixels[i * 4..i * 4 + 4];
3228                    (rgba[0], rgba[3])
3229                };
3230                pixels[i * 2] = lum;
3231                pixels[i * 2 + 1] = a;
3232            }
3233            pixels.truncate(pixel_count * 2);
3234            pixels
3235        },
3236        (TexFormat::RGBA, TexDataType::UnsignedShort4444) => {
3237            for i in 0..pixel_count {
3238                let p = {
3239                    let rgba = &pixels[i * 4..i * 4 + 4];
3240                    ((rgba[0] as u16 & 0xf0) << 8) |
3241                        ((rgba[1] as u16 & 0xf0) << 4) |
3242                        (rgba[2] as u16 & 0xf0) |
3243                        ((rgba[3] as u16 & 0xf0) >> 4)
3244                };
3245                NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p);
3246            }
3247            pixels.truncate(pixel_count * 2);
3248            pixels
3249        },
3250        (TexFormat::RGBA, TexDataType::UnsignedShort5551) => {
3251            for i in 0..pixel_count {
3252                let p = {
3253                    let rgba = &pixels[i * 4..i * 4 + 4];
3254                    ((rgba[0] as u16 & 0xf8) << 8) |
3255                        ((rgba[1] as u16 & 0xf8) << 3) |
3256                        ((rgba[2] as u16 & 0xf8) >> 2) |
3257                        ((rgba[3] as u16) >> 7)
3258                };
3259                NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p);
3260            }
3261            pixels.truncate(pixel_count * 2);
3262            pixels
3263        },
3264        (TexFormat::RGB, TexDataType::UnsignedShort565) => {
3265            for i in 0..pixel_count {
3266                let p = {
3267                    let rgb = &pixels[i * 4..i * 4 + 3];
3268                    ((rgb[0] as u16 & 0xf8) << 8) |
3269                        ((rgb[1] as u16 & 0xfc) << 3) |
3270                        ((rgb[2] as u16 & 0xf8) >> 3)
3271                };
3272                NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p);
3273            }
3274            pixels.truncate(pixel_count * 2);
3275            pixels
3276        },
3277        (TexFormat::RGBA, TexDataType::Float) | (TexFormat::RGBA32f, TexDataType::Float) => {
3278            let mut rgbaf32 = Vec::<u8>::with_capacity(pixel_count * 16);
3279            for rgba8 in pixels.chunks(4) {
3280                rgbaf32.write_f32::<NativeEndian>(rgba8[0] as f32).unwrap();
3281                rgbaf32.write_f32::<NativeEndian>(rgba8[1] as f32).unwrap();
3282                rgbaf32.write_f32::<NativeEndian>(rgba8[2] as f32).unwrap();
3283                rgbaf32.write_f32::<NativeEndian>(rgba8[3] as f32).unwrap();
3284            }
3285            rgbaf32
3286        },
3287
3288        (TexFormat::RGB, TexDataType::Float) | (TexFormat::RGB32f, TexDataType::Float) => {
3289            let mut rgbf32 = Vec::<u8>::with_capacity(pixel_count * 12);
3290            for rgba8 in pixels.chunks(4) {
3291                rgbf32.write_f32::<NativeEndian>(rgba8[0] as f32).unwrap();
3292                rgbf32.write_f32::<NativeEndian>(rgba8[1] as f32).unwrap();
3293                rgbf32.write_f32::<NativeEndian>(rgba8[2] as f32).unwrap();
3294            }
3295            rgbf32
3296        },
3297
3298        (TexFormat::Alpha, TexDataType::Float) | (TexFormat::Alpha32f, TexDataType::Float) => {
3299            for rgba8 in pixels.chunks_mut(4) {
3300                let p = rgba8[3] as f32;
3301                NativeEndian::write_f32(rgba8, p);
3302            }
3303            pixels
3304        },
3305
3306        (TexFormat::Luminance, TexDataType::Float) |
3307        (TexFormat::Luminance32f, TexDataType::Float) => {
3308            for rgba8 in pixels.chunks_mut(4) {
3309                let p = rgba8[0] as f32;
3310                NativeEndian::write_f32(rgba8, p);
3311            }
3312            pixels
3313        },
3314
3315        (TexFormat::LuminanceAlpha, TexDataType::Float) |
3316        (TexFormat::LuminanceAlpha32f, TexDataType::Float) => {
3317            let mut data = Vec::<u8>::with_capacity(pixel_count * 8);
3318            for rgba8 in pixels.chunks(4) {
3319                data.write_f32::<NativeEndian>(rgba8[0] as f32).unwrap();
3320                data.write_f32::<NativeEndian>(rgba8[3] as f32).unwrap();
3321            }
3322            data
3323        },
3324
3325        (TexFormat::RGBA, TexDataType::HalfFloat) |
3326        (TexFormat::RGBA16f, TexDataType::HalfFloat) => {
3327            let mut rgbaf16 = Vec::<u8>::with_capacity(pixel_count * 8);
3328            for rgba8 in pixels.chunks(4) {
3329                rgbaf16
3330                    .write_u16::<NativeEndian>(f16::from_f32(rgba8[0] as f32).to_bits())
3331                    .unwrap();
3332                rgbaf16
3333                    .write_u16::<NativeEndian>(f16::from_f32(rgba8[1] as f32).to_bits())
3334                    .unwrap();
3335                rgbaf16
3336                    .write_u16::<NativeEndian>(f16::from_f32(rgba8[2] as f32).to_bits())
3337                    .unwrap();
3338                rgbaf16
3339                    .write_u16::<NativeEndian>(f16::from_f32(rgba8[3] as f32).to_bits())
3340                    .unwrap();
3341            }
3342            rgbaf16
3343        },
3344
3345        (TexFormat::RGB, TexDataType::HalfFloat) | (TexFormat::RGB16f, TexDataType::HalfFloat) => {
3346            let mut rgbf16 = Vec::<u8>::with_capacity(pixel_count * 6);
3347            for rgba8 in pixels.chunks(4) {
3348                rgbf16
3349                    .write_u16::<NativeEndian>(f16::from_f32(rgba8[0] as f32).to_bits())
3350                    .unwrap();
3351                rgbf16
3352                    .write_u16::<NativeEndian>(f16::from_f32(rgba8[1] as f32).to_bits())
3353                    .unwrap();
3354                rgbf16
3355                    .write_u16::<NativeEndian>(f16::from_f32(rgba8[2] as f32).to_bits())
3356                    .unwrap();
3357            }
3358            rgbf16
3359        },
3360        (TexFormat::Alpha, TexDataType::HalfFloat) |
3361        (TexFormat::Alpha16f, TexDataType::HalfFloat) => {
3362            for i in 0..pixel_count {
3363                let p = f16::from_f32(pixels[i * 4 + 3] as f32).to_bits();
3364                NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p);
3365            }
3366            pixels.truncate(pixel_count * 2);
3367            pixels
3368        },
3369        (TexFormat::Luminance, TexDataType::HalfFloat) |
3370        (TexFormat::Luminance16f, TexDataType::HalfFloat) => {
3371            for i in 0..pixel_count {
3372                let p = f16::from_f32(pixels[i * 4] as f32).to_bits();
3373                NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p);
3374            }
3375            pixels.truncate(pixel_count * 2);
3376            pixels
3377        },
3378        (TexFormat::LuminanceAlpha, TexDataType::HalfFloat) |
3379        (TexFormat::LuminanceAlpha16f, TexDataType::HalfFloat) => {
3380            for rgba8 in pixels.chunks_mut(4) {
3381                let lum = f16::from_f32(rgba8[0] as f32).to_bits();
3382                let a = f16::from_f32(rgba8[3] as f32).to_bits();
3383                NativeEndian::write_u16(&mut rgba8[0..2], lum);
3384                NativeEndian::write_u16(&mut rgba8[2..4], a);
3385            }
3386            pixels
3387        },
3388
3389        // Validation should have ensured that we only hit the
3390        // above cases, but we haven't turned the (format, type)
3391        // into an enum yet so there's a default case here.
3392        _ => unreachable!("Unsupported formats {:?} {:?}", format, data_type),
3393    }
3394}
3395
3396fn premultiply_inplace(format: TexFormat, data_type: TexDataType, pixels: &mut [u8]) {
3397    match (format, data_type) {
3398        (TexFormat::RGBA, TexDataType::UnsignedByte) => {
3399            pixels::rgba8_premultiply_inplace(pixels);
3400        },
3401        (TexFormat::LuminanceAlpha, TexDataType::UnsignedByte) => {
3402            for la in pixels.chunks_mut(2) {
3403                la[0] = pixels::multiply_u8_color(la[0], la[1]);
3404            }
3405        },
3406        (TexFormat::RGBA, TexDataType::UnsignedShort5551) => {
3407            for rgba in pixels.chunks_mut(2) {
3408                if NativeEndian::read_u16(rgba) & 1 == 0 {
3409                    NativeEndian::write_u16(rgba, 0);
3410                }
3411            }
3412        },
3413        (TexFormat::RGBA, TexDataType::UnsignedShort4444) => {
3414            for rgba in pixels.chunks_mut(2) {
3415                let pix = NativeEndian::read_u16(rgba);
3416                let extend_to_8_bits = |val| (val | (val << 4)) as u8;
3417                let r = extend_to_8_bits((pix >> 12) & 0x0f);
3418                let g = extend_to_8_bits((pix >> 8) & 0x0f);
3419                let b = extend_to_8_bits((pix >> 4) & 0x0f);
3420                let a = extend_to_8_bits(pix & 0x0f);
3421                NativeEndian::write_u16(
3422                    rgba,
3423                    (((pixels::multiply_u8_color(r, a) & 0xf0) as u16) << 8) |
3424                        (((pixels::multiply_u8_color(g, a) & 0xf0) as u16) << 4) |
3425                        ((pixels::multiply_u8_color(b, a) & 0xf0) as u16) |
3426                        ((a & 0x0f) as u16),
3427                );
3428            }
3429        },
3430        // Other formats don't have alpha, so return their data untouched.
3431        _ => {},
3432    }
3433}
3434
3435/// Flips the pixels in the Vec on the Y axis.
3436fn flip_pixels_y(
3437    internal_format: TexFormat,
3438    data_type: TexDataType,
3439    width: usize,
3440    height: usize,
3441    unpacking_alignment: usize,
3442    pixels: Vec<u8>,
3443) -> Vec<u8> {
3444    let cpp = (data_type.element_size() * internal_format.components() /
3445        data_type.components_per_element()) as usize;
3446
3447    let stride = (width * cpp + unpacking_alignment - 1) & !(unpacking_alignment - 1);
3448
3449    let mut flipped = Vec::<u8>::with_capacity(pixels.len());
3450
3451    for y in 0..height {
3452        let flipped_y = height - 1 - y;
3453        let start = flipped_y * stride;
3454
3455        flipped.extend_from_slice(&pixels[start..(start + width * cpp)]);
3456        flipped.extend(vec![0u8; stride - width * cpp]);
3457    }
3458
3459    flipped
3460}
3461
3462// Clamp a size to the current GL context's max viewport
3463fn clamp_viewport(gl: &Gl, size: Size2D<u32>) -> Size2D<u32> {
3464    let mut max_viewport = [i32::MAX, i32::MAX];
3465    let mut max_renderbuffer = [i32::MAX];
3466
3467    unsafe {
3468        gl.get_parameter_i32_slice(gl::MAX_VIEWPORT_DIMS, &mut max_viewport);
3469        gl.get_parameter_i32_slice(gl::MAX_RENDERBUFFER_SIZE, &mut max_renderbuffer);
3470    }
3471    Size2D::new(
3472        size.width
3473            .min(max_viewport[0] as u32)
3474            .min(max_renderbuffer[0] as u32)
3475            .max(1),
3476        size.height
3477            .min(max_viewport[1] as u32)
3478            .min(max_renderbuffer[0] as u32)
3479            .max(1),
3480    )
3481}
3482
3483trait ToSurfmanVersion {
3484    fn to_surfman_version(self, api_type: GlType) -> GLVersion;
3485}
3486
3487impl ToSurfmanVersion for WebGLVersion {
3488    fn to_surfman_version(self, api_type: GlType) -> GLVersion {
3489        if api_type == GlType::Gles {
3490            return GLVersion::new(3, 0);
3491        }
3492        match self {
3493            // We make use of GL_PACK_PIXEL_BUFFER, which needs at least GL2.1
3494            // We make use of compatibility mode, which needs at most GL3.0
3495            WebGLVersion::WebGL1 => GLVersion::new(2, 1),
3496            // The WebGL2 conformance tests use std140 layout, which needs at GL3.1
3497            WebGLVersion::WebGL2 => GLVersion::new(3, 2),
3498        }
3499    }
3500}
3501
3502trait SurfmanContextAttributeFlagsConvert {
3503    fn to_surfman_context_attribute_flags(
3504        &self,
3505        webgl_version: WebGLVersion,
3506        api_type: GlType,
3507    ) -> ContextAttributeFlags;
3508}
3509
3510impl SurfmanContextAttributeFlagsConvert for GLContextAttributes {
3511    fn to_surfman_context_attribute_flags(
3512        &self,
3513        webgl_version: WebGLVersion,
3514        api_type: GlType,
3515    ) -> ContextAttributeFlags {
3516        let mut flags = ContextAttributeFlags::empty();
3517        flags.set(ContextAttributeFlags::ALPHA, self.alpha);
3518        flags.set(ContextAttributeFlags::DEPTH, self.depth);
3519        flags.set(ContextAttributeFlags::STENCIL, self.stencil);
3520        if (webgl_version == WebGLVersion::WebGL1) && (api_type == GlType::Gl) {
3521            flags.set(ContextAttributeFlags::COMPATIBILITY_PROFILE, true);
3522        }
3523        flags
3524    }
3525}
3526
3527bitflags! {
3528    struct FramebufferRebindingFlags: u8 {
3529        const REBIND_READ_FRAMEBUFFER = 0x1;
3530        const REBIND_DRAW_FRAMEBUFFER = 0x2;
3531    }
3532}
3533
3534struct FramebufferRebindingInfo {
3535    flags: FramebufferRebindingFlags,
3536    viewport: [GLint; 4],
3537}
3538
3539impl FramebufferRebindingInfo {
3540    fn detect(device: &Device, context: &Context, gl: &Gl) -> FramebufferRebindingInfo {
3541        unsafe {
3542            let read_framebuffer = gl.get_parameter_framebuffer(gl::READ_FRAMEBUFFER_BINDING);
3543            let draw_framebuffer = gl.get_parameter_framebuffer(gl::DRAW_FRAMEBUFFER_BINDING);
3544
3545            let context_surface_framebuffer = device
3546                .context_surface_info(context)
3547                .unwrap()
3548                .unwrap()
3549                .framebuffer_object;
3550
3551            let mut flags = FramebufferRebindingFlags::empty();
3552            if context_surface_framebuffer == read_framebuffer {
3553                flags.insert(FramebufferRebindingFlags::REBIND_READ_FRAMEBUFFER);
3554            }
3555            if context_surface_framebuffer == draw_framebuffer {
3556                flags.insert(FramebufferRebindingFlags::REBIND_DRAW_FRAMEBUFFER);
3557            }
3558
3559            let mut viewport = [0; 4];
3560            gl.get_parameter_i32_slice(gl::VIEWPORT, &mut viewport);
3561
3562            FramebufferRebindingInfo { flags, viewport }
3563        }
3564    }
3565
3566    fn apply(self, device: &Device, context: &Context, gl: &Gl) {
3567        if self.flags.is_empty() {
3568            return;
3569        }
3570
3571        let context_surface_framebuffer = device
3572            .context_surface_info(context)
3573            .unwrap()
3574            .unwrap()
3575            .framebuffer_object;
3576        if self
3577            .flags
3578            .contains(FramebufferRebindingFlags::REBIND_READ_FRAMEBUFFER)
3579        {
3580            unsafe { gl.bind_framebuffer(gl::READ_FRAMEBUFFER, context_surface_framebuffer) };
3581        }
3582        if self
3583            .flags
3584            .contains(FramebufferRebindingFlags::REBIND_DRAW_FRAMEBUFFER)
3585        {
3586            unsafe { gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, context_surface_framebuffer) };
3587        }
3588
3589        unsafe {
3590            gl.viewport(
3591                self.viewport[0],
3592                self.viewport[1],
3593                self.viewport[2],
3594                self.viewport[3],
3595            )
3596        };
3597    }
3598}
3599
3600/// Returns the bytes per pixel of a `readPixels` format and type, including the integer
3601/// formats and the packed types that glow's size helpers reject.
3602fn read_pixels_bytes_per_pixel(format: u32, pixel_type: u32) -> usize {
3603    let components = match format {
3604        gl::RED | gl::RED_INTEGER | gl::ALPHA | gl::LUMINANCE => 1,
3605        gl::RG | gl::RG_INTEGER | gl::LUMINANCE_ALPHA => 2,
3606        gl::RGB | gl::RGB_INTEGER => 3,
3607        gl::RGBA | gl::RGBA_INTEGER => 4,
3608        _ => unreachable!("readPixels formats are validated on the script side"),
3609    };
3610    match pixel_type {
3611        gl::UNSIGNED_SHORT_4_4_4_4 | gl::UNSIGNED_SHORT_5_5_5_1 | gl::UNSIGNED_SHORT_5_6_5 => 2,
3612        gl::UNSIGNED_INT_2_10_10_10_REV |
3613        gl::UNSIGNED_INT_10F_11F_11F_REV |
3614        gl::UNSIGNED_INT_5_9_9_9_REV => 4,
3615        _ => components * bytes_per_type(pixel_type),
3616    }
3617}
3618
3619#[cfg(test)]
3620mod tests {
3621    use servo_canvas_traits::webgl::{TexDataType, TexFormat};
3622
3623    use super::*;
3624
3625    #[test]
3626    fn test_tex_bytes_per_pixel() {
3627        // Standard uncompressed formats
3628        assert_eq!(
3629            WebGLImpl::tex_bytes_per_pixel(TexFormat::RGBA, TexDataType::UnsignedByte),
3630            4
3631        );
3632        assert_eq!(
3633            WebGLImpl::tex_bytes_per_pixel(TexFormat::RGB, TexDataType::UnsignedByte),
3634            3
3635        );
3636        assert_eq!(
3637            WebGLImpl::tex_bytes_per_pixel(TexFormat::RG, TexDataType::UnsignedByte),
3638            2
3639        );
3640        assert_eq!(
3641            WebGLImpl::tex_bytes_per_pixel(TexFormat::Red, TexDataType::UnsignedByte),
3642            1
3643        );
3644        assert_eq!(
3645            WebGLImpl::tex_bytes_per_pixel(TexFormat::RGBA, TexDataType::Float),
3646            16
3647        );
3648        assert_eq!(
3649            WebGLImpl::tex_bytes_per_pixel(TexFormat::RGBA, TexDataType::HalfFloat),
3650            8
3651        );
3652        assert_eq!(
3653            WebGLImpl::tex_bytes_per_pixel(TexFormat::DepthComponent, TexDataType::UnsignedShort),
3654            2
3655        );
3656        assert_eq!(
3657            WebGLImpl::tex_bytes_per_pixel(TexFormat::DepthComponent, TexDataType::UnsignedInt),
3658            4
3659        );
3660        assert_eq!(
3661            WebGLImpl::tex_bytes_per_pixel(TexFormat::DepthComponent, TexDataType::Float),
3662            4
3663        );
3664
3665        // Packed types
3666        assert_eq!(
3667            WebGLImpl::tex_bytes_per_pixel(TexFormat::RGB, TexDataType::UnsignedShort565),
3668            2
3669        );
3670        assert_eq!(
3671            WebGLImpl::tex_bytes_per_pixel(TexFormat::RGBA, TexDataType::UnsignedShort4444),
3672            2
3673        );
3674        assert_eq!(
3675            WebGLImpl::tex_bytes_per_pixel(TexFormat::RGBA, TexDataType::UnsignedShort5551),
3676            2
3677        );
3678        assert_eq!(
3679            WebGLImpl::tex_bytes_per_pixel(TexFormat::RGB, TexDataType::UnsignedInt10f11f11fRev),
3680            4
3681        );
3682        assert_eq!(
3683            WebGLImpl::tex_bytes_per_pixel(TexFormat::RGBA, TexDataType::UnsignedInt2101010Rev),
3684            4
3685        );
3686        assert_eq!(
3687            WebGLImpl::tex_bytes_per_pixel(TexFormat::RGB, TexDataType::UnsignedInt5999Rev),
3688            4
3689        );
3690        assert_eq!(
3691            WebGLImpl::tex_bytes_per_pixel(TexFormat::DepthStencil, TexDataType::UnsignedInt248),
3692            4
3693        );
3694        assert_eq!(
3695            WebGLImpl::tex_bytes_per_pixel(
3696                TexFormat::DepthStencil,
3697                TexDataType::Float32UnsignedInt248Rev
3698            ),
3699            8
3700        );
3701    }
3702
3703    #[test]
3704    fn test_sized_internal_formats_resolution() {
3705        let test_formats = [
3706            (TexFormat::R8, TexFormat::Red, TexDataType::UnsignedByte, 1),
3707            (TexFormat::R8SNorm, TexFormat::Red, TexDataType::Byte, 1),
3708            (TexFormat::R16f, TexFormat::Red, TexDataType::HalfFloat, 2),
3709            (TexFormat::R32f, TexFormat::Red, TexDataType::Float, 4),
3710            (
3711                TexFormat::R8ui,
3712                TexFormat::RedInteger,
3713                TexDataType::UnsignedByte,
3714                1,
3715            ),
3716            (TexFormat::R8i, TexFormat::RedInteger, TexDataType::Byte, 1),
3717            (TexFormat::RG8, TexFormat::RG, TexDataType::UnsignedByte, 2),
3718            (TexFormat::RG16f, TexFormat::RG, TexDataType::HalfFloat, 4),
3719            (TexFormat::RG32f, TexFormat::RG, TexDataType::Float, 8),
3720            (
3721                TexFormat::RGB8,
3722                TexFormat::RGB,
3723                TexDataType::UnsignedByte,
3724                3,
3725            ),
3726            (
3727                TexFormat::SRGB8,
3728                TexFormat::RGB,
3729                TexDataType::UnsignedByte,
3730                3,
3731            ),
3732            (
3733                TexFormat::RGB565,
3734                TexFormat::RGB,
3735                TexDataType::UnsignedByte,
3736                3,
3737            ),
3738            (
3739                TexFormat::R11fG11fB10f,
3740                TexFormat::RGB,
3741                TexDataType::UnsignedInt10f11f11fRev,
3742                4,
3743            ),
3744            (
3745                TexFormat::RGB9E5,
3746                TexFormat::RGB,
3747                TexDataType::UnsignedInt5999Rev,
3748                4,
3749            ),
3750            (TexFormat::RGB16f, TexFormat::RGB, TexDataType::HalfFloat, 6),
3751            (TexFormat::RGB32f, TexFormat::RGB, TexDataType::Float, 12),
3752            (
3753                TexFormat::RGBA8,
3754                TexFormat::RGBA,
3755                TexDataType::UnsignedByte,
3756                4,
3757            ),
3758            (
3759                TexFormat::SRGB8Alpha8,
3760                TexFormat::RGBA,
3761                TexDataType::UnsignedByte,
3762                4,
3763            ),
3764            (
3765                TexFormat::RGB5A1,
3766                TexFormat::RGBA,
3767                TexDataType::UnsignedByte,
3768                4,
3769            ),
3770            (
3771                TexFormat::RGBA4,
3772                TexFormat::RGBA,
3773                TexDataType::UnsignedByte,
3774                4,
3775            ),
3776            (
3777                TexFormat::RGB10A2,
3778                TexFormat::RGBA,
3779                TexDataType::UnsignedInt2101010Rev,
3780                4,
3781            ),
3782            (
3783                TexFormat::RGBA16f,
3784                TexFormat::RGBA,
3785                TexDataType::HalfFloat,
3786                8,
3787            ),
3788            (TexFormat::RGBA32f, TexFormat::RGBA, TexDataType::Float, 16),
3789            (
3790                TexFormat::RGBA8ui,
3791                TexFormat::RGBAInteger,
3792                TexDataType::UnsignedByte,
3793                4,
3794            ),
3795            (
3796                TexFormat::RGB10A2ui,
3797                TexFormat::RGBAInteger,
3798                TexDataType::UnsignedInt2101010Rev,
3799                4,
3800            ),
3801            (
3802                TexFormat::RGBA16ui,
3803                TexFormat::RGBAInteger,
3804                TexDataType::UnsignedShort,
3805                8,
3806            ),
3807            (
3808                TexFormat::RGBA32ui,
3809                TexFormat::RGBAInteger,
3810                TexDataType::UnsignedInt,
3811                16,
3812            ),
3813            (
3814                TexFormat::DepthComponent16,
3815                TexFormat::DepthComponent,
3816                TexDataType::UnsignedShort,
3817                2,
3818            ),
3819            (
3820                TexFormat::DepthComponent24,
3821                TexFormat::DepthComponent,
3822                TexDataType::UnsignedInt,
3823                4,
3824            ),
3825            (
3826                TexFormat::DepthComponent32f,
3827                TexFormat::DepthComponent,
3828                TexDataType::Float,
3829                4,
3830            ),
3831            (
3832                TexFormat::Depth24Stencil8,
3833                TexFormat::DepthStencil,
3834                TexDataType::UnsignedInt248,
3835                4,
3836            ),
3837            (
3838                TexFormat::Depth32fStencil8,
3839                TexFormat::DepthStencil,
3840                TexDataType::Float32UnsignedInt248Rev,
3841                8,
3842            ),
3843        ];
3844
3845        for (sized, expected_unsized, expected_type, expected_bpp) in test_formats {
3846            let unsized_format = sized.to_unsized();
3847            assert_eq!(
3848                unsized_format, expected_unsized,
3849                "Unsized format mismatch for {:?}",
3850                sized
3851            );
3852            let first_type = *sized
3853                .compatible_data_types()
3854                .first()
3855                .expect("Compatible data type missing");
3856            assert_eq!(
3857                first_type, expected_type,
3858                "Data type mismatch for {:?}",
3859                sized
3860            );
3861            let bpp = WebGLImpl::tex_bytes_per_pixel(unsized_format, first_type);
3862            assert_eq!(bpp, expected_bpp, "Bpp mismatch for {:?}", sized);
3863        }
3864    }
3865}