webgl/
webgl_thread.rs

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