Skip to main content

webgl/
webgl_thread.rs

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