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