Skip to main content

webgl/
webgl_thread.rs

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