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: 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            // Ensure there are no pending GL errors from other parts of the pipeline.
861            debug_assert_eq!(unsafe { data.gl.get_error() }, gl::NO_ERROR);
862
863            // Check to see if any of the current framebuffer bindings are the surface we're about
864            // to swap out. If so, we'll have to reset them after destroying the surface.
865            let framebuffer_rebinding_info =
866                FramebufferRebindingInfo::detect(&data.device, &data.ctx, &data.gl);
867            debug_assert_eq!(unsafe { data.gl.get_error() }, gl::NO_ERROR);
868
869            debug!("Getting swap chain for {:?}", context_id);
870            let swap_chain = self
871                .webrender_swap_chains
872                .get(context_id)
873                .expect("Where's the swap chain?");
874
875            debug!("Swapping {:?}", context_id);
876            swap_chain
877                .swap_buffers(
878                    &data.device,
879                    &mut data.ctx,
880                    if data.attributes.preserve_drawing_buffer {
881                        PreserveBuffer::Yes(&data.gl)
882                    } else {
883                        PreserveBuffer::No
884                    },
885                )
886                .unwrap();
887            debug_assert_eq!(unsafe { data.gl.get_error() }, gl::NO_ERROR);
888
889            if !data.attributes.preserve_drawing_buffer {
890                debug!("Clearing {:?}", context_id);
891                let alpha = data
892                    .state
893                    .requested_flags
894                    .contains(ContextAttributeFlags::ALPHA);
895                let clear_color = [0.0, 0.0, 0.0, !alpha as i32 as f32];
896                swap_chain
897                    .clear_surface(&data.device, &mut data.ctx, &data.gl, clear_color)
898                    .unwrap();
899                debug_assert_eq!(unsafe { data.gl.get_error() }, gl::NO_ERROR);
900            }
901
902            // Rebind framebuffers as appropriate.
903            debug!("Rebinding {:?}", context_id);
904            framebuffer_rebinding_info.apply(&data.device, &data.ctx, &data.gl);
905            debug_assert_eq!(unsafe { data.gl.get_error() }, gl::NO_ERROR);
906
907            let SurfaceInfo {
908                size,
909                framebuffer_object,
910                id,
911                ..
912            } = data
913                .device
914                .context_surface_info(&data.ctx)
915                .unwrap()
916                .unwrap();
917            debug!(
918                "... rebound framebuffer {:?}, new back buffer surface is {:?}",
919                framebuffer_object, id
920            );
921
922            let has_alpha = data
923                .state
924                .requested_flags
925                .contains(ContextAttributeFlags::ALPHA);
926            self.update_webrender_image_for_context(context_id, size, has_alpha, canvas_epoch);
927        }
928    }
929
930    /// Which access mode to use
931    fn surface_access(&self) -> SurfaceAccess {
932        SurfaceAccess::GPUOnly
933    }
934
935    /// Gets a reference to a Context for a given WebGLContextId and makes it current if required.
936    pub(crate) fn make_current_if_needed(
937        &mut self,
938        context_id: WebGLContextId,
939    ) -> Option<&GLContextData> {
940        let data = self.contexts.get(&context_id);
941
942        if let Some(data) = data &&
943            Some(context_id) != self.bound_context_id
944        {
945            data.device.make_context_current(&data.ctx).unwrap();
946            self.bound_context_id = Some(context_id);
947        }
948
949        data
950    }
951
952    /// Gets a mutable reference to a GLContextWrapper for a WebGLContextId and makes it current if required.
953    pub(crate) fn make_current_if_needed_mut(
954        &mut self,
955        context_id: WebGLContextId,
956    ) -> Option<&mut GLContextData> {
957        let data = self.contexts.get_mut(&context_id);
958        if let Some(ref data) = data &&
959            Some(context_id) != self.bound_context_id
960        {
961            data.device.make_context_current(&data.ctx).unwrap();
962            self.bound_context_id = Some(context_id);
963        }
964
965        data
966    }
967
968    /// Tell WebRender to invalidate any cached tiles for a given `WebGLContextId`
969    /// when the underlying surface has changed e.g due to resize or buffer swap
970    fn update_webrender_image_for_context(
971        &mut self,
972        context_id: WebGLContextId,
973        size: Size2D<i32>,
974        has_alpha: bool,
975        canvas_epoch: Option<Epoch>,
976    ) {
977        let image_data = self.external_image_data(context_id);
978        let info = self.cached_context_info.get_mut(&context_id).unwrap();
979        info.size = size;
980        info.alpha = has_alpha;
981
982        if let Some(image_key) = info.image_key {
983            self.paint_api.update_image(
984                image_key,
985                info.image_descriptor(),
986                image_data,
987                canvas_epoch,
988            );
989        }
990    }
991
992    /// Helper function to create a `ImageData::External` instance.
993    fn external_image_data(&self, context_id: WebGLContextId) -> SerializableImageData {
994        // TODO(pcwalton): Add `GL_TEXTURE_EXTERNAL_OES`?
995        let device = self.device_for_context(context_id);
996        let image_buffer_kind = match device.surface_gl_texture_target() {
997            gl::TEXTURE_RECTANGLE => ImageBufferKind::TextureRect,
998            _ => ImageBufferKind::Texture2D,
999        };
1000
1001        let data = ExternalImageData {
1002            id: ExternalImageId(context_id.0),
1003            channel_index: 0,
1004            image_type: ExternalImageType::TextureHandle(image_buffer_kind),
1005            normalized_uvs: false,
1006        };
1007        SerializableImageData::External(data)
1008    }
1009
1010    /// Gets the GLSL Version supported by a GLContext.
1011    fn get_glsl_version(gl: &Gl) -> WebGLSLVersion {
1012        let version = unsafe { gl.get_parameter_string(gl::SHADING_LANGUAGE_VERSION) };
1013        // Fomat used by SHADING_LANGUAGE_VERSION query : major.minor[.release] [vendor info]
1014        let mut values = version.split(&['.', ' '][..]);
1015        let major = values
1016            .next()
1017            .and_then(|v| v.parse::<u32>().ok())
1018            .unwrap_or(1);
1019        let minor = values
1020            .next()
1021            .and_then(|v| v.parse::<u32>().ok())
1022            .unwrap_or(20);
1023
1024        WebGLSLVersion { major, minor }
1025    }
1026
1027    fn handle_set_image_key(&mut self, context_id: WebGLContextId, image_key: ImageKey) {
1028        let external_image_data = self.external_image_data(context_id);
1029        let Some(info) = self.cached_context_info.get_mut(&context_id) else {
1030            self.paint_api.delete_image(image_key);
1031            return;
1032        };
1033
1034        if let Some(old_image_key) = info.image_key.replace(image_key) {
1035            self.paint_api.delete_image(old_image_key);
1036            return;
1037        }
1038
1039        self.paint_api.add_image(
1040            image_key,
1041            info.image_descriptor(),
1042            external_image_data,
1043            false,
1044        );
1045    }
1046}
1047
1048/// Helper struct to store cached WebGLContext information.
1049struct WebGLContextInfo {
1050    image_key: Option<ImageKey>,
1051    size: Size2D<i32>,
1052    alpha: bool,
1053}
1054
1055impl WebGLContextInfo {
1056    /// Helper function to create a `ImageDescriptor`.
1057    fn image_descriptor(&self) -> ImageDescriptor {
1058        let mut flags = ImageDescriptorFlags::empty();
1059        flags.set(ImageDescriptorFlags::IS_OPAQUE, !self.alpha);
1060        ImageDescriptor {
1061            size: DeviceIntSize::new(self.size.width, self.size.height),
1062            stride: None,
1063            format: ImageFormat::BGRA8,
1064            offset: 0,
1065            flags,
1066        }
1067    }
1068}
1069
1070/// WebGL Commands Implementation
1071pub struct WebGLImpl;
1072
1073impl WebGLImpl {
1074    pub fn apply(
1075        device: &Device,
1076        ctx: &Context,
1077        gl: &Gl,
1078        state: &mut GLState,
1079        attributes: &GLContextAttributes,
1080        command: WebGLCommand,
1081        _backtrace: WebGLCommandBacktrace,
1082    ) {
1083        // Ensure there are no pending GL errors from other parts of the pipeline.
1084        debug_assert_eq!(unsafe { gl.get_error() }, gl::NO_ERROR);
1085
1086        match command {
1087            WebGLCommand::GetContextAttributes(ref sender) => sender.send(*attributes).unwrap(),
1088            WebGLCommand::ActiveTexture(target) => unsafe { gl.active_texture(target) },
1089            WebGLCommand::AttachShader(program_id, shader_id) => unsafe {
1090                gl.attach_shader(program_id.glow(), shader_id.glow())
1091            },
1092            WebGLCommand::DetachShader(program_id, shader_id) => unsafe {
1093                gl.detach_shader(program_id.glow(), shader_id.glow())
1094            },
1095            WebGLCommand::BindAttribLocation(program_id, index, ref name) => unsafe {
1096                gl.bind_attrib_location(program_id.glow(), index, &to_name_in_compiled_shader(name))
1097            },
1098            WebGLCommand::BlendColor(r, g, b, a) => unsafe { gl.blend_color(r, g, b, a) },
1099            WebGLCommand::BlendEquation(mode) => unsafe { gl.blend_equation(mode) },
1100            WebGLCommand::BlendEquationSeparate(mode_rgb, mode_alpha) => unsafe {
1101                gl.blend_equation_separate(mode_rgb, mode_alpha)
1102            },
1103            WebGLCommand::BlendFunc(src, dest) => unsafe { gl.blend_func(src, dest) },
1104            WebGLCommand::BlendFuncSeparate(src_rgb, dest_rgb, src_alpha, dest_alpha) => unsafe {
1105                gl.blend_func_separate(src_rgb, dest_rgb, src_alpha, dest_alpha)
1106            },
1107            WebGLCommand::BufferData(buffer_type, ref receiver, usage) => unsafe {
1108                gl.buffer_data_u8_slice(buffer_type, &receiver.recv().unwrap(), usage)
1109            },
1110            WebGLCommand::BufferSubData(buffer_type, offset, ref receiver) => unsafe {
1111                gl.buffer_sub_data_u8_slice(buffer_type, offset as i32, &receiver.recv().unwrap())
1112            },
1113            WebGLCommand::CopyBufferSubData(src, dst, src_offset, dst_offset, size) => {
1114                unsafe {
1115                    gl.copy_buffer_sub_data(
1116                        src,
1117                        dst,
1118                        src_offset as i32,
1119                        dst_offset as i32,
1120                        size as i32,
1121                    )
1122                };
1123            },
1124            WebGLCommand::GetBufferSubData(buffer_type, offset, length, ref sender) => unsafe {
1125                let ptr = gl.map_buffer_range(
1126                    buffer_type,
1127                    offset as i32,
1128                    length as i32,
1129                    gl::MAP_READ_BIT,
1130                );
1131                let data: &[u8] = slice::from_raw_parts(ptr as _, length);
1132                let buffer = GenericSharedMemory::from_bytes(data);
1133                sender.send(buffer).unwrap();
1134                gl.unmap_buffer(buffer_type);
1135            },
1136            WebGLCommand::Clear(mask) => {
1137                unsafe { gl.clear(mask) };
1138            },
1139            WebGLCommand::ClearColor(r, g, b, a) => {
1140                state.clear_color = (r, g, b, a);
1141                unsafe { gl.clear_color(r, g, b, a) };
1142            },
1143            WebGLCommand::ClearDepth(depth) => {
1144                let value = depth.clamp(0., 1.) as f64;
1145                state.depth_clear_value = value;
1146                unsafe { gl.clear_depth(value) }
1147            },
1148            WebGLCommand::ClearStencil(stencil) => {
1149                state.stencil_clear_value = stencil;
1150                unsafe { gl.clear_stencil(stencil) };
1151            },
1152            WebGLCommand::ColorMask(r, g, b, a) => {
1153                state.color_write_mask = [r, g, b, a];
1154                state.restore_alpha_invariant(gl);
1155            },
1156            WebGLCommand::CopyTexImage2D(
1157                target,
1158                level,
1159                internal_format,
1160                x,
1161                y,
1162                width,
1163                height,
1164                border,
1165            ) => unsafe {
1166                gl.copy_tex_image_2d(target, level, internal_format, x, y, width, height, border)
1167            },
1168            WebGLCommand::CopyTexSubImage2D(
1169                target,
1170                level,
1171                xoffset,
1172                yoffset,
1173                x,
1174                y,
1175                width,
1176                height,
1177            ) => unsafe {
1178                gl.copy_tex_sub_image_2d(target, level, xoffset, yoffset, x, y, width, height)
1179            },
1180            WebGLCommand::CullFace(mode) => unsafe { gl.cull_face(mode) },
1181            WebGLCommand::DepthFunc(func) => unsafe { gl.depth_func(func) },
1182            WebGLCommand::DepthMask(flag) => {
1183                state.depth_write_mask = flag;
1184                state.restore_depth_invariant(gl);
1185            },
1186            WebGLCommand::DepthRange(near, far) => unsafe {
1187                gl.depth_range(near.clamp(0., 1.) as f64, far.clamp(0., 1.) as f64)
1188            },
1189            WebGLCommand::Disable(cap) => match cap {
1190                gl::SCISSOR_TEST => {
1191                    state.scissor_test_enabled = false;
1192                    state.restore_scissor_invariant(gl);
1193                },
1194                gl::DEPTH_TEST => {
1195                    state.depth_test_enabled = false;
1196                    state.restore_depth_invariant(gl);
1197                },
1198                gl::STENCIL_TEST => {
1199                    state.stencil_test_enabled = false;
1200                    state.restore_stencil_invariant(gl);
1201                },
1202                _ => unsafe { gl.disable(cap) },
1203            },
1204            WebGLCommand::Enable(cap) => match cap {
1205                gl::SCISSOR_TEST => {
1206                    state.scissor_test_enabled = true;
1207                    state.restore_scissor_invariant(gl);
1208                },
1209                gl::DEPTH_TEST => {
1210                    state.depth_test_enabled = true;
1211                    state.restore_depth_invariant(gl);
1212                },
1213                gl::STENCIL_TEST => {
1214                    state.stencil_test_enabled = true;
1215                    state.restore_stencil_invariant(gl);
1216                },
1217                _ => unsafe { gl.enable(cap) },
1218            },
1219            WebGLCommand::FramebufferRenderbuffer(target, attachment, renderbuffertarget, rb) => {
1220                let attach = |attachment| unsafe {
1221                    gl.framebuffer_renderbuffer(
1222                        target,
1223                        attachment,
1224                        renderbuffertarget,
1225                        rb.map(WebGLRenderbufferId::glow),
1226                    )
1227                };
1228                if attachment == gl::DEPTH_STENCIL_ATTACHMENT {
1229                    attach(gl::DEPTH_ATTACHMENT);
1230                    attach(gl::STENCIL_ATTACHMENT);
1231                } else {
1232                    attach(attachment);
1233                }
1234            },
1235            WebGLCommand::FramebufferTexture2D(target, attachment, textarget, texture, level) => {
1236                let attach = |attachment| unsafe {
1237                    gl.framebuffer_texture_2d(
1238                        target,
1239                        attachment,
1240                        textarget,
1241                        texture.map(WebGLTextureId::glow),
1242                        level,
1243                    )
1244                };
1245                if attachment == gl::DEPTH_STENCIL_ATTACHMENT {
1246                    attach(gl::DEPTH_ATTACHMENT);
1247                    attach(gl::STENCIL_ATTACHMENT);
1248                } else {
1249                    attach(attachment)
1250                }
1251            },
1252            WebGLCommand::FrontFace(mode) => unsafe { gl.front_face(mode) },
1253            WebGLCommand::DisableVertexAttribArray(attrib_id) => unsafe {
1254                gl.disable_vertex_attrib_array(attrib_id)
1255            },
1256            WebGLCommand::EnableVertexAttribArray(attrib_id) => unsafe {
1257                gl.enable_vertex_attrib_array(attrib_id)
1258            },
1259            WebGLCommand::Hint(name, val) => unsafe { gl.hint(name, val) },
1260            WebGLCommand::LineWidth(width) => {
1261                unsafe { gl.line_width(width) };
1262                // In OpenGL Core Profile >3.2, any non-1.0 value will generate INVALID_VALUE.
1263                if width != 1.0 {
1264                    let _ = unsafe { gl.get_error() };
1265                }
1266            },
1267            WebGLCommand::PixelStorei(name, val) => unsafe { gl.pixel_store_i32(name, val) },
1268            WebGLCommand::PolygonOffset(factor, units) => unsafe {
1269                gl.polygon_offset(factor, units)
1270            },
1271            WebGLCommand::ReadPixels(rect, format, pixel_type, ref sender) => {
1272                let len = bytes_per_type(pixel_type) *
1273                    components_per_format(format) *
1274                    rect.size.area() as usize;
1275                let mut pixels = vec![0; len];
1276                unsafe {
1277                    // We don't want any alignment padding on pixel rows.
1278                    gl.pixel_store_i32(glow::PACK_ALIGNMENT, 1);
1279                    gl.read_pixels(
1280                        rect.origin.x as i32,
1281                        rect.origin.y as i32,
1282                        rect.size.width as i32,
1283                        rect.size.height as i32,
1284                        format,
1285                        pixel_type,
1286                        glow::PixelPackData::Slice(Some(&mut pixels)),
1287                    )
1288                };
1289                let alpha_mode = match (attributes.alpha, attributes.premultiplied_alpha) {
1290                    (true, premultiplied) => SnapshotAlphaMode::Transparent { premultiplied },
1291                    (false, _) => SnapshotAlphaMode::Opaque,
1292                };
1293                sender
1294                    .send((GenericSharedMemory::from_vec(pixels), alpha_mode))
1295                    .unwrap();
1296            },
1297            WebGLCommand::ReadPixelsPP(rect, format, pixel_type, offset) => unsafe {
1298                gl.read_pixels(
1299                    rect.origin.x,
1300                    rect.origin.y,
1301                    rect.size.width,
1302                    rect.size.height,
1303                    format,
1304                    pixel_type,
1305                    glow::PixelPackData::BufferOffset(offset as u32),
1306                );
1307            },
1308            WebGLCommand::RenderbufferStorage(target, format, width, height) => unsafe {
1309                gl.renderbuffer_storage(target, format, width, height)
1310            },
1311            WebGLCommand::RenderbufferStorageMultisample(
1312                target,
1313                samples,
1314                format,
1315                width,
1316                height,
1317            ) => unsafe {
1318                gl.renderbuffer_storage_multisample(target, samples, format, width, height)
1319            },
1320            WebGLCommand::SampleCoverage(value, invert) => unsafe {
1321                gl.sample_coverage(value, invert)
1322            },
1323            WebGLCommand::Scissor(x, y, width, height) => {
1324                // FIXME(nox): Kinda unfortunate that some u32 values could
1325                // end up as negative numbers here, but I don't even think
1326                // that can happen in the real world.
1327                unsafe { gl.scissor(x, y, width as i32, height as i32) };
1328            },
1329            WebGLCommand::StencilFunc(func, ref_, mask) => {
1330                state.stencil_value_mask = (mask, mask);
1331                unsafe { gl.stencil_func(func, ref_, mask) }
1332            },
1333            WebGLCommand::StencilFuncSeparate(face, func, ref_, mask) => {
1334                if face != gl::BACK {
1335                    state.stencil_value_mask.0 = mask;
1336                }
1337                if face != gl::FRONT {
1338                    state.stencil_value_mask.1 = mask;
1339                }
1340                unsafe { gl.stencil_func_separate(face, func, ref_, mask) }
1341            },
1342            WebGLCommand::StencilMask(mask) => {
1343                state.stencil_write_mask = (mask, mask);
1344                state.restore_stencil_invariant(gl);
1345            },
1346            WebGLCommand::StencilMaskSeparate(face, mask) => {
1347                match face {
1348                    gl::FRONT => state.stencil_write_mask.0 = mask,
1349                    gl::BACK => state.stencil_write_mask.1 = mask,
1350                    gl::FRONT_AND_BACK => state.stencil_write_mask = (mask, mask),
1351                    _ => debug_assert!(
1352                        false,
1353                        "stencilMaskSeparate face {face:#x} should have been rejected by the DOM"
1354                    ),
1355                }
1356                state.restore_stencil_invariant(gl);
1357            },
1358            WebGLCommand::StencilOp(fail, zfail, zpass) => unsafe {
1359                gl.stencil_op(fail, zfail, zpass)
1360            },
1361            WebGLCommand::StencilOpSeparate(face, fail, zfail, zpass) => unsafe {
1362                gl.stencil_op_separate(face, fail, zfail, zpass)
1363            },
1364            WebGLCommand::GetRenderbufferParameter(target, pname, ref chan) => {
1365                Self::get_renderbuffer_parameter(gl, target, pname, chan)
1366            },
1367            WebGLCommand::CreateTransformFeedback(ref sender) => {
1368                let value = unsafe { gl.create_transform_feedback() }.ok();
1369                sender
1370                    .send(value.map(|ntf| ntf.0.get()).unwrap_or_default())
1371                    .unwrap()
1372            },
1373            WebGLCommand::DeleteTransformFeedback(id) => {
1374                if let Some(tf) = NonZeroU32::new(id) {
1375                    unsafe { gl.delete_transform_feedback(NativeTransformFeedback(tf)) };
1376                }
1377            },
1378            WebGLCommand::IsTransformFeedback(id, ref sender) => {
1379                let value = NonZeroU32::new(id)
1380                    .map(|id| unsafe { gl.is_transform_feedback(NativeTransformFeedback(id)) })
1381                    .unwrap_or_default();
1382                sender.send(value).unwrap()
1383            },
1384            WebGLCommand::BindTransformFeedback(target, id) => {
1385                unsafe {
1386                    gl.bind_transform_feedback(
1387                        target,
1388                        NonZeroU32::new(id).map(NativeTransformFeedback),
1389                    )
1390                };
1391            },
1392            WebGLCommand::BeginTransformFeedback(mode) => {
1393                unsafe { gl.begin_transform_feedback(mode) };
1394            },
1395            WebGLCommand::EndTransformFeedback() => {
1396                unsafe { gl.end_transform_feedback() };
1397            },
1398            WebGLCommand::PauseTransformFeedback() => {
1399                unsafe { gl.pause_transform_feedback() };
1400            },
1401            WebGLCommand::ResumeTransformFeedback() => {
1402                unsafe { gl.resume_transform_feedback() };
1403            },
1404            WebGLCommand::GetTransformFeedbackVarying(program, index, ref sender) => {
1405                let ActiveTransformFeedback { size, tftype, name } =
1406                    unsafe { gl.get_transform_feedback_varying(program.glow(), index) }.unwrap();
1407                // We need to split, because the name starts with '_u' prefix.
1408                let name = from_name_in_compiled_shader(&name);
1409                sender.send((size, tftype, name)).unwrap();
1410            },
1411            WebGLCommand::TransformFeedbackVaryings(program, ref varyings, buffer_mode) => {
1412                let varyings: Vec<String> = varyings
1413                    .iter()
1414                    .map(|varying| to_name_in_compiled_shader(varying))
1415                    .collect();
1416                let varyings_refs: Vec<&str> = varyings.iter().map(String::as_ref).collect();
1417                unsafe {
1418                    gl.transform_feedback_varyings(
1419                        program.glow(),
1420                        varyings_refs.as_slice(),
1421                        buffer_mode,
1422                    )
1423                };
1424            },
1425            WebGLCommand::GetFramebufferAttachmentParameter(
1426                target,
1427                attachment,
1428                pname,
1429                ref chan,
1430            ) => Self::get_framebuffer_attachment_parameter(gl, target, attachment, pname, chan),
1431            WebGLCommand::GetShaderPrecisionFormat(shader_type, precision_type, ref chan) => {
1432                Self::shader_precision_format(gl, shader_type, precision_type, chan)
1433            },
1434            WebGLCommand::GetExtensions(ref chan) => Self::get_extensions(gl, chan),
1435            WebGLCommand::GetFragDataLocation(program_id, ref name, ref sender) => {
1436                let location = unsafe {
1437                    gl.get_frag_data_location(program_id.glow(), &to_name_in_compiled_shader(name))
1438                };
1439                sender.send(location).unwrap();
1440            },
1441            WebGLCommand::GetUniformLocation(program_id, ref name, ref chan) => {
1442                Self::uniform_location(gl, program_id, name, chan)
1443            },
1444            WebGLCommand::GetShaderInfoLog(shader_id, ref chan) => {
1445                Self::shader_info_log(gl, shader_id, chan)
1446            },
1447            WebGLCommand::GetProgramInfoLog(program_id, ref chan) => {
1448                Self::program_info_log(gl, program_id, chan)
1449            },
1450            WebGLCommand::CompileShader(shader_id, ref source) => {
1451                Self::compile_shader(gl, shader_id, source)
1452            },
1453            WebGLCommand::CreateBuffer(ref chan) => Self::create_buffer(gl, chan),
1454            WebGLCommand::CreateFramebuffer(ref chan) => Self::create_framebuffer(gl, chan),
1455            WebGLCommand::CreateRenderbuffer(ref chan) => Self::create_renderbuffer(gl, chan),
1456            WebGLCommand::CreateTexture(ref chan) => Self::create_texture(gl, chan),
1457            WebGLCommand::CreateProgram(ref chan) => Self::create_program(gl, chan),
1458            WebGLCommand::CreateShader(shader_type, ref chan) => {
1459                Self::create_shader(gl, shader_type, chan)
1460            },
1461            WebGLCommand::DeleteBuffer(id) => unsafe { gl.delete_buffer(id.glow()) },
1462            WebGLCommand::DeleteFramebuffer(id) => unsafe { gl.delete_framebuffer(id.glow()) },
1463            WebGLCommand::DeleteRenderbuffer(id) => unsafe { gl.delete_renderbuffer(id.glow()) },
1464            WebGLCommand::DeleteTexture(id) => unsafe { gl.delete_texture(id.glow()) },
1465            WebGLCommand::DeleteProgram(id) => unsafe { gl.delete_program(id.glow()) },
1466            WebGLCommand::DeleteShader(id) => unsafe { gl.delete_shader(id.glow()) },
1467            WebGLCommand::BindBuffer(target, id) => unsafe {
1468                gl.bind_buffer(target, id.map(WebGLBufferId::glow))
1469            },
1470            WebGLCommand::BindFramebuffer(target, request) => {
1471                Self::bind_framebuffer(gl, target, request, ctx, device, state)
1472            },
1473            WebGLCommand::BindRenderbuffer(target, id) => unsafe {
1474                gl.bind_renderbuffer(target, id.map(WebGLRenderbufferId::glow))
1475            },
1476            WebGLCommand::BindTexture(target, id) => unsafe {
1477                gl.bind_texture(target, id.map(WebGLTextureId::glow))
1478            },
1479            WebGLCommand::BlitFrameBuffer(
1480                src_x0,
1481                src_y0,
1482                src_x1,
1483                src_y1,
1484                dst_x0,
1485                dst_y0,
1486                dst_x1,
1487                dst_y1,
1488                mask,
1489                filter,
1490            ) => unsafe {
1491                gl.blit_framebuffer(
1492                    src_x0, src_y0, src_x1, src_y1, dst_x0, dst_y0, dst_x1, dst_y1, mask, filter,
1493                );
1494            },
1495            WebGLCommand::Uniform1f(uniform_id, v) => unsafe {
1496                gl.uniform_1_f32(native_uniform_location(uniform_id).as_ref(), v)
1497            },
1498            WebGLCommand::Uniform1fv(uniform_id, ref v) => unsafe {
1499                gl.uniform_1_f32_slice(native_uniform_location(uniform_id).as_ref(), v)
1500            },
1501            WebGLCommand::Uniform1i(uniform_id, v) => unsafe {
1502                gl.uniform_1_i32(native_uniform_location(uniform_id).as_ref(), v)
1503            },
1504            WebGLCommand::Uniform1iv(uniform_id, ref v) => unsafe {
1505                gl.uniform_1_i32_slice(native_uniform_location(uniform_id).as_ref(), v)
1506            },
1507            WebGLCommand::Uniform1ui(uniform_id, v) => unsafe {
1508                gl.uniform_1_u32(native_uniform_location(uniform_id).as_ref(), v)
1509            },
1510            WebGLCommand::Uniform1uiv(uniform_id, ref v) => unsafe {
1511                gl.uniform_1_u32_slice(native_uniform_location(uniform_id).as_ref(), v)
1512            },
1513            WebGLCommand::Uniform2f(uniform_id, x, y) => unsafe {
1514                gl.uniform_2_f32(native_uniform_location(uniform_id).as_ref(), x, y)
1515            },
1516            WebGLCommand::Uniform2fv(uniform_id, ref v) => unsafe {
1517                gl.uniform_2_f32_slice(native_uniform_location(uniform_id).as_ref(), v)
1518            },
1519            WebGLCommand::Uniform2i(uniform_id, x, y) => unsafe {
1520                gl.uniform_2_i32(native_uniform_location(uniform_id).as_ref(), x, y)
1521            },
1522            WebGLCommand::Uniform2iv(uniform_id, ref v) => unsafe {
1523                gl.uniform_2_i32_slice(native_uniform_location(uniform_id).as_ref(), v)
1524            },
1525            WebGLCommand::Uniform2ui(uniform_id, x, y) => unsafe {
1526                gl.uniform_2_u32(native_uniform_location(uniform_id).as_ref(), x, y)
1527            },
1528            WebGLCommand::Uniform2uiv(uniform_id, ref v) => unsafe {
1529                gl.uniform_2_u32_slice(native_uniform_location(uniform_id).as_ref(), v)
1530            },
1531            WebGLCommand::Uniform3f(uniform_id, x, y, z) => unsafe {
1532                gl.uniform_3_f32(native_uniform_location(uniform_id).as_ref(), x, y, z)
1533            },
1534            WebGLCommand::Uniform3fv(uniform_id, ref v) => unsafe {
1535                gl.uniform_3_f32_slice(native_uniform_location(uniform_id).as_ref(), v)
1536            },
1537            WebGLCommand::Uniform3i(uniform_id, x, y, z) => unsafe {
1538                gl.uniform_3_i32(native_uniform_location(uniform_id).as_ref(), x, y, z)
1539            },
1540            WebGLCommand::Uniform3iv(uniform_id, ref v) => unsafe {
1541                gl.uniform_3_i32_slice(native_uniform_location(uniform_id).as_ref(), v)
1542            },
1543            WebGLCommand::Uniform3ui(uniform_id, x, y, z) => unsafe {
1544                gl.uniform_3_u32(native_uniform_location(uniform_id).as_ref(), x, y, z)
1545            },
1546            WebGLCommand::Uniform3uiv(uniform_id, ref v) => unsafe {
1547                gl.uniform_3_u32_slice(native_uniform_location(uniform_id).as_ref(), v)
1548            },
1549            WebGLCommand::Uniform4f(uniform_id, x, y, z, w) => unsafe {
1550                gl.uniform_4_f32(native_uniform_location(uniform_id).as_ref(), x, y, z, w)
1551            },
1552            WebGLCommand::Uniform4fv(uniform_id, ref v) => unsafe {
1553                gl.uniform_4_f32_slice(native_uniform_location(uniform_id).as_ref(), v)
1554            },
1555            WebGLCommand::Uniform4i(uniform_id, x, y, z, w) => unsafe {
1556                gl.uniform_4_i32(native_uniform_location(uniform_id).as_ref(), x, y, z, w)
1557            },
1558            WebGLCommand::Uniform4iv(uniform_id, ref v) => unsafe {
1559                gl.uniform_4_i32_slice(native_uniform_location(uniform_id).as_ref(), v)
1560            },
1561            WebGLCommand::Uniform4ui(uniform_id, x, y, z, w) => unsafe {
1562                gl.uniform_4_u32(native_uniform_location(uniform_id).as_ref(), x, y, z, w)
1563            },
1564            WebGLCommand::Uniform4uiv(uniform_id, ref v) => unsafe {
1565                gl.uniform_4_u32_slice(native_uniform_location(uniform_id).as_ref(), v)
1566            },
1567            WebGLCommand::UniformMatrix2fv(uniform_id, ref v) => unsafe {
1568                gl.uniform_matrix_2_f32_slice(
1569                    native_uniform_location(uniform_id).as_ref(),
1570                    false,
1571                    v,
1572                )
1573            },
1574            WebGLCommand::UniformMatrix3fv(uniform_id, ref v) => unsafe {
1575                gl.uniform_matrix_3_f32_slice(
1576                    native_uniform_location(uniform_id).as_ref(),
1577                    false,
1578                    v,
1579                )
1580            },
1581            WebGLCommand::UniformMatrix4fv(uniform_id, ref v) => unsafe {
1582                gl.uniform_matrix_4_f32_slice(
1583                    native_uniform_location(uniform_id).as_ref(),
1584                    false,
1585                    v,
1586                )
1587            },
1588            WebGLCommand::UniformMatrix3x2fv(uniform_id, ref v) => unsafe {
1589                gl.uniform_matrix_3x2_f32_slice(
1590                    native_uniform_location(uniform_id).as_ref(),
1591                    false,
1592                    v,
1593                )
1594            },
1595            WebGLCommand::UniformMatrix4x2fv(uniform_id, ref v) => unsafe {
1596                gl.uniform_matrix_4x2_f32_slice(
1597                    native_uniform_location(uniform_id).as_ref(),
1598                    false,
1599                    v,
1600                )
1601            },
1602            WebGLCommand::UniformMatrix2x3fv(uniform_id, ref v) => unsafe {
1603                gl.uniform_matrix_2x3_f32_slice(
1604                    native_uniform_location(uniform_id).as_ref(),
1605                    false,
1606                    v,
1607                )
1608            },
1609            WebGLCommand::UniformMatrix4x3fv(uniform_id, ref v) => unsafe {
1610                gl.uniform_matrix_4x3_f32_slice(
1611                    native_uniform_location(uniform_id).as_ref(),
1612                    false,
1613                    v,
1614                )
1615            },
1616            WebGLCommand::UniformMatrix2x4fv(uniform_id, ref v) => unsafe {
1617                gl.uniform_matrix_2x4_f32_slice(
1618                    native_uniform_location(uniform_id).as_ref(),
1619                    false,
1620                    v,
1621                )
1622            },
1623            WebGLCommand::UniformMatrix3x4fv(uniform_id, ref v) => unsafe {
1624                gl.uniform_matrix_3x4_f32_slice(
1625                    native_uniform_location(uniform_id).as_ref(),
1626                    false,
1627                    v,
1628                )
1629            },
1630            WebGLCommand::ValidateProgram(program_id) => unsafe {
1631                gl.validate_program(program_id.glow())
1632            },
1633            WebGLCommand::VertexAttrib(attrib_id, x, y, z, w) => unsafe {
1634                gl.vertex_attrib_4_f32(attrib_id, x, y, z, w)
1635            },
1636            WebGLCommand::VertexAttribI(attrib_id, x, y, z, w) => unsafe {
1637                gl.vertex_attrib_4_i32(attrib_id, x, y, z, w)
1638            },
1639            WebGLCommand::VertexAttribU(attrib_id, x, y, z, w) => unsafe {
1640                gl.vertex_attrib_4_u32(attrib_id, x, y, z, w)
1641            },
1642            WebGLCommand::VertexAttribPointer2f(attrib_id, size, normalized, stride, offset) => unsafe {
1643                gl.vertex_attrib_pointer_f32(
1644                    attrib_id,
1645                    size,
1646                    gl::FLOAT,
1647                    normalized,
1648                    stride,
1649                    offset as _,
1650                )
1651            },
1652            WebGLCommand::VertexAttribPointer(
1653                attrib_id,
1654                size,
1655                data_type,
1656                normalized,
1657                stride,
1658                offset,
1659            ) => unsafe {
1660                gl.vertex_attrib_pointer_f32(
1661                    attrib_id,
1662                    size,
1663                    data_type,
1664                    normalized,
1665                    stride,
1666                    offset as _,
1667                )
1668            },
1669            WebGLCommand::SetViewport(x, y, width, height) => unsafe {
1670                gl.viewport(x, y, width, height)
1671            },
1672            WebGLCommand::TexImage3D {
1673                target,
1674                level,
1675                internal_format,
1676                size,
1677                depth,
1678                format,
1679                data_type,
1680                effective_data_type,
1681                unpacking_alignment,
1682                alpha_treatment,
1683                y_axis_treatment,
1684                pixel_format,
1685                ref data,
1686            } => {
1687                let pixels = prepare_pixels(
1688                    internal_format,
1689                    data_type,
1690                    size,
1691                    unpacking_alignment,
1692                    alpha_treatment,
1693                    y_axis_treatment,
1694                    pixel_format,
1695                    Cow::Borrowed(data),
1696                );
1697
1698                unsafe {
1699                    gl.pixel_store_i32(gl::UNPACK_ALIGNMENT, unpacking_alignment as i32);
1700                    gl.tex_image_3d(
1701                        target,
1702                        level as i32,
1703                        internal_format.as_gl_constant() as i32,
1704                        size.width as i32,
1705                        size.height as i32,
1706                        depth as i32,
1707                        0,
1708                        format.as_gl_constant(),
1709                        effective_data_type,
1710                        PixelUnpackData::Slice(Some(&pixels)),
1711                    );
1712                }
1713            },
1714            WebGLCommand::TexImage2D {
1715                target,
1716                level,
1717                internal_format,
1718                size,
1719                format,
1720                data_type,
1721                effective_data_type,
1722                unpacking_alignment,
1723                alpha_treatment,
1724                y_axis_treatment,
1725                pixel_format,
1726                ref data,
1727            } => {
1728                let pixels = prepare_pixels(
1729                    internal_format,
1730                    data_type,
1731                    size,
1732                    unpacking_alignment,
1733                    alpha_treatment,
1734                    y_axis_treatment,
1735                    pixel_format,
1736                    Cow::Borrowed(data),
1737                );
1738
1739                unsafe {
1740                    gl.pixel_store_i32(gl::UNPACK_ALIGNMENT, unpacking_alignment as i32);
1741                    gl.tex_image_2d(
1742                        target,
1743                        level as i32,
1744                        internal_format.as_gl_constant() as i32,
1745                        size.width as i32,
1746                        size.height as i32,
1747                        0,
1748                        format.as_gl_constant(),
1749                        effective_data_type,
1750                        PixelUnpackData::Slice(Some(&pixels)),
1751                    );
1752                }
1753            },
1754            WebGLCommand::TexImage2DPBO {
1755                target,
1756                level,
1757                internal_format,
1758                size,
1759                format,
1760                effective_data_type,
1761                unpacking_alignment,
1762                offset,
1763            } => unsafe {
1764                gl.pixel_store_i32(gl::UNPACK_ALIGNMENT, unpacking_alignment as i32);
1765
1766                gl.tex_image_2d(
1767                    target,
1768                    level as i32,
1769                    internal_format.as_gl_constant() as i32,
1770                    size.width as i32,
1771                    size.height as i32,
1772                    0,
1773                    format.as_gl_constant(),
1774                    effective_data_type,
1775                    PixelUnpackData::BufferOffset(offset as u32),
1776                );
1777            },
1778            WebGLCommand::TexSubImage2D {
1779                target,
1780                level,
1781                xoffset,
1782                yoffset,
1783                size,
1784                format,
1785                data_type,
1786                effective_data_type,
1787                unpacking_alignment,
1788                alpha_treatment,
1789                y_axis_treatment,
1790                pixel_format,
1791                ref data,
1792            } => {
1793                let pixels = prepare_pixels(
1794                    format,
1795                    data_type,
1796                    size,
1797                    unpacking_alignment,
1798                    alpha_treatment,
1799                    y_axis_treatment,
1800                    pixel_format,
1801                    Cow::Borrowed(data),
1802                );
1803
1804                unsafe {
1805                    gl.pixel_store_i32(gl::UNPACK_ALIGNMENT, unpacking_alignment as i32);
1806                    gl.tex_sub_image_2d(
1807                        target,
1808                        level as i32,
1809                        xoffset,
1810                        yoffset,
1811                        size.width as i32,
1812                        size.height as i32,
1813                        format.as_gl_constant(),
1814                        effective_data_type,
1815                        glow::PixelUnpackData::Slice(Some(&pixels)),
1816                    );
1817                }
1818            },
1819            WebGLCommand::CompressedTexImage2D {
1820                target,
1821                level,
1822                internal_format,
1823                size,
1824                ref data,
1825            } => unsafe {
1826                gl.compressed_tex_image_2d(
1827                    target,
1828                    level as i32,
1829                    internal_format as i32,
1830                    size.width as i32,
1831                    size.height as i32,
1832                    0,
1833                    data.len() as i32,
1834                    data,
1835                )
1836            },
1837            WebGLCommand::CompressedTexSubImage2D {
1838                target,
1839                level,
1840                xoffset,
1841                yoffset,
1842                size,
1843                format,
1844                ref data,
1845            } => {
1846                unsafe {
1847                    gl.compressed_tex_sub_image_2d(
1848                        target,
1849                        level,
1850                        xoffset,
1851                        yoffset,
1852                        size.width as i32,
1853                        size.height as i32,
1854                        format,
1855                        glow::CompressedPixelUnpackData::Slice(data),
1856                    )
1857                };
1858            },
1859            WebGLCommand::TexStorage2D(target, levels, internal_format, width, height) => unsafe {
1860                gl.tex_storage_2d(
1861                    target,
1862                    levels as i32,
1863                    internal_format.as_gl_constant(),
1864                    width as i32,
1865                    height as i32,
1866                )
1867            },
1868            WebGLCommand::TexStorage3D(target, levels, internal_format, width, height, depth) => unsafe {
1869                gl.tex_storage_3d(
1870                    target,
1871                    levels as i32,
1872                    internal_format.as_gl_constant(),
1873                    width as i32,
1874                    height as i32,
1875                    depth as i32,
1876                )
1877            },
1878            WebGLCommand::DrawingBufferWidth(ref sender) => {
1879                let size = device
1880                    .context_surface_info(ctx)
1881                    .unwrap()
1882                    .expect("Where's the front buffer?")
1883                    .size;
1884                sender.send(size.width).unwrap()
1885            },
1886            WebGLCommand::DrawingBufferHeight(ref sender) => {
1887                let size = device
1888                    .context_surface_info(ctx)
1889                    .unwrap()
1890                    .expect("Where's the front buffer?")
1891                    .size;
1892                sender.send(size.height).unwrap()
1893            },
1894            WebGLCommand::Finish(ref sender) => Self::finish(gl, sender),
1895            WebGLCommand::Flush => unsafe { gl.flush() },
1896            WebGLCommand::GenerateMipmap(target) => unsafe { gl.generate_mipmap(target) },
1897            WebGLCommand::CreateVertexArray(ref chan) => {
1898                let id = Self::create_vertex_array(gl);
1899                let _ = chan.send(id);
1900            },
1901            WebGLCommand::DeleteVertexArray(id) => {
1902                unsafe { gl.delete_vertex_array(id.glow()) };
1903            },
1904            WebGLCommand::BindVertexArray(id) => {
1905                let id = id.map(WebGLVertexArrayId::glow).or(state.default_vao);
1906                unsafe { gl.bind_vertex_array(id) }
1907            },
1908            WebGLCommand::GetParameterBool(param, ref sender) => {
1909                let value = match param {
1910                    webgl::ParameterBool::DepthWritemask => state.depth_write_mask,
1911                    _ => unsafe { gl.get_parameter_bool(param as u32) },
1912                };
1913                sender.send(value).unwrap()
1914            },
1915            WebGLCommand::FenceSync(ref sender) => {
1916                let value = unsafe { gl.fence_sync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0).unwrap() };
1917                sender.send(WebGLSyncId::from_glow(value)).unwrap();
1918            },
1919            WebGLCommand::IsSync(sync_id, ref sender) => {
1920                let value = unsafe { gl.is_sync(sync_id.glow()) };
1921                sender.send(value).unwrap();
1922            },
1923            WebGLCommand::ClientWaitSync(sync_id, flags, timeout, ref sender) => {
1924                let value = unsafe { gl.client_wait_sync(sync_id.glow(), flags, timeout as _) };
1925                sender.send(value).unwrap();
1926            },
1927            WebGLCommand::WaitSync(sync_id, flags, timeout) => {
1928                unsafe { gl.wait_sync(sync_id.glow(), flags, timeout as u64) };
1929            },
1930            WebGLCommand::GetSyncParameter(sync_id, param, ref sender) => {
1931                let value = unsafe { gl.get_sync_parameter_i32(sync_id.glow(), param) };
1932                sender.send(value as u32).unwrap();
1933            },
1934            WebGLCommand::DeleteSync(sync_id) => {
1935                unsafe { gl.delete_sync(sync_id.glow()) };
1936            },
1937            WebGLCommand::GetParameterBool4(param, ref sender) => {
1938                let value = match param {
1939                    webgl::ParameterBool4::ColorWritemask => state.color_write_mask,
1940                };
1941                sender.send(value).unwrap()
1942            },
1943            WebGLCommand::GetParameterInt(param, ref sender) => {
1944                let value = match param {
1945                    webgl::ParameterInt::AlphaBits if state.fake_no_alpha() => 0,
1946                    webgl::ParameterInt::DepthBits if state.fake_no_depth() => 0,
1947                    webgl::ParameterInt::StencilBits if state.fake_no_stencil() => 0,
1948                    _ => unsafe { gl.get_parameter_i32(param as u32) },
1949                };
1950                sender.send(value).unwrap()
1951            },
1952            WebGLCommand::GetParameterUInt(param, ref sender) => {
1953                let value = match param {
1954                    webgl::ParameterUInt::StencilWritemask => state.stencil_write_mask.0,
1955                    webgl::ParameterUInt::StencilBackWritemask => state.stencil_write_mask.1,
1956                    webgl::ParameterUInt::StencilValueMask => state.stencil_value_mask.0,
1957                    webgl::ParameterUInt::StencilBackValueMask => state.stencil_value_mask.1,
1958                };
1959                sender.send(value).unwrap()
1960            },
1961            WebGLCommand::GetParameterInt2(param, ref sender) => {
1962                let mut value = [0; 2];
1963                unsafe {
1964                    gl.get_parameter_i32_slice(param as u32, &mut value);
1965                }
1966                sender.send(value).unwrap()
1967            },
1968            WebGLCommand::GetParameterInt4(param, ref sender) => {
1969                let mut value = [0; 4];
1970                unsafe {
1971                    gl.get_parameter_i32_slice(param as u32, &mut value);
1972                }
1973                sender.send(value).unwrap()
1974            },
1975            WebGLCommand::GetParameterFloat(param, ref sender) => {
1976                let mut value = [0.];
1977                unsafe {
1978                    gl.get_parameter_f32_slice(param as u32, &mut value);
1979                }
1980                sender.send(value[0]).unwrap()
1981            },
1982            WebGLCommand::GetParameterFloat2(param, ref sender) => {
1983                let mut value = [0.; 2];
1984                unsafe {
1985                    gl.get_parameter_f32_slice(param as u32, &mut value);
1986                }
1987                sender.send(value).unwrap()
1988            },
1989            WebGLCommand::GetParameterFloat4(param, ref sender) => {
1990                let mut value = [0.; 4];
1991                unsafe {
1992                    gl.get_parameter_f32_slice(param as u32, &mut value);
1993                }
1994                sender.send(value).unwrap()
1995            },
1996            WebGLCommand::GetProgramValidateStatus(program, ref sender) => sender
1997                .send(unsafe { gl.get_program_validate_status(program.glow()) })
1998                .unwrap(),
1999            WebGLCommand::GetProgramActiveUniforms(program, ref sender) => sender
2000                .send(unsafe { gl.get_program_parameter_i32(program.glow(), gl::ACTIVE_UNIFORMS) })
2001                .unwrap(),
2002            WebGLCommand::GetCurrentVertexAttrib(index, ref sender) => {
2003                let mut value = [0.; 4];
2004                unsafe {
2005                    gl.get_vertex_attrib_parameter_f32_slice(
2006                        index,
2007                        gl::CURRENT_VERTEX_ATTRIB,
2008                        &mut value,
2009                    );
2010                }
2011                sender.send(value).unwrap();
2012            },
2013            WebGLCommand::GetTexParameterFloat(target, param, ref sender) => {
2014                sender
2015                    .send(unsafe { gl.get_tex_parameter_f32(target, param as u32) })
2016                    .unwrap();
2017            },
2018            WebGLCommand::GetTexParameterInt(target, param, ref sender) => {
2019                sender
2020                    .send(unsafe { gl.get_tex_parameter_i32(target, param as u32) })
2021                    .unwrap();
2022            },
2023            WebGLCommand::GetTexParameterBool(target, param, ref sender) => {
2024                sender
2025                    .send(unsafe { gl.get_tex_parameter_i32(target, param as u32) } != 0)
2026                    .unwrap();
2027            },
2028            WebGLCommand::GetInternalFormatIntVec(target, internal_format, param, ref sender) => {
2029                match param {
2030                    InternalFormatIntVec::Samples => {
2031                        let mut count = [0; 1];
2032                        unsafe {
2033                            gl.get_internal_format_i32_slice(
2034                                target,
2035                                internal_format,
2036                                gl::NUM_SAMPLE_COUNTS,
2037                                &mut count,
2038                            )
2039                        };
2040                        assert!(count[0] >= 0);
2041
2042                        let mut values = vec![0; count[0] as usize];
2043                        unsafe {
2044                            gl.get_internal_format_i32_slice(
2045                                target,
2046                                internal_format,
2047                                param as u32,
2048                                &mut values,
2049                            )
2050                        };
2051                        sender.send(values).unwrap()
2052                    },
2053                }
2054            },
2055            WebGLCommand::TexParameteri(target, param, value) => unsafe {
2056                gl.tex_parameter_i32(target, param, value)
2057            },
2058            WebGLCommand::TexParameterf(target, param, value) => unsafe {
2059                gl.tex_parameter_f32(target, param, value)
2060            },
2061            WebGLCommand::LinkProgram(program_id, ref sender) => {
2062                sender.send(Self::link_program(gl, program_id)).unwrap()
2063            },
2064            WebGLCommand::UseProgram(program_id) => unsafe {
2065                gl.use_program(program_id.map(|p| p.glow()))
2066            },
2067            WebGLCommand::DrawArrays { mode, first, count } => unsafe {
2068                gl.draw_arrays(mode, first, count)
2069            },
2070            WebGLCommand::DrawArraysInstanced {
2071                mode,
2072                first,
2073                count,
2074                primcount,
2075            } => unsafe { gl.draw_arrays_instanced(mode, first, count, primcount) },
2076            WebGLCommand::DrawElements {
2077                mode,
2078                count,
2079                type_,
2080                offset,
2081            } => unsafe { gl.draw_elements(mode, count, type_, offset as _) },
2082            WebGLCommand::DrawElementsInstanced {
2083                mode,
2084                count,
2085                type_,
2086                offset,
2087                primcount,
2088            } => unsafe {
2089                gl.draw_elements_instanced(mode, count, type_, offset as i32, primcount)
2090            },
2091            WebGLCommand::VertexAttribDivisor { index, divisor } => unsafe {
2092                gl.vertex_attrib_divisor(index, divisor)
2093            },
2094            WebGLCommand::GetUniformBool(program_id, loc, ref sender) => {
2095                let mut value = [0];
2096                unsafe {
2097                    gl.get_uniform_i32(
2098                        program_id.glow(),
2099                        &NativeUniformLocation(loc as u32),
2100                        &mut value,
2101                    );
2102                }
2103                sender.send(value[0] != 0).unwrap();
2104            },
2105            WebGLCommand::GetUniformBool2(program_id, loc, ref sender) => {
2106                let mut value = [0; 2];
2107                unsafe {
2108                    gl.get_uniform_i32(
2109                        program_id.glow(),
2110                        &NativeUniformLocation(loc as u32),
2111                        &mut value,
2112                    );
2113                }
2114                let value = [value[0] != 0, value[1] != 0];
2115                sender.send(value).unwrap();
2116            },
2117            WebGLCommand::GetUniformBool3(program_id, loc, ref sender) => {
2118                let mut value = [0; 3];
2119                unsafe {
2120                    gl.get_uniform_i32(
2121                        program_id.glow(),
2122                        &NativeUniformLocation(loc as u32),
2123                        &mut value,
2124                    );
2125                }
2126                let value = [value[0] != 0, value[1] != 0, value[2] != 0];
2127                sender.send(value).unwrap();
2128            },
2129            WebGLCommand::GetUniformBool4(program_id, loc, ref sender) => {
2130                let mut value = [0; 4];
2131                unsafe {
2132                    gl.get_uniform_i32(
2133                        program_id.glow(),
2134                        &NativeUniformLocation(loc as u32),
2135                        &mut value,
2136                    );
2137                }
2138                let value = [value[0] != 0, value[1] != 0, value[2] != 0, value[3] != 0];
2139                sender.send(value).unwrap();
2140            },
2141            WebGLCommand::GetUniformInt(program_id, loc, ref sender) => {
2142                let mut value = [0];
2143                unsafe {
2144                    gl.get_uniform_i32(
2145                        program_id.glow(),
2146                        &NativeUniformLocation(loc as u32),
2147                        &mut value,
2148                    );
2149                }
2150                sender.send(value[0]).unwrap();
2151            },
2152            WebGLCommand::GetUniformInt2(program_id, loc, ref sender) => {
2153                let mut value = [0; 2];
2154                unsafe {
2155                    gl.get_uniform_i32(
2156                        program_id.glow(),
2157                        &NativeUniformLocation(loc as u32),
2158                        &mut value,
2159                    );
2160                }
2161                sender.send(value).unwrap();
2162            },
2163            WebGLCommand::GetUniformInt3(program_id, loc, ref sender) => {
2164                let mut value = [0; 3];
2165                unsafe {
2166                    gl.get_uniform_i32(
2167                        program_id.glow(),
2168                        &NativeUniformLocation(loc as u32),
2169                        &mut value,
2170                    );
2171                }
2172                sender.send(value).unwrap();
2173            },
2174            WebGLCommand::GetUniformInt4(program_id, loc, ref sender) => {
2175                let mut value = [0; 4];
2176                unsafe {
2177                    gl.get_uniform_i32(
2178                        program_id.glow(),
2179                        &NativeUniformLocation(loc as u32),
2180                        &mut value,
2181                    );
2182                }
2183                sender.send(value).unwrap();
2184            },
2185            WebGLCommand::GetUniformUint(program_id, loc, ref sender) => {
2186                let mut value = [0];
2187                unsafe {
2188                    gl.get_uniform_u32(
2189                        program_id.glow(),
2190                        &NativeUniformLocation(loc as u32),
2191                        &mut value,
2192                    );
2193                }
2194                sender.send(value[0]).unwrap();
2195            },
2196            WebGLCommand::GetUniformUint2(program_id, loc, ref sender) => {
2197                let mut value = [0; 2];
2198                unsafe {
2199                    gl.get_uniform_u32(
2200                        program_id.glow(),
2201                        &NativeUniformLocation(loc as u32),
2202                        &mut value,
2203                    );
2204                }
2205                sender.send(value).unwrap();
2206            },
2207            WebGLCommand::GetUniformUint3(program_id, loc, ref sender) => {
2208                let mut value = [0; 3];
2209                unsafe {
2210                    gl.get_uniform_u32(
2211                        program_id.glow(),
2212                        &NativeUniformLocation(loc as u32),
2213                        &mut value,
2214                    );
2215                }
2216                sender.send(value).unwrap();
2217            },
2218            WebGLCommand::GetUniformUint4(program_id, loc, ref sender) => {
2219                let mut value = [0; 4];
2220                unsafe {
2221                    gl.get_uniform_u32(
2222                        program_id.glow(),
2223                        &NativeUniformLocation(loc as u32),
2224                        &mut value,
2225                    );
2226                }
2227                sender.send(value).unwrap();
2228            },
2229            WebGLCommand::GetUniformFloat(program_id, loc, ref sender) => {
2230                let mut value = [0.];
2231                unsafe {
2232                    gl.get_uniform_f32(
2233                        program_id.glow(),
2234                        &NativeUniformLocation(loc as u32),
2235                        &mut value,
2236                    );
2237                }
2238                sender.send(value[0]).unwrap();
2239            },
2240            WebGLCommand::GetUniformFloat2(program_id, loc, ref sender) => {
2241                let mut value = [0.; 2];
2242                unsafe {
2243                    gl.get_uniform_f32(
2244                        program_id.glow(),
2245                        &NativeUniformLocation(loc as u32),
2246                        &mut value,
2247                    );
2248                }
2249                sender.send(value).unwrap();
2250            },
2251            WebGLCommand::GetUniformFloat3(program_id, loc, ref sender) => {
2252                let mut value = [0.; 3];
2253                unsafe {
2254                    gl.get_uniform_f32(
2255                        program_id.glow(),
2256                        &NativeUniformLocation(loc as u32),
2257                        &mut value,
2258                    );
2259                }
2260                sender.send(value).unwrap();
2261            },
2262            WebGLCommand::GetUniformFloat4(program_id, loc, ref sender) => {
2263                let mut value = [0.; 4];
2264                unsafe {
2265                    gl.get_uniform_f32(
2266                        program_id.glow(),
2267                        &NativeUniformLocation(loc as u32),
2268                        &mut value,
2269                    );
2270                }
2271                sender.send(value).unwrap();
2272            },
2273            WebGLCommand::GetUniformFloat9(program_id, loc, ref sender) => {
2274                let mut value = [0.; 9];
2275                unsafe {
2276                    gl.get_uniform_f32(
2277                        program_id.glow(),
2278                        &NativeUniformLocation(loc as u32),
2279                        &mut value,
2280                    );
2281                }
2282                sender.send(value).unwrap();
2283            },
2284            WebGLCommand::GetUniformFloat16(program_id, loc, ref sender) => {
2285                let mut value = [0.; 16];
2286                unsafe {
2287                    gl.get_uniform_f32(
2288                        program_id.glow(),
2289                        &NativeUniformLocation(loc as u32),
2290                        &mut value,
2291                    );
2292                }
2293                sender.send(value).unwrap();
2294            },
2295            WebGLCommand::GetUniformFloat2x3(program_id, loc, ref sender) => {
2296                let mut value = [0.; 2 * 3];
2297                unsafe {
2298                    gl.get_uniform_f32(
2299                        program_id.glow(),
2300                        &NativeUniformLocation(loc as u32),
2301                        &mut value,
2302                    );
2303                }
2304                sender.send(value).unwrap()
2305            },
2306            WebGLCommand::GetUniformFloat2x4(program_id, loc, ref sender) => {
2307                let mut value = [0.; 2 * 4];
2308                unsafe {
2309                    gl.get_uniform_f32(
2310                        program_id.glow(),
2311                        &NativeUniformLocation(loc as u32),
2312                        &mut value,
2313                    );
2314                }
2315                sender.send(value).unwrap()
2316            },
2317            WebGLCommand::GetUniformFloat3x2(program_id, loc, ref sender) => {
2318                let mut value = [0.; 3 * 2];
2319                unsafe {
2320                    gl.get_uniform_f32(
2321                        program_id.glow(),
2322                        &NativeUniformLocation(loc as u32),
2323                        &mut value,
2324                    );
2325                }
2326                sender.send(value).unwrap()
2327            },
2328            WebGLCommand::GetUniformFloat3x4(program_id, loc, ref sender) => {
2329                let mut value = [0.; 3 * 4];
2330                unsafe {
2331                    gl.get_uniform_f32(
2332                        program_id.glow(),
2333                        &NativeUniformLocation(loc as u32),
2334                        &mut value,
2335                    );
2336                }
2337                sender.send(value).unwrap()
2338            },
2339            WebGLCommand::GetUniformFloat4x2(program_id, loc, ref sender) => {
2340                let mut value = [0.; 4 * 2];
2341                unsafe {
2342                    gl.get_uniform_f32(
2343                        program_id.glow(),
2344                        &NativeUniformLocation(loc as u32),
2345                        &mut value,
2346                    );
2347                }
2348                sender.send(value).unwrap()
2349            },
2350            WebGLCommand::GetUniformFloat4x3(program_id, loc, ref sender) => {
2351                let mut value = [0.; 4 * 3];
2352                unsafe {
2353                    gl.get_uniform_f32(
2354                        program_id.glow(),
2355                        &NativeUniformLocation(loc as u32),
2356                        &mut value,
2357                    );
2358                }
2359                sender.send(value).unwrap()
2360            },
2361            WebGLCommand::GetUniformBlockIndex(program_id, ref name, ref sender) => {
2362                let name = to_name_in_compiled_shader(name);
2363                let index = unsafe { gl.get_uniform_block_index(program_id.glow(), &name) };
2364                // TODO(#34300): use Option<u32>
2365                sender.send(index.unwrap_or(gl::INVALID_INDEX)).unwrap();
2366            },
2367            WebGLCommand::GetUniformIndices(program_id, ref names, ref sender) => {
2368                let names = names
2369                    .iter()
2370                    .map(|name| to_name_in_compiled_shader(name))
2371                    .collect::<Vec<_>>();
2372                let name_strs = names.iter().map(|name| name.as_str()).collect::<Vec<_>>();
2373                let indices = unsafe {
2374                    gl.get_uniform_indices(program_id.glow(), &name_strs)
2375                        .iter()
2376                        .map(|index| index.unwrap_or(gl::INVALID_INDEX))
2377                        .collect()
2378                };
2379                sender.send(indices).unwrap();
2380            },
2381            WebGLCommand::GetActiveUniforms(program_id, ref indices, pname, ref sender) => {
2382                let results =
2383                    unsafe { gl.get_active_uniforms_parameter(program_id.glow(), indices, pname) };
2384                sender.send(results).unwrap();
2385            },
2386            WebGLCommand::GetActiveUniformBlockName(program_id, block_idx, ref sender) => {
2387                let name =
2388                    unsafe { gl.get_active_uniform_block_name(program_id.glow(), block_idx) };
2389                sender.send(name).unwrap();
2390            },
2391            WebGLCommand::GetActiveUniformBlockParameter(
2392                program_id,
2393                block_idx,
2394                pname,
2395                ref sender,
2396            ) => {
2397                let size = match pname {
2398                    gl::UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES => unsafe {
2399                        gl.get_active_uniform_block_parameter_i32(
2400                            program_id.glow(),
2401                            block_idx,
2402                            gl::UNIFORM_BLOCK_ACTIVE_UNIFORMS,
2403                        ) as usize
2404                    },
2405                    _ => 1,
2406                };
2407                let mut result = vec![0; size];
2408                unsafe {
2409                    gl.get_active_uniform_block_parameter_i32_slice(
2410                        program_id.glow(),
2411                        block_idx,
2412                        pname,
2413                        &mut result,
2414                    )
2415                };
2416                sender.send(result).unwrap();
2417            },
2418            WebGLCommand::UniformBlockBinding(program_id, block_idx, block_binding) => unsafe {
2419                gl.uniform_block_binding(program_id.glow(), block_idx, block_binding)
2420            },
2421            WebGLCommand::InitializeFramebuffer {
2422                color,
2423                depth,
2424                stencil,
2425            } => Self::initialize_framebuffer(gl, state, color, depth, stencil),
2426            WebGLCommand::BeginQuery(target, query_id) => {
2427                unsafe { gl.begin_query(target, query_id.glow()) };
2428            },
2429            WebGLCommand::EndQuery(target) => {
2430                unsafe { gl.end_query(target) };
2431            },
2432            WebGLCommand::DeleteQuery(query_id) => {
2433                unsafe { gl.delete_query(query_id.glow()) };
2434            },
2435            WebGLCommand::GenerateQuery(ref sender) => {
2436                // TODO(#34300): use Option<WebGLQueryId>
2437                let id = unsafe { gl.create_query().unwrap() };
2438                sender.send(WebGLQueryId::from_glow(id)).unwrap()
2439            },
2440            WebGLCommand::GetQueryState(ref sender, query_id, pname) => {
2441                let value = unsafe { gl.get_query_parameter_u32(query_id.glow(), pname) };
2442                sender.send(value).unwrap()
2443            },
2444            WebGLCommand::GenerateSampler(ref sender) => {
2445                let id = unsafe { gl.create_sampler().unwrap() };
2446                sender.send(WebGLSamplerId::from_glow(id)).unwrap()
2447            },
2448            WebGLCommand::DeleteSampler(sampler_id) => {
2449                unsafe { gl.delete_sampler(sampler_id.glow()) };
2450            },
2451            WebGLCommand::BindSampler(unit, sampler_id) => {
2452                unsafe { gl.bind_sampler(unit, Some(sampler_id.glow())) };
2453            },
2454            WebGLCommand::SetSamplerParameterInt(sampler_id, pname, value) => {
2455                unsafe { gl.sampler_parameter_i32(sampler_id.glow(), pname, value) };
2456            },
2457            WebGLCommand::SetSamplerParameterFloat(sampler_id, pname, value) => {
2458                unsafe { gl.sampler_parameter_f32(sampler_id.glow(), pname, value) };
2459            },
2460            WebGLCommand::GetSamplerParameterInt(sampler_id, pname, ref sender) => {
2461                let value = unsafe { gl.get_sampler_parameter_i32(sampler_id.glow(), pname) };
2462                sender.send(value).unwrap();
2463            },
2464            WebGLCommand::GetSamplerParameterFloat(sampler_id, pname, ref sender) => {
2465                let value = unsafe { gl.get_sampler_parameter_f32(sampler_id.glow(), pname) };
2466                sender.send(value).unwrap();
2467            },
2468            WebGLCommand::BindBufferBase(target, index, id) => {
2469                // https://searchfox.org/mozilla-central/rev/13b081a62d3f3e3e3120f95564529257b0bf451c/dom/canvas/WebGLContextBuffers.cpp#208-210
2470                // BindBufferBase/Range will fail (on some drivers) if the buffer name has
2471                // never been bound. (GenBuffers makes a name, but BindBuffer initializes
2472                // that name as a real buffer object)
2473                let id = id.map(WebGLBufferId::glow);
2474                unsafe {
2475                    gl.bind_buffer(target, id);
2476                    gl.bind_buffer(target, None);
2477                    gl.bind_buffer_base(target, index, id);
2478                }
2479            },
2480            WebGLCommand::BindBufferRange(target, index, id, offset, size) => {
2481                // https://searchfox.org/mozilla-central/rev/13b081a62d3f3e3e3120f95564529257b0bf451c/dom/canvas/WebGLContextBuffers.cpp#208-210
2482                // BindBufferBase/Range will fail (on some drivers) if the buffer name has
2483                // never been bound. (GenBuffers makes a name, but BindBuffer initializes
2484                // that name as a real buffer object)
2485                let id = id.map(WebGLBufferId::glow);
2486                unsafe {
2487                    gl.bind_buffer(target, id);
2488                    gl.bind_buffer(target, None);
2489                    gl.bind_buffer_range(target, index, id, offset as i32, size as i32);
2490                }
2491            },
2492            WebGLCommand::ClearBufferfv(buffer, draw_buffer, ref value) => unsafe {
2493                gl.clear_buffer_f32_slice(buffer, draw_buffer as u32, value)
2494            },
2495            WebGLCommand::ClearBufferiv(buffer, draw_buffer, ref value) => unsafe {
2496                gl.clear_buffer_i32_slice(buffer, draw_buffer as u32, value)
2497            },
2498            WebGLCommand::ClearBufferuiv(buffer, draw_buffer, ref value) => unsafe {
2499                gl.clear_buffer_u32_slice(buffer, draw_buffer as u32, value)
2500            },
2501            WebGLCommand::ClearBufferfi(buffer, draw_buffer, depth, stencil) => unsafe {
2502                gl.clear_buffer_depth_stencil(buffer, draw_buffer as u32, depth, stencil)
2503            },
2504            WebGLCommand::InvalidateFramebuffer(target, ref attachments) => unsafe {
2505                gl.invalidate_framebuffer(target, attachments)
2506            },
2507            WebGLCommand::InvalidateSubFramebuffer(target, ref attachments, x, y, w, h) => unsafe {
2508                gl.invalidate_sub_framebuffer(target, attachments, x, y, w, h)
2509            },
2510            WebGLCommand::FramebufferTextureLayer(target, attachment, tex_id, level, layer) => {
2511                let tex_id = tex_id.map(WebGLTextureId::glow);
2512                let attach = |attachment| unsafe {
2513                    gl.framebuffer_texture_layer(target, attachment, tex_id, level, layer)
2514                };
2515
2516                if attachment == gl::DEPTH_STENCIL_ATTACHMENT {
2517                    attach(gl::DEPTH_ATTACHMENT);
2518                    attach(gl::STENCIL_ATTACHMENT);
2519                } else {
2520                    attach(attachment)
2521                }
2522            },
2523            WebGLCommand::ReadBuffer(buffer) => unsafe { gl.read_buffer(buffer) },
2524            WebGLCommand::DrawBuffers(ref buffers) => unsafe { gl.draw_buffers(buffers) },
2525        }
2526
2527        // If debug asertions are enabled, then check the error state.
2528        #[cfg(debug_assertions)]
2529        {
2530            let error = unsafe { gl.get_error() };
2531            if error != gl::NO_ERROR {
2532                error!("Last GL operation failed: {:?}", command);
2533                if error == gl::INVALID_FRAMEBUFFER_OPERATION {
2534                    let framebuffer_bindings =
2535                        unsafe { gl.get_parameter_framebuffer(gl::DRAW_FRAMEBUFFER_BINDING) };
2536                    debug!(
2537                        "(thread {:?}) Current draw framebuffer binding: {:?}",
2538                        ::std::thread::current().id(),
2539                        framebuffer_bindings
2540                    );
2541                }
2542                #[cfg(feature = "webgl_backtrace")]
2543                {
2544                    error!("Backtrace from failed WebGL API:\n{}", _backtrace.backtrace);
2545                    if let Some(backtrace) = _backtrace.js_backtrace {
2546                        error!("JS backtrace from failed WebGL API:\n{}", backtrace);
2547                    }
2548                }
2549                // TODO(servo#30568) revert to panic!() once underlying bug is fixed
2550                log::warn!(
2551                    "debug assertion failed! Unexpected WebGL error: 0x{:x} ({}) [{:?}]",
2552                    error,
2553                    error,
2554                    command
2555                );
2556            }
2557        }
2558    }
2559
2560    fn initialize_framebuffer(gl: &Gl, state: &GLState, color: bool, depth: bool, stencil: bool) {
2561        let bits = [
2562            (color, gl::COLOR_BUFFER_BIT),
2563            (depth, gl::DEPTH_BUFFER_BIT),
2564            (stencil, gl::STENCIL_BUFFER_BIT),
2565        ]
2566        .iter()
2567        .fold(0, |bits, &(enabled, bit)| {
2568            bits | if enabled { bit } else { 0 }
2569        });
2570
2571        unsafe {
2572            gl.disable(gl::SCISSOR_TEST);
2573            gl.color_mask(true, true, true, true);
2574            gl.clear_color(0., 0., 0., 0.);
2575            gl.depth_mask(true);
2576            gl.clear_depth(1.);
2577            gl.stencil_mask_separate(gl::FRONT, 0xFFFFFFFF);
2578            gl.stencil_mask_separate(gl::BACK, 0xFFFFFFFF);
2579            gl.clear_stencil(0);
2580            gl.clear(bits);
2581        }
2582
2583        state.restore_invariant(gl);
2584    }
2585
2586    fn link_program(gl: &Gl, program: WebGLProgramId) -> ProgramLinkInfo {
2587        unsafe { gl.link_program(program.glow()) };
2588        let linked = unsafe { gl.get_program_link_status(program.glow()) };
2589        if !linked {
2590            return ProgramLinkInfo {
2591                linked: false,
2592                active_attribs: vec![].into(),
2593                active_uniforms: vec![].into(),
2594                active_uniform_blocks: vec![].into(),
2595                transform_feedback_length: Default::default(),
2596                transform_feedback_mode: Default::default(),
2597            };
2598        }
2599        let num_active_attribs =
2600            unsafe { gl.get_program_parameter_i32(program.glow(), gl::ACTIVE_ATTRIBUTES) };
2601        let active_attribs = (0..num_active_attribs as u32)
2602            .map(|i| {
2603                let active_attribute =
2604                    unsafe { gl.get_active_attribute(program.glow(), i) }.unwrap();
2605                let name = &active_attribute.name;
2606                let location = if name.starts_with("gl_") {
2607                    None
2608                } else {
2609                    unsafe { gl.get_attrib_location(program.glow(), name) }
2610                };
2611                ActiveAttribInfo {
2612                    name: from_name_in_compiled_shader(name),
2613                    size: active_attribute.size,
2614                    type_: active_attribute.atype,
2615                    location,
2616                }
2617            })
2618            .collect::<Vec<_>>()
2619            .into();
2620
2621        let num_active_uniforms =
2622            unsafe { gl.get_program_parameter_i32(program.glow(), gl::ACTIVE_UNIFORMS) };
2623        let active_uniforms = (0..num_active_uniforms as u32)
2624            .map(|i| {
2625                let active_uniform = unsafe { gl.get_active_uniform(program.glow(), i) }.unwrap();
2626                let is_array = active_uniform.name.ends_with("[0]");
2627                let active_uniform_name = active_uniform
2628                    .name
2629                    .strip_suffix("[0]")
2630                    .unwrap_or_else(|| &active_uniform.name);
2631                ActiveUniformInfo {
2632                    base_name: from_name_in_compiled_shader(active_uniform_name).into(),
2633                    size: if is_array {
2634                        Some(active_uniform.size)
2635                    } else {
2636                        None
2637                    },
2638                    type_: active_uniform.utype,
2639                    bind_index: None,
2640                }
2641            })
2642            .collect::<Vec<_>>()
2643            .into();
2644
2645        let num_active_uniform_blocks =
2646            unsafe { gl.get_program_parameter_i32(program.glow(), gl::ACTIVE_UNIFORM_BLOCKS) };
2647        let active_uniform_blocks = (0..num_active_uniform_blocks as u32)
2648            .map(|i| {
2649                let name = unsafe { gl.get_active_uniform_block_name(program.glow(), i) };
2650                let size = unsafe {
2651                    gl.get_active_uniform_block_parameter_i32(
2652                        program.glow(),
2653                        i,
2654                        gl::UNIFORM_BLOCK_DATA_SIZE,
2655                    )
2656                };
2657                ActiveUniformBlockInfo { name, size }
2658            })
2659            .collect::<Vec<_>>()
2660            .into();
2661
2662        let transform_feedback_length = unsafe {
2663            gl.get_program_parameter_i32(program.glow(), gl::TRANSFORM_FEEDBACK_VARYINGS)
2664        };
2665        let transform_feedback_mode = unsafe {
2666            gl.get_program_parameter_i32(program.glow(), gl::TRANSFORM_FEEDBACK_BUFFER_MODE)
2667        };
2668
2669        ProgramLinkInfo {
2670            linked: true,
2671            active_attribs,
2672            active_uniforms,
2673            active_uniform_blocks,
2674            transform_feedback_length,
2675            transform_feedback_mode,
2676        }
2677    }
2678
2679    fn finish(gl: &Gl, chan: &GenericSender<()>) {
2680        unsafe { gl.finish() };
2681        chan.send(()).unwrap();
2682    }
2683
2684    fn shader_precision_format(
2685        gl: &Gl,
2686        shader_type: u32,
2687        precision_type: u32,
2688        chan: &GenericSender<(i32, i32, i32)>,
2689    ) {
2690        let ShaderPrecisionFormat {
2691            range_min,
2692            range_max,
2693            precision,
2694        } = unsafe {
2695            gl.get_shader_precision_format(shader_type, precision_type)
2696                .unwrap_or_else(|| {
2697                    ShaderPrecisionFormat::common_desktop_hardware(
2698                        precision_type,
2699                        gl.version().is_embedded,
2700                    )
2701                })
2702        };
2703        chan.send((range_min, range_max, precision)).unwrap();
2704    }
2705
2706    /// This is an implementation of `getSupportedExtensions()` from
2707    /// <https://registry.khronos.org/webgl/specs/latest/1.0/#5.14>
2708    fn get_extensions(gl: &Gl, result_sender: &GenericSender<String>) {
2709        let _ = result_sender.send(gl.supported_extensions().iter().join(" "));
2710    }
2711
2712    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6>
2713    fn get_framebuffer_attachment_parameter(
2714        gl: &Gl,
2715        target: u32,
2716        attachment: u32,
2717        pname: u32,
2718        chan: &GenericSender<i32>,
2719    ) {
2720        let parameter =
2721            unsafe { gl.get_framebuffer_attachment_parameter_i32(target, attachment, pname) };
2722        chan.send(parameter).unwrap();
2723    }
2724
2725    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7>
2726    fn get_renderbuffer_parameter(gl: &Gl, target: u32, pname: u32, chan: &GenericSender<i32>) {
2727        let parameter = unsafe { gl.get_renderbuffer_parameter_i32(target, pname) };
2728        chan.send(parameter).unwrap();
2729    }
2730
2731    fn uniform_location(
2732        gl: &Gl,
2733        program_id: WebGLProgramId,
2734        name: &str,
2735        chan: &GenericSender<i32>,
2736    ) {
2737        let location = unsafe {
2738            gl.get_uniform_location(program_id.glow(), &to_name_in_compiled_shader(name))
2739        };
2740        // (#34300): replace this with WebGLUniformId
2741        chan.send(location.map(|l| l.0).unwrap_or_default() as i32)
2742            .unwrap();
2743    }
2744
2745    fn shader_info_log(gl: &Gl, shader_id: WebGLShaderId, chan: &GenericSender<String>) {
2746        let log = unsafe { gl.get_shader_info_log(shader_id.glow()) };
2747        chan.send(log).unwrap();
2748    }
2749
2750    fn program_info_log(gl: &Gl, program_id: WebGLProgramId, chan: &GenericSender<String>) {
2751        let log = unsafe { gl.get_program_info_log(program_id.glow()) };
2752        chan.send(log).unwrap();
2753    }
2754
2755    fn create_buffer(gl: &Gl, chan: &GenericSender<Option<WebGLBufferId>>) {
2756        let buffer = unsafe { gl.create_buffer() }
2757            .ok()
2758            .map(WebGLBufferId::from_glow);
2759        chan.send(buffer).unwrap();
2760    }
2761
2762    fn create_framebuffer(gl: &Gl, chan: &GenericSender<Option<WebGLFramebufferId>>) {
2763        let framebuffer = unsafe { gl.create_framebuffer() }
2764            .ok()
2765            .map(WebGLFramebufferId::from_glow);
2766        chan.send(framebuffer).unwrap();
2767    }
2768
2769    fn create_renderbuffer(gl: &Gl, chan: &GenericSender<Option<WebGLRenderbufferId>>) {
2770        let renderbuffer = unsafe { gl.create_renderbuffer() }
2771            .ok()
2772            .map(WebGLRenderbufferId::from_glow);
2773        chan.send(renderbuffer).unwrap();
2774    }
2775
2776    fn create_texture(gl: &Gl, chan: &GenericSender<Option<WebGLTextureId>>) {
2777        let texture = unsafe { gl.create_texture() }
2778            .ok()
2779            .map(WebGLTextureId::from_glow);
2780        chan.send(texture).unwrap();
2781    }
2782
2783    fn create_program(gl: &Gl, chan: &GenericSender<Option<WebGLProgramId>>) {
2784        let program = unsafe { gl.create_program() }
2785            .ok()
2786            .map(WebGLProgramId::from_glow);
2787        chan.send(program).unwrap();
2788    }
2789
2790    fn create_shader(gl: &Gl, shader_type: u32, chan: &GenericSender<Option<WebGLShaderId>>) {
2791        let shader = unsafe { gl.create_shader(shader_type) }
2792            .ok()
2793            .map(WebGLShaderId::from_glow);
2794        chan.send(shader).unwrap();
2795    }
2796
2797    fn create_vertex_array(gl: &Gl) -> Option<WebGLVertexArrayId> {
2798        let vao = unsafe { gl.create_vertex_array() }
2799            .ok()
2800            .map(WebGLVertexArrayId::from_glow);
2801        if vao.is_none() {
2802            let code = unsafe { gl.get_error() };
2803            warn!("Failed to create vertex array with error code {:x}", code);
2804        }
2805        vao
2806    }
2807
2808    #[inline]
2809    fn bind_framebuffer(
2810        gl: &Gl,
2811        target: u32,
2812        request: WebGLFramebufferBindingRequest,
2813        ctx: &Context,
2814        device: &Device,
2815        state: &mut GLState,
2816    ) {
2817        let id = match request {
2818            WebGLFramebufferBindingRequest::Explicit(id) => Some(id.glow()),
2819            WebGLFramebufferBindingRequest::Default => {
2820                device
2821                    .context_surface_info(ctx)
2822                    .unwrap()
2823                    .expect("No surface attached!")
2824                    .framebuffer_object
2825            },
2826        };
2827
2828        debug!("WebGLImpl::bind_framebuffer: {:?}", id);
2829        unsafe { gl.bind_framebuffer(target, id) };
2830
2831        if (target == gl::FRAMEBUFFER) || (target == gl::DRAW_FRAMEBUFFER) {
2832            state.drawing_to_default_framebuffer =
2833                request == WebGLFramebufferBindingRequest::Default;
2834            state.restore_invariant(gl);
2835        }
2836    }
2837
2838    #[inline]
2839    fn compile_shader(gl: &Gl, shader_id: WebGLShaderId, source: &str) {
2840        unsafe {
2841            gl.shader_source(shader_id.glow(), source);
2842            gl.compile_shader(shader_id.glow());
2843        }
2844    }
2845}
2846
2847/// ANGLE adds a `_u` prefix to variable names:
2848///
2849/// <https://chromium.googlesource.com/angle/angle/+/855d964bd0d05f6b2cb303f625506cf53d37e94f>
2850///
2851/// To avoid hard-coding this we would need to use the `sh::GetAttributes` and `sh::GetUniforms`
2852/// API to look up the `x.name` and `x.mappedName` members.
2853const ANGLE_NAME_PREFIX: &str = "_u";
2854
2855/// Adds `_u` prefix to variable names
2856fn to_name_in_compiled_shader(s: &str) -> String {
2857    map_dot_separated(s, |s, mapped| {
2858        mapped.push_str(ANGLE_NAME_PREFIX);
2859        mapped.push_str(s);
2860    })
2861}
2862
2863/// Removes `_u` prefix from variable names
2864fn from_name_in_compiled_shader(s: &str) -> String {
2865    map_dot_separated(s, |s, mapped| {
2866        mapped.push_str(if let Some(stripped) = s.strip_prefix(ANGLE_NAME_PREFIX) {
2867            stripped
2868        } else {
2869            s
2870        })
2871    })
2872}
2873
2874fn map_dot_separated<F: Fn(&str, &mut String)>(s: &str, f: F) -> String {
2875    let mut iter = s.split('.');
2876    let mut mapped = String::new();
2877    f(iter.next().unwrap(), &mut mapped);
2878    for s in iter {
2879        mapped.push('.');
2880        f(s, &mut mapped);
2881    }
2882    mapped
2883}
2884
2885#[expect(clippy::too_many_arguments)]
2886fn prepare_pixels(
2887    internal_format: TexFormat,
2888    data_type: TexDataType,
2889    size: Size2D<u32>,
2890    unpacking_alignment: u32,
2891    alpha_treatment: Option<AlphaTreatment>,
2892    y_axis_treatment: YAxisTreatment,
2893    pixel_format: Option<PixelFormat>,
2894    mut pixels: Cow<[u8]>,
2895) -> Cow<[u8]> {
2896    match alpha_treatment {
2897        Some(AlphaTreatment::Premultiply) => {
2898            if let Some(pixel_format) = pixel_format {
2899                match pixel_format {
2900                    PixelFormat::BGRA8 | PixelFormat::RGBA8 => {},
2901                    _ => unimplemented!("unsupported pixel format ({:?})", pixel_format),
2902                }
2903                premultiply_inplace(TexFormat::RGBA, TexDataType::UnsignedByte, pixels.to_mut());
2904            } else {
2905                premultiply_inplace(internal_format, data_type, pixels.to_mut());
2906            }
2907        },
2908        Some(AlphaTreatment::Unmultiply) => {
2909            assert!(pixel_format.is_some());
2910            unmultiply_inplace::<false>(pixels.to_mut());
2911        },
2912        None => {},
2913    }
2914
2915    if let Some(pixel_format) = pixel_format {
2916        pixels = image_to_tex_image_data(
2917            pixel_format,
2918            internal_format,
2919            data_type,
2920            pixels.into_owned(),
2921        )
2922        .into();
2923    }
2924
2925    if y_axis_treatment == YAxisTreatment::Flipped {
2926        // FINISHME: Consider doing premultiply and flip in a single mutable Vec.
2927        pixels = flip_pixels_y(
2928            internal_format,
2929            data_type,
2930            size.width as usize,
2931            size.height as usize,
2932            unpacking_alignment as usize,
2933            pixels.into_owned(),
2934        )
2935        .into();
2936    }
2937
2938    pixels
2939}
2940
2941/// Translates an image in rgba8 (red in the first byte) format to
2942/// the format that was requested of TexImage.
2943fn image_to_tex_image_data(
2944    pixel_format: PixelFormat,
2945    format: TexFormat,
2946    data_type: TexDataType,
2947    mut pixels: Vec<u8>,
2948) -> Vec<u8> {
2949    // hint for vector allocation sizing.
2950    let pixel_count = pixels.len() / 4;
2951
2952    match pixel_format {
2953        PixelFormat::BGRA8 => pixels::rgba8_byte_swap_colors_inplace(&mut pixels),
2954        PixelFormat::RGBA8 => {},
2955        _ => unimplemented!("unsupported pixel format ({:?})", pixel_format),
2956    }
2957
2958    match (format, data_type) {
2959        (TexFormat::RGBA, TexDataType::UnsignedByte) |
2960        (TexFormat::RGBA8, TexDataType::UnsignedByte) => pixels,
2961        (TexFormat::RGB, TexDataType::UnsignedByte) |
2962        (TexFormat::RGB8, TexDataType::UnsignedByte) => {
2963            for i in 0..pixel_count {
2964                let rgb = {
2965                    let rgb = &pixels[i * 4..i * 4 + 3];
2966                    [rgb[0], rgb[1], rgb[2]]
2967                };
2968                pixels[i * 3..i * 3 + 3].copy_from_slice(&rgb);
2969            }
2970            pixels.truncate(pixel_count * 3);
2971            pixels
2972        },
2973        (TexFormat::Alpha, TexDataType::UnsignedByte) => {
2974            for i in 0..pixel_count {
2975                let p = pixels[i * 4 + 3];
2976                pixels[i] = p;
2977            }
2978            pixels.truncate(pixel_count);
2979            pixels
2980        },
2981        (TexFormat::Luminance, TexDataType::UnsignedByte) => {
2982            for i in 0..pixel_count {
2983                let p = pixels[i * 4];
2984                pixels[i] = p;
2985            }
2986            pixels.truncate(pixel_count);
2987            pixels
2988        },
2989        (TexFormat::LuminanceAlpha, TexDataType::UnsignedByte) => {
2990            for i in 0..pixel_count {
2991                let (lum, a) = {
2992                    let rgba = &pixels[i * 4..i * 4 + 4];
2993                    (rgba[0], rgba[3])
2994                };
2995                pixels[i * 2] = lum;
2996                pixels[i * 2 + 1] = a;
2997            }
2998            pixels.truncate(pixel_count * 2);
2999            pixels
3000        },
3001        (TexFormat::RGBA, TexDataType::UnsignedShort4444) => {
3002            for i in 0..pixel_count {
3003                let p = {
3004                    let rgba = &pixels[i * 4..i * 4 + 4];
3005                    ((rgba[0] as u16 & 0xf0) << 8) |
3006                        ((rgba[1] as u16 & 0xf0) << 4) |
3007                        (rgba[2] as u16 & 0xf0) |
3008                        ((rgba[3] as u16 & 0xf0) >> 4)
3009                };
3010                NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p);
3011            }
3012            pixels.truncate(pixel_count * 2);
3013            pixels
3014        },
3015        (TexFormat::RGBA, TexDataType::UnsignedShort5551) => {
3016            for i in 0..pixel_count {
3017                let p = {
3018                    let rgba = &pixels[i * 4..i * 4 + 4];
3019                    ((rgba[0] as u16 & 0xf8) << 8) |
3020                        ((rgba[1] as u16 & 0xf8) << 3) |
3021                        ((rgba[2] as u16 & 0xf8) >> 2) |
3022                        ((rgba[3] as u16) >> 7)
3023                };
3024                NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p);
3025            }
3026            pixels.truncate(pixel_count * 2);
3027            pixels
3028        },
3029        (TexFormat::RGB, TexDataType::UnsignedShort565) => {
3030            for i in 0..pixel_count {
3031                let p = {
3032                    let rgb = &pixels[i * 4..i * 4 + 3];
3033                    ((rgb[0] as u16 & 0xf8) << 8) |
3034                        ((rgb[1] as u16 & 0xfc) << 3) |
3035                        ((rgb[2] as u16 & 0xf8) >> 3)
3036                };
3037                NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p);
3038            }
3039            pixels.truncate(pixel_count * 2);
3040            pixels
3041        },
3042        (TexFormat::RGBA, TexDataType::Float) | (TexFormat::RGBA32f, TexDataType::Float) => {
3043            let mut rgbaf32 = Vec::<u8>::with_capacity(pixel_count * 16);
3044            for rgba8 in pixels.chunks(4) {
3045                rgbaf32.write_f32::<NativeEndian>(rgba8[0] as f32).unwrap();
3046                rgbaf32.write_f32::<NativeEndian>(rgba8[1] as f32).unwrap();
3047                rgbaf32.write_f32::<NativeEndian>(rgba8[2] as f32).unwrap();
3048                rgbaf32.write_f32::<NativeEndian>(rgba8[3] as f32).unwrap();
3049            }
3050            rgbaf32
3051        },
3052
3053        (TexFormat::RGB, TexDataType::Float) | (TexFormat::RGB32f, TexDataType::Float) => {
3054            let mut rgbf32 = Vec::<u8>::with_capacity(pixel_count * 12);
3055            for rgba8 in pixels.chunks(4) {
3056                rgbf32.write_f32::<NativeEndian>(rgba8[0] as f32).unwrap();
3057                rgbf32.write_f32::<NativeEndian>(rgba8[1] as f32).unwrap();
3058                rgbf32.write_f32::<NativeEndian>(rgba8[2] as f32).unwrap();
3059            }
3060            rgbf32
3061        },
3062
3063        (TexFormat::Alpha, TexDataType::Float) | (TexFormat::Alpha32f, TexDataType::Float) => {
3064            for rgba8 in pixels.chunks_mut(4) {
3065                let p = rgba8[3] as f32;
3066                NativeEndian::write_f32(rgba8, p);
3067            }
3068            pixels
3069        },
3070
3071        (TexFormat::Luminance, TexDataType::Float) |
3072        (TexFormat::Luminance32f, TexDataType::Float) => {
3073            for rgba8 in pixels.chunks_mut(4) {
3074                let p = rgba8[0] as f32;
3075                NativeEndian::write_f32(rgba8, p);
3076            }
3077            pixels
3078        },
3079
3080        (TexFormat::LuminanceAlpha, TexDataType::Float) |
3081        (TexFormat::LuminanceAlpha32f, TexDataType::Float) => {
3082            let mut data = Vec::<u8>::with_capacity(pixel_count * 8);
3083            for rgba8 in pixels.chunks(4) {
3084                data.write_f32::<NativeEndian>(rgba8[0] as f32).unwrap();
3085                data.write_f32::<NativeEndian>(rgba8[3] as f32).unwrap();
3086            }
3087            data
3088        },
3089
3090        (TexFormat::RGBA, TexDataType::HalfFloat) |
3091        (TexFormat::RGBA16f, TexDataType::HalfFloat) => {
3092            let mut rgbaf16 = Vec::<u8>::with_capacity(pixel_count * 8);
3093            for rgba8 in pixels.chunks(4) {
3094                rgbaf16
3095                    .write_u16::<NativeEndian>(f16::from_f32(rgba8[0] as f32).to_bits())
3096                    .unwrap();
3097                rgbaf16
3098                    .write_u16::<NativeEndian>(f16::from_f32(rgba8[1] as f32).to_bits())
3099                    .unwrap();
3100                rgbaf16
3101                    .write_u16::<NativeEndian>(f16::from_f32(rgba8[2] as f32).to_bits())
3102                    .unwrap();
3103                rgbaf16
3104                    .write_u16::<NativeEndian>(f16::from_f32(rgba8[3] as f32).to_bits())
3105                    .unwrap();
3106            }
3107            rgbaf16
3108        },
3109
3110        (TexFormat::RGB, TexDataType::HalfFloat) | (TexFormat::RGB16f, TexDataType::HalfFloat) => {
3111            let mut rgbf16 = Vec::<u8>::with_capacity(pixel_count * 6);
3112            for rgba8 in pixels.chunks(4) {
3113                rgbf16
3114                    .write_u16::<NativeEndian>(f16::from_f32(rgba8[0] as f32).to_bits())
3115                    .unwrap();
3116                rgbf16
3117                    .write_u16::<NativeEndian>(f16::from_f32(rgba8[1] as f32).to_bits())
3118                    .unwrap();
3119                rgbf16
3120                    .write_u16::<NativeEndian>(f16::from_f32(rgba8[2] as f32).to_bits())
3121                    .unwrap();
3122            }
3123            rgbf16
3124        },
3125        (TexFormat::Alpha, TexDataType::HalfFloat) |
3126        (TexFormat::Alpha16f, TexDataType::HalfFloat) => {
3127            for i in 0..pixel_count {
3128                let p = f16::from_f32(pixels[i * 4 + 3] as f32).to_bits();
3129                NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p);
3130            }
3131            pixels.truncate(pixel_count * 2);
3132            pixels
3133        },
3134        (TexFormat::Luminance, TexDataType::HalfFloat) |
3135        (TexFormat::Luminance16f, TexDataType::HalfFloat) => {
3136            for i in 0..pixel_count {
3137                let p = f16::from_f32(pixels[i * 4] as f32).to_bits();
3138                NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p);
3139            }
3140            pixels.truncate(pixel_count * 2);
3141            pixels
3142        },
3143        (TexFormat::LuminanceAlpha, TexDataType::HalfFloat) |
3144        (TexFormat::LuminanceAlpha16f, TexDataType::HalfFloat) => {
3145            for rgba8 in pixels.chunks_mut(4) {
3146                let lum = f16::from_f32(rgba8[0] as f32).to_bits();
3147                let a = f16::from_f32(rgba8[3] as f32).to_bits();
3148                NativeEndian::write_u16(&mut rgba8[0..2], lum);
3149                NativeEndian::write_u16(&mut rgba8[2..4], a);
3150            }
3151            pixels
3152        },
3153
3154        // Validation should have ensured that we only hit the
3155        // above cases, but we haven't turned the (format, type)
3156        // into an enum yet so there's a default case here.
3157        _ => unreachable!("Unsupported formats {:?} {:?}", format, data_type),
3158    }
3159}
3160
3161fn premultiply_inplace(format: TexFormat, data_type: TexDataType, pixels: &mut [u8]) {
3162    match (format, data_type) {
3163        (TexFormat::RGBA, TexDataType::UnsignedByte) => {
3164            pixels::rgba8_premultiply_inplace(pixels);
3165        },
3166        (TexFormat::LuminanceAlpha, TexDataType::UnsignedByte) => {
3167            for la in pixels.chunks_mut(2) {
3168                la[0] = pixels::multiply_u8_color(la[0], la[1]);
3169            }
3170        },
3171        (TexFormat::RGBA, TexDataType::UnsignedShort5551) => {
3172            for rgba in pixels.chunks_mut(2) {
3173                if NativeEndian::read_u16(rgba) & 1 == 0 {
3174                    NativeEndian::write_u16(rgba, 0);
3175                }
3176            }
3177        },
3178        (TexFormat::RGBA, TexDataType::UnsignedShort4444) => {
3179            for rgba in pixels.chunks_mut(2) {
3180                let pix = NativeEndian::read_u16(rgba);
3181                let extend_to_8_bits = |val| (val | (val << 4)) as u8;
3182                let r = extend_to_8_bits((pix >> 12) & 0x0f);
3183                let g = extend_to_8_bits((pix >> 8) & 0x0f);
3184                let b = extend_to_8_bits((pix >> 4) & 0x0f);
3185                let a = extend_to_8_bits(pix & 0x0f);
3186                NativeEndian::write_u16(
3187                    rgba,
3188                    (((pixels::multiply_u8_color(r, a) & 0xf0) as u16) << 8) |
3189                        (((pixels::multiply_u8_color(g, a) & 0xf0) as u16) << 4) |
3190                        ((pixels::multiply_u8_color(b, a) & 0xf0) as u16) |
3191                        ((a & 0x0f) as u16),
3192                );
3193            }
3194        },
3195        // Other formats don't have alpha, so return their data untouched.
3196        _ => {},
3197    }
3198}
3199
3200/// Flips the pixels in the Vec on the Y axis.
3201fn flip_pixels_y(
3202    internal_format: TexFormat,
3203    data_type: TexDataType,
3204    width: usize,
3205    height: usize,
3206    unpacking_alignment: usize,
3207    pixels: Vec<u8>,
3208) -> Vec<u8> {
3209    let cpp = (data_type.element_size() * internal_format.components() /
3210        data_type.components_per_element()) as usize;
3211
3212    let stride = (width * cpp + unpacking_alignment - 1) & !(unpacking_alignment - 1);
3213
3214    let mut flipped = Vec::<u8>::with_capacity(pixels.len());
3215
3216    for y in 0..height {
3217        let flipped_y = height - 1 - y;
3218        let start = flipped_y * stride;
3219
3220        flipped.extend_from_slice(&pixels[start..(start + width * cpp)]);
3221        flipped.extend(vec![0u8; stride - width * cpp]);
3222    }
3223
3224    flipped
3225}
3226
3227// Clamp a size to the current GL context's max viewport
3228fn clamp_viewport(gl: &Gl, size: Size2D<u32>) -> Size2D<u32> {
3229    let mut max_viewport = [i32::MAX, i32::MAX];
3230    let mut max_renderbuffer = [i32::MAX];
3231
3232    unsafe {
3233        gl.get_parameter_i32_slice(gl::MAX_VIEWPORT_DIMS, &mut max_viewport);
3234        gl.get_parameter_i32_slice(gl::MAX_RENDERBUFFER_SIZE, &mut max_renderbuffer);
3235    }
3236    Size2D::new(
3237        size.width
3238            .min(max_viewport[0] as u32)
3239            .min(max_renderbuffer[0] as u32)
3240            .max(1),
3241        size.height
3242            .min(max_viewport[1] as u32)
3243            .min(max_renderbuffer[0] as u32)
3244            .max(1),
3245    )
3246}
3247
3248trait ToSurfmanVersion {
3249    fn to_surfman_version(self, api_type: GlType) -> GLVersion;
3250}
3251
3252impl ToSurfmanVersion for WebGLVersion {
3253    fn to_surfman_version(self, api_type: GlType) -> GLVersion {
3254        if api_type == GlType::Gles {
3255            return GLVersion::new(3, 0);
3256        }
3257        match self {
3258            // We make use of GL_PACK_PIXEL_BUFFER, which needs at least GL2.1
3259            // We make use of compatibility mode, which needs at most GL3.0
3260            WebGLVersion::WebGL1 => GLVersion::new(2, 1),
3261            // The WebGL2 conformance tests use std140 layout, which needs at GL3.1
3262            WebGLVersion::WebGL2 => GLVersion::new(3, 2),
3263        }
3264    }
3265}
3266
3267trait SurfmanContextAttributeFlagsConvert {
3268    fn to_surfman_context_attribute_flags(
3269        &self,
3270        webgl_version: WebGLVersion,
3271        api_type: GlType,
3272    ) -> ContextAttributeFlags;
3273}
3274
3275impl SurfmanContextAttributeFlagsConvert for GLContextAttributes {
3276    fn to_surfman_context_attribute_flags(
3277        &self,
3278        webgl_version: WebGLVersion,
3279        api_type: GlType,
3280    ) -> ContextAttributeFlags {
3281        let mut flags = ContextAttributeFlags::empty();
3282        flags.set(ContextAttributeFlags::ALPHA, self.alpha);
3283        flags.set(ContextAttributeFlags::DEPTH, self.depth);
3284        flags.set(ContextAttributeFlags::STENCIL, self.stencil);
3285        if (webgl_version == WebGLVersion::WebGL1) && (api_type == GlType::Gl) {
3286            flags.set(ContextAttributeFlags::COMPATIBILITY_PROFILE, true);
3287        }
3288        flags
3289    }
3290}
3291
3292bitflags! {
3293    struct FramebufferRebindingFlags: u8 {
3294        const REBIND_READ_FRAMEBUFFER = 0x1;
3295        const REBIND_DRAW_FRAMEBUFFER = 0x2;
3296    }
3297}
3298
3299struct FramebufferRebindingInfo {
3300    flags: FramebufferRebindingFlags,
3301    viewport: [GLint; 4],
3302}
3303
3304impl FramebufferRebindingInfo {
3305    fn detect(device: &Device, context: &Context, gl: &Gl) -> FramebufferRebindingInfo {
3306        unsafe {
3307            let read_framebuffer = gl.get_parameter_framebuffer(gl::READ_FRAMEBUFFER_BINDING);
3308            let draw_framebuffer = gl.get_parameter_framebuffer(gl::DRAW_FRAMEBUFFER_BINDING);
3309
3310            let context_surface_framebuffer = device
3311                .context_surface_info(context)
3312                .unwrap()
3313                .unwrap()
3314                .framebuffer_object;
3315
3316            let mut flags = FramebufferRebindingFlags::empty();
3317            if context_surface_framebuffer == read_framebuffer {
3318                flags.insert(FramebufferRebindingFlags::REBIND_READ_FRAMEBUFFER);
3319            }
3320            if context_surface_framebuffer == draw_framebuffer {
3321                flags.insert(FramebufferRebindingFlags::REBIND_DRAW_FRAMEBUFFER);
3322            }
3323
3324            let mut viewport = [0; 4];
3325            gl.get_parameter_i32_slice(gl::VIEWPORT, &mut viewport);
3326
3327            FramebufferRebindingInfo { flags, viewport }
3328        }
3329    }
3330
3331    fn apply(self, device: &Device, context: &Context, gl: &Gl) {
3332        if self.flags.is_empty() {
3333            return;
3334        }
3335
3336        let context_surface_framebuffer = device
3337            .context_surface_info(context)
3338            .unwrap()
3339            .unwrap()
3340            .framebuffer_object;
3341        if self
3342            .flags
3343            .contains(FramebufferRebindingFlags::REBIND_READ_FRAMEBUFFER)
3344        {
3345            unsafe { gl.bind_framebuffer(gl::READ_FRAMEBUFFER, context_surface_framebuffer) };
3346        }
3347        if self
3348            .flags
3349            .contains(FramebufferRebindingFlags::REBIND_DRAW_FRAMEBUFFER)
3350        {
3351            unsafe { gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, context_surface_framebuffer) };
3352        }
3353
3354        unsafe {
3355            gl.viewport(
3356                self.viewport[0],
3357                self.viewport[1],
3358                self.viewport[2],
3359                self.viewport[3],
3360            )
3361        };
3362    }
3363}