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