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