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