Skip to main content

webrender/device/
gl.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 http://mozilla.org/MPL/2.0/. */
4
5use super::super::shader_source::{OPTIMIZED_SHADERS, UNOPTIMIZED_SHADERS};
6use api::{ImageDescriptor, ImageFormat, Parameter, BoolParameter, IntParameter, ImageRendering};
7use api::{MixBlendMode, ImageBufferKind, VoidPtrToSizeFn};
8use api::{CrashAnnotator, CrashAnnotation, CrashAnnotatorGuard};
9use api::units::*;
10use euclid::default::Transform3D;
11use gleam::gl;
12use crate::render_api::MemoryReport;
13use crate::internal_types::{FastHashMap, RenderTargetInfo, Swizzle, SwizzleSettings};
14use crate::util::round_up_to_multiple;
15use crate::profiler;
16use log::Level;
17use smallvec::SmallVec;
18use std::{
19    borrow::Cow,
20    cell::{Cell, RefCell},
21    cmp,
22    collections::hash_map::Entry,
23    marker::PhantomData,
24    mem,
25    num::NonZeroUsize,
26    os::raw::c_void,
27    ops::Add,
28    path::PathBuf,
29    ptr,
30    rc::Rc,
31    slice,
32    sync::Arc,
33    thread,
34    time::Duration,
35};
36use webrender_build::shader::{
37    ProgramSourceDigest, ShaderKind, ShaderVersion, build_shader_main_string,
38    build_shader_prefix_string, do_build_shader_string, shader_source_from_file,
39};
40use malloc_size_of::MallocSizeOfOps;
41
42/// Sequence number for frames, as tracked by the device layer.
43#[derive(Debug, Copy, Clone, PartialEq, Ord, Eq, PartialOrd)]
44#[cfg_attr(feature = "capture", derive(Serialize))]
45#[cfg_attr(feature = "replay", derive(Deserialize))]
46pub struct GpuFrameId(usize);
47
48impl GpuFrameId {
49    pub fn new(value: usize) -> Self {
50        GpuFrameId(value)
51    }
52}
53
54impl Add<usize> for GpuFrameId {
55    type Output = GpuFrameId;
56
57    fn add(self, other: usize) -> GpuFrameId {
58        GpuFrameId(self.0 + other)
59    }
60}
61
62pub struct TextureSlot(pub usize);
63
64// In some places we need to temporarily bind a texture to any slot.
65const DEFAULT_TEXTURE: TextureSlot = TextureSlot(0);
66
67#[repr(u32)]
68pub enum DepthFunction {
69    Always = gl::ALWAYS,
70    Less = gl::LESS,
71    LessEqual = gl::LEQUAL,
72}
73
74#[repr(u32)]
75#[derive(Copy, Clone, Debug, Eq, PartialEq)]
76#[cfg_attr(feature = "capture", derive(Serialize))]
77#[cfg_attr(feature = "replay", derive(Deserialize))]
78pub enum TextureFilter {
79    Nearest,
80    Linear,
81    Trilinear,
82}
83
84/// A structure defining a particular workflow of texture transfers.
85#[derive(Clone, Debug)]
86#[cfg_attr(feature = "capture", derive(Serialize))]
87#[cfg_attr(feature = "replay", derive(Deserialize))]
88pub struct TextureFormatPair<T> {
89    /// Format the GPU natively stores texels in.
90    pub internal: T,
91    /// Format we expect the users to provide the texels in.
92    pub external: T,
93}
94
95impl<T: Copy> From<T> for TextureFormatPair<T> {
96    fn from(value: T) -> Self {
97        TextureFormatPair {
98            internal: value,
99            external: value,
100        }
101    }
102}
103
104#[derive(Debug)]
105pub enum VertexAttributeKind {
106    F32,
107    U8Norm,
108    U16Norm,
109    I32,
110    U16,
111}
112
113#[derive(Debug)]
114pub struct VertexAttribute {
115    pub name: &'static str,
116    pub count: u32,
117    pub kind: VertexAttributeKind,
118}
119
120impl VertexAttribute {
121    pub const fn quad_instance_vertex() -> Self {
122        VertexAttribute {
123            name: "aPosition",
124            count: 2,
125            kind: VertexAttributeKind::U8Norm,
126        }
127    }
128
129    pub const fn gpu_buffer_address(name: &'static str) -> Self {
130        VertexAttribute {
131            name,
132            count: 1,
133            kind: VertexAttributeKind::I32,
134        }
135    }
136
137    pub const fn f32x4(name: &'static str) -> Self {
138        VertexAttribute {
139            name,
140            count: 4,
141            kind: VertexAttributeKind::F32,
142        }
143    }
144
145    pub const fn f32x3(name: &'static str) -> Self {
146        VertexAttribute {
147            name,
148            count: 3,
149            kind: VertexAttributeKind::F32,
150        }
151    }
152
153    pub const fn f32x2(name: &'static str) -> Self {
154        VertexAttribute {
155            name,
156            count: 2,
157            kind: VertexAttributeKind::F32,
158        }
159    }
160
161    pub const fn f32(name: &'static str) -> Self {
162        VertexAttribute {
163            name,
164            count: 1,
165            kind: VertexAttributeKind::F32,
166        }
167    }
168
169    pub const fn i32x4(name: &'static str) -> Self {
170        VertexAttribute {
171            name,
172            count: 4,
173            kind: VertexAttributeKind::I32,
174        }
175    }
176
177    pub const fn i32x2(name: &'static str) -> Self {
178        VertexAttribute {
179            name,
180            count: 2,
181            kind: VertexAttributeKind::I32,
182        }
183    }
184
185    pub const fn i32(name: &'static str) -> Self {
186        VertexAttribute {
187            name,
188            count: 1,
189            kind: VertexAttributeKind::I32,
190        }
191    }
192
193    pub const fn u16(name: &'static str) -> Self {
194        VertexAttribute {
195            name,
196            count: 1,
197            kind: VertexAttributeKind::U16,
198        }
199    }
200
201    pub const fn u16x2(name: &'static str) -> Self {
202        VertexAttribute {
203            name,
204            count: 2,
205            kind: VertexAttributeKind::U16,
206        }
207    }
208}
209
210#[derive(Debug)]
211pub struct VertexDescriptor {
212    pub vertex_attributes: &'static [VertexAttribute],
213    pub instance_attributes: &'static [VertexAttribute],
214}
215
216enum FBOTarget {
217    Read,
218    Draw,
219}
220
221/// Method of uploading texel data from CPU to GPU.
222#[derive(Debug, Clone)]
223pub enum UploadMethod {
224    /// Just call `glTexSubImage` directly with the CPU data pointer
225    Immediate,
226    /// Accumulate the changes in PBO first before transferring to a texture.
227    PixelBuffer(VertexUsageHint),
228}
229
230/// Plain old data that can be used to initialize a texture.
231pub unsafe trait Texel: Copy + Default {
232    fn image_format() -> ImageFormat;
233}
234
235unsafe impl Texel for u8 {
236    fn image_format() -> ImageFormat { ImageFormat::R8 }
237}
238
239/// Returns the size in bytes of a depth target with the given dimensions.
240fn depth_target_size_in_bytes(dimensions: &DeviceIntSize) -> usize {
241    // DEPTH24 textures generally reserve 3 bytes for depth and 1 byte
242    // for stencil, so we measure them as 32 bits.
243    let pixels = dimensions.width * dimensions.height;
244    (pixels as usize) * 4
245}
246
247pub fn get_gl_target(target: ImageBufferKind) -> gl::GLuint {
248    match target {
249        ImageBufferKind::Texture2D => gl::TEXTURE_2D,
250        ImageBufferKind::TextureRect => gl::TEXTURE_RECTANGLE,
251        ImageBufferKind::TextureExternal => gl::TEXTURE_EXTERNAL_OES,
252        ImageBufferKind::TextureExternalBT709 => gl::TEXTURE_EXTERNAL_OES,
253    }
254}
255
256pub fn from_gl_target(target: gl::GLuint) -> ImageBufferKind {
257    match target {
258        gl::TEXTURE_2D => ImageBufferKind::Texture2D,
259        gl::TEXTURE_RECTANGLE => ImageBufferKind::TextureRect,
260        gl::TEXTURE_EXTERNAL_OES => ImageBufferKind::TextureExternal,
261        _ => panic!("Unexpected target {:?}", target),
262    }
263}
264
265fn supports_extension(extensions: &[String], extension: &str) -> bool {
266    extensions.iter().any(|s| s == extension)
267}
268
269fn get_shader_version(gl: &dyn gl::Gl) -> ShaderVersion {
270    match gl.get_type() {
271        gl::GlType::Gl => ShaderVersion::Gl,
272        gl::GlType::Gles => ShaderVersion::Gles,
273    }
274}
275
276// Get an unoptimized shader string by name, from the built in resources or
277// an override path, if supplied.
278pub fn get_unoptimized_shader_source(shader_name: &str, base_path: Option<&PathBuf>) -> Cow<'static, str> {
279    if let Some(ref base) = base_path {
280        let shader_path = base.join(&format!("{}.glsl", shader_name));
281        Cow::Owned(shader_source_from_file(&shader_path))
282    } else {
283        Cow::Borrowed(
284            UNOPTIMIZED_SHADERS
285            .get(shader_name)
286            .expect("Shader not found")
287            .source
288        )
289    }
290}
291
292impl VertexAttributeKind {
293    fn size_in_bytes(&self) -> u32 {
294        match *self {
295            VertexAttributeKind::F32 => 4,
296            VertexAttributeKind::U8Norm => 1,
297            VertexAttributeKind::U16Norm => 2,
298            VertexAttributeKind::I32 => 4,
299            VertexAttributeKind::U16 => 2,
300        }
301    }
302}
303
304impl VertexAttribute {
305    fn size_in_bytes(&self) -> u32 {
306        self.count * self.kind.size_in_bytes()
307    }
308
309    fn bind_to_vao(
310        &self,
311        attr_index: gl::GLuint,
312        divisor: gl::GLuint,
313        stride: gl::GLint,
314        offset: gl::GLuint,
315        gl: &dyn gl::Gl,
316    ) {
317        gl.enable_vertex_attrib_array(attr_index);
318        gl.vertex_attrib_divisor(attr_index, divisor);
319
320        match self.kind {
321            VertexAttributeKind::F32 => {
322                gl.vertex_attrib_pointer(
323                    attr_index,
324                    self.count as gl::GLint,
325                    gl::FLOAT,
326                    false,
327                    stride,
328                    offset,
329                );
330            }
331            VertexAttributeKind::U8Norm => {
332                gl.vertex_attrib_pointer(
333                    attr_index,
334                    self.count as gl::GLint,
335                    gl::UNSIGNED_BYTE,
336                    true,
337                    stride,
338                    offset,
339                );
340            }
341            VertexAttributeKind::U16Norm => {
342                gl.vertex_attrib_pointer(
343                    attr_index,
344                    self.count as gl::GLint,
345                    gl::UNSIGNED_SHORT,
346                    true,
347                    stride,
348                    offset,
349                );
350            }
351            VertexAttributeKind::I32 => {
352                gl.vertex_attrib_i_pointer(
353                    attr_index,
354                    self.count as gl::GLint,
355                    gl::INT,
356                    stride,
357                    offset,
358                );
359            }
360            VertexAttributeKind::U16 => {
361                gl.vertex_attrib_i_pointer(
362                    attr_index,
363                    self.count as gl::GLint,
364                    gl::UNSIGNED_SHORT,
365                    stride,
366                    offset,
367                );
368            }
369        }
370    }
371}
372
373impl VertexDescriptor {
374    fn instance_stride(&self) -> u32 {
375        self.instance_attributes
376            .iter()
377            .map(|attr| attr.size_in_bytes())
378            .sum()
379    }
380
381    fn bind_attributes(
382        attributes: &[VertexAttribute],
383        start_index: usize,
384        divisor: u32,
385        gl: &dyn gl::Gl,
386        vbo: VBOId,
387    ) {
388        vbo.bind(gl);
389
390        let stride: u32 = attributes
391            .iter()
392            .map(|attr| attr.size_in_bytes())
393            .sum();
394
395        let mut offset = 0;
396        for (i, attr) in attributes.iter().enumerate() {
397            let attr_index = (start_index + i) as gl::GLuint;
398            attr.bind_to_vao(attr_index, divisor, stride as _, offset, gl);
399            offset += attr.size_in_bytes();
400        }
401    }
402
403    fn bind(&self, gl: &dyn gl::Gl, main: VBOId, instance: VBOId, instance_divisor: u32) {
404        Self::bind_attributes(self.vertex_attributes, 0, 0, gl, main);
405
406        if !self.instance_attributes.is_empty() {
407            Self::bind_attributes(
408                self.instance_attributes,
409                self.vertex_attributes.len(),
410                instance_divisor,
411                gl,
412                instance,
413            );
414        }
415    }
416}
417
418impl VBOId {
419    fn bind(&self, gl: &dyn gl::Gl) {
420        gl.bind_buffer(gl::ARRAY_BUFFER, self.0);
421    }
422}
423
424impl IBOId {
425    fn bind(&self, gl: &dyn gl::Gl) {
426        gl.bind_buffer(gl::ELEMENT_ARRAY_BUFFER, self.0);
427    }
428}
429
430impl FBOId {
431    fn bind(&self, gl: &dyn gl::Gl, target: FBOTarget) {
432        let target = match target {
433            FBOTarget::Read => gl::READ_FRAMEBUFFER,
434            FBOTarget::Draw => gl::DRAW_FRAMEBUFFER,
435        };
436        gl.bind_framebuffer(target, self.0);
437    }
438}
439
440pub struct Stream<'a> {
441    attributes: &'a [VertexAttribute],
442    vbo: VBOId,
443}
444
445pub struct VBO<V> {
446    id: gl::GLuint,
447    target: gl::GLenum,
448    allocated_count: usize,
449    marker: PhantomData<V>,
450}
451
452impl<V> VBO<V> {
453    pub fn allocated_count(&self) -> usize {
454        self.allocated_count
455    }
456
457    pub fn stream_with<'a>(&self, attributes: &'a [VertexAttribute]) -> Stream<'a> {
458        debug_assert_eq!(
459            mem::size_of::<V>(),
460            attributes.iter().map(|a| a.size_in_bytes() as usize).sum::<usize>()
461        );
462        Stream {
463            attributes,
464            vbo: VBOId(self.id),
465        }
466    }
467}
468
469impl<T> Drop for VBO<T> {
470    fn drop(&mut self) {
471        debug_assert!(thread::panicking() || self.id == 0);
472    }
473}
474
475#[cfg_attr(feature = "replay", derive(Clone))]
476#[derive(Debug)]
477pub struct ExternalTexture {
478    id: gl::GLuint,
479    target: gl::GLuint,
480    uv_rect: TexelRect,
481    image_rendering: ImageRendering,
482}
483
484impl ExternalTexture {
485    pub fn new(
486        id: u32,
487        target: ImageBufferKind,
488        uv_rect: TexelRect,
489        image_rendering: ImageRendering,
490    ) -> Self {
491        ExternalTexture {
492            id,
493            target: get_gl_target(target),
494            uv_rect,
495            image_rendering,
496        }
497    }
498
499    #[cfg(feature = "replay")]
500    pub fn internal_id(&self) -> gl::GLuint {
501        self.id
502    }
503
504    pub fn get_uv_rect(&self) -> TexelRect {
505        self.uv_rect
506    }
507}
508
509bitflags! {
510    #[derive(Default, Debug, Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
511    pub struct TextureFlags: u32 {
512        /// This texture corresponds to one of the shared texture caches.
513        const IS_SHARED_TEXTURE_CACHE = 1 << 0;
514    }
515}
516
517/// WebRender interface to an OpenGL texture.
518///
519/// Because freeing a texture requires various device handles that are not
520/// reachable from this struct, manual destruction via `Device` is required.
521/// Our `Drop` implementation asserts that this has happened.
522#[derive(Debug)]
523pub struct Texture {
524    id: gl::GLuint,
525    target: gl::GLuint,
526    format: ImageFormat,
527    size: DeviceIntSize,
528    filter: TextureFilter,
529    flags: TextureFlags,
530    /// An internally mutable swizzling state that may change between batches.
531    active_swizzle: Cell<Swizzle>,
532    /// Framebuffer Object allowing this texture to be rendered to.
533    ///
534    /// Empty if this texture is not used as a render target or if a depth buffer is needed.
535    fbo: Option<FBOId>,
536    /// Same as the above, but with a depth buffer attached.
537    ///
538    /// FBOs are cheap to create but expensive to reconfigure (since doing so
539    /// invalidates framebuffer completeness caching). Moreover, rendering with
540    /// a depth buffer attached but the depth write+test disabled relies on the
541    /// driver to optimize it out of the rendering pass, which most drivers
542    /// probably do but, according to jgilbert, is best not to rely on.
543    ///
544    /// So we lazily generate a second list of FBOs with depth. This list is
545    /// empty if this texture is not used as a render target _or_ if it is, but
546    /// the depth buffer has never been requested.
547    ///
548    /// Note that we always fill fbo, and then lazily create fbo_with_depth
549    /// when needed. We could make both lazy (i.e. render targets would have one
550    /// or the other, but not both, unless they were actually used in both
551    /// configurations). But that would complicate a lot of logic in this module,
552    /// and FBOs are cheap enough to create.
553    fbo_with_depth: Option<FBOId>,
554    last_frame_used: GpuFrameId,
555}
556
557impl Texture {
558    pub fn get_dimensions(&self) -> DeviceIntSize {
559        self.size
560    }
561
562    pub fn get_format(&self) -> ImageFormat {
563        self.format
564    }
565
566    pub fn get_filter(&self) -> TextureFilter {
567        self.filter
568    }
569
570    pub fn get_target(&self) -> ImageBufferKind {
571        from_gl_target(self.target)
572    }
573
574    pub fn supports_depth(&self) -> bool {
575        self.fbo_with_depth.is_some()
576    }
577
578    pub fn last_frame_used(&self) -> GpuFrameId {
579        self.last_frame_used
580    }
581
582    pub fn used_in_frame(&self, frame_id: GpuFrameId) -> bool {
583        self.last_frame_used == frame_id
584    }
585
586    pub fn is_render_target(&self) -> bool {
587        self.fbo.is_some()
588    }
589
590    /// Returns true if this texture was used within `threshold` frames of
591    /// the current frame.
592    pub fn used_recently(&self, current_frame_id: GpuFrameId, threshold: usize) -> bool {
593        self.last_frame_used + threshold >= current_frame_id
594    }
595
596    /// Returns the flags for this texture.
597    pub fn flags(&self) -> &TextureFlags {
598        &self.flags
599    }
600
601    /// Returns a mutable borrow of the flags for this texture.
602    pub fn flags_mut(&mut self) -> &mut TextureFlags {
603        &mut self.flags
604    }
605
606    /// Returns the number of bytes (generally in GPU memory) that this texture
607    /// consumes.
608    pub fn size_in_bytes(&self) -> usize {
609        let bpp = self.format.bytes_per_pixel() as usize;
610        let w = self.size.width as usize;
611        let h = self.size.height as usize;
612        bpp * w * h
613    }
614
615    #[cfg(feature = "replay")]
616    pub fn into_external(mut self) -> ExternalTexture {
617        let ext = ExternalTexture {
618            id: self.id,
619            target: self.target,
620            // TODO(gw): Support custom UV rect for external textures during captures
621            uv_rect: TexelRect::new(
622                0.0,
623                0.0,
624                self.size.width as f32,
625                self.size.height as f32,
626            ),
627            image_rendering: ImageRendering::Auto,
628        };
629        self.id = 0; // don't complain, moved out
630        ext
631    }
632}
633
634impl Drop for Texture {
635    fn drop(&mut self) {
636        debug_assert!(thread::panicking() || self.id == 0);
637    }
638}
639
640pub struct Program {
641    id: gl::GLuint,
642    u_transform: gl::GLint,
643    u_texture_size: gl::GLint,
644    source_info: ProgramSourceInfo,
645    is_initialized: bool,
646}
647
648impl Program {
649    pub fn is_initialized(&self) -> bool {
650        self.is_initialized
651    }
652}
653
654impl Drop for Program {
655    fn drop(&mut self) {
656        debug_assert!(
657            thread::panicking() || self.id == 0,
658            "renderer::deinit not called"
659        );
660    }
661}
662
663pub struct CustomVAO {
664    id: gl::GLuint,
665}
666
667impl Drop for CustomVAO {
668    fn drop(&mut self) {
669        debug_assert!(
670            thread::panicking() || self.id == 0,
671            "renderer::deinit not called"
672        );
673    }
674}
675
676pub struct VAO {
677    id: gl::GLuint,
678    ibo_id: IBOId,
679    main_vbo_id: VBOId,
680    instance_vbo_id: VBOId,
681    instance_stride: usize,
682    instance_divisor: u32,
683    owns_vertices_and_indices: bool,
684}
685
686impl Drop for VAO {
687    fn drop(&mut self) {
688        debug_assert!(
689            thread::panicking() || self.id == 0,
690            "renderer::deinit not called"
691        );
692    }
693}
694
695#[derive(Debug)]
696pub struct PBO {
697    id: gl::GLuint,
698    reserved_size: usize,
699}
700
701impl PBO {
702    pub fn get_reserved_size(&self) -> usize {
703        self.reserved_size
704    }
705}
706
707impl Drop for PBO {
708    fn drop(&mut self) {
709        debug_assert!(
710            thread::panicking() || self.id == 0,
711            "renderer::deinit not called or PBO not returned to pool"
712        );
713    }
714}
715
716pub struct BoundPBO<'a> {
717    device: &'a mut Device,
718    pub data: &'a [u8]
719}
720
721impl<'a> Drop for BoundPBO<'a> {
722    fn drop(&mut self) {
723        self.device.gl.unmap_buffer(gl::PIXEL_PACK_BUFFER);
724        self.device.gl.bind_buffer(gl::PIXEL_PACK_BUFFER, 0);
725    }
726}
727
728#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
729pub struct FBOId(gl::GLuint);
730
731#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
732pub struct RBOId(gl::GLuint);
733
734#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
735pub struct VBOId(gl::GLuint);
736
737#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
738struct IBOId(gl::GLuint);
739
740#[derive(Clone, Debug)]
741enum ProgramSourceType {
742    Unoptimized,
743    Optimized(ShaderVersion),
744}
745
746#[derive(Clone, Debug)]
747pub struct ProgramSourceInfo {
748    base_filename: &'static str,
749    features: Vec<&'static str>,
750    full_name_cstr: Rc<std::ffi::CString>,
751    source_type: ProgramSourceType,
752    digest: ProgramSourceDigest,
753}
754
755impl ProgramSourceInfo {
756    fn new(
757        device: &Device,
758        name: &'static str,
759        features: &[&'static str],
760    ) -> Self {
761
762        // Compute the digest. Assuming the device has a `ProgramCache`, this
763        // will always be needed, whereas the source is rarely needed.
764
765        use std::collections::hash_map::DefaultHasher;
766        use std::hash::Hasher;
767
768        // Setup.
769        let mut hasher = DefaultHasher::new();
770        let gl_version = get_shader_version(&*device.gl());
771
772        // Hash the renderer name.
773        hasher.write(device.capabilities.renderer_name.as_bytes());
774
775        let full_name = Self::make_full_name(name, features);
776
777        let optimized_source = if device.use_optimized_shaders {
778            OPTIMIZED_SHADERS.get(&(gl_version, &full_name)).or_else(|| {
779                warn!("Missing optimized shader source for {}", &full_name);
780                None
781            })
782        } else {
783            None
784        };
785
786        let source_type = match optimized_source {
787            Some(source_and_digest) => {
788                // Optimized shader sources are used as-is, without any run-time processing.
789                // The vertex and fragment shaders are different, so must both be hashed.
790                // We use the hashes that were computed at build time, and verify it in debug builds.
791                if cfg!(debug_assertions) {
792                    let mut h = DefaultHasher::new();
793                    h.write(source_and_digest.vert_source.as_bytes());
794                    h.write(source_and_digest.frag_source.as_bytes());
795                    let d: ProgramSourceDigest = h.into();
796                    let digest = d.to_string();
797                    debug_assert_eq!(digest, source_and_digest.digest);
798                    hasher.write(digest.as_bytes());
799                } else {
800                    hasher.write(source_and_digest.digest.as_bytes());
801                }
802
803                ProgramSourceType::Optimized(gl_version)
804            }
805            None => {
806                // For non-optimized sources we compute the hash by walking the static strings
807                // in the same order as we would when concatenating the source, to avoid
808                // heap-allocating in the common case.
809                //
810                // Note that we cheat a bit to make the hashing more efficient. First, the only
811                // difference between the vertex and fragment shader is a single deterministic
812                // define, so we don't need to hash both. Second, we precompute the digest of the
813                // expanded source file at build time, and then just hash that digest here.
814                let override_path = device.resource_override_path.as_ref();
815                let source_and_digest = UNOPTIMIZED_SHADERS.get(&name).expect("Shader not found");
816
817                // Hash the prefix string.
818                build_shader_prefix_string(
819                    gl_version,
820                    &features,
821                    ShaderKind::Vertex,
822                    &name,
823                    &mut |s| hasher.write(s.as_bytes()),
824                );
825
826                // Hash the shader file contents. We use a precomputed digest, and
827                // verify it in debug builds.
828                if override_path.is_some() || cfg!(debug_assertions) {
829                    let mut h = DefaultHasher::new();
830                    build_shader_main_string(
831                        &name,
832                        &|f| get_unoptimized_shader_source(f, override_path),
833                        &mut |s| h.write(s.as_bytes())
834                    );
835                    let d: ProgramSourceDigest = h.into();
836                    let digest = format!("{}", d);
837                    debug_assert!(override_path.is_some() || digest == source_and_digest.digest);
838                    hasher.write(digest.as_bytes());
839                } else {
840                    hasher.write(source_and_digest.digest.as_bytes());
841                }
842
843                ProgramSourceType::Unoptimized
844            }
845        };
846
847        // Finish.
848        ProgramSourceInfo {
849            base_filename: name,
850            features: features.to_vec(),
851            full_name_cstr: Rc::new(std::ffi::CString::new(full_name).unwrap()),
852            source_type,
853            digest: hasher.into(),
854        }
855    }
856
857    fn compute_source(&self, device: &Device, kind: ShaderKind) -> String {
858        let full_name = self.full_name();
859        match self.source_type {
860            ProgramSourceType::Optimized(gl_version) => {
861                let shader = OPTIMIZED_SHADERS
862                    .get(&(gl_version, &full_name))
863                    .unwrap_or_else(|| panic!("Missing optimized shader source for {}", full_name));
864
865                match kind {
866                    ShaderKind::Vertex => shader.vert_source.to_string(),
867                    ShaderKind::Fragment => shader.frag_source.to_string(),
868                }
869            },
870            ProgramSourceType::Unoptimized => {
871                let mut src = String::new();
872                device.build_shader_string(
873                    &self.features,
874                    kind,
875                    self.base_filename,
876                    |s| src.push_str(s),
877                );
878                src
879            }
880        }
881    }
882
883    fn make_full_name(base_filename: &'static str, features: &[&'static str]) -> String {
884        if features.is_empty() {
885            base_filename.to_string()
886        } else {
887            format!("{}_{}", base_filename, features.join("_"))
888        }
889    }
890
891    fn full_name(&self) -> String {
892        Self::make_full_name(self.base_filename, &self.features)
893    }
894}
895
896#[cfg_attr(feature = "serialize_program", derive(Deserialize, Serialize))]
897pub struct ProgramBinary {
898    bytes: Vec<u8>,
899    format: gl::GLenum,
900    source_digest: ProgramSourceDigest,
901}
902
903impl ProgramBinary {
904    fn new(bytes: Vec<u8>,
905           format: gl::GLenum,
906           source_digest: ProgramSourceDigest) -> Self {
907        ProgramBinary {
908            bytes,
909            format,
910            source_digest,
911        }
912    }
913
914    /// Returns a reference to the source digest hash.
915    pub fn source_digest(&self) -> &ProgramSourceDigest {
916        &self.source_digest
917    }
918}
919
920/// The interfaces that an application can implement to handle ProgramCache update
921pub trait ProgramCacheObserver {
922    fn save_shaders_to_disk(&self, entries: Vec<Arc<ProgramBinary>>);
923    fn set_startup_shaders(&self, entries: Vec<Arc<ProgramBinary>>);
924    fn try_load_shader_from_disk(&self, digest: &ProgramSourceDigest, program_cache: &Rc<ProgramCache>);
925    fn notify_program_binary_failed(&self, program_binary: &Arc<ProgramBinary>);
926}
927
928struct ProgramCacheEntry {
929    /// The binary.
930    binary: Arc<ProgramBinary>,
931    /// True if the binary has been linked, i.e. used for rendering.
932    linked: bool,
933}
934
935pub struct ProgramCache {
936    entries: RefCell<FastHashMap<ProgramSourceDigest, ProgramCacheEntry>>,
937
938    /// Optional trait object that allows the client
939    /// application to handle ProgramCache updating
940    program_cache_handler: Option<Box<dyn ProgramCacheObserver>>,
941
942    /// Programs that have not yet been cached to disk (by program_cache_handler)
943    pending_entries: RefCell<Vec<Arc<ProgramBinary>>>,
944}
945
946impl ProgramCache {
947    pub fn new(program_cache_observer: Option<Box<dyn ProgramCacheObserver>>) -> Rc<Self> {
948        Rc::new(
949            ProgramCache {
950                entries: RefCell::new(FastHashMap::default()),
951                program_cache_handler: program_cache_observer,
952                pending_entries: RefCell::new(Vec::default()),
953            }
954        )
955    }
956
957    /// Save any new program binaries to the disk cache, and if startup has
958    /// just completed then write the list of shaders to load on next startup.
959    fn update_disk_cache(&self, startup_complete: bool) {
960        if let Some(ref handler) = self.program_cache_handler {
961            if !self.pending_entries.borrow().is_empty() {
962                let pending_entries = self.pending_entries.replace(Vec::default());
963                handler.save_shaders_to_disk(pending_entries);
964            }
965
966            if startup_complete {
967                let startup_shaders = self.entries.borrow().values()
968                    .filter(|e| e.linked).map(|e| e.binary.clone())
969                    .collect::<Vec<_>>();
970                handler.set_startup_shaders(startup_shaders);
971            }
972        }
973    }
974
975    /// Add a new ProgramBinary to the cache.
976    /// This function is typically used after compiling and linking a new program.
977    /// The binary will be saved to disk the next time update_disk_cache() is called.
978    fn add_new_program_binary(&self, program_binary: Arc<ProgramBinary>) {
979        self.pending_entries.borrow_mut().push(program_binary.clone());
980
981        let digest = program_binary.source_digest.clone();
982        let entry = ProgramCacheEntry {
983            binary: program_binary,
984            linked: true,
985        };
986        self.entries.borrow_mut().insert(digest, entry);
987    }
988
989    /// Load ProgramBinary to ProgramCache.
990    /// The function is typically used to load ProgramBinary from disk.
991    #[cfg(feature = "serialize_program")]
992    pub fn load_program_binary(&self, program_binary: Arc<ProgramBinary>) {
993        let digest = program_binary.source_digest.clone();
994        let entry = ProgramCacheEntry {
995            binary: program_binary,
996            linked: false,
997        };
998        self.entries.borrow_mut().insert(digest, entry);
999    }
1000
1001    /// Returns the number of bytes allocated for shaders in the cache.
1002    pub fn report_memory(&self, op: VoidPtrToSizeFn) -> usize {
1003        self.entries.borrow().values()
1004            .map(|e| unsafe { op(e.binary.bytes.as_ptr() as *const c_void ) })
1005            .sum()
1006    }
1007}
1008
1009#[derive(Debug, Copy, Clone)]
1010pub enum VertexUsageHint {
1011    Static,
1012    Dynamic,
1013    Stream,
1014}
1015
1016impl VertexUsageHint {
1017    fn to_gl(&self) -> gl::GLuint {
1018        match *self {
1019            VertexUsageHint::Static => gl::STATIC_DRAW,
1020            VertexUsageHint::Dynamic => gl::DYNAMIC_DRAW,
1021            VertexUsageHint::Stream => gl::STREAM_DRAW,
1022        }
1023    }
1024}
1025
1026#[derive(Copy, Clone, Debug)]
1027pub struct UniformLocation(#[allow(dead_code)] gl::GLint);
1028
1029impl UniformLocation {
1030    pub const INVALID: Self = UniformLocation(-1);
1031}
1032
1033#[derive(Debug)]
1034pub struct Capabilities {
1035    /// Whether multisampled render targets are supported.
1036    pub supports_multisampling: bool,
1037    /// Whether the function `glCopyImageSubData` is available.
1038    pub supports_copy_image_sub_data: bool,
1039    /// Whether the RGBAF32 textures can be bound to framebuffers.
1040    pub supports_color_buffer_float: bool,
1041    /// Whether the device supports persistently mapped buffers, via glBufferStorage.
1042    pub supports_buffer_storage: bool,
1043    /// Whether advanced blend equations are supported.
1044    pub supports_advanced_blend_equation: bool,
1045    /// Whether dual-source blending is supported.
1046    pub supports_dual_source_blending: bool,
1047    /// Whether KHR_debug is supported for getting debug messages from
1048    /// the driver.
1049    pub supports_khr_debug: bool,
1050    /// Whether we can configure texture units to do swizzling on sampling.
1051    pub supports_texture_swizzle: bool,
1052    /// Whether the driver supports uploading to textures from a non-zero
1053    /// offset within a PBO.
1054    pub supports_nonzero_pbo_offsets: bool,
1055    /// Whether the driver supports specifying the texture usage up front.
1056    pub supports_texture_usage: bool,
1057    /// Whether offscreen render targets can be partially updated.
1058    pub supports_render_target_partial_update: bool,
1059    /// Whether we can use SSBOs.
1060    pub supports_shader_storage_object: bool,
1061    /// Whether to enforce that texture uploads be batched regardless of what
1062    /// the pref says.
1063    pub requires_batched_texture_uploads: Option<bool>,
1064    /// Whether we are able to ue glClear to clear regions of an alpha render target.
1065    /// If false, we must use a shader to clear instead.
1066    pub supports_alpha_target_clears: bool,
1067    /// Whether we must perform a full unscissored glClear on alpha targets
1068    /// prior to rendering.
1069    pub requires_alpha_target_full_clear: bool,
1070    /// Whether clearing a render target (immediately after binding it) is faster using a scissor
1071    /// rect to clear just the required area, or clearing the entire target without a scissor rect.
1072    pub prefers_clear_scissor: bool,
1073    /// Whether the driver can correctly invalidate render targets. This can be
1074    /// a worthwhile optimization, but is buggy on some devices.
1075    pub supports_render_target_invalidate: bool,
1076    /// Whether the driver can reliably upload data to R8 format textures.
1077    pub supports_r8_texture_upload: bool,
1078    /// Whether the extension QCOM_tiled_rendering is supported.
1079    pub supports_qcom_tiled_rendering: bool,
1080    /// Whether clip-masking is supported natively by the GL implementation
1081    /// rather than emulated in shaders.
1082    pub uses_native_clip_mask: bool,
1083    /// Whether anti-aliasing is supported natively by the GL implementation
1084    /// rather than emulated in shaders.
1085    pub uses_native_antialiasing: bool,
1086    /// Whether the extension GL_OES_EGL_image_external_essl3 is supported. If true, external
1087    /// textures can be used as normal. If false, external textures can only be rendered with
1088    /// certain shaders, and must first be copied in to regular textures for others.
1089    pub supports_image_external_essl3: bool,
1090    /// Whether the VAO must be rebound after an attached VBO has been orphaned.
1091    pub requires_vao_rebind_after_orphaning: bool,
1092    /// The name of the renderer, as reported by GL
1093    pub renderer_name: String,
1094}
1095
1096#[derive(Clone, Debug)]
1097pub enum ShaderError {
1098    Compilation(String, String), // name, error message
1099    Link(String, String),        // name, error message
1100}
1101
1102/// A refcounted depth target, which may be shared by multiple textures across
1103/// the device.
1104struct SharedDepthTarget {
1105    /// The Render Buffer Object representing the depth target.
1106    rbo_id: RBOId,
1107    /// Reference count. When this drops to zero, the RBO is deleted.
1108    refcount: usize,
1109}
1110
1111#[cfg(debug_assertions)]
1112impl Drop for SharedDepthTarget {
1113    fn drop(&mut self) {
1114        debug_assert!(thread::panicking() || self.refcount == 0);
1115    }
1116}
1117
1118/// Describes for which texture formats to use the glTexStorage*
1119/// family of functions.
1120#[derive(PartialEq, Debug)]
1121enum TexStorageUsage {
1122    Never,
1123    NonBGRA8,
1124    Always,
1125}
1126
1127/// Describes a required alignment for a stride,
1128/// which can either be represented in bytes or pixels.
1129#[derive(Copy, Clone, Debug)]
1130pub enum StrideAlignment {
1131    Bytes(NonZeroUsize),
1132    Pixels(NonZeroUsize),
1133}
1134
1135impl StrideAlignment {
1136    pub fn num_bytes(&self, format: ImageFormat) -> NonZeroUsize {
1137        match *self {
1138            Self::Bytes(bytes) => bytes,
1139            Self::Pixels(pixels) => {
1140                assert!(format.bytes_per_pixel() > 0);
1141                NonZeroUsize::new(pixels.get() * format.bytes_per_pixel() as usize).unwrap()
1142            }
1143        }
1144    }
1145}
1146
1147// We get 24 bits of Z value - use up 22 bits of it to give us
1148// 4 bits to account for GPU issues. This seems to manifest on
1149// some GPUs under certain perspectives due to z interpolation
1150// precision problems.
1151const RESERVE_DEPTH_BITS: i32 = 2;
1152
1153pub struct Device {
1154    gl: Rc<dyn gl::Gl>,
1155
1156    /// If non-None, |gl| points to a profiling wrapper, and this points to the
1157    /// underling Gl instance.
1158    base_gl: Option<Rc<dyn gl::Gl>>,
1159
1160    // device state
1161    bound_textures: [gl::GLuint; 16],
1162    bound_program: gl::GLuint,
1163    bound_program_name: Rc<std::ffi::CString>,
1164    bound_vao: gl::GLuint,
1165    bound_read_fbo: (FBOId, DeviceIntPoint),
1166    bound_draw_fbo: FBOId,
1167    default_read_fbo: FBOId,
1168    default_draw_fbo: FBOId,
1169
1170    /// Track depth state for assertions. Note that the default FBO has depth,
1171    /// so this defaults to true.
1172    depth_available: bool,
1173
1174    upload_method: UploadMethod,
1175    use_batched_texture_uploads: bool,
1176    /// Whether to use draw calls instead of regular blitting commands.
1177    ///
1178    /// Note: this currently only applies to the batched texture uploads
1179    /// path.
1180    use_draw_calls_for_texture_copy: bool,
1181    /// Number of pixels below which we prefer batched uploads.
1182    batched_upload_threshold: i32,
1183
1184    // HW or API capabilities
1185    capabilities: Capabilities,
1186
1187    color_formats: TextureFormatPair<ImageFormat>,
1188    bgra_formats: TextureFormatPair<gl::GLuint>,
1189    bgra_pixel_type: gl::GLuint,
1190    swizzle_settings: SwizzleSettings,
1191    depth_format: gl::GLuint,
1192
1193    /// Map from texture dimensions to shared depth buffers for render targets.
1194    ///
1195    /// Render targets often have the same width/height, so we can save memory
1196    /// by sharing these across targets.
1197    depth_targets: FastHashMap<DeviceIntSize, SharedDepthTarget>,
1198
1199    // debug
1200    inside_frame: bool,
1201    crash_annotator: Option<Box<dyn CrashAnnotator>>,
1202    annotate_draw_call_crashes: bool,
1203
1204    // resources
1205    resource_override_path: Option<PathBuf>,
1206
1207    /// Whether to use shaders that have been optimized at build time.
1208    use_optimized_shaders: bool,
1209
1210    max_texture_size: i32,
1211    cached_programs: Option<Rc<ProgramCache>>,
1212
1213    // Frame counter. This is used to map between CPU
1214    // frames and GPU frames.
1215    frame_id: GpuFrameId,
1216
1217    /// When to use glTexStorage*. We prefer this over glTexImage* because it
1218    /// guarantees that mipmaps won't be generated (which they otherwise are on
1219    /// some drivers, particularly ANGLE). However, it is not always supported
1220    /// at all, or for BGRA8 format. If it's not supported for the required
1221    /// format, we fall back to glTexImage*.
1222    texture_storage_usage: TexStorageUsage,
1223
1224    /// Required stride alignment for pixel transfers. This may be required for
1225    /// correctness reasons due to driver bugs, or for performance reasons to
1226    /// ensure we remain on the fast-path for transfers.
1227    required_pbo_stride: StrideAlignment,
1228
1229    /// Whether we must ensure the source strings passed to glShaderSource()
1230    /// are null-terminated, to work around driver bugs.
1231    requires_null_terminated_shader_source: bool,
1232
1233    /// Whether we must unbind any texture from GL_TEXTURE_EXTERNAL_OES before
1234    /// binding to GL_TEXTURE_2D, to work around an android emulator bug.
1235    requires_texture_external_unbind: bool,
1236
1237    ///
1238    is_software_webrender: bool,
1239
1240    // GL extensions
1241    extensions: Vec<String>,
1242
1243    /// Dumps the source of the shader with the given name
1244    dump_shader_source: Option<String>,
1245
1246    surface_origin_is_top_left: bool,
1247
1248    /// A debug boolean for tracking if the shader program has been set after
1249    /// a blend mode change.
1250    ///
1251    /// This is needed for compatibility with next-gen
1252    /// GPU APIs that switch states using "pipeline object" that bundles
1253    /// together the blending state with the shader.
1254    ///
1255    /// Having the constraint of always binding the shader last would allow
1256    /// us to have the "pipeline object" bound at that time. Without this
1257    /// constraint, we'd either have to eagerly bind the "pipeline object"
1258    /// on changing either the shader or the blend more, or lazily bind it
1259    /// at draw call time, neither of which is desirable.
1260    #[cfg(debug_assertions)]
1261    shader_is_ready: bool,
1262
1263    // count created/deleted textures to report in the profiler.
1264    pub textures_created: u32,
1265    pub textures_deleted: u32,
1266}
1267
1268/// Contains the parameters necessary to bind a draw target.
1269#[derive(Clone, Copy, Debug)]
1270pub enum DrawTarget {
1271    /// Use the device's default draw target, with the provided dimensions,
1272    /// which are used to set the viewport.
1273    Default {
1274        /// Target rectangle to draw.
1275        rect: FramebufferIntRect,
1276        /// Total size of the target.
1277        total_size: FramebufferIntSize,
1278        surface_origin_is_top_left: bool,
1279    },
1280    /// Use the provided texture.
1281    Texture {
1282        /// Size of the texture in pixels
1283        dimensions: DeviceIntSize,
1284        /// Whether to draw with the texture's associated depth target
1285        with_depth: bool,
1286        /// FBO that corresponds to the selected layer / depth mode
1287        fbo_id: FBOId,
1288        /// Native GL texture ID
1289        id: gl::GLuint,
1290        /// Native GL texture target
1291        target: gl::GLuint,
1292    },
1293    /// Use an FBO attached to an external texture.
1294    External {
1295        fbo: FBOId,
1296        size: FramebufferIntSize,
1297    },
1298    /// An OS compositor surface
1299    NativeSurface {
1300        offset: DeviceIntPoint,
1301        external_fbo_id: u32,
1302        dimensions: DeviceIntSize,
1303    },
1304}
1305
1306impl DrawTarget {
1307    pub fn new_default(size: DeviceIntSize, surface_origin_is_top_left: bool) -> Self {
1308        let total_size = device_size_as_framebuffer_size(size);
1309        DrawTarget::Default {
1310            rect: total_size.into(),
1311            total_size,
1312            surface_origin_is_top_left,
1313        }
1314    }
1315
1316    /// Returns true if this draw target corresponds to the default framebuffer.
1317    pub fn is_default(&self) -> bool {
1318        match *self {
1319            DrawTarget::Default {..} => true,
1320            _ => false,
1321        }
1322    }
1323
1324    pub fn from_texture(
1325        texture: &Texture,
1326        with_depth: bool,
1327    ) -> Self {
1328        let fbo_id = if with_depth {
1329            texture.fbo_with_depth.unwrap()
1330        } else {
1331            texture.fbo.unwrap()
1332        };
1333
1334        DrawTarget::Texture {
1335            dimensions: texture.get_dimensions(),
1336            fbo_id,
1337            with_depth,
1338            id: texture.id,
1339            target: texture.target,
1340        }
1341    }
1342
1343    /// Returns the dimensions of this draw-target.
1344    pub fn dimensions(&self) -> DeviceIntSize {
1345        match *self {
1346            DrawTarget::Default { total_size, .. } => total_size.cast_unit(),
1347            DrawTarget::Texture { dimensions, .. } => dimensions,
1348            DrawTarget::External { size, .. } => size.cast_unit(),
1349            DrawTarget::NativeSurface { dimensions, .. } => dimensions,
1350        }
1351    }
1352
1353    pub fn offset(&self) -> DeviceIntPoint {
1354        match *self {
1355            DrawTarget::Default { .. } |
1356            DrawTarget::Texture { .. } |
1357            DrawTarget::External { .. } => {
1358                DeviceIntPoint::zero()
1359            }
1360            DrawTarget::NativeSurface { offset, .. } => offset,
1361        }
1362    }
1363
1364    pub fn to_framebuffer_rect(&self, device_rect: DeviceIntRect) -> FramebufferIntRect {
1365        let mut fb_rect = device_rect_as_framebuffer_rect(&device_rect);
1366        match *self {
1367            DrawTarget::Default { ref rect, surface_origin_is_top_left, .. } => {
1368                // perform a Y-flip here
1369                if !surface_origin_is_top_left {
1370                    let w = fb_rect.width();
1371                    let h = fb_rect.height();
1372                    fb_rect.min.x = fb_rect.min.x + rect.min.x;
1373                    fb_rect.min.y = rect.max.y - fb_rect.max.y;
1374                    fb_rect.max.x = fb_rect.min.x + w;
1375                    fb_rect.max.y = fb_rect.min.y + h;
1376                }
1377            }
1378            DrawTarget::Texture { .. } | DrawTarget::External { .. } | DrawTarget::NativeSurface { .. } => (),
1379        }
1380        fb_rect
1381    }
1382
1383    pub fn surface_origin_is_top_left(&self) -> bool {
1384        match *self {
1385            DrawTarget::Default { surface_origin_is_top_left, .. } => surface_origin_is_top_left,
1386            DrawTarget::Texture { .. } | DrawTarget::External { .. } | DrawTarget::NativeSurface { .. } => true,
1387        }
1388    }
1389
1390    /// Given a scissor rect, convert it to the right coordinate space
1391    /// depending on the draw target kind. If no scissor rect was supplied,
1392    /// returns a scissor rect that encloses the entire render target.
1393    pub fn build_scissor_rect(
1394        &self,
1395        scissor_rect: Option<DeviceIntRect>,
1396    ) -> FramebufferIntRect {
1397        let dimensions = self.dimensions();
1398
1399        match scissor_rect {
1400            Some(scissor_rect) => match *self {
1401                DrawTarget::Default { ref rect, .. } => {
1402                    self.to_framebuffer_rect(scissor_rect)
1403                        .intersection(rect)
1404                        .unwrap_or_else(FramebufferIntRect::zero)
1405                }
1406                DrawTarget::NativeSurface { offset, .. } => {
1407                    device_rect_as_framebuffer_rect(&scissor_rect.translate(offset.to_vector()))
1408                }
1409                DrawTarget::Texture { .. } | DrawTarget::External { .. } => {
1410                    device_rect_as_framebuffer_rect(&scissor_rect)
1411                }
1412            }
1413            None => {
1414                FramebufferIntRect::from_size(
1415                    device_size_as_framebuffer_size(dimensions),
1416                )
1417            }
1418        }
1419    }
1420}
1421
1422/// Contains the parameters necessary to bind a texture-backed read target.
1423#[derive(Clone, Copy, Debug)]
1424pub enum ReadTarget {
1425    /// Use the device's default draw target.
1426    Default,
1427    /// Use the provided texture,
1428    Texture {
1429        /// ID of the FBO to read from.
1430        fbo_id: FBOId,
1431    },
1432    /// Use an FBO attached to an external texture.
1433    External {
1434        fbo: FBOId,
1435    },
1436    /// An FBO bound to a native (OS compositor) surface
1437    NativeSurface {
1438        fbo_id: FBOId,
1439        offset: DeviceIntPoint,
1440    },
1441}
1442
1443impl ReadTarget {
1444    pub fn from_texture(
1445        texture: &Texture,
1446    ) -> Self {
1447        ReadTarget::Texture {
1448            fbo_id: texture.fbo.unwrap(),
1449        }
1450    }
1451
1452    fn offset(&self) -> DeviceIntPoint {
1453        match *self {
1454            ReadTarget::Default |
1455            ReadTarget::Texture { .. } |
1456            ReadTarget::External { .. } => {
1457                DeviceIntPoint::zero()
1458            }
1459
1460            ReadTarget::NativeSurface { offset, .. } => {
1461                offset
1462            }
1463        }
1464    }
1465}
1466
1467impl From<DrawTarget> for ReadTarget {
1468    fn from(t: DrawTarget) -> Self {
1469        match t {
1470            DrawTarget::Default { .. } => {
1471                ReadTarget::Default
1472            }
1473            DrawTarget::NativeSurface { external_fbo_id, offset, .. } => {
1474                ReadTarget::NativeSurface {
1475                    fbo_id: FBOId(external_fbo_id),
1476                    offset,
1477                }
1478            }
1479            DrawTarget::Texture { fbo_id, .. } => {
1480                ReadTarget::Texture { fbo_id }
1481            }
1482            DrawTarget::External { fbo, .. } => {
1483                ReadTarget::External { fbo }
1484            }
1485        }
1486    }
1487}
1488
1489/// Parses the major, release, and patch versions from a GL_VERSION string on
1490/// Mali devices. For example, for the version string
1491/// "OpenGL ES 3.2 v1.r36p0-01eac0.28ab3a577f105e026887e2b4c93552fb" this
1492/// returns Some((1, 36, 0)). Returns None if the version cannot be parsed.
1493fn parse_mali_version(version_string: &str) -> Option<(u32, u32, u32)> {
1494    let (_prefix, version_string) = version_string.split_once("v")?;
1495    let (v_str, version_string) = version_string.split_once(".r")?;
1496    let v = v_str.parse().ok()?;
1497
1498    let (r_str, version_string) = version_string.split_once("p")?;
1499    let r = r_str.parse().ok()?;
1500
1501    // Not all devices have the trailing string following the "p" number.
1502    let (p_str, _) = version_string.split_once("-").unwrap_or((version_string, ""));
1503    let p = p_str.parse().ok()?;
1504
1505    Some((v, r, p))
1506}
1507
1508/// Returns whether this GPU belongs to the Mali Midgard family
1509fn is_mali_midgard(renderer_name: &str) -> bool {
1510    renderer_name.starts_with("Mali-T")
1511}
1512
1513/// Returns whether this GPU belongs to the Mali Bifrost family
1514fn is_mali_bifrost(renderer_name: &str) -> bool {
1515    renderer_name == "Mali-G31"
1516        || renderer_name == "Mali-G51"
1517        || renderer_name == "Mali-G71"
1518        || renderer_name == "Mali-G52"
1519        || renderer_name == "Mali-G72"
1520        || renderer_name == "Mali-G76"
1521}
1522
1523/// Returns whether this GPU belongs to the Mali Valhall family
1524fn is_mali_valhall(renderer_name: &str) -> bool {
1525    // As new Valhall GPUs may be released in the future we match all Mali-G models, apart from
1526    // Bifrost models (of which we don't expect any new ones to be released)
1527    renderer_name.starts_with("Mali-G") && !is_mali_bifrost(renderer_name)
1528}
1529#[inline(never)]
1530fn gl_error_string(code: u32) -> &'static str {
1531    match code {
1532        gl::INVALID_ENUM => "GL_INVALID_ENUM",
1533        gl::INVALID_VALUE => "GL_INVALID_VALUE",
1534        gl::INVALID_OPERATION => "GL_INVALID_OPERATION",
1535        gl::STACK_OVERFLOW => "GL_STACK_OVERFLOW",
1536        gl::STACK_UNDERFLOW => "GL_STACK_UNDERFLOW",
1537        gl::OUT_OF_MEMORY => "GL_OUT_OF_MEMORY",
1538        gl::INVALID_FRAMEBUFFER_OPERATION => "GL_INVALID_FRAMEBUFFER_OPERATION",
1539        0x507 => "GL_CONTEXT_LOST",
1540        _ => "(unknown error code)",
1541    }
1542}
1543
1544impl Device {
1545    pub fn new(
1546        mut gl: Rc<dyn gl::Gl>,
1547        crash_annotator: Option<Box<dyn CrashAnnotator>>,
1548        resource_override_path: Option<PathBuf>,
1549        use_optimized_shaders: bool,
1550        upload_method: UploadMethod,
1551        batched_upload_threshold: i32,
1552        cached_programs: Option<Rc<ProgramCache>>,
1553        allow_texture_storage_support: bool,
1554        allow_texture_swizzling: bool,
1555        dump_shader_source: Option<String>,
1556        surface_origin_is_top_left: bool,
1557        panic_on_gl_error: bool,
1558    ) -> Device {
1559        let mut max_texture_size = [0];
1560        unsafe {
1561            gl.get_integer_v(gl::MAX_TEXTURE_SIZE, &mut max_texture_size);
1562        }
1563
1564        // We cap the max texture size at 16384. Some hardware report higher
1565        // capabilities but get very unstable with very large textures.
1566        // Bug 1702494 tracks re-evaluating this cap.
1567        let max_texture_size = max_texture_size[0].min(16384);
1568
1569        let renderer_name = gl.get_string(gl::RENDERER);
1570        info!("Renderer: {}", renderer_name);
1571        let version_string = gl.get_string(gl::VERSION);
1572        info!("Version: {}", version_string);
1573        info!("Max texture size: {}", max_texture_size);
1574
1575        let mut extension_count = [0];
1576        unsafe {
1577            gl.get_integer_v(gl::NUM_EXTENSIONS, &mut extension_count);
1578        }
1579        let extension_count = extension_count[0] as gl::GLuint;
1580        let mut extensions = Vec::new();
1581        for i in 0 .. extension_count {
1582            extensions.push(gl.get_string_i(gl::EXTENSIONS, i));
1583        }
1584
1585        // We block this on Mali Valhall GPUs as the extension's functions always return
1586        // GL_OUT_OF_MEMORY, causing us to panic in debug builds.
1587        let supports_khr_debug = supports_extension(&extensions, "GL_KHR_debug")
1588            && !is_mali_valhall(&renderer_name);
1589
1590        // On debug builds, assert that each GL call is error-free. We don't do
1591        // this on release builds because the synchronous call can stall the
1592        // pipeline.
1593        if panic_on_gl_error || cfg!(debug_assertions) {
1594            gl = gl::ErrorReactingGl::wrap(gl, move |gl, name, code| {
1595                if supports_khr_debug {
1596                    Self::log_driver_messages(gl);
1597                }
1598                let err_name = gl_error_string(code);
1599                error!("Caught GL error 0x{:x} {} at {}", code, err_name, name);
1600                panic!("Caught GL error 0x{:x} {} at {}", code, err_name, name);
1601            });
1602        }
1603
1604        if supports_extension(&extensions, "GL_ANGLE_provoking_vertex") {
1605            gl.provoking_vertex_angle(gl::FIRST_VERTEX_CONVENTION);
1606        }
1607
1608        let supports_texture_usage = supports_extension(&extensions, "GL_ANGLE_texture_usage");
1609
1610        // Our common-case image data in Firefox is BGRA, so we make an effort
1611        // to use BGRA as the internal texture storage format to avoid the need
1612        // to swizzle during upload. Currently we only do this on GLES (and thus
1613        // for Windows, via ANGLE).
1614        //
1615        // On Mac, Apple docs [1] claim that BGRA is a more efficient internal
1616        // format, but they don't support it with glTextureStorage. As a workaround,
1617        // we pretend that it's RGBA8 for the purposes of texture transfers,
1618        // but swizzle R with B for the texture sampling.
1619        //
1620        // We also need our internal format types to be sized, since glTexStorage*
1621        // will reject non-sized internal format types.
1622        //
1623        // Unfortunately, with GL_EXT_texture_format_BGRA8888, BGRA8 is not a
1624        // valid internal format (for glTexImage* or glTexStorage*) unless
1625        // GL_EXT_texture_storage is also available [2][3], which is usually
1626        // not the case on GLES 3 as the latter's functionality has been
1627        // included by default but the former has not been updated.
1628        // The extension is available on ANGLE, but on Android this usually
1629        // means we must fall back to using unsized BGRA and glTexImage*.
1630        //
1631        // Overall, we have the following factors in play when choosing the formats:
1632        //   - with glTexStorage, the internal format needs to match the external format,
1633        //     or the driver would have to do the conversion, which is slow
1634        //   - on desktop GL, there is no BGRA internal format. However, initializing
1635        //     the textures with glTexImage as RGBA appears to use BGRA internally,
1636        //     preferring BGRA external data [4].
1637        //   - when glTexStorage + BGRA internal format is not supported,
1638        //     and the external data is BGRA, we have the following options:
1639        //       1. use glTexImage with RGBA internal format, this costs us VRAM for mipmaps
1640        //       2. use glTexStorage with RGBA internal format, this costs us the conversion by the driver
1641        //       3. pretend we are uploading RGBA and set up the swizzling of the texture unit - this costs us batch breaks
1642        //
1643        // [1] https://developer.apple.com/library/archive/documentation/
1644        //     GraphicsImaging/Conceptual/OpenGL-MacProgGuide/opengl_texturedata/
1645        //     opengl_texturedata.html#//apple_ref/doc/uid/TP40001987-CH407-SW22
1646        // [2] https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_texture_format_BGRA8888.txt
1647        // [3] https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_texture_storage.txt
1648        // [4] http://http.download.nvidia.com/developer/Papers/2005/Fast_Texture_Transfers/Fast_Texture_Transfers.pdf
1649
1650        // On the android emulator glTexImage fails to create textures larger than 3379.
1651        // So we must use glTexStorage instead. See bug 1591436.
1652        let is_emulator = renderer_name.starts_with("Android Emulator");
1653        let avoid_tex_image = is_emulator;
1654        let mut gl_version = [0; 2];
1655        unsafe {
1656            gl.get_integer_v(gl::MAJOR_VERSION, &mut gl_version[0..1]);
1657            gl.get_integer_v(gl::MINOR_VERSION, &mut gl_version[1..2]);
1658        }
1659        info!("GL context {:?} {}.{}", gl.get_type(), gl_version[0], gl_version[1]);
1660
1661        // We block texture storage on mac because it doesn't support BGRA
1662        let supports_texture_storage = allow_texture_storage_support && !cfg!(target_os = "macos") &&
1663            match gl.get_type() {
1664                gl::GlType::Gl => supports_extension(&extensions, "GL_ARB_texture_storage"),
1665                gl::GlType::Gles => true,
1666            };
1667
1668        // The GL_EXT_texture_format_BGRA8888 extension allows us to use BGRA as an internal format
1669        // with glTexImage on GLES. However, we can only use BGRA8 as an internal format for
1670        // glTexStorage when GL_EXT_texture_storage is also explicitly supported. This is because
1671        // glTexStorage was added in GLES 3, but GL_EXT_texture_format_BGRA8888 was written against
1672        // GLES 2 and GL_EXT_texture_storage.
1673        // To complicate things even further, some Intel devices claim to support both extensions
1674        // but in practice do not allow BGRA to be used with glTexStorage.
1675        let supports_gles_bgra = supports_extension(&extensions, "GL_EXT_texture_format_BGRA8888");
1676        let supports_texture_storage_with_gles_bgra = supports_gles_bgra
1677            && supports_extension(&extensions, "GL_EXT_texture_storage")
1678            && !renderer_name.starts_with("Intel(R) HD Graphics for BayTrail")
1679            && !renderer_name.starts_with("Intel(R) HD Graphics for Atom(TM) x5/x7");
1680
1681        let supports_texture_swizzle = allow_texture_swizzling &&
1682            match gl.get_type() {
1683                // see https://www.g-truc.net/post-0734.html
1684                gl::GlType::Gl => gl_version >= [3, 3] ||
1685                    supports_extension(&extensions, "GL_ARB_texture_swizzle"),
1686                gl::GlType::Gles => true,
1687            };
1688
1689        let (color_formats, bgra_formats, bgra_pixel_type, bgra8_sampling_swizzle, texture_storage_usage) = match gl.get_type() {
1690            // There is `glTexStorage`, use it and expect RGBA on the input.
1691            gl::GlType::Gl if supports_texture_storage && supports_texture_swizzle => (
1692                TextureFormatPair::from(ImageFormat::RGBA8),
1693                TextureFormatPair { internal: gl::RGBA8, external: gl::RGBA },
1694                gl::UNSIGNED_BYTE,
1695                Swizzle::Bgra, // pretend it's RGBA, rely on swizzling
1696                TexStorageUsage::Always
1697            ),
1698            // There is no `glTexStorage`, upload as `glTexImage` with BGRA input.
1699            gl::GlType::Gl => (
1700                TextureFormatPair { internal: ImageFormat::BGRA8, external: ImageFormat::BGRA8 },
1701                TextureFormatPair { internal: gl::RGBA, external: gl::BGRA },
1702                gl::UNSIGNED_INT_8_8_8_8_REV,
1703                Swizzle::Rgba, // converted on uploads by the driver, no swizzling needed
1704                TexStorageUsage::Never
1705            ),
1706            // glTexStorage is always supported in GLES 3, but because the GL_EXT_texture_storage
1707            // extension is supported we can use glTexStorage with BGRA8 as the internal format.
1708            // Prefer BGRA textures over RGBA.
1709            gl::GlType::Gles if supports_texture_storage_with_gles_bgra => (
1710                TextureFormatPair::from(ImageFormat::BGRA8),
1711                TextureFormatPair { internal: gl::BGRA8_EXT, external: gl::BGRA_EXT },
1712                gl::UNSIGNED_BYTE,
1713                Swizzle::Rgba, // no conversion needed
1714                TexStorageUsage::Always,
1715            ),
1716            // BGRA is not supported as an internal format with glTexStorage, therefore we will
1717            // use RGBA textures instead and pretend BGRA data is RGBA when uploading.
1718            // The swizzling will happen at the texture unit.
1719            gl::GlType::Gles if supports_texture_swizzle => (
1720                TextureFormatPair::from(ImageFormat::RGBA8),
1721                TextureFormatPair { internal: gl::RGBA8, external: gl::RGBA },
1722                gl::UNSIGNED_BYTE,
1723                Swizzle::Bgra, // pretend it's RGBA, rely on swizzling
1724                TexStorageUsage::Always,
1725            ),
1726            // BGRA is not supported as an internal format with glTexStorage, and we cannot use
1727            // swizzling either. Therefore prefer BGRA textures over RGBA, but use glTexImage
1728            // to initialize BGRA textures. glTexStorage can still be used for other formats.
1729            gl::GlType::Gles if supports_gles_bgra && !avoid_tex_image => (
1730                TextureFormatPair::from(ImageFormat::BGRA8),
1731                TextureFormatPair::from(gl::BGRA_EXT),
1732                gl::UNSIGNED_BYTE,
1733                Swizzle::Rgba, // no conversion needed
1734                TexStorageUsage::NonBGRA8,
1735            ),
1736            // Neither BGRA or swizzling are supported. GLES does not allow format conversion
1737            // during upload so we must use RGBA textures and pretend BGRA data is RGBA when
1738            // uploading. Images may be rendered incorrectly as a result.
1739            gl::GlType::Gles => {
1740                warn!("Neither BGRA or texture swizzling are supported. Images may be rendered incorrectly.");
1741                (
1742                    TextureFormatPair::from(ImageFormat::RGBA8),
1743                    TextureFormatPair { internal: gl::RGBA8, external: gl::RGBA },
1744                    gl::UNSIGNED_BYTE,
1745                    Swizzle::Rgba,
1746                    TexStorageUsage::Always,
1747                )
1748            }
1749        };
1750
1751        let is_software_webrender = renderer_name.starts_with("Software WebRender");
1752        let upload_method = if is_software_webrender {
1753            // Uploads in SWGL generally reduce to simple memory copies.
1754            UploadMethod::Immediate
1755        } else {
1756            upload_method
1757        };
1758        // Prefer 24-bit depth format. While 16-bit depth also works, it may exhaust depth ids easily.
1759        let depth_format = gl::DEPTH_COMPONENT24;
1760
1761        info!("GL texture cache {:?}, bgra {:?} swizzle {:?}, texture storage {:?}, depth {:?}",
1762            color_formats, bgra_formats, bgra8_sampling_swizzle, texture_storage_usage, depth_format);
1763
1764        // On Mali-T devices glCopyImageSubData appears to stall the pipeline until any pending
1765        // renders to the source texture have completed. On Mali-G, it has been observed to
1766        // indefinitely hang in some circumstances. Using an alternative such as glBlitFramebuffer
1767        // is preferable on such devices, so pretend we don't support glCopyImageSubData.
1768        // See bugs 1669494 and 1677757.
1769        let supports_copy_image_sub_data = if renderer_name.starts_with("Mali") {
1770            false
1771        } else {
1772            supports_extension(&extensions, "GL_EXT_copy_image") ||
1773            supports_extension(&extensions, "GL_ARB_copy_image")
1774        };
1775
1776        // We have seen crashes on x86 PowerVR Rogue G6430 devices during GPU cache
1777        // updates using the scatter shader. It seems likely that GL_EXT_color_buffer_float
1778        // is broken. See bug 1709408.
1779        let is_x86_powervr_rogue_g6430 = renderer_name.starts_with("PowerVR Rogue G6430")
1780            && cfg!(target_arch = "x86");
1781        let supports_color_buffer_float = match gl.get_type() {
1782            gl::GlType::Gl => true,
1783            gl::GlType::Gles if is_x86_powervr_rogue_g6430 => false,
1784            gl::GlType::Gles => supports_extension(&extensions, "GL_EXT_color_buffer_float"),
1785        };
1786
1787        let is_adreno = renderer_name.starts_with("Adreno");
1788
1789        // There appears to be a driver bug on older versions of the Adreno
1790        // driver which prevents usage of persistenly mapped buffers.
1791        // See bugs 1678585 and 1683936.
1792        // TODO: only disable feature for affected driver versions.
1793        let supports_buffer_storage = if is_adreno {
1794            false
1795        } else {
1796            supports_extension(&extensions, "GL_EXT_buffer_storage") ||
1797            supports_extension(&extensions, "GL_ARB_buffer_storage")
1798        };
1799
1800        // KHR_blend_equation_advanced renders incorrectly on Adreno
1801        // devices. This has only been confirmed up to Adreno 5xx, and has been
1802        // fixed for Android 9, so this condition could be made more specific.
1803        let supports_advanced_blend_equation =
1804            supports_extension(&extensions, "GL_KHR_blend_equation_advanced") &&
1805            !is_adreno;
1806
1807        let supports_dual_source_blending = match gl.get_type() {
1808            gl::GlType::Gl => supports_extension(&extensions,"GL_ARB_blend_func_extended") &&
1809                supports_extension(&extensions,"GL_ARB_explicit_attrib_location"),
1810            gl::GlType::Gles => supports_extension(&extensions,"GL_EXT_blend_func_extended"),
1811        };
1812
1813        // Software webrender relies on the unoptimized shader source.
1814        let use_optimized_shaders = use_optimized_shaders && !is_software_webrender;
1815
1816        // On the android emulator, and possibly some Mali devices, glShaderSource
1817        // can crash if the source strings are not null-terminated.
1818        // See bug 1591945 and bug 1799722.
1819        // Likewise on Lenovo devices with Adreno 750 GPUs we have seen glCompileShader
1820        // failures and subsequent crashes due to glGetShaderInfoLog returning invalid
1821        // UTF-8. See bug 2014925.
1822        let requires_null_terminated_shader_source = is_emulator || renderer_name == "Mali-T628"
1823            || renderer_name == "Mali-T720" || renderer_name == "Mali-T760"
1824            || renderer_name == "Mali-G57" || renderer_name == "Adreno (TM) 750";
1825
1826        // The android emulator gets confused if you don't explicitly unbind any texture
1827        // from GL_TEXTURE_EXTERNAL_OES before binding another to GL_TEXTURE_2D. See bug 1636085.
1828        let requires_texture_external_unbind = is_emulator;
1829
1830        let is_macos = cfg!(target_os = "macos");
1831             //  && renderer_name.starts_with("AMD");
1832             //  (XXX: we apply this restriction to all GPUs to handle switching)
1833
1834        let is_windows_angle = cfg!(target_os = "windows")
1835            && renderer_name.starts_with("ANGLE");
1836        let is_adreno_3xx = renderer_name.starts_with("Adreno (TM) 3");
1837
1838        // Some GPUs require the stride of the data during texture uploads to be
1839        // aligned to certain requirements, either for correctness or performance
1840        // reasons.
1841        let required_pbo_stride = if is_adreno_3xx {
1842            // On Adreno 3xx, alignments of < 128 bytes can result in corrupted
1843            // glyphs. See bug 1696039.
1844            StrideAlignment::Bytes(NonZeroUsize::new(128).unwrap())
1845        } else if is_adreno {
1846            // On later Adreno devices it must be a multiple of 64 *pixels* to
1847            // hit the fast path, meaning value in bytes varies with the texture
1848            // format. This is purely an optimization.
1849            StrideAlignment::Pixels(NonZeroUsize::new(64).unwrap())
1850        } else if is_macos {
1851            // On AMD Mac, it must always be a multiple of 256 bytes.
1852            // We apply this restriction to all GPUs to handle switching
1853            StrideAlignment::Bytes(NonZeroUsize::new(256).unwrap())
1854        } else if is_windows_angle {
1855            // On ANGLE-on-D3D, PBO texture uploads get incorrectly truncated
1856            // if the stride is greater than the width * bpp.
1857            StrideAlignment::Bytes(NonZeroUsize::new(1).unwrap())
1858        } else {
1859            // Other platforms may have similar requirements and should be added
1860            // here. The default value should be 4 bytes.
1861            StrideAlignment::Bytes(NonZeroUsize::new(4).unwrap())
1862        };
1863
1864        // On AMD Macs there is a driver bug which causes some texture uploads
1865        // from a non-zero offset within a PBO to fail. See bug 1603783.
1866        let supports_nonzero_pbo_offsets = !is_macos;
1867
1868        // We have encountered several issues when only partially updating render targets on a
1869        // variety of Mali GPUs. As a precaution avoid doing so on all Midgard and Bifrost GPUs.
1870        // Valhall (eg Mali-Gx7 onwards) appears to be unaffected. See bug 1691955, bug 1558374,
1871        // and bug 1663355.
1872        // We have Additionally encountered issues on PowerVR D-Series. See bug 2005312.
1873        let supports_render_target_partial_update = !is_mali_midgard(&renderer_name)
1874            && !is_mali_bifrost(&renderer_name)
1875            && !renderer_name.starts_with("PowerVR D-Series");
1876
1877        let supports_shader_storage_object = match gl.get_type() {
1878            // see https://www.g-truc.net/post-0734.html
1879            gl::GlType::Gl => supports_extension(&extensions, "GL_ARB_shader_storage_buffer_object"),
1880            gl::GlType::Gles => gl_version >= [3, 1],
1881        };
1882
1883        // SWGL uses swgl_clipMask() instead of implementing clip-masking in shaders.
1884        // This allows certain shaders to potentially bypass the more expensive alpha-
1885        // pass variants if they know the alpha-pass was only required to deal with
1886        // clip-masking.
1887        let uses_native_clip_mask = is_software_webrender;
1888
1889        // SWGL uses swgl_antiAlias() instead of implementing anti-aliasing in shaders.
1890        // As above, this allows bypassing certain alpha-pass variants.
1891        let uses_native_antialiasing = is_software_webrender;
1892
1893        // If running on android with a mesa driver (eg intel chromebooks), parse the mesa version.
1894        let mut android_mesa_version = None;
1895        if cfg!(target_os = "android") && renderer_name.starts_with("Mesa") {
1896            if let Some((_, mesa_version)) = version_string.split_once("Mesa ") {
1897                if let Some((major_str, _)) = mesa_version.split_once(".") {
1898                    if let Ok(major) = major_str.parse::<i32>() {
1899                        android_mesa_version = Some(major);
1900                    }
1901                }
1902            }
1903        }
1904
1905        // If the device supports OES_EGL_image_external_essl3 we can use it to render
1906        // external images. If not, we must use the ESSL 1.0 OES_EGL_image_external
1907        // extension instead.
1908        // Mesa versions prior to 20.0 do not implement textureSize(samplerExternalOES),
1909        // so we must use the fallback path.
1910        let supports_image_external_essl3 = match android_mesa_version {
1911            Some(major) if major < 20 => false,
1912            _ => supports_extension(&extensions, "GL_OES_EGL_image_external_essl3"),
1913        };
1914
1915        let mut requires_batched_texture_uploads = None;
1916        if is_software_webrender {
1917            // No benefit to batching texture uploads with swgl.
1918            requires_batched_texture_uploads = Some(false);
1919        } else if renderer_name.starts_with("Mali-G") {
1920            // On Mali-Gxx the driver really struggles with many small texture uploads,
1921            // and handles fewer, larger uploads better.
1922            requires_batched_texture_uploads = Some(true);
1923        }
1924
1925        // On Mali-Txxx devices we have observed crashes during draw calls when rendering
1926        // to an alpha target immediately after using glClear to clear regions of it.
1927        // Using a shader to clear the regions avoids the crash. See bug 1638593.
1928        // On Adreno 510 devices we have seen garbage being used as masks when clearing
1929        // alpha targets with glClear. Using quads to clear avoids this. See bug 1941154.
1930        let is_adreno_510 = renderer_name.starts_with("Adreno (TM) 510");
1931        let supports_alpha_target_clears = !is_mali_midgard(&renderer_name) && !is_adreno_510;
1932
1933        // On Adreno 4xx devices with older drivers we have seen render tasks to alpha targets have
1934        // no effect unless the target is fully cleared prior to rendering. See bug 1714227.
1935        let is_adreno_4xx = renderer_name.starts_with("Adreno (TM) 4");
1936        let requires_alpha_target_full_clear = is_adreno_4xx;
1937
1938        // Testing on Intel and nVidia GPUs, as well as software webrender, showed large performance
1939        // wins applying a scissor rect when clearing render targets. Assume this is the best
1940        // default. On mobile GPUs, however, it can be much more efficient to clear the entire
1941        // render target. For now, enable the scissor everywhere except Android hardware
1942        // webrender. We can tweak this further if needs be.
1943        let prefers_clear_scissor = !cfg!(target_os = "android") || is_software_webrender;
1944
1945        let mut supports_render_target_invalidate = true;
1946
1947        // On PowerVR Rogue devices we have seen that invalidating render targets after we are done
1948        // with them can incorrectly cause pending renders to be written to different targets
1949        // instead. See bug 1719345.
1950        let is_powervr_rogue = renderer_name.starts_with("PowerVR Rogue");
1951        if is_powervr_rogue {
1952            supports_render_target_invalidate = false;
1953        }
1954
1955        // On Mali Valhall devices with a driver version v1.r36p0 we have seen that invalidating
1956        // render targets can result in image corruption, perhaps due to subsequent reuses of the
1957        // render target not correctly reinitializing them to a valid state. See bug 1787520.
1958        if is_mali_valhall(&renderer_name) {
1959            match parse_mali_version(&version_string) {
1960                Some(version) if version >= (1, 36, 0) => supports_render_target_invalidate = false,
1961                _ => {}
1962            }
1963        }
1964
1965        // On Linux we we have seen uploads to R8 format textures result in
1966        // corruption on some AMD cards.
1967        // See https://bugzilla.mozilla.org/show_bug.cgi?id=1687554#c13
1968        let supports_r8_texture_upload = if cfg!(target_os = "linux")
1969            && renderer_name.starts_with("AMD Radeon RX")
1970        {
1971            false
1972        } else {
1973            true
1974        };
1975
1976        let supports_qcom_tiled_rendering = if is_adreno && version_string.contains("V@0490") {
1977            // We have encountered rendering errors on a variety of Adreno GPUs specifically on
1978            // driver version V@0490, so block this extension on that driver version. See bug 1828248.
1979            false
1980        } else if renderer_name == "Adreno (TM) 308" {
1981            // And specifically on Areno 308 GPUs we have encountered rendering errors on driver
1982            // versions V@331, V@415, and V@0502. We presume this therefore affects all driver
1983            // versions. See bug 1843749 and bug 1847319.
1984            false
1985        } else {
1986            supports_extension(&extensions, "GL_QCOM_tiled_rendering")
1987        };
1988
1989        // On some Adreno 3xx devices the vertex array object must be unbound and rebound after
1990        // an attached buffer has been orphaned.
1991        let requires_vao_rebind_after_orphaning = is_adreno_3xx;
1992
1993        Device {
1994            gl,
1995            base_gl: None,
1996            crash_annotator,
1997            annotate_draw_call_crashes: false,
1998            resource_override_path,
1999            use_optimized_shaders,
2000            upload_method,
2001            use_batched_texture_uploads: requires_batched_texture_uploads.unwrap_or(false),
2002            use_draw_calls_for_texture_copy: false,
2003            batched_upload_threshold,
2004
2005            inside_frame: false,
2006
2007            capabilities: Capabilities {
2008                supports_multisampling: false, //TODO
2009                supports_copy_image_sub_data,
2010                supports_color_buffer_float,
2011                supports_buffer_storage,
2012                supports_advanced_blend_equation,
2013                supports_dual_source_blending,
2014                supports_khr_debug,
2015                supports_texture_swizzle,
2016                supports_nonzero_pbo_offsets,
2017                supports_texture_usage,
2018                supports_render_target_partial_update,
2019                supports_shader_storage_object,
2020                requires_batched_texture_uploads,
2021                supports_alpha_target_clears,
2022                requires_alpha_target_full_clear,
2023                prefers_clear_scissor,
2024                supports_render_target_invalidate,
2025                supports_r8_texture_upload,
2026                supports_qcom_tiled_rendering,
2027                uses_native_clip_mask,
2028                uses_native_antialiasing,
2029                supports_image_external_essl3,
2030                requires_vao_rebind_after_orphaning,
2031                renderer_name,
2032            },
2033
2034            color_formats,
2035            bgra_formats,
2036            bgra_pixel_type,
2037            swizzle_settings: SwizzleSettings {
2038                bgra8_sampling_swizzle,
2039            },
2040            depth_format,
2041
2042            depth_targets: FastHashMap::default(),
2043
2044            bound_textures: [0; 16],
2045            bound_program: 0,
2046            bound_program_name: Rc::new(std::ffi::CString::new("").unwrap()),
2047            bound_vao: 0,
2048            bound_read_fbo: (FBOId(0), DeviceIntPoint::zero()),
2049            bound_draw_fbo: FBOId(0),
2050            default_read_fbo: FBOId(0),
2051            default_draw_fbo: FBOId(0),
2052
2053            depth_available: true,
2054
2055            max_texture_size,
2056            cached_programs,
2057            frame_id: GpuFrameId(0),
2058            extensions,
2059            texture_storage_usage,
2060            requires_null_terminated_shader_source,
2061            requires_texture_external_unbind,
2062            is_software_webrender,
2063            required_pbo_stride,
2064            dump_shader_source,
2065            surface_origin_is_top_left,
2066
2067            #[cfg(debug_assertions)]
2068            shader_is_ready: false,
2069
2070            textures_created: 0,
2071            textures_deleted: 0,
2072        }
2073    }
2074
2075    pub fn gl(&self) -> &dyn gl::Gl {
2076        &*self.gl
2077    }
2078
2079    pub fn rc_gl(&self) -> &Rc<dyn gl::Gl> {
2080        &self.gl
2081    }
2082
2083    pub fn set_parameter(&mut self, param: &Parameter) {
2084        match param {
2085            Parameter::Bool(BoolParameter::PboUploads, enabled) => {
2086                if !self.is_software_webrender {
2087                    self.upload_method = if *enabled {
2088                        UploadMethod::PixelBuffer(crate::ONE_TIME_USAGE_HINT)
2089                    } else {
2090                        UploadMethod::Immediate
2091                    };
2092                }
2093            }
2094            Parameter::Bool(BoolParameter::BatchedUploads, enabled) => {
2095                if self.capabilities.requires_batched_texture_uploads.is_none() {
2096                    self.use_batched_texture_uploads = *enabled;
2097                }
2098            }
2099            Parameter::Bool(BoolParameter::DrawCallsForTextureCopy, enabled) => {
2100                self.use_draw_calls_for_texture_copy = *enabled;
2101            }
2102            Parameter::Int(IntParameter::BatchedUploadThreshold, threshold) => {
2103                self.batched_upload_threshold = *threshold;
2104            }
2105            _ => {}
2106        }
2107    }
2108
2109    /// Ensures that the maximum texture size is less than or equal to the
2110    /// provided value. If the provided value is less than the value supported
2111    /// by the driver, the latter is used.
2112    pub fn clamp_max_texture_size(&mut self, size: i32) {
2113        self.max_texture_size = self.max_texture_size.min(size);
2114    }
2115
2116    /// Returns the limit on texture dimensions (width or height).
2117    pub fn max_texture_size(&self) -> i32 {
2118        self.max_texture_size
2119    }
2120
2121    pub fn surface_origin_is_top_left(&self) -> bool {
2122        self.surface_origin_is_top_left
2123    }
2124
2125    pub fn get_capabilities(&self) -> &Capabilities {
2126        &self.capabilities
2127    }
2128
2129    pub fn preferred_color_formats(&self) -> TextureFormatPair<ImageFormat> {
2130        self.color_formats.clone()
2131    }
2132
2133    pub fn swizzle_settings(&self) -> Option<SwizzleSettings> {
2134        if self.capabilities.supports_texture_swizzle {
2135            Some(self.swizzle_settings)
2136        } else {
2137            None
2138        }
2139    }
2140
2141    pub fn depth_bits(&self) -> i32 {
2142        match self.depth_format {
2143            gl::DEPTH_COMPONENT16 => 16,
2144            gl::DEPTH_COMPONENT24 => 24,
2145            _ => panic!("Unknown depth format {:?}", self.depth_format),
2146        }
2147    }
2148
2149    // See gpu_types.rs where we declare the number of possible documents and
2150    // number of items per document. This should match up with that.
2151    pub fn max_depth_ids(&self) -> i32 {
2152        return 1 << (self.depth_bits() - RESERVE_DEPTH_BITS);
2153    }
2154
2155    pub fn ortho_near_plane(&self) -> f32 {
2156        return -self.max_depth_ids() as f32;
2157    }
2158
2159    pub fn ortho_far_plane(&self) -> f32 {
2160        return (self.max_depth_ids() - 1) as f32;
2161    }
2162
2163    pub fn required_pbo_stride(&self) -> StrideAlignment {
2164        self.required_pbo_stride
2165    }
2166
2167    pub fn upload_method(&self) -> &UploadMethod {
2168        &self.upload_method
2169    }
2170
2171    pub fn use_batched_texture_uploads(&self) -> bool {
2172        self.use_batched_texture_uploads
2173    }
2174
2175    pub fn use_draw_calls_for_texture_copy(&self) -> bool {
2176        self.use_draw_calls_for_texture_copy
2177    }
2178
2179    pub fn batched_upload_threshold(&self) -> i32 {
2180        self.batched_upload_threshold
2181    }
2182
2183    pub fn reset_state(&mut self) {
2184        for i in 0 .. self.bound_textures.len() {
2185            self.bound_textures[i] = 0;
2186            self.gl.active_texture(gl::TEXTURE0 + i as gl::GLuint);
2187            self.gl.bind_texture(gl::TEXTURE_2D, 0);
2188        }
2189
2190        self.bound_vao = 0;
2191        self.gl.bind_vertex_array(0);
2192
2193        self.bound_read_fbo = (self.default_read_fbo, DeviceIntPoint::zero());
2194        self.gl.bind_framebuffer(gl::READ_FRAMEBUFFER, self.default_read_fbo.0);
2195
2196        self.bound_draw_fbo = self.default_draw_fbo;
2197        self.gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, self.bound_draw_fbo.0);
2198    }
2199
2200    #[cfg(debug_assertions)]
2201    fn print_shader_errors(source: &str, log: &str) {
2202        // hacky way to extract the offending lines
2203        if !log.starts_with("0:") && !log.starts_with("0(") {
2204            return;
2205        }
2206        let end_pos = match log[2..].chars().position(|c| !c.is_digit(10)) {
2207            Some(pos) => 2 + pos,
2208            None => return,
2209        };
2210        let base_line_number = match log[2 .. end_pos].parse::<usize>() {
2211            Ok(number) if number >= 2 => number - 2,
2212            _ => return,
2213        };
2214        for (line, prefix) in source.lines().skip(base_line_number).zip(&["|",">","|"]) {
2215            error!("{}\t{}", prefix, line);
2216        }
2217    }
2218
2219    pub fn compile_shader(
2220        &self,
2221        name: &str,
2222        shader_type: gl::GLenum,
2223        source: &String,
2224    ) -> Result<gl::GLuint, ShaderError> {
2225        debug!("compile {}", name);
2226        let id = self.gl.create_shader(shader_type);
2227
2228        let mut new_source = Cow::from(source.as_str());
2229        // Ensure the source strings we pass to glShaderSource are
2230        // null-terminated on buggy platforms.
2231        if self.requires_null_terminated_shader_source {
2232            new_source.to_mut().push('\0');
2233        }
2234
2235        self.gl.shader_source(id, &[new_source.as_bytes()]);
2236        self.gl.compile_shader(id);
2237        let log = self.gl.get_shader_info_log(id);
2238        let mut status = [0];
2239        unsafe {
2240            self.gl.get_shader_iv(id, gl::COMPILE_STATUS, &mut status);
2241        }
2242        if status[0] == 0 {
2243            let type_str = match shader_type {
2244                gl::VERTEX_SHADER => "vertex",
2245                gl::FRAGMENT_SHADER => "fragment",
2246                _ => panic!("Unexpected shader type {:x}", shader_type),
2247            };
2248            error!("Failed to compile {} shader: {}\n{}", type_str, name, log);
2249            #[cfg(debug_assertions)]
2250            Self::print_shader_errors(source, &log);
2251            Err(ShaderError::Compilation(name.to_string(), log))
2252        } else {
2253            if !log.is_empty() {
2254                warn!("Warnings detected on shader: {}\n{}", name, log);
2255            }
2256            Ok(id)
2257        }
2258    }
2259
2260    pub fn begin_frame(&mut self) -> GpuFrameId {
2261        debug_assert!(!self.inside_frame);
2262        self.inside_frame = true;
2263        #[cfg(debug_assertions)]
2264        {
2265            self.shader_is_ready = false;
2266        }
2267
2268        self.textures_created = 0;
2269        self.textures_deleted = 0;
2270
2271        // If our profiler state has changed, apply or remove the profiling
2272        // wrapper from our GL context.
2273        let being_profiled = profiler::thread_is_being_profiled();
2274        let using_wrapper = self.base_gl.is_some();
2275
2276        // We can usually unwind driver stacks on OSes other than Android, so we don't need to
2277        // manually instrument gl calls there. Timestamps can be pretty expensive on Windows (2us
2278        // each and perhaps an opportunity to be descheduled?) which makes the profiles gathered
2279        // with this turned on less useful so only profile on ARM Android.
2280        if cfg!(any(target_arch = "arm", target_arch = "aarch64"))
2281            && cfg!(target_os = "android")
2282            && being_profiled
2283            && !using_wrapper
2284        {
2285            fn note(name: &str, duration: Duration) {
2286                profiler::add_text_marker("OpenGL Calls", name, duration);
2287            }
2288            let threshold = Duration::from_millis(1);
2289            let wrapped = gl::ProfilingGl::wrap(self.gl.clone(), threshold, note);
2290            let base = mem::replace(&mut self.gl, wrapped);
2291            self.base_gl = Some(base);
2292        } else if !being_profiled && using_wrapper {
2293            self.gl = self.base_gl.take().unwrap();
2294        }
2295
2296        // Retrieve the currently set FBO.
2297        let mut default_read_fbo = [0];
2298        unsafe {
2299            self.gl.get_integer_v(gl::READ_FRAMEBUFFER_BINDING, &mut default_read_fbo);
2300        }
2301        self.default_read_fbo = FBOId(default_read_fbo[0] as gl::GLuint);
2302        let mut default_draw_fbo = [0];
2303        unsafe {
2304            self.gl.get_integer_v(gl::DRAW_FRAMEBUFFER_BINDING, &mut default_draw_fbo);
2305        }
2306        self.default_draw_fbo = FBOId(default_draw_fbo[0] as gl::GLuint);
2307
2308        // Shader state
2309        self.bound_program = 0;
2310        self.gl.use_program(0);
2311
2312        // Reset common state
2313        self.reset_state();
2314
2315        // Pixel op state
2316        self.gl.pixel_store_i(gl::UNPACK_ALIGNMENT, 1);
2317        self.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, 0);
2318
2319        // Default is sampler 0, always
2320        self.gl.active_texture(gl::TEXTURE0);
2321
2322        self.frame_id
2323    }
2324
2325    fn bind_texture_impl(
2326        &mut self,
2327        slot: TextureSlot,
2328        id: gl::GLuint,
2329        target: gl::GLenum,
2330        set_swizzle: Option<Swizzle>,
2331        image_rendering: Option<ImageRendering>,
2332    ) {
2333        debug_assert!(self.inside_frame);
2334
2335        if self.bound_textures[slot.0] != id || set_swizzle.is_some() || image_rendering.is_some() {
2336            self.gl.active_texture(gl::TEXTURE0 + slot.0 as gl::GLuint);
2337            // The android emulator gets confused if you don't explicitly unbind any texture
2338            // from GL_TEXTURE_EXTERNAL_OES before binding to GL_TEXTURE_2D. See bug 1636085.
2339            if target == gl::TEXTURE_2D && self.requires_texture_external_unbind {
2340                self.gl.bind_texture(gl::TEXTURE_EXTERNAL_OES, 0);
2341            }
2342            self.gl.bind_texture(target, id);
2343            if let Some(swizzle) = set_swizzle {
2344                if self.capabilities.supports_texture_swizzle {
2345                    let components = match swizzle {
2346                        Swizzle::Rgba => [gl::RED, gl::GREEN, gl::BLUE, gl::ALPHA],
2347                        Swizzle::Bgra => [gl::BLUE, gl::GREEN, gl::RED, gl::ALPHA],
2348                    };
2349                    self.gl.tex_parameter_i(target, gl::TEXTURE_SWIZZLE_R, components[0] as i32);
2350                    self.gl.tex_parameter_i(target, gl::TEXTURE_SWIZZLE_G, components[1] as i32);
2351                    self.gl.tex_parameter_i(target, gl::TEXTURE_SWIZZLE_B, components[2] as i32);
2352                    self.gl.tex_parameter_i(target, gl::TEXTURE_SWIZZLE_A, components[3] as i32);
2353                } else {
2354                    debug_assert_eq!(swizzle, Swizzle::default());
2355                }
2356            }
2357            if let Some(image_rendering) = image_rendering {
2358                let filter = match image_rendering {
2359                    ImageRendering::Auto | ImageRendering::CrispEdges => gl::LINEAR,
2360                    ImageRendering::Pixelated => gl::NEAREST,
2361                };
2362                self.gl.tex_parameter_i(target, gl::TEXTURE_MIN_FILTER, filter as i32);
2363                self.gl.tex_parameter_i(target, gl::TEXTURE_MAG_FILTER, filter as i32);
2364            }
2365            self.gl.active_texture(gl::TEXTURE0);
2366            self.bound_textures[slot.0] = id;
2367        }
2368    }
2369
2370    pub fn bind_texture<S>(&mut self, slot: S, texture: &Texture, swizzle: Swizzle)
2371    where
2372        S: Into<TextureSlot>,
2373    {
2374        let old_swizzle = texture.active_swizzle.replace(swizzle);
2375        let set_swizzle = if old_swizzle != swizzle {
2376            Some(swizzle)
2377        } else {
2378            None
2379        };
2380        self.bind_texture_impl(slot.into(), texture.id, texture.target, set_swizzle, None);
2381    }
2382
2383    pub fn bind_external_texture<S>(&mut self, slot: S, external_texture: &ExternalTexture)
2384    where
2385        S: Into<TextureSlot>,
2386    {
2387        self.bind_texture_impl(
2388            slot.into(),
2389            external_texture.id,
2390            external_texture.target,
2391            None,
2392            Some(external_texture.image_rendering),
2393        );
2394    }
2395
2396    pub fn bind_read_target_impl(
2397        &mut self,
2398        fbo_id: FBOId,
2399        offset: DeviceIntPoint,
2400    ) {
2401        debug_assert!(self.inside_frame);
2402
2403        if self.bound_read_fbo != (fbo_id, offset) {
2404            fbo_id.bind(self.gl(), FBOTarget::Read);
2405        }
2406
2407        self.bound_read_fbo = (fbo_id, offset);
2408    }
2409
2410    pub fn bind_read_target(&mut self, target: ReadTarget) {
2411        let fbo_id = match target {
2412            ReadTarget::Default => self.default_read_fbo,
2413            ReadTarget::Texture { fbo_id } => fbo_id,
2414            ReadTarget::External { fbo } => fbo,
2415            ReadTarget::NativeSurface { fbo_id, .. } => fbo_id,
2416        };
2417
2418        self.bind_read_target_impl(fbo_id, target.offset())
2419    }
2420
2421    fn bind_draw_target_impl(&mut self, fbo_id: FBOId) {
2422        debug_assert!(self.inside_frame);
2423
2424        if self.bound_draw_fbo != fbo_id {
2425            self.bound_draw_fbo = fbo_id;
2426            fbo_id.bind(self.gl(), FBOTarget::Draw);
2427        }
2428    }
2429
2430    pub fn reset_read_target(&mut self) {
2431        let fbo = self.default_read_fbo;
2432        self.bind_read_target_impl(fbo, DeviceIntPoint::zero());
2433    }
2434
2435
2436    pub fn reset_draw_target(&mut self) {
2437        let fbo = self.default_draw_fbo;
2438        self.bind_draw_target_impl(fbo);
2439        self.depth_available = true;
2440    }
2441
2442    pub fn bind_draw_target(
2443        &mut self,
2444        target: DrawTarget,
2445    ) {
2446        let (fbo_id, rect, depth_available) = match target {
2447            DrawTarget::Default { rect, .. } => {
2448                (self.default_draw_fbo, rect, false)
2449            }
2450            DrawTarget::Texture { dimensions, fbo_id, with_depth, .. } => {
2451                let rect = FramebufferIntRect::from_size(
2452                    device_size_as_framebuffer_size(dimensions),
2453                );
2454                (fbo_id, rect, with_depth)
2455            },
2456            DrawTarget::External { fbo, size } => {
2457                (fbo, size.into(), false)
2458            }
2459            DrawTarget::NativeSurface { external_fbo_id, offset, dimensions, .. } => {
2460                (
2461                    FBOId(external_fbo_id),
2462                    device_rect_as_framebuffer_rect(&DeviceIntRect::from_origin_and_size(offset, dimensions)),
2463                    true
2464                )
2465            }
2466        };
2467
2468        self.depth_available = depth_available;
2469        self.bind_draw_target_impl(fbo_id);
2470        self.gl.viewport(
2471            rect.min.x,
2472            rect.min.y,
2473            rect.width(),
2474            rect.height(),
2475        );
2476    }
2477
2478    /// Creates an unbound FBO object. Additional attachment API calls are
2479    /// required to make it complete.
2480    pub fn create_fbo(&mut self) -> FBOId {
2481        FBOId(self.gl.gen_framebuffers(1)[0])
2482    }
2483
2484    /// Creates an FBO with the given texture bound as the color attachment.
2485    pub fn create_fbo_for_external_texture(&mut self, texture_id: u32) -> FBOId {
2486        let fbo = self.create_fbo();
2487        fbo.bind(self.gl(), FBOTarget::Draw);
2488        self.gl.framebuffer_texture_2d(
2489            gl::DRAW_FRAMEBUFFER,
2490            gl::COLOR_ATTACHMENT0,
2491            gl::TEXTURE_2D,
2492            texture_id,
2493            0,
2494        );
2495        debug_assert_eq!(
2496            self.gl.check_frame_buffer_status(gl::DRAW_FRAMEBUFFER),
2497            gl::FRAMEBUFFER_COMPLETE,
2498            "Incomplete framebuffer",
2499        );
2500        self.bound_draw_fbo.bind(self.gl(), FBOTarget::Draw);
2501        fbo
2502    }
2503
2504    pub fn delete_fbo(&mut self, fbo: FBOId) {
2505        self.gl.delete_framebuffers(&[fbo.0]);
2506    }
2507
2508    pub fn bind_external_draw_target(&mut self, fbo_id: FBOId) {
2509        debug_assert!(self.inside_frame);
2510
2511        if self.bound_draw_fbo != fbo_id {
2512            self.bound_draw_fbo = fbo_id;
2513            fbo_id.bind(self.gl(), FBOTarget::Draw);
2514        }
2515    }
2516
2517    /// Link a program, attaching the supplied vertex format.
2518    ///
2519    /// If `create_program()` finds a binary shader on disk, it will kick
2520    /// off linking immediately, which some drivers (notably ANGLE) run
2521    /// in parallel on background threads. As such, this function should
2522    /// ideally be run sometime later, to give the driver time to do that
2523    /// before blocking due to an API call accessing the shader.
2524    ///
2525    /// This generally means that the first run of the application will have
2526    /// to do a bunch of blocking work to compile the shader from source, but
2527    /// subsequent runs should load quickly.
2528    pub fn link_program(
2529        &mut self,
2530        program: &mut Program,
2531        descriptor: &VertexDescriptor,
2532    ) -> Result<(), ShaderError> {
2533        profile_scope!("compile shader");
2534
2535        let _guard = CrashAnnotatorGuard::new(
2536            &self.crash_annotator,
2537            CrashAnnotation::CompileShader,
2538            &program.source_info.full_name_cstr
2539        );
2540
2541        assert!(!program.is_initialized());
2542        let mut build_program = true;
2543        let info = &program.source_info;
2544
2545        // See if we hit the binary shader cache
2546        if let Some(ref cached_programs) = self.cached_programs {
2547            // If the shader is not in the cache, attempt to load it from disk
2548            if cached_programs.entries.borrow().get(&program.source_info.digest).is_none() {
2549                if let Some(ref handler) = cached_programs.program_cache_handler {
2550                    handler.try_load_shader_from_disk(&program.source_info.digest, cached_programs);
2551                    if let Some(entry) = cached_programs.entries.borrow().get(&program.source_info.digest) {
2552                        self.gl.program_binary(program.id, entry.binary.format, &entry.binary.bytes);
2553                    }
2554                }
2555            }
2556
2557            if let Some(entry) = cached_programs.entries.borrow_mut().get_mut(&info.digest) {
2558                let mut link_status = [0];
2559                unsafe {
2560                    self.gl.get_program_iv(program.id, gl::LINK_STATUS, &mut link_status);
2561                }
2562                if link_status[0] == 0 {
2563                    let error_log = self.gl.get_program_info_log(program.id);
2564                    error!(
2565                      "Failed to load a program object with a program binary: {} renderer {}\n{}",
2566                      &info.base_filename,
2567                      self.capabilities.renderer_name,
2568                      error_log
2569                    );
2570                    if let Some(ref program_cache_handler) = cached_programs.program_cache_handler {
2571                        program_cache_handler.notify_program_binary_failed(&entry.binary);
2572                    }
2573                } else {
2574                    entry.linked = true;
2575                    build_program = false;
2576                }
2577            }
2578        }
2579
2580        // If not, we need to do a normal compile + link pass.
2581        if build_program {
2582            // Compile the vertex shader
2583            let vs_source = info.compute_source(self, ShaderKind::Vertex);
2584            let vs_id = match self.compile_shader(&info.full_name(), gl::VERTEX_SHADER, &vs_source) {
2585                    Ok(vs_id) => vs_id,
2586                    Err(err) => return Err(err),
2587                };
2588
2589            // Compile the fragment shader
2590            let fs_source = info.compute_source(self, ShaderKind::Fragment);
2591            let fs_id =
2592                match self.compile_shader(&info.full_name(), gl::FRAGMENT_SHADER, &fs_source) {
2593                    Ok(fs_id) => fs_id,
2594                    Err(err) => {
2595                        self.gl.delete_shader(vs_id);
2596                        return Err(err);
2597                    }
2598                };
2599
2600            // Check if shader source should be dumped
2601            if Some(info.base_filename) == self.dump_shader_source.as_ref().map(String::as_ref) {
2602                let path = std::path::Path::new(info.base_filename);
2603                std::fs::write(path.with_extension("vert"), vs_source).unwrap();
2604                std::fs::write(path.with_extension("frag"), fs_source).unwrap();
2605            }
2606
2607            // Attach shaders
2608            self.gl.attach_shader(program.id, vs_id);
2609            self.gl.attach_shader(program.id, fs_id);
2610
2611            // Bind vertex attributes
2612            for (i, attr) in descriptor
2613                .vertex_attributes
2614                .iter()
2615                .chain(descriptor.instance_attributes.iter())
2616                .enumerate()
2617            {
2618                self.gl
2619                    .bind_attrib_location(program.id, i as gl::GLuint, attr.name);
2620            }
2621
2622            if self.cached_programs.is_some() {
2623                self.gl.program_parameter_i(program.id, gl::PROGRAM_BINARY_RETRIEVABLE_HINT, gl::TRUE as gl::GLint);
2624            }
2625
2626            // Link!
2627            self.gl.link_program(program.id);
2628
2629            // GL recommends detaching and deleting shaders once the link
2630            // is complete (whether successful or not). This allows the driver
2631            // to free any memory associated with the parsing and compilation.
2632            self.gl.detach_shader(program.id, vs_id);
2633            self.gl.detach_shader(program.id, fs_id);
2634            self.gl.delete_shader(vs_id);
2635            self.gl.delete_shader(fs_id);
2636
2637            let mut link_status = [0];
2638            unsafe {
2639                self.gl.get_program_iv(program.id, gl::LINK_STATUS, &mut link_status);
2640            }
2641            if link_status[0] == 0 {
2642                let error_log = self.gl.get_program_info_log(program.id);
2643                error!(
2644                    "Failed to link shader program: {}\n{}",
2645                    &info.base_filename,
2646                    error_log
2647                );
2648                self.gl.delete_program(program.id);
2649                return Err(ShaderError::Link(info.base_filename.to_owned(), error_log));
2650            }
2651
2652            if let Some(ref cached_programs) = self.cached_programs {
2653                if !cached_programs.entries.borrow().contains_key(&info.digest) {
2654                    let (buffer, format) = self.gl.get_program_binary(program.id);
2655                    if buffer.len() > 0 {
2656                        let binary = Arc::new(ProgramBinary::new(buffer, format, info.digest.clone()));
2657                        cached_programs.add_new_program_binary(binary);
2658                    }
2659                }
2660            }
2661        }
2662
2663        // If we get here, the link succeeded, so get the uniforms.
2664        program.is_initialized = true;
2665        program.u_transform = self.gl.get_uniform_location(program.id, "uTransform");
2666        program.u_texture_size = self.gl.get_uniform_location(program.id, "uTextureSize");
2667
2668        Ok(())
2669    }
2670
2671    pub fn bind_program(&mut self, program: &Program) -> bool {
2672        debug_assert!(self.inside_frame);
2673        debug_assert!(program.is_initialized());
2674        if !program.is_initialized() {
2675            return false;
2676        }
2677        #[cfg(debug_assertions)]
2678        {
2679            self.shader_is_ready = true;
2680        }
2681
2682        if self.bound_program != program.id {
2683            self.gl.use_program(program.id);
2684            self.bound_program = program.id;
2685            self.bound_program_name = program.source_info.full_name_cstr.clone();
2686        }
2687        true
2688    }
2689
2690    pub fn create_texture(
2691        &mut self,
2692        target: ImageBufferKind,
2693        format: ImageFormat,
2694        mut width: i32,
2695        mut height: i32,
2696        filter: TextureFilter,
2697        render_target: Option<RenderTargetInfo>,
2698    ) -> Texture {
2699        debug_assert!(self.inside_frame);
2700
2701        if width > self.max_texture_size || height > self.max_texture_size {
2702            error!("Attempting to allocate a texture of size {}x{} above the limit, trimming", width, height);
2703            width = width.min(self.max_texture_size);
2704            height = height.min(self.max_texture_size);
2705        }
2706
2707        // Set up the texture book-keeping.
2708        let mut texture = Texture {
2709            id: self.gl.gen_textures(1)[0],
2710            target: get_gl_target(target),
2711            size: DeviceIntSize::new(width, height),
2712            format,
2713            filter,
2714            active_swizzle: Cell::default(),
2715            fbo: None,
2716            fbo_with_depth: None,
2717            last_frame_used: self.frame_id,
2718            flags: TextureFlags::default(),
2719        };
2720        self.bind_texture(DEFAULT_TEXTURE, &texture, Swizzle::default());
2721        self.set_texture_parameters(texture.target, filter);
2722
2723        if self.capabilities.supports_texture_usage && render_target.is_some() {
2724            self.gl.tex_parameter_i(texture.target, gl::TEXTURE_USAGE_ANGLE, gl::FRAMEBUFFER_ATTACHMENT_ANGLE as gl::GLint);
2725        }
2726
2727        // Allocate storage.
2728        let desc = self.gl_describe_format(texture.format);
2729
2730        // Firefox doesn't use mipmaps, but Servo uses them for standalone image
2731        // textures images larger than 512 pixels. This is the only case where
2732        // we set the filter to trilinear.
2733        let mipmap_levels =  if texture.filter == TextureFilter::Trilinear {
2734            let max_dimension = cmp::max(width, height);
2735            ((max_dimension) as f64).log2() as gl::GLint + 1
2736        } else {
2737            1
2738        };
2739
2740        // We never want to upload texture data at the same time as allocating the texture.
2741        self.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, 0);
2742
2743        // Use glTexStorage where available, since it avoids allocating
2744        // unnecessary mipmap storage and generally improves performance with
2745        // stronger invariants.
2746        let use_texture_storage = match self.texture_storage_usage {
2747            TexStorageUsage::Always => true,
2748            TexStorageUsage::NonBGRA8 => texture.format != ImageFormat::BGRA8,
2749            TexStorageUsage::Never => false,
2750        };
2751        if use_texture_storage {
2752            self.gl.tex_storage_2d(
2753                texture.target,
2754                mipmap_levels,
2755                desc.internal,
2756                texture.size.width as gl::GLint,
2757                texture.size.height as gl::GLint,
2758            );
2759        } else {
2760            self.gl.tex_image_2d(
2761                texture.target,
2762                0,
2763                desc.internal as gl::GLint,
2764                texture.size.width as gl::GLint,
2765                texture.size.height as gl::GLint,
2766                0,
2767                desc.external,
2768                desc.pixel_type,
2769                None,
2770            );
2771        }
2772
2773        // Set up FBOs, if required.
2774        if let Some(rt_info) = render_target {
2775            self.init_fbos(&mut texture, false);
2776            if rt_info.has_depth {
2777                self.init_fbos(&mut texture, true);
2778            }
2779        }
2780
2781        self.textures_created += 1;
2782
2783        texture
2784    }
2785
2786    fn set_texture_parameters(&mut self, target: gl::GLuint, filter: TextureFilter) {
2787        let mag_filter = match filter {
2788            TextureFilter::Nearest => gl::NEAREST,
2789            TextureFilter::Linear | TextureFilter::Trilinear => gl::LINEAR,
2790        };
2791
2792        let min_filter = match filter {
2793            TextureFilter::Nearest => gl::NEAREST,
2794            TextureFilter::Linear => gl::LINEAR,
2795            TextureFilter::Trilinear => gl::LINEAR_MIPMAP_LINEAR,
2796        };
2797
2798        self.gl
2799            .tex_parameter_i(target, gl::TEXTURE_MAG_FILTER, mag_filter as gl::GLint);
2800        self.gl
2801            .tex_parameter_i(target, gl::TEXTURE_MIN_FILTER, min_filter as gl::GLint);
2802
2803        self.gl
2804            .tex_parameter_i(target, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as gl::GLint);
2805        self.gl
2806            .tex_parameter_i(target, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as gl::GLint);
2807    }
2808
2809    /// Copies the entire contents of one texture to another. The dest texture must be at least
2810    /// as large as the source texture in each dimension. No scaling is performed, so if the dest
2811    /// texture is larger than the source texture then some of its pixels will not be written to.
2812    pub fn copy_entire_texture(
2813        &mut self,
2814        dst: &mut Texture,
2815        src: &Texture,
2816    ) {
2817        debug_assert!(self.inside_frame);
2818        debug_assert!(dst.size.width >= src.size.width);
2819        debug_assert!(dst.size.height >= src.size.height);
2820
2821        self.copy_texture_sub_region(
2822            src,
2823            0,
2824            0,
2825            dst,
2826            0,
2827            0,
2828            src.size.width as _,
2829            src.size.height as _,
2830        );
2831    }
2832
2833    /// Copies the specified subregion from src_texture to dest_texture.
2834    pub fn copy_texture_sub_region(
2835        &mut self,
2836        src_texture: &Texture,
2837        src_x: usize,
2838        src_y: usize,
2839        dest_texture: &Texture,
2840        dest_x: usize,
2841        dest_y: usize,
2842        width: usize,
2843        height: usize,
2844    ) {
2845        if self.capabilities.supports_copy_image_sub_data {
2846            assert_ne!(
2847                src_texture.id, dest_texture.id,
2848                "glCopyImageSubData's behaviour is undefined if src and dst images are identical and the rectangles overlap."
2849            );
2850            unsafe {
2851                self.gl.copy_image_sub_data(
2852                    src_texture.id,
2853                    src_texture.target,
2854                    0,
2855                    src_x as _,
2856                    src_y as _,
2857                    0,
2858                    dest_texture.id,
2859                    dest_texture.target,
2860                    0,
2861                    dest_x as _,
2862                    dest_y as _,
2863                    0,
2864                    width as _,
2865                    height as _,
2866                    1,
2867                );
2868            }
2869        } else {
2870            let src_offset = FramebufferIntPoint::new(src_x as i32, src_y as i32);
2871            let dest_offset = FramebufferIntPoint::new(dest_x as i32, dest_y as i32);
2872            let size = FramebufferIntSize::new(width as i32, height as i32);
2873
2874            self.blit_render_target(
2875                ReadTarget::from_texture(src_texture),
2876                FramebufferIntRect::from_origin_and_size(src_offset, size),
2877                DrawTarget::from_texture(dest_texture, false),
2878                FramebufferIntRect::from_origin_and_size(dest_offset, size),
2879                // In most cases the filter shouldn't matter, as there is no scaling involved
2880                // in the blit. We were previously using Linear, but this caused issues when
2881                // blitting RGBAF32 textures on Mali, so use Nearest to be safe.
2882                TextureFilter::Nearest,
2883            );
2884        }
2885    }
2886
2887    /// Notifies the device that the contents of a render target are no longer
2888    /// needed.
2889    pub fn invalidate_render_target(&mut self, texture: &Texture) {
2890        if self.capabilities.supports_render_target_invalidate {
2891            let (fbo, attachments) = if texture.supports_depth() {
2892                (&texture.fbo_with_depth,
2893                 &[gl::COLOR_ATTACHMENT0, gl::DEPTH_ATTACHMENT] as &[gl::GLenum])
2894            } else {
2895                (&texture.fbo, &[gl::COLOR_ATTACHMENT0] as &[gl::GLenum])
2896            };
2897
2898            if let Some(fbo_id) = fbo {
2899                let original_bound_fbo = self.bound_draw_fbo;
2900                // Note: The invalidate extension may not be supported, in which
2901                // case this is a no-op. That's ok though, because it's just a
2902                // hint.
2903                self.bind_external_draw_target(*fbo_id);
2904                self.gl.invalidate_framebuffer(gl::FRAMEBUFFER, attachments);
2905                self.bind_external_draw_target(original_bound_fbo);
2906            }
2907        }
2908    }
2909
2910    /// Notifies the device that the contents of the current framebuffer's depth
2911    /// attachment is no longer needed. Unlike invalidate_render_target, this can
2912    /// be called even when the contents of the colour attachment is still required.
2913    /// This should be called before unbinding the framebuffer at the end of a pass,
2914    /// to allow tiled GPUs to avoid writing the contents back to memory.
2915    pub fn invalidate_depth_target(&mut self) {
2916        assert!(self.depth_available);
2917        let attachments = if self.bound_draw_fbo == self.default_draw_fbo {
2918            &[gl::DEPTH] as &[gl::GLenum]
2919        } else {
2920            &[gl::DEPTH_ATTACHMENT] as &[gl::GLenum]
2921        };
2922        self.gl.invalidate_framebuffer(gl::DRAW_FRAMEBUFFER, attachments);
2923    }
2924
2925    /// Notifies the device that a render target is about to be reused.
2926    ///
2927    /// This method adds or removes a depth target as necessary.
2928    pub fn reuse_render_target<T: Texel>(
2929        &mut self,
2930        texture: &mut Texture,
2931        rt_info: RenderTargetInfo,
2932    ) {
2933        texture.last_frame_used = self.frame_id;
2934
2935        // Add depth support if needed.
2936        if rt_info.has_depth && !texture.supports_depth() {
2937            self.init_fbos(texture, true);
2938        }
2939    }
2940
2941    fn init_fbos(&mut self, texture: &mut Texture, with_depth: bool) {
2942        let (fbo, depth_rb) = if with_depth {
2943            let depth_target = self.acquire_depth_target(texture.get_dimensions());
2944            (&mut texture.fbo_with_depth, Some(depth_target))
2945        } else {
2946            (&mut texture.fbo, None)
2947        };
2948
2949        // Generate the FBOs.
2950        assert!(fbo.is_none());
2951        let fbo_id = FBOId(*self.gl.gen_framebuffers(1).first().unwrap());
2952        *fbo = Some(fbo_id);
2953
2954        // Bind the FBOs.
2955        let original_bound_fbo = self.bound_draw_fbo;
2956
2957        self.bind_external_draw_target(fbo_id);
2958
2959        self.gl.framebuffer_texture_2d(
2960            gl::DRAW_FRAMEBUFFER,
2961            gl::COLOR_ATTACHMENT0,
2962            texture.target,
2963            texture.id,
2964            0,
2965        );
2966
2967        if let Some(depth_rb) = depth_rb {
2968            self.gl.framebuffer_renderbuffer(
2969                gl::DRAW_FRAMEBUFFER,
2970                gl::DEPTH_ATTACHMENT,
2971                gl::RENDERBUFFER,
2972                depth_rb.0,
2973            );
2974        }
2975
2976        debug_assert_eq!(
2977            self.gl.check_frame_buffer_status(gl::DRAW_FRAMEBUFFER),
2978            gl::FRAMEBUFFER_COMPLETE,
2979            "Incomplete framebuffer",
2980        );
2981
2982        self.bind_external_draw_target(original_bound_fbo);
2983    }
2984
2985    fn acquire_depth_target(&mut self, dimensions: DeviceIntSize) -> RBOId {
2986        let gl = &self.gl;
2987        let depth_format = self.depth_format;
2988        let target = self.depth_targets.entry(dimensions).or_insert_with(|| {
2989            let renderbuffer_ids = gl.gen_renderbuffers(1);
2990            let depth_rb = renderbuffer_ids[0];
2991            gl.bind_renderbuffer(gl::RENDERBUFFER, depth_rb);
2992            gl.renderbuffer_storage(
2993                gl::RENDERBUFFER,
2994                depth_format,
2995                dimensions.width as _,
2996                dimensions.height as _,
2997            );
2998            SharedDepthTarget {
2999                rbo_id: RBOId(depth_rb),
3000                refcount: 0,
3001            }
3002        });
3003        target.refcount += 1;
3004        target.rbo_id
3005    }
3006
3007    fn release_depth_target(&mut self, dimensions: DeviceIntSize) {
3008        let mut entry = match self.depth_targets.entry(dimensions) {
3009            Entry::Occupied(x) => x,
3010            Entry::Vacant(..) => panic!("Releasing unknown depth target"),
3011        };
3012        debug_assert!(entry.get().refcount != 0);
3013        entry.get_mut().refcount -= 1;
3014        if entry.get().refcount == 0 {
3015            let (_, target) = entry.remove_entry();
3016            self.gl.delete_renderbuffers(&[target.rbo_id.0]);
3017        }
3018    }
3019
3020    /// Perform a blit between self.bound_read_fbo and self.bound_draw_fbo.
3021    fn blit_render_target_impl(
3022        &mut self,
3023        src_rect: FramebufferIntRect,
3024        dest_rect: FramebufferIntRect,
3025        filter: TextureFilter,
3026    ) {
3027        debug_assert!(self.inside_frame);
3028
3029        let filter = match filter {
3030            TextureFilter::Nearest => gl::NEAREST,
3031            TextureFilter::Linear | TextureFilter::Trilinear => gl::LINEAR,
3032        };
3033
3034        let src_x0 = src_rect.min.x + self.bound_read_fbo.1.x;
3035        let src_y0 = src_rect.min.y + self.bound_read_fbo.1.y;
3036
3037        self.gl.blit_framebuffer(
3038            src_x0,
3039            src_y0,
3040            src_x0 + src_rect.width(),
3041            src_y0 + src_rect.height(),
3042            dest_rect.min.x,
3043            dest_rect.min.y,
3044            dest_rect.max.x,
3045            dest_rect.max.y,
3046            gl::COLOR_BUFFER_BIT,
3047            filter,
3048        );
3049    }
3050
3051    /// Perform a blit between src_target and dest_target.
3052    /// This will overwrite self.bound_read_fbo and self.bound_draw_fbo.
3053    pub fn blit_render_target(
3054        &mut self,
3055        src_target: ReadTarget,
3056        src_rect: FramebufferIntRect,
3057        dest_target: DrawTarget,
3058        dest_rect: FramebufferIntRect,
3059        filter: TextureFilter,
3060    ) {
3061        debug_assert!(self.inside_frame);
3062
3063        self.bind_read_target(src_target);
3064
3065        self.bind_draw_target(dest_target);
3066
3067        self.blit_render_target_impl(src_rect, dest_rect, filter);
3068    }
3069
3070    /// Performs a blit while flipping vertically. Useful for blitting textures
3071    /// (which use origin-bottom-left) to the main framebuffer (which uses
3072    /// origin-top-left).
3073    pub fn blit_render_target_invert_y(
3074        &mut self,
3075        src_target: ReadTarget,
3076        src_rect: FramebufferIntRect,
3077        dest_target: DrawTarget,
3078        dest_rect: FramebufferIntRect,
3079    ) {
3080        debug_assert!(self.inside_frame);
3081
3082        let mut inverted_dest_rect = dest_rect;
3083        inverted_dest_rect.min.y = dest_rect.max.y;
3084        inverted_dest_rect.max.y = dest_rect.min.y;
3085
3086        self.blit_render_target(
3087            src_target,
3088            src_rect,
3089            dest_target,
3090            inverted_dest_rect,
3091            TextureFilter::Linear,
3092        );
3093    }
3094
3095    pub fn delete_texture(&mut self, mut texture: Texture) {
3096        debug_assert!(self.inside_frame);
3097        let had_depth = texture.supports_depth();
3098        if let Some(fbo) = texture.fbo {
3099            self.gl.delete_framebuffers(&[fbo.0]);
3100            texture.fbo = None;
3101        }
3102        if let Some(fbo) = texture.fbo_with_depth {
3103            self.gl.delete_framebuffers(&[fbo.0]);
3104            texture.fbo_with_depth = None;
3105        }
3106
3107        if had_depth {
3108            self.release_depth_target(texture.get_dimensions());
3109        }
3110
3111        self.gl.delete_textures(&[texture.id]);
3112
3113        for bound_texture in &mut self.bound_textures {
3114            if *bound_texture == texture.id {
3115                *bound_texture = 0;
3116            }
3117        }
3118
3119        self.textures_deleted += 1;
3120
3121        // Disarm the assert in Texture::drop().
3122        texture.id = 0;
3123    }
3124
3125    #[cfg(feature = "replay")]
3126    pub fn delete_external_texture(&mut self, external: ExternalTexture) {
3127        self.gl.delete_textures(&[external.id]);
3128    }
3129
3130    pub fn delete_program(&mut self, mut program: Program) {
3131        self.gl.delete_program(program.id);
3132        program.id = 0;
3133    }
3134
3135    /// Create a shader program and link it immediately.
3136    pub fn create_program_linked(
3137        &mut self,
3138        base_filename: &'static str,
3139        features: &[&'static str],
3140        descriptor: &VertexDescriptor,
3141    ) -> Result<Program, ShaderError> {
3142        let mut program = self.create_program(base_filename, features)?;
3143        self.link_program(&mut program, descriptor)?;
3144        Ok(program)
3145    }
3146
3147    /// Create a shader program. This does minimal amount of work to start
3148    /// loading a binary shader. If a binary shader is found, we invoke
3149    /// glProgramBinary, which, at least on ANGLE, will load and link the
3150    /// binary on a background thread. This can speed things up later when
3151    /// we invoke `link_program()`.
3152    pub fn create_program(
3153        &mut self,
3154        base_filename: &'static str,
3155        features: &[&'static str],
3156    ) -> Result<Program, ShaderError> {
3157        debug_assert!(self.inside_frame);
3158
3159        let source_info = ProgramSourceInfo::new(self, base_filename, features);
3160
3161        // Create program
3162        let pid = self.gl.create_program();
3163
3164        // Attempt to load a cached binary if possible.
3165        if let Some(ref cached_programs) = self.cached_programs {
3166            if let Some(entry) = cached_programs.entries.borrow().get(&source_info.digest) {
3167                self.gl.program_binary(pid, entry.binary.format, &entry.binary.bytes);
3168            }
3169        }
3170
3171        // Use 0 for the uniforms as they are initialized by link_program.
3172        let program = Program {
3173            id: pid,
3174            u_transform: 0,
3175            u_texture_size: 0,
3176            source_info,
3177            is_initialized: false,
3178        };
3179
3180        Ok(program)
3181    }
3182
3183    fn build_shader_string<F: FnMut(&str)>(
3184        &self,
3185        features: &[&'static str],
3186        kind: ShaderKind,
3187        base_filename: &str,
3188        output: F,
3189    ) {
3190        do_build_shader_string(
3191            get_shader_version(&*self.gl),
3192            features,
3193            kind,
3194            base_filename,
3195            &|f| get_unoptimized_shader_source(f, self.resource_override_path.as_ref()),
3196            output,
3197        )
3198    }
3199
3200    pub fn bind_shader_samplers<S>(&mut self, program: &Program, bindings: &[(&'static str, S)])
3201    where
3202        S: Into<TextureSlot> + Copy,
3203    {
3204        // bind_program() must be called before calling bind_shader_samplers
3205        assert_eq!(self.bound_program, program.id);
3206
3207        for binding in bindings {
3208            let u_location = self.gl.get_uniform_location(program.id, binding.0);
3209            if u_location != -1 {
3210                self.bind_program(program);
3211                self.gl
3212                    .uniform_1i(u_location, binding.1.into().0 as gl::GLint);
3213            }
3214        }
3215    }
3216
3217    pub fn get_uniform_location(&self, program: &Program, name: &str) -> UniformLocation {
3218        UniformLocation(self.gl.get_uniform_location(program.id, name))
3219    }
3220
3221    pub fn set_uniforms(
3222        &self,
3223        program: &Program,
3224        transform: &Transform3D<f32>,
3225    ) {
3226        debug_assert!(self.inside_frame);
3227        #[cfg(debug_assertions)]
3228        debug_assert!(self.shader_is_ready);
3229
3230        self.gl
3231            .uniform_matrix_4fv(program.u_transform, false, &transform.to_array());
3232    }
3233
3234    /// Sets the uTextureSize uniform. Most shaders do not require this to be called
3235    /// as they use the textureSize GLSL function instead.
3236    pub fn set_shader_texture_size(
3237        &self,
3238        program: &Program,
3239        texture_size: DeviceSize,
3240    ) {
3241        debug_assert!(self.inside_frame);
3242        #[cfg(debug_assertions)]
3243        debug_assert!(self.shader_is_ready);
3244
3245        if program.u_texture_size != -1 {
3246            self.gl.uniform_2f(program.u_texture_size, texture_size.width, texture_size.height);
3247        }
3248    }
3249
3250    pub fn create_pbo(&mut self) -> PBO {
3251        let id = self.gl.gen_buffers(1)[0];
3252        PBO {
3253            id,
3254            reserved_size: 0,
3255        }
3256    }
3257
3258    pub fn create_pbo_with_size(&mut self, size: usize) -> PBO {
3259        let mut pbo = self.create_pbo();
3260
3261        self.gl.bind_buffer(gl::PIXEL_PACK_BUFFER, pbo.id);
3262        self.gl.pixel_store_i(gl::PACK_ALIGNMENT, 1);
3263        self.gl.buffer_data_untyped(
3264            gl::PIXEL_PACK_BUFFER,
3265            size as _,
3266            ptr::null(),
3267            gl::STREAM_READ,
3268        );
3269        self.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, 0);
3270
3271        pbo.reserved_size = size;
3272        pbo
3273    }
3274
3275    pub fn read_pixels_into_pbo(
3276        &mut self,
3277        read_target: ReadTarget,
3278        rect: DeviceIntRect,
3279        format: ImageFormat,
3280        pbo: &PBO,
3281    ) {
3282        let byte_size = rect.area() as usize * format.bytes_per_pixel() as usize;
3283
3284        assert!(byte_size <= pbo.reserved_size);
3285
3286        self.bind_read_target(read_target);
3287
3288        self.gl.bind_buffer(gl::PIXEL_PACK_BUFFER, pbo.id);
3289        self.gl.pixel_store_i(gl::PACK_ALIGNMENT, 1);
3290
3291        let gl_format = self.gl_describe_format(format);
3292
3293        unsafe {
3294            self.gl.read_pixels_into_pbo(
3295                rect.min.x as _,
3296                rect.min.y as _,
3297                rect.width() as _,
3298                rect.height() as _,
3299                gl_format.read,
3300                gl_format.pixel_type,
3301            );
3302        }
3303
3304        self.gl.bind_buffer(gl::PIXEL_PACK_BUFFER, 0);
3305    }
3306
3307    pub fn map_pbo_for_readback<'a>(&'a mut self, pbo: &'a PBO) -> Option<BoundPBO<'a>> {
3308        self.gl.bind_buffer(gl::PIXEL_PACK_BUFFER, pbo.id);
3309
3310        let buf_ptr = match self.gl.get_type() {
3311            gl::GlType::Gl => {
3312                self.gl.map_buffer(gl::PIXEL_PACK_BUFFER, gl::READ_ONLY)
3313            }
3314
3315            gl::GlType::Gles => {
3316                self.gl.map_buffer_range(
3317                    gl::PIXEL_PACK_BUFFER,
3318                    0,
3319                    pbo.reserved_size as _,
3320                    gl::MAP_READ_BIT)
3321            }
3322        };
3323
3324        if buf_ptr.is_null() {
3325            return None;
3326        }
3327
3328        let buffer = unsafe { slice::from_raw_parts(buf_ptr as *const u8, pbo.reserved_size) };
3329
3330        Some(BoundPBO {
3331            device: self,
3332            data: buffer,
3333        })
3334    }
3335
3336    pub fn delete_pbo(&mut self, mut pbo: PBO) {
3337        self.gl.delete_buffers(&[pbo.id]);
3338        pbo.id = 0;
3339        pbo.reserved_size = 0
3340    }
3341
3342    /// Returns the size and stride in bytes required to upload an area of pixels
3343    /// of the specified size, to a texture of the specified format.
3344    pub fn required_upload_size_and_stride(&self, size: DeviceIntSize, format: ImageFormat) -> (usize, usize) {
3345        assert!(size.width >= 0);
3346        assert!(size.height >= 0);
3347
3348        let bytes_pp = format.bytes_per_pixel() as usize;
3349        let width_bytes = size.width as usize * bytes_pp;
3350
3351        let dst_stride = round_up_to_multiple(width_bytes, self.required_pbo_stride.num_bytes(format));
3352
3353        // The size of the chunk should only need to be (height - 1) * dst_stride + width_bytes,
3354        // however, the android emulator will error unless it is height * dst_stride.
3355        // See bug 1587047 for details.
3356        // Using the full final row also ensures that the offset of the next chunk is
3357        // optimally aligned.
3358        let dst_size = dst_stride * size.height as usize;
3359
3360        (dst_size, dst_stride)
3361    }
3362
3363    /// Returns a `TextureUploader` which can be used to upload texture data to `texture`.
3364    /// Once uploads have been performed the uploader must be flushed with `TextureUploader::flush()`.
3365    pub fn upload_texture<'a>(
3366        &mut self,
3367        pbo_pool: &'a mut UploadPBOPool,
3368    ) -> TextureUploader<'a> {
3369        debug_assert!(self.inside_frame);
3370
3371        pbo_pool.begin_frame(self);
3372
3373        TextureUploader {
3374            buffers: Vec::new(),
3375            pbo_pool,
3376        }
3377    }
3378
3379    /// Performs an immediate (non-PBO) texture upload.
3380    pub fn upload_texture_immediate<T: Texel>(
3381        &mut self,
3382        texture: &Texture,
3383        pixels: &[T]
3384    ) {
3385        self.bind_texture(DEFAULT_TEXTURE, texture, Swizzle::default());
3386        let desc = self.gl_describe_format(texture.format);
3387        self.gl.tex_sub_image_2d(
3388            texture.target,
3389            0,
3390            0,
3391            0,
3392            texture.size.width as gl::GLint,
3393            texture.size.height as gl::GLint,
3394            desc.external,
3395            desc.pixel_type,
3396            texels_to_u8_slice(pixels),
3397        );
3398    }
3399
3400    pub fn read_pixels(&mut self, img_desc: &ImageDescriptor) -> Vec<u8> {
3401        let desc = self.gl_describe_format(img_desc.format);
3402        self.gl.read_pixels(
3403            0, 0,
3404            img_desc.size.width as i32,
3405            img_desc.size.height as i32,
3406            desc.read,
3407            desc.pixel_type,
3408        )
3409    }
3410
3411    /// Read rectangle of pixels into the specified output slice.
3412    pub fn read_pixels_into(
3413        &mut self,
3414        rect: FramebufferIntRect,
3415        format: ImageFormat,
3416        output: &mut [u8],
3417    ) {
3418        let bytes_per_pixel = format.bytes_per_pixel();
3419        let desc = self.gl_describe_format(format);
3420        let size_in_bytes = (bytes_per_pixel * rect.area()) as usize;
3421        assert_eq!(output.len(), size_in_bytes);
3422
3423        self.gl.flush();
3424        self.gl.read_pixels_into_buffer(
3425            rect.min.x as _,
3426            rect.min.y as _,
3427            rect.width() as _,
3428            rect.height() as _,
3429            desc.read,
3430            desc.pixel_type,
3431            output,
3432        );
3433    }
3434
3435    /// Get texels of a texture into the specified output slice.
3436    pub fn get_tex_image_into(
3437        &mut self,
3438        texture: &Texture,
3439        format: ImageFormat,
3440        output: &mut [u8],
3441    ) {
3442        self.bind_texture(DEFAULT_TEXTURE, texture, Swizzle::default());
3443        let desc = self.gl_describe_format(format);
3444        self.gl.get_tex_image_into_buffer(
3445            texture.target,
3446            0,
3447            desc.external,
3448            desc.pixel_type,
3449            output,
3450        );
3451    }
3452
3453    /// Attaches the provided texture to the current Read FBO binding.
3454    fn attach_read_texture_raw(&mut self, texture_id: gl::GLuint, target: gl::GLuint) {
3455        self.gl.framebuffer_texture_2d(
3456            gl::READ_FRAMEBUFFER,
3457            gl::COLOR_ATTACHMENT0,
3458            target,
3459            texture_id,
3460            0,
3461        )
3462    }
3463
3464    pub fn attach_read_texture_external(
3465        &mut self, texture_id: gl::GLuint, target: ImageBufferKind
3466    ) {
3467        self.attach_read_texture_raw(texture_id, get_gl_target(target))
3468    }
3469
3470    pub fn attach_read_texture(&mut self, texture: &Texture) {
3471        self.attach_read_texture_raw(texture.id, texture.target)
3472    }
3473
3474    fn bind_vao_impl(&mut self, id: gl::GLuint) {
3475        debug_assert!(self.inside_frame);
3476
3477        if self.bound_vao != id {
3478            self.bound_vao = id;
3479            self.gl.bind_vertex_array(id);
3480        }
3481    }
3482
3483    pub fn bind_vao(&mut self, vao: &VAO) {
3484        self.bind_vao_impl(vao.id)
3485    }
3486
3487    pub fn bind_custom_vao(&mut self, vao: &CustomVAO) {
3488        self.bind_vao_impl(vao.id)
3489    }
3490
3491    fn create_vao_with_vbos(
3492        &mut self,
3493        descriptor: &VertexDescriptor,
3494        main_vbo_id: VBOId,
3495        instance_vbo_id: VBOId,
3496        instance_divisor: u32,
3497        ibo_id: IBOId,
3498        owns_vertices_and_indices: bool,
3499    ) -> VAO {
3500        let instance_stride = descriptor.instance_stride() as usize;
3501        let vao_id = self.gl.gen_vertex_arrays(1)[0];
3502
3503        self.bind_vao_impl(vao_id);
3504
3505        descriptor.bind(self.gl(), main_vbo_id, instance_vbo_id, instance_divisor);
3506        ibo_id.bind(self.gl()); // force it to be a part of VAO
3507
3508        VAO {
3509            id: vao_id,
3510            ibo_id,
3511            main_vbo_id,
3512            instance_vbo_id,
3513            instance_stride,
3514            instance_divisor,
3515            owns_vertices_and_indices,
3516        }
3517    }
3518
3519    pub fn create_custom_vao(
3520        &mut self,
3521        streams: &[Stream],
3522    ) -> CustomVAO {
3523        debug_assert!(self.inside_frame);
3524
3525        let vao_id = self.gl.gen_vertex_arrays(1)[0];
3526        self.bind_vao_impl(vao_id);
3527
3528        let mut attrib_index = 0;
3529        for stream in streams {
3530            VertexDescriptor::bind_attributes(
3531                stream.attributes,
3532                attrib_index,
3533                0,
3534                self.gl(),
3535                stream.vbo,
3536            );
3537            attrib_index += stream.attributes.len();
3538        }
3539
3540        CustomVAO {
3541            id: vao_id,
3542        }
3543    }
3544
3545    pub fn delete_custom_vao(&mut self, mut vao: CustomVAO) {
3546        self.gl.delete_vertex_arrays(&[vao.id]);
3547        vao.id = 0;
3548    }
3549
3550    pub fn create_vbo<T>(&mut self) -> VBO<T> {
3551        let ids = self.gl.gen_buffers(1);
3552        VBO {
3553            id: ids[0],
3554            target: gl::ARRAY_BUFFER,
3555            allocated_count: 0,
3556            marker: PhantomData,
3557        }
3558    }
3559
3560    pub fn delete_vbo<T>(&mut self, mut vbo: VBO<T>) {
3561        self.gl.delete_buffers(&[vbo.id]);
3562        vbo.id = 0;
3563    }
3564
3565    pub fn create_vao(&mut self, descriptor: &VertexDescriptor, instance_divisor: u32) -> VAO {
3566        debug_assert!(self.inside_frame);
3567
3568        let buffer_ids = self.gl.gen_buffers(3);
3569        let ibo_id = IBOId(buffer_ids[0]);
3570        let main_vbo_id = VBOId(buffer_ids[1]);
3571        let intance_vbo_id = VBOId(buffer_ids[2]);
3572
3573        self.create_vao_with_vbos(descriptor, main_vbo_id, intance_vbo_id, instance_divisor, ibo_id, true)
3574    }
3575
3576    pub fn delete_vao(&mut self, mut vao: VAO) {
3577        self.gl.delete_vertex_arrays(&[vao.id]);
3578        vao.id = 0;
3579
3580        if vao.owns_vertices_and_indices {
3581            self.gl.delete_buffers(&[vao.ibo_id.0]);
3582            self.gl.delete_buffers(&[vao.main_vbo_id.0]);
3583        }
3584
3585        self.gl.delete_buffers(&[vao.instance_vbo_id.0])
3586    }
3587
3588    pub fn allocate_vbo<V>(
3589        &mut self,
3590        vbo: &mut VBO<V>,
3591        count: usize,
3592        usage_hint: VertexUsageHint,
3593    ) {
3594        debug_assert!(self.inside_frame);
3595        vbo.allocated_count = count;
3596
3597        self.gl.bind_buffer(vbo.target, vbo.id);
3598        self.gl.buffer_data_untyped(
3599            vbo.target,
3600            (count * mem::size_of::<V>()) as _,
3601            ptr::null(),
3602            usage_hint.to_gl(),
3603        );
3604    }
3605
3606    pub fn fill_vbo<V>(
3607        &mut self,
3608        vbo: &VBO<V>,
3609        data: &[V],
3610        offset: usize,
3611    ) {
3612        debug_assert!(self.inside_frame);
3613        assert!(offset + data.len() <= vbo.allocated_count);
3614        let stride = mem::size_of::<V>();
3615
3616        self.gl.bind_buffer(vbo.target, vbo.id);
3617        self.gl.buffer_sub_data_untyped(
3618            vbo.target,
3619            (offset * stride) as _,
3620            (data.len() * stride) as _,
3621            data.as_ptr() as _,
3622        );
3623    }
3624
3625    fn update_vbo_data<V>(
3626        &mut self,
3627        vbo: VBOId,
3628        vertices: &[V],
3629        usage_hint: VertexUsageHint,
3630    ) {
3631        debug_assert!(self.inside_frame);
3632
3633        vbo.bind(self.gl());
3634        gl::buffer_data(self.gl(), gl::ARRAY_BUFFER, vertices, usage_hint.to_gl());
3635    }
3636
3637    pub fn create_vao_with_new_instances(
3638        &mut self,
3639        descriptor: &VertexDescriptor,
3640        base_vao: &VAO,
3641    ) -> VAO {
3642        debug_assert!(self.inside_frame);
3643
3644        let buffer_ids = self.gl.gen_buffers(1);
3645        let intance_vbo_id = VBOId(buffer_ids[0]);
3646
3647        self.create_vao_with_vbos(
3648            descriptor,
3649            base_vao.main_vbo_id,
3650            intance_vbo_id,
3651            base_vao.instance_divisor,
3652            base_vao.ibo_id,
3653            false,
3654        )
3655    }
3656
3657    pub fn update_vao_main_vertices<V>(
3658        &mut self,
3659        vao: &VAO,
3660        vertices: &[V],
3661        usage_hint: VertexUsageHint,
3662    ) {
3663        debug_assert_eq!(self.bound_vao, vao.id);
3664        self.update_vbo_data(vao.main_vbo_id, vertices, usage_hint)
3665    }
3666
3667    pub fn update_vao_instances<V: Clone>(
3668        &mut self,
3669        vao: &VAO,
3670        instances: &[V],
3671        usage_hint: VertexUsageHint,
3672        // if `Some(count)`, each instance is repeated `count` times
3673        repeat: Option<NonZeroUsize>,
3674    ) {
3675        debug_assert_eq!(self.bound_vao, vao.id);
3676        debug_assert_eq!(vao.instance_stride as usize, mem::size_of::<V>());
3677
3678        match repeat {
3679            Some(count) => {
3680                let target = gl::ARRAY_BUFFER;
3681                self.gl.bind_buffer(target, vao.instance_vbo_id.0);
3682                let size = instances.len() * count.get() * mem::size_of::<V>();
3683                self.gl.buffer_data_untyped(
3684                    target,
3685                    size as _,
3686                    ptr::null(),
3687                    usage_hint.to_gl(),
3688                );
3689
3690                let ptr = match self.gl.get_type() {
3691                    gl::GlType::Gl => {
3692                        self.gl.map_buffer(target, gl::WRITE_ONLY)
3693                    }
3694                    gl::GlType::Gles => {
3695                        self.gl.map_buffer_range(target, 0, size as _, gl::MAP_WRITE_BIT)
3696                    }
3697                };
3698                assert!(!ptr.is_null());
3699
3700                let buffer_slice = unsafe {
3701                    slice::from_raw_parts_mut(ptr as *mut V, instances.len() * count.get())
3702                };
3703                for (quad, instance) in buffer_slice.chunks_mut(4).zip(instances) {
3704                    quad[0] = instance.clone();
3705                    quad[1] = instance.clone();
3706                    quad[2] = instance.clone();
3707                    quad[3] = instance.clone();
3708                }
3709                self.gl.unmap_buffer(target);
3710            }
3711            None => {
3712                self.update_vbo_data(vao.instance_vbo_id, instances, usage_hint);
3713            }
3714        }
3715
3716        // On some devices the VAO must be manually unbound and rebound after an attached buffer has
3717        // been orphaned. Failure to do so appeared to result in the orphaned buffer's contents
3718        // being used for the subsequent draw call, rather than the new buffer's contents.
3719        if self.capabilities.requires_vao_rebind_after_orphaning {
3720            self.bind_vao_impl(0);
3721            self.bind_vao_impl(vao.id);
3722        }
3723    }
3724
3725    pub fn update_vao_indices<I>(&mut self, vao: &VAO, indices: &[I], usage_hint: VertexUsageHint) {
3726        debug_assert!(self.inside_frame);
3727        debug_assert_eq!(self.bound_vao, vao.id);
3728
3729        vao.ibo_id.bind(self.gl());
3730        gl::buffer_data(
3731            self.gl(),
3732            gl::ELEMENT_ARRAY_BUFFER,
3733            indices,
3734            usage_hint.to_gl(),
3735        );
3736    }
3737
3738    pub fn draw_triangles_u16(&mut self, first_vertex: i32, index_count: i32) {
3739        debug_assert!(self.inside_frame);
3740        #[cfg(debug_assertions)]
3741        debug_assert!(self.shader_is_ready);
3742
3743        let _guard = if self.annotate_draw_call_crashes {
3744            Some(CrashAnnotatorGuard::new(
3745                &self.crash_annotator,
3746                CrashAnnotation::DrawShader,
3747                &self.bound_program_name,
3748            ))
3749        } else {
3750            None
3751        };
3752
3753        self.gl.draw_elements(
3754            gl::TRIANGLES,
3755            index_count,
3756            gl::UNSIGNED_SHORT,
3757            first_vertex as u32 * 2,
3758        );
3759    }
3760
3761    pub fn draw_triangles_u32(&mut self, first_vertex: i32, index_count: i32) {
3762        debug_assert!(self.inside_frame);
3763        #[cfg(debug_assertions)]
3764        debug_assert!(self.shader_is_ready);
3765
3766        let _guard = if self.annotate_draw_call_crashes {
3767            Some(CrashAnnotatorGuard::new(
3768                &self.crash_annotator,
3769                CrashAnnotation::DrawShader,
3770                &self.bound_program_name,
3771            ))
3772        } else {
3773            None
3774        };
3775
3776        self.gl.draw_elements(
3777            gl::TRIANGLES,
3778            index_count,
3779            gl::UNSIGNED_INT,
3780            first_vertex as u32 * 4,
3781        );
3782    }
3783
3784    pub fn draw_nonindexed_points(&mut self, first_vertex: i32, vertex_count: i32) {
3785        debug_assert!(self.inside_frame);
3786        #[cfg(debug_assertions)]
3787        debug_assert!(self.shader_is_ready);
3788
3789        let _guard = if self.annotate_draw_call_crashes {
3790            Some(CrashAnnotatorGuard::new(
3791                &self.crash_annotator,
3792                CrashAnnotation::DrawShader,
3793                &self.bound_program_name,
3794            ))
3795        } else {
3796            None
3797        };
3798
3799        self.gl.draw_arrays(gl::POINTS, first_vertex, vertex_count);
3800    }
3801
3802    pub fn draw_nonindexed_lines(&mut self, first_vertex: i32, vertex_count: i32) {
3803        debug_assert!(self.inside_frame);
3804        #[cfg(debug_assertions)]
3805        debug_assert!(self.shader_is_ready);
3806
3807        let _guard = if self.annotate_draw_call_crashes {
3808            Some(CrashAnnotatorGuard::new(
3809                &self.crash_annotator,
3810                CrashAnnotation::DrawShader,
3811                &self.bound_program_name,
3812            ))
3813        } else {
3814            None
3815        };
3816
3817        self.gl.draw_arrays(gl::LINES, first_vertex, vertex_count);
3818    }
3819
3820    pub fn draw_indexed_triangles(&mut self, index_count: i32) {
3821        debug_assert!(self.inside_frame);
3822        #[cfg(debug_assertions)]
3823        debug_assert!(self.shader_is_ready);
3824
3825        let _guard = if self.annotate_draw_call_crashes {
3826            Some(CrashAnnotatorGuard::new(
3827                &self.crash_annotator,
3828                CrashAnnotation::DrawShader,
3829                &self.bound_program_name,
3830            ))
3831        } else {
3832            None
3833        };
3834
3835        self.gl.draw_elements(
3836            gl::TRIANGLES,
3837            index_count,
3838            gl::UNSIGNED_SHORT,
3839            0,
3840        );
3841    }
3842
3843    pub fn draw_indexed_triangles_instanced_u16(&mut self, index_count: i32, instance_count: i32) {
3844        debug_assert!(self.inside_frame);
3845        #[cfg(debug_assertions)]
3846        debug_assert!(self.shader_is_ready);
3847
3848        let _guard = if self.annotate_draw_call_crashes {
3849            Some(CrashAnnotatorGuard::new(
3850                &self.crash_annotator,
3851                CrashAnnotation::DrawShader,
3852                &self.bound_program_name,
3853            ))
3854        } else {
3855            None
3856        };
3857
3858        self.gl.draw_elements_instanced(
3859            gl::TRIANGLES,
3860            index_count,
3861            gl::UNSIGNED_SHORT,
3862            0,
3863            instance_count,
3864        );
3865    }
3866
3867    pub fn end_frame(&mut self) {
3868        self.reset_draw_target();
3869        self.reset_read_target();
3870
3871        debug_assert!(self.inside_frame);
3872        self.inside_frame = false;
3873
3874        self.gl.bind_texture(gl::TEXTURE_2D, 0);
3875        self.gl.use_program(0);
3876
3877        for i in 0 .. self.bound_textures.len() {
3878            self.gl.active_texture(gl::TEXTURE0 + i as gl::GLuint);
3879            self.gl.bind_texture(gl::TEXTURE_2D, 0);
3880        }
3881
3882        self.gl.active_texture(gl::TEXTURE0);
3883
3884        self.frame_id.0 += 1;
3885
3886        // Save any shaders compiled this frame to disk.
3887        // If this is the tenth frame then treat startup as complete, meaning the
3888        // current set of in-use shaders are the ones to load on the next startup.
3889        if let Some(ref cache) = self.cached_programs {
3890            cache.update_disk_cache(self.frame_id.0 == 10);
3891        }
3892    }
3893
3894    pub fn clear_target(
3895        &self,
3896        color: Option<[f32; 4]>,
3897        depth: Option<f32>,
3898        rect: Option<FramebufferIntRect>,
3899    ) {
3900        let mut clear_bits = 0;
3901
3902        if let Some(color) = color {
3903            self.gl.clear_color(color[0], color[1], color[2], color[3]);
3904            clear_bits |= gl::COLOR_BUFFER_BIT;
3905        }
3906
3907        if let Some(depth) = depth {
3908            if cfg!(debug_assertions) {
3909                let mut mask = [0];
3910                unsafe {
3911                    self.gl.get_boolean_v(gl::DEPTH_WRITEMASK, &mut mask);
3912                }
3913                assert_ne!(mask[0], 0);
3914            }
3915            self.gl.clear_depth(depth as f64);
3916            clear_bits |= gl::DEPTH_BUFFER_BIT;
3917        }
3918
3919        if clear_bits != 0 {
3920            match rect {
3921                Some(rect) => {
3922                    self.gl.enable(gl::SCISSOR_TEST);
3923                    self.gl.scissor(
3924                        rect.min.x,
3925                        rect.min.y,
3926                        rect.width(),
3927                        rect.height(),
3928                    );
3929                    self.gl.clear(clear_bits);
3930                    self.gl.disable(gl::SCISSOR_TEST);
3931                }
3932                None => {
3933                    self.gl.clear(clear_bits);
3934                }
3935            }
3936        }
3937    }
3938
3939    pub fn enable_depth(&self, depth_func: DepthFunction) {
3940        assert!(self.depth_available, "Enabling depth test without depth target");
3941        self.gl.enable(gl::DEPTH_TEST);
3942        self.gl.depth_func(depth_func as gl::GLuint);
3943    }
3944
3945    pub fn disable_depth(&self) {
3946        self.gl.disable(gl::DEPTH_TEST);
3947    }
3948
3949    pub fn enable_depth_write(&self) {
3950        assert!(self.depth_available, "Enabling depth write without depth target");
3951        self.gl.depth_mask(true);
3952    }
3953
3954    pub fn disable_depth_write(&self) {
3955        self.gl.depth_mask(false);
3956    }
3957
3958    pub fn disable_stencil(&self) {
3959        self.gl.disable(gl::STENCIL_TEST);
3960    }
3961
3962    pub fn set_scissor_rect(&self, rect: FramebufferIntRect) {
3963        self.gl.scissor(
3964            rect.min.x,
3965            rect.min.y,
3966            rect.width(),
3967            rect.height(),
3968        );
3969    }
3970
3971    pub fn enable_scissor(&self) {
3972        self.gl.enable(gl::SCISSOR_TEST);
3973    }
3974
3975    pub fn disable_scissor(&self) {
3976        self.gl.disable(gl::SCISSOR_TEST);
3977    }
3978
3979    pub fn enable_color_write(&self) {
3980        self.gl.color_mask(true, true, true, true);
3981    }
3982
3983    pub fn disable_color_write(&self) {
3984        self.gl.color_mask(false, false, false, false);
3985    }
3986
3987    pub fn set_blend(&mut self, enable: bool) {
3988        if enable {
3989            self.gl.enable(gl::BLEND);
3990        } else {
3991            self.gl.disable(gl::BLEND);
3992        }
3993        #[cfg(debug_assertions)]
3994        {
3995            self.shader_is_ready = false;
3996        }
3997    }
3998
3999    fn set_blend_factors(
4000        &mut self,
4001        color: (gl::GLenum, gl::GLenum),
4002        alpha: (gl::GLenum, gl::GLenum),
4003    ) {
4004        self.gl.blend_equation(gl::FUNC_ADD);
4005        if color == alpha {
4006            self.gl.blend_func(color.0, color.1);
4007        } else {
4008            self.gl.blend_func_separate(color.0, color.1, alpha.0, alpha.1);
4009        }
4010        #[cfg(debug_assertions)]
4011        {
4012            self.shader_is_ready = false;
4013        }
4014    }
4015
4016    pub fn set_blend_mode_alpha(&mut self) {
4017        self.set_blend_factors(
4018            (gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA),
4019            (gl::ONE, gl::ONE_MINUS_SRC_ALPHA),
4020        );
4021    }
4022
4023    pub fn set_blend_mode_premultiplied_alpha(&mut self) {
4024        self.set_blend_factors(
4025            (gl::ONE, gl::ONE_MINUS_SRC_ALPHA),
4026            (gl::ONE, gl::ONE_MINUS_SRC_ALPHA),
4027        );
4028    }
4029
4030    pub fn set_blend_mode_premultiplied_dest_out(&mut self) {
4031        self.set_blend_factors(
4032            (gl::ZERO, gl::ONE_MINUS_SRC_ALPHA),
4033            (gl::ZERO, gl::ONE_MINUS_SRC_ALPHA),
4034        );
4035    }
4036
4037    pub fn set_blend_mode_multiply(&mut self) {
4038        self.set_blend_factors(
4039            (gl::ZERO, gl::SRC_COLOR),
4040            (gl::ZERO, gl::SRC_ALPHA),
4041        );
4042    }
4043    pub fn set_blend_mode_subpixel_pass0(&mut self) {
4044        self.set_blend_factors(
4045            (gl::ZERO, gl::ONE_MINUS_SRC_COLOR),
4046            (gl::ZERO, gl::ONE_MINUS_SRC_ALPHA),
4047        );
4048    }
4049    pub fn set_blend_mode_subpixel_pass1(&mut self) {
4050        self.set_blend_factors(
4051            (gl::ONE, gl::ONE),
4052            (gl::ONE, gl::ONE),
4053        );
4054    }
4055    pub fn set_blend_mode_subpixel_dual_source(&mut self) {
4056        self.set_blend_factors(
4057            (gl::ONE, gl::ONE_MINUS_SRC1_COLOR),
4058            (gl::ONE, gl::ONE_MINUS_SRC1_ALPHA),
4059        );
4060    }
4061    pub fn set_blend_mode_multiply_dual_source(&mut self) {
4062        self.set_blend_factors(
4063            (gl::ONE_MINUS_DST_ALPHA, gl::ONE_MINUS_SRC1_COLOR),
4064            (gl::ONE, gl::ONE_MINUS_SRC_ALPHA),
4065        );
4066    }
4067    pub fn set_blend_mode_screen(&mut self) {
4068        self.set_blend_factors(
4069            (gl::ONE, gl::ONE_MINUS_SRC_COLOR),
4070            (gl::ONE, gl::ONE_MINUS_SRC_ALPHA),
4071        );
4072    }
4073    pub fn set_blend_mode_plus_lighter(&mut self) {
4074        self.set_blend_factors(
4075            (gl::ONE, gl::ONE),
4076            (gl::ONE, gl::ONE),
4077        );
4078    }
4079    pub fn set_blend_mode_exclusion(&mut self) {
4080        self.set_blend_factors(
4081            (gl::ONE_MINUS_DST_COLOR, gl::ONE_MINUS_SRC_COLOR),
4082            (gl::ONE, gl::ONE_MINUS_SRC_ALPHA),
4083        );
4084    }
4085    pub fn set_blend_mode_show_overdraw(&mut self) {
4086        self.set_blend_factors(
4087            (gl::ONE, gl::ONE_MINUS_SRC_ALPHA),
4088            (gl::ONE, gl::ONE_MINUS_SRC_ALPHA),
4089        );
4090    }
4091
4092    pub fn set_blend_mode_max(&mut self) {
4093        self.gl
4094            .blend_func_separate(gl::ONE, gl::ONE, gl::ONE, gl::ONE);
4095        self.gl.blend_equation_separate(gl::MAX, gl::FUNC_ADD);
4096        #[cfg(debug_assertions)]
4097        {
4098            self.shader_is_ready = false;
4099        }
4100    }
4101    pub fn set_blend_mode_min(&mut self) {
4102        self.gl
4103            .blend_func_separate(gl::ONE, gl::ONE, gl::ONE, gl::ONE);
4104        self.gl.blend_equation_separate(gl::MIN, gl::FUNC_ADD);
4105        #[cfg(debug_assertions)]
4106        {
4107            self.shader_is_ready = false;
4108        }
4109    }
4110    pub fn set_blend_mode_advanced(&mut self, mode: MixBlendMode) {
4111        self.gl.blend_equation(match mode {
4112            MixBlendMode::Normal => {
4113                // blend factor only make sense for the normal mode
4114                self.gl.blend_func_separate(gl::ZERO, gl::SRC_COLOR, gl::ZERO, gl::SRC_ALPHA);
4115                gl::FUNC_ADD
4116            },
4117            MixBlendMode::PlusLighter => {
4118                return self.set_blend_mode_plus_lighter();
4119            },
4120            MixBlendMode::Multiply => gl::MULTIPLY_KHR,
4121            MixBlendMode::Screen => gl::SCREEN_KHR,
4122            MixBlendMode::Overlay => gl::OVERLAY_KHR,
4123            MixBlendMode::Darken => gl::DARKEN_KHR,
4124            MixBlendMode::Lighten => gl::LIGHTEN_KHR,
4125            MixBlendMode::ColorDodge => gl::COLORDODGE_KHR,
4126            MixBlendMode::ColorBurn => gl::COLORBURN_KHR,
4127            MixBlendMode::HardLight => gl::HARDLIGHT_KHR,
4128            MixBlendMode::SoftLight => gl::SOFTLIGHT_KHR,
4129            MixBlendMode::Difference => gl::DIFFERENCE_KHR,
4130            MixBlendMode::Exclusion => gl::EXCLUSION_KHR,
4131            MixBlendMode::Hue => gl::HSL_HUE_KHR,
4132            MixBlendMode::Saturation => gl::HSL_SATURATION_KHR,
4133            MixBlendMode::Color => gl::HSL_COLOR_KHR,
4134            MixBlendMode::Luminosity => gl::HSL_LUMINOSITY_KHR,
4135        });
4136        #[cfg(debug_assertions)]
4137        {
4138            self.shader_is_ready = false;
4139        }
4140    }
4141
4142    pub fn supports_extension(&self, extension: &str) -> bool {
4143        supports_extension(&self.extensions, extension)
4144    }
4145
4146    pub fn echo_driver_messages(&self) {
4147        if self.capabilities.supports_khr_debug {
4148            Device::log_driver_messages(self.gl());
4149        }
4150    }
4151
4152    fn log_driver_messages(gl: &dyn gl::Gl) {
4153        for msg in gl.get_debug_messages() {
4154            let level = match msg.severity {
4155                gl::DEBUG_SEVERITY_HIGH => Level::Error,
4156                gl::DEBUG_SEVERITY_MEDIUM => Level::Warn,
4157                gl::DEBUG_SEVERITY_LOW => Level::Info,
4158                gl::DEBUG_SEVERITY_NOTIFICATION => Level::Debug,
4159                _ => Level::Trace,
4160            };
4161            let ty = match msg.ty {
4162                gl::DEBUG_TYPE_ERROR => "error",
4163                gl::DEBUG_TYPE_DEPRECATED_BEHAVIOR => "deprecated",
4164                gl::DEBUG_TYPE_UNDEFINED_BEHAVIOR => "undefined",
4165                gl::DEBUG_TYPE_PORTABILITY => "portability",
4166                gl::DEBUG_TYPE_PERFORMANCE => "perf",
4167                gl::DEBUG_TYPE_MARKER => "marker",
4168                gl::DEBUG_TYPE_PUSH_GROUP => "group push",
4169                gl::DEBUG_TYPE_POP_GROUP => "group pop",
4170                gl::DEBUG_TYPE_OTHER => "other",
4171                _ => "?",
4172            };
4173            log!(level, "({}) {}", ty, msg.message);
4174        }
4175    }
4176
4177    pub fn gl_describe_format(&self, format: ImageFormat) -> FormatDesc {
4178        match format {
4179            ImageFormat::R8 => FormatDesc {
4180                internal: gl::R8,
4181                external: gl::RED,
4182                read: gl::RED,
4183                pixel_type: gl::UNSIGNED_BYTE,
4184            },
4185            ImageFormat::R16 => FormatDesc {
4186                internal: gl::R16,
4187                external: gl::RED,
4188                read: gl::RED,
4189                pixel_type: gl::UNSIGNED_SHORT,
4190            },
4191            ImageFormat::BGRA8 => {
4192                FormatDesc {
4193                    internal: self.bgra_formats.internal,
4194                    external: self.bgra_formats.external,
4195                    read: gl::BGRA,
4196                    pixel_type: self.bgra_pixel_type,
4197                }
4198            },
4199            ImageFormat::RGBA8 => {
4200                FormatDesc {
4201                    internal: gl::RGBA8,
4202                    external: gl::RGBA,
4203                    read: gl::RGBA,
4204                    pixel_type: gl::UNSIGNED_BYTE,
4205                }
4206            },
4207            ImageFormat::RGBAF32 => FormatDesc {
4208                internal: gl::RGBA32F,
4209                external: gl::RGBA,
4210                read: gl::RGBA,
4211                pixel_type: gl::FLOAT,
4212            },
4213            ImageFormat::RGBAI32 => FormatDesc {
4214                internal: gl::RGBA32I,
4215                external: gl::RGBA_INTEGER,
4216                read: gl::RGBA_INTEGER,
4217                pixel_type: gl::INT,
4218            },
4219            ImageFormat::RG8 => FormatDesc {
4220                internal: gl::RG8,
4221                external: gl::RG,
4222                read: gl::RG,
4223                pixel_type: gl::UNSIGNED_BYTE,
4224            },
4225            ImageFormat::RG16 => FormatDesc {
4226                internal: gl::RG16,
4227                external: gl::RG,
4228                read: gl::RG,
4229                pixel_type: gl::UNSIGNED_SHORT,
4230            },
4231        }
4232    }
4233
4234    /// Generates a memory report for the resources managed by the device layer.
4235    pub fn report_memory(&self, size_op_funs: &MallocSizeOfOps, swgl: *mut c_void) -> MemoryReport {
4236        let mut report = MemoryReport::default();
4237        report.depth_target_textures += self.depth_targets_memory();
4238
4239        #[cfg(feature = "sw_compositor")]
4240        if !swgl.is_null() {
4241            report.swgl += swgl::Context::from(swgl).report_memory(size_op_funs.size_of_op);
4242        }
4243        // unconditionally use swgl stuff
4244        let _ = size_op_funs;
4245        let _ = swgl;
4246        report
4247    }
4248
4249    pub fn depth_targets_memory(&self) -> usize {
4250        let mut total = 0;
4251        for dim in self.depth_targets.keys() {
4252            total += depth_target_size_in_bytes(dim);
4253        }
4254
4255        total
4256    }
4257}
4258
4259pub struct FormatDesc {
4260    /// Format the texel data is internally stored in within a texture.
4261    pub internal: gl::GLenum,
4262    /// Format that we expect the data to be provided when filling the texture.
4263    pub external: gl::GLuint,
4264    /// Format to read the texels as, so that they can be uploaded as `external`
4265    /// later on.
4266    pub read: gl::GLuint,
4267    /// Associated pixel type.
4268    pub pixel_type: gl::GLuint,
4269}
4270
4271#[derive(Debug)]
4272struct UploadChunk<'a> {
4273    rect: DeviceIntRect,
4274    stride: Option<i32>,
4275    offset: usize,
4276    format_override: Option<ImageFormat>,
4277    texture: &'a Texture,
4278}
4279
4280#[derive(Debug)]
4281struct PixelBuffer<'a> {
4282    size_used: usize,
4283    // small vector avoids heap allocation for a single chunk
4284    chunks: SmallVec<[UploadChunk<'a>; 1]>,
4285    inner: UploadPBO,
4286    mapping: &'a mut [mem::MaybeUninit<u8>],
4287}
4288
4289impl<'a> PixelBuffer<'a> {
4290    fn new(
4291        pbo: UploadPBO,
4292    ) -> Self {
4293        let mapping = unsafe {
4294            slice::from_raw_parts_mut(pbo.mapping.get_ptr().as_ptr(), pbo.pbo.reserved_size)
4295        };
4296        Self {
4297            size_used: 0,
4298            chunks: SmallVec::new(),
4299            inner: pbo,
4300            mapping,
4301        }
4302    }
4303
4304    fn flush_chunks(&mut self, device: &mut Device) {
4305        for chunk in self.chunks.drain(..) {
4306            TextureUploader::update_impl(device, chunk);
4307        }
4308    }
4309}
4310
4311impl<'a> Drop for PixelBuffer<'a> {
4312    fn drop(&mut self) {
4313        assert_eq!(self.chunks.len(), 0, "PixelBuffer must be flushed before dropping.");
4314    }
4315}
4316
4317#[derive(Debug)]
4318enum PBOMapping {
4319    Unmapped,
4320    Transient(ptr::NonNull<mem::MaybeUninit<u8>>),
4321    Persistent(ptr::NonNull<mem::MaybeUninit<u8>>),
4322}
4323
4324impl PBOMapping {
4325    fn get_ptr(&self) -> ptr::NonNull<mem::MaybeUninit<u8>> {
4326        match self {
4327            PBOMapping::Unmapped => unreachable!("Cannot get pointer to unmapped PBO."),
4328            PBOMapping::Transient(ptr) => *ptr,
4329            PBOMapping::Persistent(ptr) => *ptr,
4330        }
4331    }
4332}
4333
4334/// A PBO for uploading texture data, managed by UploadPBOPool.
4335#[derive(Debug)]
4336struct UploadPBO {
4337    pbo: PBO,
4338    mapping: PBOMapping,
4339    can_recycle: bool,
4340}
4341
4342impl UploadPBO {
4343    fn empty() -> Self {
4344        Self {
4345            pbo: PBO {
4346                id: 0,
4347                reserved_size: 0,
4348            },
4349            mapping: PBOMapping::Unmapped,
4350            can_recycle: false,
4351        }
4352    }
4353}
4354
4355/// Allocates and recycles PBOs used for uploading texture data.
4356/// Tries to allocate and recycle PBOs of a fixed size, but will make exceptions when
4357/// a larger buffer is required or to work around driver bugs.
4358pub struct UploadPBOPool {
4359    /// Usage hint to provide to the driver for optimizations.
4360    usage_hint: VertexUsageHint,
4361    /// The preferred size, in bytes, of the buffers to allocate.
4362    default_size: usize,
4363    /// List of allocated PBOs ready to be re-used.
4364    available_buffers: Vec<UploadPBO>,
4365    /// PBOs which have been returned during the current frame,
4366    /// and do not yet have an associated sync object.
4367    returned_buffers: Vec<UploadPBO>,
4368    /// PBOs which are waiting until their sync object is signalled,
4369    /// indicating they can are ready to be re-used.
4370    waiting_buffers: Vec<(gl::GLsync, Vec<UploadPBO>)>,
4371    /// PBOs which have been orphaned.
4372    /// We can recycle their IDs but must reallocate their storage.
4373    orphaned_buffers: Vec<PBO>,
4374}
4375
4376impl UploadPBOPool {
4377    pub fn new(device: &mut Device, default_size: usize) -> Self {
4378        let usage_hint = match device.upload_method {
4379            UploadMethod::Immediate => VertexUsageHint::Stream,
4380            UploadMethod::PixelBuffer(usage_hint) => usage_hint,
4381        };
4382        Self {
4383            usage_hint,
4384            default_size,
4385            available_buffers: Vec::new(),
4386            returned_buffers: Vec::new(),
4387            waiting_buffers: Vec::new(),
4388            orphaned_buffers: Vec::new(),
4389        }
4390    }
4391
4392    /// To be called at the beginning of a series of uploads.
4393    /// Moves any buffers which are now ready to be used from the waiting list to the ready list.
4394    pub fn begin_frame(&mut self, device: &mut Device) {
4395        // Iterate through the waiting buffers and check if each fence has been signalled.
4396        // If a fence is signalled, move its corresponding buffers to the available list.
4397        // On error, delete the buffers. Stop when we find the first non-signalled fence,
4398        // and clean up the signalled fences.
4399        let mut first_not_signalled = self.waiting_buffers.len();
4400        for (i, (sync, buffers)) in self.waiting_buffers.iter_mut().enumerate() {
4401            match device.gl.client_wait_sync(*sync, 0, 0) {
4402                gl::TIMEOUT_EXPIRED => {
4403                    first_not_signalled = i;
4404                    break;
4405                },
4406                gl::ALREADY_SIGNALED | gl::CONDITION_SATISFIED => {
4407                    self.available_buffers.extend(buffers.drain(..));
4408                }
4409                gl::WAIT_FAILED | _ => {
4410                    warn!("glClientWaitSync error in UploadPBOPool::begin_frame()");
4411                    for buffer in buffers.drain(..) {
4412                        device.delete_pbo(buffer.pbo);
4413                    }
4414                }
4415            }
4416        }
4417
4418        // Delete signalled fences, and remove their now-empty Vecs from waiting_buffers.
4419        for (sync, _) in self.waiting_buffers.drain(0..first_not_signalled) {
4420            device.gl.delete_sync(sync);
4421        }
4422    }
4423
4424    // To be called at the end of a series of uploads.
4425    // Creates a sync object, and adds the buffers returned during this frame to waiting_buffers.
4426    pub fn end_frame(&mut self, device: &mut Device) {
4427        if !self.returned_buffers.is_empty() {
4428            let sync = device.gl.fence_sync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0);
4429            if !sync.is_null() {
4430                self.waiting_buffers.push((sync, mem::replace(&mut self.returned_buffers, Vec::new())))
4431            } else {
4432                warn!("glFenceSync error in UploadPBOPool::end_frame()");
4433
4434                for buffer in self.returned_buffers.drain(..) {
4435                    device.delete_pbo(buffer.pbo);
4436                }
4437            }
4438        }
4439    }
4440
4441    /// Obtain a PBO, either by reusing an existing PBO or allocating a new one.
4442    /// min_size specifies the minimum required size of the PBO. The returned PBO
4443    /// may be larger than required.
4444    fn get_pbo(&mut self, device: &mut Device, min_size: usize) -> Result<UploadPBO, String> {
4445
4446        // If min_size is smaller than our default size, then use the default size.
4447        // The exception to this is when due to driver bugs we cannot upload from
4448        // offsets other than zero within a PBO. In this case, there is no point in
4449        // allocating buffers larger than required, as they cannot be shared.
4450        let (can_recycle, size) = if min_size <= self.default_size && device.capabilities.supports_nonzero_pbo_offsets {
4451            (true, self.default_size)
4452        } else {
4453            (false, min_size)
4454        };
4455
4456        // Try to recycle an already allocated PBO.
4457        if can_recycle {
4458            if let Some(mut buffer) = self.available_buffers.pop() {
4459                assert_eq!(buffer.pbo.reserved_size, size);
4460                assert!(buffer.can_recycle);
4461
4462                device.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, buffer.pbo.id);
4463
4464                match buffer.mapping {
4465                    PBOMapping::Unmapped => {
4466                        // If buffer was unmapped then transiently map it.
4467                        let ptr = device.gl.map_buffer_range(
4468                            gl::PIXEL_UNPACK_BUFFER,
4469                            0,
4470                            buffer.pbo.reserved_size as _,
4471                            gl::MAP_WRITE_BIT | gl::MAP_UNSYNCHRONIZED_BIT,
4472                        ) as *mut _;
4473
4474                        let ptr = ptr::NonNull::new(ptr).ok_or_else(
4475                            || format!("Failed to transiently map PBO of size {} bytes", buffer.pbo.reserved_size)
4476                        )?;
4477
4478                        buffer.mapping = PBOMapping::Transient(ptr);
4479                    }
4480                    PBOMapping::Transient(_) => {
4481                        unreachable!("Transiently mapped UploadPBO must be unmapped before returning to pool.");
4482                    }
4483                    PBOMapping::Persistent(_) => {
4484                    }
4485                }
4486
4487                return Ok(buffer);
4488            }
4489        }
4490
4491        // Try to recycle a PBO ID (but not its allocation) from a previously allocated PBO.
4492        // If there are none available, create a new PBO.
4493        let mut pbo = match self.orphaned_buffers.pop() {
4494            Some(pbo) => pbo,
4495            None => device.create_pbo(),
4496        };
4497
4498        assert_eq!(pbo.reserved_size, 0);
4499        pbo.reserved_size = size;
4500
4501        device.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, pbo.id);
4502        let mapping = if device.capabilities.supports_buffer_storage && can_recycle {
4503            device.gl.buffer_storage(
4504                gl::PIXEL_UNPACK_BUFFER,
4505                pbo.reserved_size as _,
4506                ptr::null(),
4507                gl::MAP_WRITE_BIT | gl::MAP_PERSISTENT_BIT,
4508            );
4509            let ptr = device.gl.map_buffer_range(
4510                gl::PIXEL_UNPACK_BUFFER,
4511                0,
4512                pbo.reserved_size as _,
4513                // GL_MAP_COHERENT_BIT doesn't seem to work on Adreno, so use glFlushMappedBufferRange.
4514                // kvark notes that coherent memory can be faster on some platforms, such as nvidia,
4515                // so in the future we could choose which to use at run time.
4516                gl::MAP_WRITE_BIT | gl::MAP_PERSISTENT_BIT | gl::MAP_FLUSH_EXPLICIT_BIT,
4517            ) as *mut _;
4518
4519            let ptr = ptr::NonNull::new(ptr).ok_or_else(
4520                || format!("Failed to transiently map PBO of size {} bytes", pbo.reserved_size)
4521            )?;
4522
4523            PBOMapping::Persistent(ptr)
4524        } else {
4525            device.gl.buffer_data_untyped(
4526                gl::PIXEL_UNPACK_BUFFER,
4527                pbo.reserved_size as _,
4528                ptr::null(),
4529                self.usage_hint.to_gl(),
4530            );
4531            let ptr = device.gl.map_buffer_range(
4532                gl::PIXEL_UNPACK_BUFFER,
4533                0,
4534                pbo.reserved_size as _,
4535                // Unlike the above code path, where we are re-mapping a buffer that has previously been unmapped,
4536                // this buffer has just been created there is no need for GL_MAP_UNSYNCHRONIZED_BIT.
4537                gl::MAP_WRITE_BIT,
4538            ) as *mut _;
4539
4540            let ptr = ptr::NonNull::new(ptr).ok_or_else(
4541                || format!("Failed to transiently map PBO of size {} bytes", pbo.reserved_size)
4542            )?;
4543
4544            PBOMapping::Transient(ptr)
4545        };
4546
4547        Ok(UploadPBO { pbo, mapping, can_recycle })
4548    }
4549
4550    /// Returns a PBO to the pool. If the PBO is recyclable it is placed in the waiting list.
4551    /// Otherwise we orphan the allocation immediately, and will subsequently reuse just the ID.
4552    fn return_pbo(&mut self, device: &mut Device, mut buffer: UploadPBO) {
4553        assert!(
4554            !matches!(buffer.mapping, PBOMapping::Transient(_)),
4555            "Transiently mapped UploadPBO must be unmapped before returning to pool.",
4556        );
4557
4558        if buffer.can_recycle {
4559            self.returned_buffers.push(buffer);
4560        } else {
4561            device.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, buffer.pbo.id);
4562            device.gl.buffer_data_untyped(
4563                gl::PIXEL_UNPACK_BUFFER,
4564                0,
4565                ptr::null(),
4566                gl::STREAM_DRAW,
4567            );
4568            buffer.pbo.reserved_size = 0;
4569            self.orphaned_buffers.push(buffer.pbo);
4570        }
4571
4572        device.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, 0);
4573    }
4574
4575    /// Frees all allocated buffers in response to a memory pressure event.
4576    pub fn on_memory_pressure(&mut self, device: &mut Device) {
4577        for buffer in self.available_buffers.drain(..) {
4578            device.delete_pbo(buffer.pbo);
4579        }
4580        for buffer in self.returned_buffers.drain(..) {
4581            device.delete_pbo(buffer.pbo)
4582        }
4583        for (sync, buffers) in self.waiting_buffers.drain(..) {
4584            device.gl.delete_sync(sync);
4585            for buffer in buffers {
4586                device.delete_pbo(buffer.pbo)
4587            }
4588        }
4589        // There is no need to delete orphaned PBOs on memory pressure.
4590    }
4591
4592    /// Generates a memory report.
4593    pub fn report_memory(&self) -> MemoryReport {
4594        let mut report = MemoryReport::default();
4595        for buffer in &self.available_buffers {
4596            report.texture_upload_pbos += buffer.pbo.reserved_size;
4597        }
4598        for buffer in &self.returned_buffers {
4599            report.texture_upload_pbos += buffer.pbo.reserved_size;
4600        }
4601        for (_, buffers) in &self.waiting_buffers {
4602            for buffer in buffers {
4603                report.texture_upload_pbos += buffer.pbo.reserved_size;
4604            }
4605        }
4606        report
4607    }
4608
4609    pub fn deinit(&mut self, device: &mut Device) {
4610        for buffer in self.available_buffers.drain(..) {
4611            device.delete_pbo(buffer.pbo);
4612        }
4613        for buffer in self.returned_buffers.drain(..) {
4614            device.delete_pbo(buffer.pbo)
4615        }
4616        for (sync, buffers) in self.waiting_buffers.drain(..) {
4617            device.gl.delete_sync(sync);
4618            for buffer in buffers {
4619                device.delete_pbo(buffer.pbo)
4620            }
4621        }
4622        for pbo in self.orphaned_buffers.drain(..) {
4623            device.delete_pbo(pbo);
4624        }
4625    }
4626}
4627
4628/// Used to perform a series of texture uploads.
4629/// Create using Device::upload_texture(). Perform a series of uploads using either
4630/// upload(), or stage() and upload_staged(), then call flush().
4631pub struct TextureUploader<'a> {
4632    /// A list of buffers containing uploads that need to be flushed.
4633    buffers: Vec<PixelBuffer<'a>>,
4634    /// Pool used to obtain PBOs to fill with texture data.
4635    pub pbo_pool: &'a mut UploadPBOPool,
4636}
4637
4638impl<'a> Drop for TextureUploader<'a> {
4639    fn drop(&mut self) {
4640        assert!(
4641            thread::panicking() || self.buffers.is_empty(),
4642            "TextureUploader must be flushed before it is dropped."
4643        );
4644    }
4645}
4646
4647/// A buffer used to manually stage data to be uploaded to a texture.
4648/// Created by calling TextureUploader::stage(), the data can then be written to via get_mapping().
4649#[derive(Debug)]
4650pub struct UploadStagingBuffer<'a> {
4651    /// The PixelBuffer containing this upload.
4652    buffer: PixelBuffer<'a>,
4653    /// The offset of this upload within the PixelBuffer.
4654    offset: usize,
4655    /// The size of this upload.
4656    size: usize,
4657    /// The stride of the data within the buffer.
4658    stride: usize,
4659}
4660
4661impl<'a> UploadStagingBuffer<'a> {
4662    /// Returns the required stride of the data to be written to the buffer.
4663    pub fn get_stride(&self) -> usize {
4664        self.stride
4665    }
4666
4667    /// Returns a mapping of the data in the buffer, to be written to.
4668    pub fn get_mapping(&mut self) -> &mut [mem::MaybeUninit<u8>] {
4669        &mut self.buffer.mapping[self.offset..self.offset + self.size]
4670    }
4671}
4672
4673impl<'a> TextureUploader<'a> {
4674    /// Returns an UploadStagingBuffer which can be used to manually stage data to be uploaded.
4675    /// Once the data has been staged, it can be uploaded with upload_staged().
4676    pub fn stage(
4677        &mut self,
4678        device: &mut Device,
4679        format: ImageFormat,
4680        size: DeviceIntSize,
4681    ) -> Result<UploadStagingBuffer<'a>, String> {
4682        assert!(matches!(device.upload_method, UploadMethod::PixelBuffer(_)), "Texture uploads should only be staged when using pixel buffers.");
4683
4684        // for optimal PBO texture uploads the offset and stride of the data in
4685        // the buffer may have to be a multiple of a certain value.
4686        let (dst_size, dst_stride) = device.required_upload_size_and_stride(
4687            size,
4688            format,
4689        );
4690
4691        // Find a pixel buffer with enough space remaining, creating a new one if required.
4692        let buffer_index = self.buffers.iter().position(|buffer| {
4693            buffer.size_used + dst_size <= buffer.inner.pbo.reserved_size
4694        });
4695        let buffer = match buffer_index {
4696            Some(i) => self.buffers.swap_remove(i),
4697            None => PixelBuffer::new(self.pbo_pool.get_pbo(device, dst_size)?),
4698        };
4699
4700        if !device.capabilities.supports_nonzero_pbo_offsets {
4701            assert_eq!(buffer.size_used, 0, "PBO uploads from non-zero offset are not supported.");
4702        }
4703        assert!(buffer.size_used + dst_size <= buffer.inner.pbo.reserved_size, "PixelBuffer is too small");
4704
4705        let offset = buffer.size_used;
4706
4707        Ok(UploadStagingBuffer {
4708            buffer,
4709            offset,
4710            size: dst_size,
4711            stride: dst_stride,
4712        })
4713    }
4714
4715    /// Uploads manually staged texture data to the specified texture.
4716    pub fn upload_staged(
4717        &mut self,
4718        device: &mut Device,
4719        texture: &'a Texture,
4720        rect: DeviceIntRect,
4721        format_override: Option<ImageFormat>,
4722        mut staging_buffer: UploadStagingBuffer<'a>,
4723    ) -> usize {
4724        let size = staging_buffer.size;
4725
4726        staging_buffer.buffer.chunks.push(UploadChunk {
4727            rect,
4728            stride: Some(staging_buffer.stride as i32),
4729            offset: staging_buffer.offset,
4730            format_override,
4731            texture,
4732        });
4733        staging_buffer.buffer.size_used += staging_buffer.size;
4734
4735        // Flush the buffer if it is full, otherwise return it to the uploader for further use.
4736        if staging_buffer.buffer.size_used < staging_buffer.buffer.inner.pbo.reserved_size {
4737            self.buffers.push(staging_buffer.buffer);
4738        } else {
4739            Self::flush_buffer(device, self.pbo_pool, staging_buffer.buffer);
4740        }
4741
4742        size
4743    }
4744
4745    /// Uploads texture data to the specified texture.
4746    pub fn upload<T>(
4747        &mut self,
4748        device: &mut Device,
4749        texture: &'a Texture,
4750        mut rect: DeviceIntRect,
4751        stride: Option<i32>,
4752        format_override: Option<ImageFormat>,
4753        data: *const T,
4754        len: usize,
4755    ) -> usize {
4756        // Textures dimensions may have been clamped by the hardware. Crop the
4757        // upload region to match.
4758        let cropped = rect.intersection(
4759            &DeviceIntRect::from_size(texture.get_dimensions())
4760        );
4761        if cfg!(debug_assertions) && cropped.map_or(true, |r| r != rect) {
4762            warn!("Cropping texture upload {:?} to {:?}", rect, cropped);
4763        }
4764        rect = match cropped {
4765            None => return 0,
4766            Some(r) => r,
4767        };
4768
4769        let bytes_pp = texture.format.bytes_per_pixel() as usize;
4770        let width_bytes = rect.width() as usize * bytes_pp;
4771
4772        let src_stride = stride.map_or(width_bytes, |stride| {
4773            assert!(stride >= 0);
4774            stride as usize
4775        });
4776        let src_size = (rect.height() as usize - 1) * src_stride + width_bytes;
4777        assert!(src_size <= len * mem::size_of::<T>());
4778
4779        match device.upload_method {
4780            UploadMethod::Immediate => {
4781                if cfg!(debug_assertions) {
4782                    let mut bound_buffer = [0];
4783                    unsafe {
4784                        device.gl.get_integer_v(gl::PIXEL_UNPACK_BUFFER_BINDING, &mut bound_buffer);
4785                    }
4786                    assert_eq!(bound_buffer[0], 0, "GL_PIXEL_UNPACK_BUFFER must not be bound for immediate uploads.");
4787                }
4788
4789                Self::update_impl(device, UploadChunk {
4790                    rect,
4791                    stride: Some(src_stride as i32),
4792                    offset: data as _,
4793                    format_override,
4794                    texture,
4795                });
4796
4797                width_bytes * rect.height() as usize
4798            }
4799            UploadMethod::PixelBuffer(_) => {
4800                let mut staging_buffer = match self.stage(device, texture.format, rect.size()) {
4801                    Ok(staging_buffer) => staging_buffer,
4802                    Err(_) => return 0,
4803                };
4804                let dst_stride = staging_buffer.get_stride();
4805
4806                unsafe {
4807                    let src: &[mem::MaybeUninit<u8>] = slice::from_raw_parts(data as *const _, src_size);
4808
4809                    if src_stride == dst_stride {
4810                        // the stride is already optimal, so simply copy
4811                        // the data as-is in to the buffer
4812                        staging_buffer.get_mapping()[..src_size].copy_from_slice(src);
4813                    } else {
4814                        // copy the data line-by-line in to the buffer so
4815                        // that it has an optimal stride
4816                        for y in 0..rect.height() as usize {
4817                            let src_start = y * src_stride;
4818                            let src_end = src_start + width_bytes;
4819                            let dst_start = y * staging_buffer.get_stride();
4820                            let dst_end = dst_start + width_bytes;
4821
4822                            staging_buffer.get_mapping()[dst_start..dst_end].copy_from_slice(&src[src_start..src_end])
4823                        }
4824                    }
4825                }
4826
4827                self.upload_staged(device, texture, rect, format_override, staging_buffer)
4828            }
4829        }
4830    }
4831
4832    fn flush_buffer(device: &mut Device, pbo_pool: &mut UploadPBOPool, mut buffer: PixelBuffer) {
4833        device.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, buffer.inner.pbo.id);
4834        match buffer.inner.mapping {
4835            PBOMapping::Unmapped => unreachable!("UploadPBO should be mapped at this stage."),
4836            PBOMapping::Transient(_) => {
4837                device.gl.unmap_buffer(gl::PIXEL_UNPACK_BUFFER);
4838                buffer.inner.mapping = PBOMapping::Unmapped;
4839            }
4840            PBOMapping::Persistent(_) => {
4841                device.gl.flush_mapped_buffer_range(gl::PIXEL_UNPACK_BUFFER, 0, buffer.size_used as _);
4842            }
4843        }
4844        buffer.flush_chunks(device);
4845        let pbo = mem::replace(&mut buffer.inner, UploadPBO::empty());
4846        pbo_pool.return_pbo(device, pbo);
4847    }
4848
4849    /// Flushes all pending texture uploads. Must be called after all
4850    /// required upload() or upload_staged() calls have been made.
4851    pub fn flush(mut self, device: &mut Device) {
4852        for buffer in self.buffers.drain(..) {
4853            Self::flush_buffer(device, self.pbo_pool, buffer);
4854        }
4855
4856        device.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, 0);
4857    }
4858
4859    fn update_impl(device: &mut Device, chunk: UploadChunk) {
4860        device.bind_texture(DEFAULT_TEXTURE, chunk.texture, Swizzle::default());
4861
4862        let format = chunk.format_override.unwrap_or(chunk.texture.format);
4863        let (gl_format, bpp, data_type) = match format {
4864            ImageFormat::R8 => (gl::RED, 1, gl::UNSIGNED_BYTE),
4865            ImageFormat::R16 => (gl::RED, 2, gl::UNSIGNED_SHORT),
4866            ImageFormat::BGRA8 => (device.bgra_formats.external, 4, device.bgra_pixel_type),
4867            ImageFormat::RGBA8 => (gl::RGBA, 4, gl::UNSIGNED_BYTE),
4868            ImageFormat::RG8 => (gl::RG, 2, gl::UNSIGNED_BYTE),
4869            ImageFormat::RG16 => (gl::RG, 4, gl::UNSIGNED_SHORT),
4870            ImageFormat::RGBAF32 => (gl::RGBA, 16, gl::FLOAT),
4871            ImageFormat::RGBAI32 => (gl::RGBA_INTEGER, 16, gl::INT),
4872        };
4873
4874        let row_length = match chunk.stride {
4875            Some(value) => value / bpp,
4876            None => chunk.texture.size.width,
4877        };
4878
4879        if chunk.stride.is_some() {
4880            device.gl.pixel_store_i(
4881                gl::UNPACK_ROW_LENGTH,
4882                row_length as _,
4883            );
4884        }
4885
4886        let pos = chunk.rect.min;
4887        let size = chunk.rect.size();
4888
4889        match chunk.texture.target {
4890            gl::TEXTURE_2D | gl::TEXTURE_RECTANGLE | gl::TEXTURE_EXTERNAL_OES => {
4891                device.gl.tex_sub_image_2d_pbo(
4892                    chunk.texture.target,
4893                    0,
4894                    pos.x as _,
4895                    pos.y as _,
4896                    size.width as _,
4897                    size.height as _,
4898                    gl_format,
4899                    data_type,
4900                    chunk.offset,
4901                );
4902            }
4903            _ => panic!("BUG: Unexpected texture target!"),
4904        }
4905
4906        // If using tri-linear filtering, build the mip-map chain for this texture.
4907        if chunk.texture.filter == TextureFilter::Trilinear {
4908            device.gl.generate_mipmap(chunk.texture.target);
4909        }
4910
4911        // Reset row length to 0, otherwise the stride would apply to all texture uploads.
4912        if chunk.stride.is_some() {
4913            device.gl.pixel_store_i(gl::UNPACK_ROW_LENGTH, 0 as _);
4914        }
4915    }
4916}
4917
4918fn texels_to_u8_slice<T: Texel>(texels: &[T]) -> &[u8] {
4919    unsafe {
4920        slice::from_raw_parts(texels.as_ptr() as *const u8, texels.len() * mem::size_of::<T>())
4921    }
4922}