webgl/
webgl_thread.rs

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