wgpu_hal/gles/
adapter.rs

1use alloc::{borrow::ToOwned as _, format, string::String, sync::Arc, vec, vec::Vec};
2
3use glow::HasContext;
4use wgpu_sync::{atomic::AtomicU8, Mutex};
5use wgt::AstcChannel;
6
7use crate::auxil::db;
8use crate::gles::ShaderClearProgram;
9
10// https://webgl2fundamentals.org/webgl/lessons/webgl-data-textures.html
11
12const GL_UNMASKED_VENDOR_WEBGL: u32 = 0x9245;
13const GL_UNMASKED_RENDERER_WEBGL: u32 = 0x9246;
14
15impl super::Adapter {
16    pub fn get_glsl_version(&self) -> naga::back::glsl::Version {
17        self.shared.shading_language_version
18    }
19
20    /// Note that this function is intentionally lenient in regards to parsing,
21    /// and will try to recover at least the first two version numbers without
22    /// resulting in an `Err`.
23    /// # Notes
24    /// `WebGL 2` version returned as `OpenGL ES 3.0`
25    fn parse_version(mut src: &str) -> Result<(u8, u8), crate::InstanceError> {
26        let webgl_sig = "WebGL ";
27        // According to the WebGL specification
28        // VERSION  WebGL<space>1.0<space><vendor-specific information>
29        // SHADING_LANGUAGE_VERSION WebGL<space>GLSL<space>ES<space>1.0<space><vendor-specific information>
30        let is_webgl = src.starts_with(webgl_sig);
31        if is_webgl {
32            let pos = src.rfind(webgl_sig).unwrap_or(0);
33            src = &src[pos + webgl_sig.len()..];
34        } else {
35            let es_sig = " ES ";
36            match src.rfind(es_sig) {
37                Some(pos) => {
38                    src = &src[pos + es_sig.len()..];
39                }
40                None => {
41                    return Err(crate::InstanceError::new(format!(
42                        "OpenGL version {src:?} does not contain 'ES'"
43                    )));
44                }
45            }
46        };
47
48        let glsl_es_sig = "GLSL ES ";
49        let is_glsl = match src.find(glsl_es_sig) {
50            Some(pos) => {
51                src = &src[pos + glsl_es_sig.len()..];
52                true
53            }
54            None => false,
55        };
56
57        Self::parse_full_version(src).map(|(major, minor)| {
58            (
59                // Return WebGL 2.0 version as OpenGL ES 3.0
60                if is_webgl && !is_glsl {
61                    major + 1
62                } else {
63                    major
64                },
65                minor,
66            )
67        })
68    }
69
70    /// According to the OpenGL specification, the version information is
71    /// expected to follow the following syntax:
72    ///
73    /// ~~~bnf
74    /// <major>       ::= <number>
75    /// <minor>       ::= <number>
76    /// <revision>    ::= <number>
77    /// <vendor-info> ::= <string>
78    /// <release>     ::= <major> "." <minor> ["." <release>]
79    /// <version>     ::= <release> [" " <vendor-info>]
80    /// ~~~
81    ///
82    /// Note that this function is intentionally lenient in regards to parsing,
83    /// and will try to recover at least the first two version numbers without
84    /// resulting in an `Err`.
85    pub(super) fn parse_full_version(src: &str) -> Result<(u8, u8), crate::InstanceError> {
86        let (version, _vendor_info) = match src.find(' ') {
87            Some(i) => (&src[..i], src[i + 1..].to_owned()),
88            None => (src, String::new()),
89        };
90
91        // TODO: make this even more lenient so that we can also accept
92        // `<major> "." <minor> [<???>]`
93        let mut it = version.split('.');
94        let major = it.next().and_then(|s| s.parse().ok());
95        let minor = it.next().and_then(|s| {
96            let trimmed = if s.starts_with('0') {
97                "0"
98            } else {
99                s.trim_end_matches('0')
100            };
101            trimmed.parse().ok()
102        });
103
104        match (major, minor) {
105            (Some(major), Some(minor)) => Ok((major, minor)),
106            _ => Err(crate::InstanceError::new(format!(
107                "unable to extract OpenGL version from {version:?}"
108            ))),
109        }
110    }
111
112    fn make_info(vendor_orig: String, renderer_orig: String, version: String) -> wgt::AdapterInfo {
113        let vendor = vendor_orig.to_lowercase();
114        let renderer = renderer_orig.to_lowercase();
115
116        // opengl has no way to discern device_type, so we can try to infer it from the renderer string
117        let strings_that_imply_integrated = [
118            " xpress", // space here is on purpose so we don't match express
119            "amd renoir",
120            "radeon hd 4200",
121            "radeon hd 4250",
122            "radeon hd 4290",
123            "radeon hd 4270",
124            "radeon hd 4225",
125            "radeon hd 3100",
126            "radeon hd 3200",
127            "radeon hd 3000",
128            "radeon hd 3300",
129            "radeon(tm) r4 graphics",
130            "radeon(tm) r5 graphics",
131            "radeon(tm) r6 graphics",
132            "radeon(tm) r7 graphics",
133            "radeon r7 graphics",
134            "nforce", // all nvidia nforce are integrated
135            "tegra",  // all nvidia tegra are integrated
136            "shield", // all nvidia shield are integrated
137            "igp",
138            "mali",
139            "intel",
140            "v3d",
141            "apple m", // all apple m are integrated
142        ];
143        let strings_that_imply_cpu = ["mesa offscreen", "swiftshader", "llvmpipe"];
144
145        //TODO: handle Intel Iris XE as discreet
146        let inferred_device_type = if vendor.contains("qualcomm")
147            || vendor.contains("intel")
148            || strings_that_imply_integrated
149                .iter()
150                .any(|&s| renderer.contains(s))
151        {
152            wgt::DeviceType::IntegratedGpu
153        } else if strings_that_imply_cpu.iter().any(|&s| renderer.contains(s)) {
154            wgt::DeviceType::Cpu
155        } else {
156            // At this point the Device type is Unknown.
157            // It's most likely DiscreteGpu, but we do not know for sure.
158            // Use "Other" to avoid possibly making incorrect assumptions.
159            // Note that if this same device is available under some other API (ex: Vulkan),
160            // It will mostly likely get a different device type (probably DiscreteGpu).
161            wgt::DeviceType::Other
162        };
163
164        // source: Sascha Willems at Vulkan
165        let vendor_id = if vendor.contains("amd") {
166            db::amd::VENDOR
167        } else if vendor.contains("imgtec") {
168            db::imgtec::VENDOR
169        } else if vendor.contains("nvidia") {
170            db::nvidia::VENDOR
171        } else if vendor.contains("arm") {
172            db::arm::VENDOR
173        } else if vendor.contains("qualcomm") {
174            db::qualcomm::VENDOR
175        } else if vendor.contains("intel") {
176            db::intel::VENDOR
177        } else if vendor.contains("broadcom") {
178            db::broadcom::VENDOR
179        } else if vendor.contains("mesa") {
180            db::mesa::VENDOR
181        } else if vendor.contains("apple") {
182            db::apple::VENDOR
183        } else {
184            0
185        };
186
187        wgt::AdapterInfo {
188            name: renderer_orig,
189            vendor: vendor_id,
190            driver_info: version,
191            ..wgt::AdapterInfo::new(inferred_device_type, wgt::Backend::Gl)
192        }
193    }
194
195    pub(super) unsafe fn expose(
196        context: super::AdapterContext,
197        backend_options: wgt::GlBackendOptions,
198    ) -> Option<crate::ExposedAdapter<super::Api>> {
199        let gl = context.lock();
200        let extensions = gl.supported_extensions();
201
202        let (vendor_const, renderer_const) = if extensions.contains("WEBGL_debug_renderer_info") {
203            // emscripten doesn't enable "WEBGL_debug_renderer_info" extension by default. so, we do it manually.
204            // See https://github.com/gfx-rs/wgpu/issues/3245 for context
205            #[cfg(Emscripten)]
206            if unsafe {
207                super::emscripten::enable_extension(c"WEBGL_debug_renderer_info".to_str().unwrap())
208            } {
209                (GL_UNMASKED_VENDOR_WEBGL, GL_UNMASKED_RENDERER_WEBGL)
210            } else {
211                (glow::VENDOR, glow::RENDERER)
212            }
213            // glow already enables WEBGL_debug_renderer_info on wasm32-unknown-unknown target by default.
214            #[cfg(not(Emscripten))]
215            (GL_UNMASKED_VENDOR_WEBGL, GL_UNMASKED_RENDERER_WEBGL)
216        } else {
217            (glow::VENDOR, glow::RENDERER)
218        };
219
220        let vendor = unsafe { gl.get_parameter_string(vendor_const) };
221        let renderer = unsafe { gl.get_parameter_string(renderer_const) };
222        let version = unsafe { gl.get_parameter_string(glow::VERSION) };
223        log::debug!("Vendor: {vendor}");
224        log::debug!("Renderer: {renderer}");
225        log::debug!("Version: {version}");
226
227        let full_ver = Self::parse_full_version(&version).ok();
228        let es_ver = full_ver.map_or_else(|| Self::parse_version(&version).ok(), |_| None);
229
230        if let Some(full_ver) = full_ver {
231            let core_profile = (full_ver >= (3, 2)).then(|| unsafe {
232                gl.get_parameter_i32(glow::CONTEXT_PROFILE_MASK)
233                    & glow::CONTEXT_CORE_PROFILE_BIT as i32
234                    != 0
235            });
236            log::trace!(
237                "Profile: {}",
238                core_profile
239                    .map(|core_profile| if core_profile {
240                        "Core"
241                    } else {
242                        "Compatibility"
243                    })
244                    .unwrap_or("Legacy")
245            );
246        }
247
248        if es_ver.is_none() && full_ver.is_none() {
249            log::warn!("Unable to parse OpenGL version");
250            return None;
251        }
252
253        if let Some(es_ver) = es_ver {
254            if es_ver < (3, 0) {
255                log::warn!(
256                    "Returned GLES context is {}.{}, when 3.0+ was requested",
257                    es_ver.0,
258                    es_ver.1
259                );
260                return None;
261            }
262        }
263
264        if let Some(full_ver) = full_ver {
265            if full_ver < (3, 3) {
266                log::warn!(
267                    "Returned GL context is {}.{}, when 3.3+ is needed",
268                    full_ver.0,
269                    full_ver.1
270                );
271                return None;
272            }
273        }
274
275        let shading_language_version = {
276            let sl_version = unsafe { gl.get_parameter_string(glow::SHADING_LANGUAGE_VERSION) };
277            log::debug!("SL version: {}", &sl_version);
278            if full_ver.is_some() {
279                let (sl_major, sl_minor) = Self::parse_full_version(&sl_version).ok()?;
280                let mut value = sl_major as u16 * 100 + sl_minor as u16 * 10;
281                // Naga doesn't think it supports GL 460+, so we cap it at 450
282                if value > 450 {
283                    value = 450;
284                }
285                naga::back::glsl::Version::Desktop(value)
286            } else {
287                let (sl_major, sl_minor) = Self::parse_version(&sl_version).ok()?;
288                let value = sl_major as u16 * 100 + sl_minor as u16 * 10;
289                naga::back::glsl::Version::Embedded {
290                    version: value,
291                    is_webgl: cfg!(any(webgl, Emscripten)),
292                }
293            }
294        };
295
296        log::debug!("Supported GL Extensions: {extensions:#?}");
297
298        let supported = |(req_es_major, req_es_minor), (req_full_major, req_full_minor)| {
299            let es_supported = es_ver
300                .map(|es_ver| es_ver >= (req_es_major, req_es_minor))
301                .unwrap_or_default();
302
303            let full_supported = full_ver
304                .map(|full_ver| full_ver >= (req_full_major, req_full_minor))
305                .unwrap_or_default();
306
307            es_supported || full_supported
308        };
309
310        let supports_storage =
311            supported((3, 1), (4, 3)) || extensions.contains("GL_ARB_shader_storage_buffer_object");
312        let supports_compute =
313            supported((3, 1), (4, 3)) || extensions.contains("GL_ARB_compute_shader");
314        let supports_work_group_params = supports_compute;
315
316        // ANGLE provides renderer strings like: "ANGLE (Apple, Apple M1 Pro, OpenGL 4.1)"
317        let is_angle = renderer.contains("ANGLE");
318
319        let vertex_shader_storage_blocks = if supports_storage {
320            let value =
321                (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_SHADER_STORAGE_BLOCKS) } as u32);
322
323            if value == 0 && extensions.contains("GL_ARB_shader_storage_buffer_object") {
324                // The driver for AMD Radeon HD 5870 returns zero here, so assume the value matches the compute shader storage block count.
325                // Windows doesn't recognize `GL_MAX_VERTEX_ATTRIB_STRIDE`.
326                let new = (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_SHADER_STORAGE_BLOCKS) }
327                    as u32);
328                log::debug!("Max vertex shader storage blocks is zero, but GL_ARB_shader_storage_buffer_object is specified. Assuming the compute value {new}");
329                new
330            } else {
331                value
332            }
333        } else {
334            0
335        };
336        let fragment_shader_storage_blocks = if supports_storage {
337            (unsafe { gl.get_parameter_i32(glow::MAX_FRAGMENT_SHADER_STORAGE_BLOCKS) } as u32)
338        } else {
339            0
340        };
341        let vertex_shader_storage_textures = if supports_storage {
342            (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_IMAGE_UNIFORMS) } as u32)
343        } else {
344            0
345        };
346        let fragment_shader_storage_textures = if supports_storage {
347            (unsafe { gl.get_parameter_i32(glow::MAX_FRAGMENT_IMAGE_UNIFORMS) } as u32)
348        } else {
349            0
350        };
351        let max_storage_block_size = if supports_storage {
352            (unsafe { gl.get_parameter_i32(glow::MAX_SHADER_STORAGE_BLOCK_SIZE) } as u32)
353        } else {
354            0
355        };
356        let max_element_index = unsafe { gl.get_parameter_i32(glow::MAX_ELEMENT_INDEX) } as u32;
357
358        // WORKAROUND: In order to work around an issue with GL on RPI4 and similar, we ignore a
359        // zero vertex ssbo count if there are vertex sstos. (more info:
360        // https://github.com/gfx-rs/wgpu/pull/1607#issuecomment-874938961) The hardware does not
361        // want us to write to these SSBOs, but GLES cannot express that. We detect this case and
362        // disable writing to SSBOs.
363        let vertex_ssbo_false_zero =
364            vertex_shader_storage_blocks == 0 && vertex_shader_storage_textures != 0;
365        if vertex_ssbo_false_zero {
366            // We only care about fragment here as the 0 is a lie.
367            log::debug!("Max vertex shader SSBO == 0 and SSTO != 0. Interpreting as false zero.");
368        }
369
370        let max_storage_buffers_per_shader_stage = if vertex_shader_storage_blocks == 0 {
371            fragment_shader_storage_blocks
372        } else {
373            vertex_shader_storage_blocks.min(fragment_shader_storage_blocks)
374        };
375        let max_storage_textures_per_shader_stage = if vertex_shader_storage_textures == 0 {
376            fragment_shader_storage_textures
377        } else {
378            vertex_shader_storage_textures.min(fragment_shader_storage_textures)
379        };
380        // NOTE: GL_ARB_compute_shader adds support for indirect dispatch
381        let indirect_execution = supported((3, 1), (4, 3))
382            || (extensions.contains("GL_ARB_draw_indirect") && supports_compute);
383        let supports_cube_array = supported((3, 2), (4, 0))
384            || (supported((3, 1), (4, 0)) && extensions.contains("GL_EXT_texture_cube_map_array"));
385
386        let mut downlevel_flags = wgt::DownlevelFlags::empty()
387            | wgt::DownlevelFlags::NON_POWER_OF_TWO_MIPMAPPED_TEXTURES
388            | wgt::DownlevelFlags::COMPARISON_SAMPLERS
389            | wgt::DownlevelFlags::SHADER_F16_IN_F32
390            | wgt::DownlevelFlags::MSL2_1;
391        downlevel_flags.set(
392            wgt::DownlevelFlags::CUBE_ARRAY_TEXTURES,
393            supports_cube_array,
394        );
395        downlevel_flags.set(wgt::DownlevelFlags::COMPUTE_SHADERS, supports_compute);
396        downlevel_flags.set(
397            wgt::DownlevelFlags::FRAGMENT_WRITABLE_STORAGE,
398            max_storage_block_size != 0,
399        );
400        downlevel_flags.set(wgt::DownlevelFlags::INDIRECT_EXECUTION, indirect_execution);
401        downlevel_flags.set(wgt::DownlevelFlags::BASE_VERTEX, supported((3, 2), (3, 2)));
402        downlevel_flags.set(
403            wgt::DownlevelFlags::INDEPENDENT_BLEND,
404            supported((3, 2), (4, 0)) || extensions.contains("GL_EXT_draw_buffers_indexed"),
405        );
406        downlevel_flags.set(
407            wgt::DownlevelFlags::VERTEX_STORAGE,
408            max_storage_block_size != 0
409                && max_storage_buffers_per_shader_stage != 0
410                && (vertex_shader_storage_blocks != 0 || vertex_ssbo_false_zero),
411        );
412        downlevel_flags.set(wgt::DownlevelFlags::FRAGMENT_STORAGE, supports_storage);
413        if extensions.contains("EXT_texture_filter_anisotropic")
414            || extensions.contains("GL_EXT_texture_filter_anisotropic")
415        {
416            let max_aniso =
417                unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_MAX_ANISOTROPY_EXT) } as u32;
418            downlevel_flags.set(wgt::DownlevelFlags::ANISOTROPIC_FILTERING, max_aniso >= 16);
419        }
420        downlevel_flags.set(
421            wgt::DownlevelFlags::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED,
422            !(cfg!(any(webgl, Emscripten)) || is_angle),
423        );
424        // see https://registry.khronos.org/webgl/specs/latest/2.0/#BUFFER_OBJECT_BINDING
425        downlevel_flags.set(
426            wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER,
427            !cfg!(any(webgl, Emscripten)),
428        );
429        downlevel_flags.set(
430            wgt::DownlevelFlags::UNRESTRICTED_EXTERNAL_TEXTURE_COPIES,
431            !cfg!(any(webgl, Emscripten)),
432        );
433        downlevel_flags.set(
434            wgt::DownlevelFlags::FULL_DRAW_INDEX_UINT32,
435            max_element_index == u32::MAX,
436        );
437        downlevel_flags.set(
438            wgt::DownlevelFlags::MULTISAMPLED_SHADING,
439            supported((3, 2), (4, 0)) || extensions.contains("OES_sample_variables"),
440        );
441        // GLSL ES has no `noperspective` qualifier, so `@interpolate(linear)` is only
442        // expressible on desktop GLSL (where we require at least 330, well past the 130
443        // that introduced `noperspective`).
444        downlevel_flags.set(
445            wgt::DownlevelFlags::LINEAR_INTERPOLATION,
446            !shading_language_version.is_es(),
447        );
448        let query_buffers = extensions.contains("GL_ARB_query_buffer_object")
449            || extensions.contains("GL_AMD_query_buffer_object");
450        if query_buffers {
451            downlevel_flags.set(wgt::DownlevelFlags::NONBLOCKING_QUERY_RESOLVE, true);
452        }
453
454        // Desktop GL: norm16 is core since GL 3.0/3.1; we minimum-version
455        // to GL 3.3, so always on. GLES/WebGL2: needs `EXT_texture_norm16`.
456        let supports_16bit_norm = if es_ver.is_some() {
457            extensions.contains("GL_EXT_texture_norm16")
458                || extensions.contains("EXT_texture_norm16")
459        } else {
460            true
461        };
462        // SNORM color-rendering is not spec-guaranteed on either path
463        // (GLES Table 8.13 marks it not-renderable; desktop GL exposes
464        // it as only "optionally renderable"), so gate it on
465        // `EXT_render_snorm` for both - matching how `COLOR_BUFFER_FLOAT`
466        // is probed above.
467        let supports_16bit_snorm_renderable = supports_16bit_norm
468            && (extensions.contains("GL_EXT_render_snorm")
469                || extensions.contains("EXT_render_snorm"));
470        // Storage on norm16. ARB_shader_image_load_store / GL 4.2 Table X.2 and
471        // NV_image_formats Table 8.27 both list `r16/rg16/rgba16` and the SNORM
472        // variants as image-unit formats, so one gate covers UNORM and SNORM.
473        // Paths:
474        //   * Desktop:  core in GL 4.2+; on 3.3..=4.1 via `GL_ARB_shader_image_load_store`.
475        //   * GLES/WebGL2: only via `GL_NV_image_formats` - the ES 3.1/3.2 core
476        //     Table 8.27 omits these formats, and `GL_EXT_texture_norm16` does not
477        //     extend Table 8.27 (NV_image_formats itself depends on EXT_texture_norm16
478        //     for the norm16 entries, so an ES driver exposing NV_image_formats
479        //     without EXT_texture_norm16 still wouldn't accept them - the
480        //     `supports_16bit_norm` prerequisite below catches that).
481        let supports_16bit_norm_storage = supports_16bit_norm
482            && if es_ver.is_some() {
483                extensions.contains("GL_NV_image_formats")
484            } else {
485                full_ver.is_some_and(|v| v >= (4, 2))
486                    || extensions.contains("GL_ARB_shader_image_load_store")
487            };
488
489        let mut features = wgt::Features::empty()
490            | wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES
491            | wgt::Features::CLEAR_TEXTURE
492            | wgt::Features::IMMEDIATES
493            | wgt::Features::DEPTH32FLOAT_STENCIL8
494            | wgt::Features::PASSTHROUGH_SHADERS;
495        features.set(
496            wgt::Features::TEXTURE_FORMAT_16BIT_NORM,
497            supports_16bit_norm,
498        );
499        features.set(
500            wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER | wgt::Features::ADDRESS_MODE_CLAMP_TO_ZERO,
501            extensions.contains("GL_EXT_texture_border_clamp")
502                || extensions.contains("GL_ARB_texture_border_clamp"),
503        );
504        features.set(
505            wgt::Features::DEPTH_CLIP_CONTROL,
506            extensions.contains("GL_EXT_depth_clamp") || extensions.contains("GL_ARB_depth_clamp"),
507        );
508        features.set(
509            wgt::Features::VERTEX_WRITABLE_STORAGE,
510            downlevel_flags.contains(wgt::DownlevelFlags::VERTEX_STORAGE)
511                && vertex_shader_storage_textures != 0,
512        );
513        features.set(
514            wgt::Features::MULTIVIEW,
515            extensions.contains("OVR_multiview2") || extensions.contains("GL_OVR_multiview2"),
516        );
517        features.set(
518            wgt::Features::DUAL_SOURCE_BLENDING,
519            extensions.contains("GL_EXT_blend_func_extended")
520                || extensions.contains("GL_ARB_blend_func_extended"),
521        );
522        features.set(
523            wgt::Features::CLIP_DISTANCES,
524            full_ver.is_some() || extensions.contains("GL_EXT_clip_cull_distance"),
525        );
526        features.set(
527            wgt::Features::PRIMITIVE_INDEX,
528            supported((3, 2), (3, 2))
529                || extensions.contains("OES_geometry_shader")
530                || extensions.contains("GL_ARB_geometry_shader4"),
531        );
532        features.set(
533            wgt::Features::SHADER_EARLY_DEPTH_TEST,
534            supported((3, 1), (4, 2)) || extensions.contains("GL_ARB_shader_image_load_store"),
535        );
536        if extensions.contains("GL_ARB_timer_query") {
537            features.set(wgt::Features::TIMESTAMP_QUERY, true);
538            features.set(wgt::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS, true);
539            features.set(wgt::Features::TIMESTAMP_QUERY_INSIDE_PASSES, true);
540        }
541        let gl_bcn_exts = [
542            "GL_EXT_texture_compression_s3tc",
543            "GL_EXT_texture_compression_rgtc",
544            "GL_ARB_texture_compression_bptc",
545        ];
546        let gles_bcn_exts = [
547            "GL_EXT_texture_compression_s3tc_srgb",
548            "GL_EXT_texture_compression_rgtc",
549            "GL_EXT_texture_compression_bptc",
550        ];
551        let webgl_bcn_exts = [
552            "WEBGL_compressed_texture_s3tc",
553            "WEBGL_compressed_texture_s3tc_srgb",
554            "EXT_texture_compression_rgtc",
555            "EXT_texture_compression_bptc",
556        ];
557        let bcn_exts = if cfg!(any(webgl, Emscripten)) {
558            &webgl_bcn_exts[..]
559        } else if es_ver.is_some() {
560            &gles_bcn_exts[..]
561        } else {
562            &gl_bcn_exts[..]
563        };
564        features.set(
565            wgt::Features::TEXTURE_COMPRESSION_BC,
566            bcn_exts.iter().all(|&ext| extensions.contains(ext)),
567        );
568        features.set(
569            wgt::Features::TEXTURE_COMPRESSION_BC_SLICED_3D,
570            bcn_exts.iter().all(|&ext| extensions.contains(ext)), // BC guaranteed Sliced 3D
571        );
572        let has_etc = if cfg!(any(webgl, Emscripten)) {
573            extensions.contains("WEBGL_compressed_texture_etc")
574        } else {
575            es_ver.is_some() || extensions.contains("GL_ARB_ES3_compatibility")
576        };
577        features.set(wgt::Features::TEXTURE_COMPRESSION_ETC2, has_etc);
578
579        // `OES_texture_compression_astc` provides 2D + 3D, LDR + HDR support
580        if extensions.contains("WEBGL_compressed_texture_astc")
581            || extensions.contains("GL_OES_texture_compression_astc")
582        {
583            #[cfg(webgl)]
584            {
585                if context
586                    .glow_context
587                    .compressed_texture_astc_supports_ldr_profile()
588                {
589                    features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC);
590                    features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D);
591                }
592                if context
593                    .glow_context
594                    .compressed_texture_astc_supports_hdr_profile()
595                {
596                    features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR);
597                }
598            }
599
600            #[cfg(any(native, Emscripten))]
601            {
602                features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC);
603                features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D);
604                features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR);
605            }
606        } else {
607            features.set(
608                wgt::Features::TEXTURE_COMPRESSION_ASTC,
609                extensions.contains("GL_KHR_texture_compression_astc_ldr"),
610            );
611            features.set(
612                wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D,
613                extensions.contains("GL_KHR_texture_compression_astc_ldr")
614                    && extensions.contains("GL_KHR_texture_compression_astc_sliced_3d"),
615            );
616            features.set(
617                wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR,
618                extensions.contains("GL_KHR_texture_compression_astc_hdr"),
619            );
620        }
621
622        downlevel_flags.set(
623            wgt::DownlevelFlags::TEXTURE_COMPRESSION,
624            features.contains(wgt::Features::TEXTURE_COMPRESSION_BC)
625                || features.contains(
626                    wgt::Features::TEXTURE_COMPRESSION_ETC2
627                        | wgt::Features::TEXTURE_COMPRESSION_ASTC,
628                ),
629        );
630
631        features.set(
632            wgt::Features::FLOAT32_FILTERABLE,
633            extensions.contains("GL_ARB_color_buffer_float")
634                || extensions.contains("GL_EXT_color_buffer_float")
635                || extensions.contains("OES_texture_float_linear"),
636        );
637
638        if es_ver.is_none() {
639            features |= wgt::Features::POLYGON_MODE_LINE | wgt::Features::POLYGON_MODE_POINT;
640        }
641
642        // We *might* be able to emulate bgra8unorm-storage but currently don't attempt to.
643
644        let mut private_caps = super::PrivateCapabilities::empty();
645        private_caps.set(
646            super::PrivateCapabilities::BUFFER_ALLOCATION,
647            extensions.contains("GL_EXT_buffer_storage")
648                || extensions.contains("GL_ARB_buffer_storage"),
649        );
650        private_caps.set(
651            super::PrivateCapabilities::SHADER_BINDING_LAYOUT,
652            supports_compute,
653        );
654        private_caps.set(
655            super::PrivateCapabilities::SHADER_TEXTURE_SHADOW_LOD,
656            extensions.contains("GL_EXT_texture_shadow_lod"),
657        );
658        private_caps.set(
659            super::PrivateCapabilities::MEMORY_BARRIERS,
660            supported((3, 1), (4, 2)),
661        );
662        private_caps.set(
663            super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT,
664            supported((3, 1), (4, 3)) || extensions.contains("GL_ARB_vertex_attrib_binding"),
665        );
666        private_caps.set(
667            super::PrivateCapabilities::INDEX_BUFFER_ROLE_CHANGE,
668            !cfg!(any(webgl, Emscripten)),
669        );
670        private_caps.set(
671            super::PrivateCapabilities::GET_BUFFER_SUB_DATA,
672            cfg!(any(webgl, Emscripten)) || full_ver.is_some(),
673        );
674        let color_buffer_float = extensions.contains("GL_EXT_color_buffer_float")
675            || extensions.contains("GL_ARB_color_buffer_float")
676            || extensions.contains("EXT_color_buffer_float");
677        let color_buffer_half_float = extensions.contains("GL_EXT_color_buffer_half_float")
678            || extensions.contains("GL_ARB_half_float_pixel");
679        private_caps.set(
680            super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT,
681            color_buffer_half_float || color_buffer_float,
682        );
683        private_caps.set(
684            super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
685            color_buffer_float,
686        );
687        private_caps.set(super::PrivateCapabilities::QUERY_BUFFERS, query_buffers);
688        private_caps.set(super::PrivateCapabilities::QUERY_64BIT, full_ver.is_some());
689        private_caps.set(
690            super::PrivateCapabilities::TEXTURE_STORAGE,
691            supported((3, 0), (4, 2)),
692        );
693        let is_mali = renderer.to_lowercase().contains("mali");
694        let debug_fns_enabled = match backend_options.debug_fns {
695            wgt::GlDebugFns::Auto => gl.supports_debug() && !is_mali,
696            wgt::GlDebugFns::ForceEnabled => gl.supports_debug(),
697            wgt::GlDebugFns::Disabled => false,
698        };
699        private_caps.set(super::PrivateCapabilities::DEBUG_FNS, debug_fns_enabled);
700        private_caps.set(
701            super::PrivateCapabilities::INVALIDATE_FRAMEBUFFER,
702            supported((3, 0), (4, 3)),
703        );
704        if let Some(full_ver) = full_ver {
705            let supported =
706                full_ver >= (4, 2) && extensions.contains("GL_ARB_shader_draw_parameters");
707            private_caps.set(
708                super::PrivateCapabilities::FULLY_FEATURED_INSTANCING,
709                supported,
710            );
711            // Desktop 4.2 and greater specify the first instance parameter.
712            //
713            // For all other versions, the behavior is undefined.
714            //
715            // We only support indirect first instance when we also have ARB_shader_draw_parameters as
716            // that's the only way to get gl_InstanceID to work correctly.
717            features.set(wgt::Features::INDIRECT_FIRST_INSTANCE, supported);
718        }
719        private_caps.set(
720            super::PrivateCapabilities::MULTISAMPLED_RENDER_TO_TEXTURE,
721            extensions.contains("GL_EXT_multisampled_render_to_texture"),
722        );
723        private_caps.set(
724            super::PrivateCapabilities::TEXTURE_FORMAT_NORM16,
725            supports_16bit_norm,
726        );
727        private_caps.set(
728            super::PrivateCapabilities::TEXTURE_FORMAT_SNORM16_RENDERABLE,
729            supports_16bit_snorm_renderable,
730        );
731        private_caps.set(
732            super::PrivateCapabilities::TEXTURE_FORMAT_NORM16_STORAGE,
733            supports_16bit_norm_storage,
734        );
735
736        // GLSL ES 3.10+ / GLSL 4.30+ natively support coherent/volatile qualifiers
737        // on storage buffers. These were introduced alongside storage buffer support.
738        if supports_storage {
739            features |= wgt::Features::MEMORY_DECORATION_COHERENT
740                | wgt::Features::MEMORY_DECORATION_VOLATILE;
741        }
742
743        let max_texture_size = unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_SIZE) } as u32;
744        let max_texture_3d_size = unsafe { gl.get_parameter_i32(glow::MAX_3D_TEXTURE_SIZE) } as u32;
745
746        let min_uniform_buffer_offset_alignment =
747            (unsafe { gl.get_parameter_i32(glow::UNIFORM_BUFFER_OFFSET_ALIGNMENT) } as u32);
748        let min_storage_buffer_offset_alignment = if supports_storage {
749            (unsafe { gl.get_parameter_i32(glow::SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT) } as u32)
750        } else {
751            256
752        };
753        let max_uniform_buffers_per_shader_stage =
754            unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_UNIFORM_BLOCKS) }
755                .min(unsafe { gl.get_parameter_i32(glow::MAX_FRAGMENT_UNIFORM_BLOCKS) })
756                as u32;
757
758        let max_compute_workgroups_per_dimension = if supports_work_group_params {
759            unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 0) }
760                .min(unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 1) })
761                .min(unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 2) })
762                as u32
763        } else {
764            0
765        };
766
767        let max_color_attachments = unsafe {
768            gl.get_parameter_i32(glow::MAX_COLOR_ATTACHMENTS)
769                .min(gl.get_parameter_i32(glow::MAX_DRAW_BUFFERS)) as u32
770        };
771
772        // 16 bytes per sample is the maximum size of a color attachment.
773        let max_color_attachment_bytes_per_sample =
774            max_color_attachments * wgt::TextureFormat::MAX_TARGET_PIXEL_BYTE_COST;
775
776        let limits = crate::auxil::adjust_raw_limits(wgt::Limits {
777            max_texture_dimension_1d: max_texture_size,
778            max_texture_dimension_2d: max_texture_size,
779            max_texture_dimension_3d: max_texture_3d_size,
780            max_texture_array_layers: unsafe {
781                gl.get_parameter_i32(glow::MAX_ARRAY_TEXTURE_LAYERS)
782            } as u32,
783            max_bind_groups: u32::MAX,
784            // No limit.
785            max_bind_groups_plus_vertex_buffers: u32::MAX,
786            // No limit.
787            max_bindings_per_bind_group: u32::MAX,
788            max_dynamic_uniform_buffers_per_pipeline_layout: max_uniform_buffers_per_shader_stage,
789            max_dynamic_storage_buffers_per_pipeline_layout: max_storage_buffers_per_shader_stage,
790            max_sampled_textures_per_shader_stage: super::MAX_TEXTURE_SLOTS as u32,
791            max_samplers_per_shader_stage: super::MAX_SAMPLERS as u32,
792            max_storage_buffers_per_shader_stage,
793            max_storage_textures_per_shader_stage,
794            max_uniform_buffers_per_shader_stage,
795            max_binding_array_elements_per_shader_stage: 0,
796            max_binding_array_sampler_elements_per_shader_stage: 0,
797            max_binding_array_acceleration_structure_elements_per_shader_stage: 0,
798            max_uniform_buffer_binding_size: unsafe {
799                gl.get_parameter_i32(glow::MAX_UNIFORM_BLOCK_SIZE)
800            } as u64,
801            max_storage_buffer_binding_size: if supports_storage {
802                unsafe { gl.get_parameter_i32(glow::MAX_SHADER_STORAGE_BLOCK_SIZE) }
803            } else {
804                0
805            } as u64,
806            max_vertex_buffers: if private_caps
807                .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
808            {
809                (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_BINDINGS) } as u32)
810            } else {
811                16 // should this be different?
812            },
813            max_vertex_attributes: (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIBS) }
814                as u32)
815                .min(super::MAX_VERTEX_ATTRIBUTES as u32),
816            max_vertex_buffer_array_stride: if private_caps
817                .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
818            {
819                if let Some(full_ver) = full_ver {
820                    if full_ver >= (4, 4) {
821                        // We can query `GL_MAX_VERTEX_ATTRIB_STRIDE` in OpenGL 4.4+
822                        let value =
823                            (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) })
824                                as u32;
825
826                        if value == 0 {
827                            // This should be at least 2048, but the driver for AMD Radeon HD 5870 on
828                            // Windows doesn't recognize `GL_MAX_VERTEX_ATTRIB_STRIDE`.
829
830                            log::debug!("Max vertex attribute stride is 0. Assuming it is the OpenGL minimum spec 2048");
831                            2048
832                        } else {
833                            value
834                        }
835                    } else {
836                        log::debug!("Max vertex attribute stride unknown. Assuming it is the OpenGL minimum spec 2048");
837                        2048
838                    }
839                } else {
840                    (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) }) as u32
841                }
842            } else {
843                !0
844            },
845            max_immediate_size: super::MAX_IMMEDIATES as u32 * 4,
846            min_uniform_buffer_offset_alignment,
847            min_storage_buffer_offset_alignment,
848            max_inter_stage_shader_variables: {
849                // MAX_VARYING_COMPONENTS may return 0, because it is deprecated since OpenGL 3.2 core,
850                // and an OpenGL Context with the core profile and with forward-compatibility=true,
851                // will make deprecated constants unavailable.
852                let max_varying_components =
853                    unsafe { gl.get_parameter_i32(glow::MAX_VARYING_COMPONENTS) } as u32;
854                if max_varying_components == 0 {
855                    // default value for max_inter_stage_shader_variables
856                    15
857                } else {
858                    max_varying_components / 4
859                }
860            },
861            max_color_attachments,
862            max_color_attachment_bytes_per_sample,
863            max_compute_workgroup_storage_size: if supports_work_group_params {
864                (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_SHARED_MEMORY_SIZE) } as u32)
865            } else {
866                0
867            },
868            max_compute_invocations_per_workgroup: if supports_work_group_params {
869                (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_WORK_GROUP_INVOCATIONS) } as u32)
870            } else {
871                0
872            },
873            max_compute_workgroup_size_x: if supports_work_group_params {
874                (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 0) }
875                    as u32)
876            } else {
877                0
878            },
879            max_compute_workgroup_size_y: if supports_work_group_params {
880                (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 1) }
881                    as u32)
882            } else {
883                0
884            },
885            max_compute_workgroup_size_z: if supports_work_group_params {
886                (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 2) }
887                    as u32)
888            } else {
889                0
890            },
891            max_compute_workgroups_per_dimension,
892            max_buffer_size: i32::MAX as u64,
893            max_non_sampler_bindings: u32::MAX,
894
895            max_task_workgroup_total_count: 0,
896            max_task_workgroups_per_dimension: 0,
897            max_mesh_workgroup_total_count: 0,
898            max_mesh_workgroups_per_dimension: 0,
899            max_task_invocations_per_workgroup: 0,
900            max_task_invocations_per_dimension: 0,
901            max_mesh_invocations_per_workgroup: 0,
902            max_mesh_invocations_per_dimension: 0,
903            max_task_payload_size: 0,
904            max_mesh_output_vertices: 0,
905            max_mesh_output_primitives: 0,
906            max_mesh_output_layers: 0,
907            max_mesh_multiview_view_count: 0,
908
909            max_blas_primitive_count: 0,
910            max_blas_geometry_count: 0,
911            max_tlas_instance_count: 0,
912            max_acceleration_structures_per_shader_stage: 0,
913            max_buffers_and_acceleration_structures_per_shader_stage: u32::MAX,
914
915            max_multiview_view_count: 0,
916
917            max_ray_dispatch_count: 0,
918            max_ray_recursion_depth: 0,
919        });
920
921        let mut workarounds = super::Workarounds::empty();
922
923        workarounds.set(
924            super::Workarounds::EMULATE_BUFFER_MAP,
925            cfg!(any(webgl, Emscripten)),
926        );
927
928        let r = renderer.to_lowercase();
929        // Check for Mesa sRGB clear bug. See
930        // [`super::PrivateCapabilities::MESA_I915_SRGB_SHADER_CLEAR`].
931        if context.is_owned()
932            && r.contains("mesa")
933            && r.contains("intel")
934            && r.split(&[' ', '(', ')'][..])
935                .any(|substr| substr.len() == 3 && substr.chars().nth(2) == Some('l'))
936        {
937            log::debug!(
938                "Detected skylake derivative running on mesa i915. Clears to srgb textures will \
939                use manual shader clears."
940            );
941            workarounds.set(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR, true);
942        }
943
944        let downlevel_defaults = wgt::DownlevelLimits {};
945        let max_samples = unsafe { gl.get_parameter_i32(glow::MAX_SAMPLES) };
946
947        // Drop the GL guard so we can move the context into AdapterShared
948        // ( on Wasm the gl handle is just a ref so we tell clippy to allow
949        // dropping the ref )
950        #[cfg_attr(target_family = "wasm", allow(dropping_references))]
951        drop(gl);
952
953        Some(crate::ExposedAdapter {
954            adapter: super::Adapter {
955                shared: Arc::new(super::AdapterShared {
956                    context,
957                    private_caps,
958                    workarounds,
959                    features,
960                    limits: limits.clone(),
961                    options: backend_options,
962                    shading_language_version,
963                    next_shader_id: Default::default(),
964                    program_cache: Default::default(),
965                    es: es_ver.is_some(),
966                    max_msaa_samples: max_samples,
967                }),
968            },
969            info: Self::make_info(vendor, renderer, version),
970            features,
971            capabilities: crate::Capabilities {
972                limits,
973                downlevel: wgt::DownlevelCapabilities {
974                    flags: downlevel_flags,
975                    limits: downlevel_defaults,
976                    shader_model: wgt::ShaderModel::Sm5,
977                },
978                alignments: crate::Alignments {
979                    buffer_copy_offset: wgt::BufferSize::new(4).unwrap(),
980                    buffer_copy_pitch: wgt::BufferSize::new(4).unwrap(),
981                    // #6151: `wgpu_hal::gles` doesn't ask Naga to inject bounds
982                    // checks in GLSL, and it doesn't request extensions like
983                    // `KHR_robust_buffer_access_behavior` that would provide
984                    // them, so we can't really implement the checks promised by
985                    // [`crate::BufferBinding`].
986                    //
987                    // Since this is a pre-existing condition, for the time
988                    // being, provide 1 as the value here, to cause as little
989                    // trouble as possible.
990                    uniform_bounds_check_alignment: wgt::BufferSize::new(1).unwrap(),
991                    raw_tlas_instance_size: 0,
992                    ray_tracing_scratch_buffer_alignment: 0,
993                    ray_tracing_pipeline_group_data_size: 0,
994                    ray_tracing_pipeline_group_data_alignment: 0,
995                    ray_tracing_pipeline_data_offset_alignment: 0,
996                },
997                cooperative_matrix_properties: Vec::new(),
998            },
999        })
1000    }
1001
1002    unsafe fn compile_shader(
1003        source: &str,
1004        gl: &glow::Context,
1005        shader_type: u32,
1006        es: bool,
1007    ) -> Option<glow::Shader> {
1008        let source = if es {
1009            format!("#version 300 es\nprecision lowp float;\n{source}")
1010        } else {
1011            let version = gl.version();
1012            if version.major == 3 && version.minor == 0 {
1013                // OpenGL 3.0 only supports this format
1014                format!("#version 130\n{source}")
1015            } else {
1016                // OpenGL 3.1+ support this format
1017                format!("#version 140\n{source}")
1018            }
1019        };
1020        let shader = unsafe { gl.create_shader(shader_type) }.expect("Could not create shader");
1021        unsafe { gl.shader_source(shader, &source) };
1022        unsafe { gl.compile_shader(shader) };
1023
1024        if !unsafe { gl.get_shader_compile_status(shader) } {
1025            let msg = unsafe { gl.get_shader_info_log(shader) };
1026            if !msg.is_empty() {
1027                log::error!("\tShader compile error: {msg}");
1028            }
1029            unsafe { gl.delete_shader(shader) };
1030            None
1031        } else {
1032            Some(shader)
1033        }
1034    }
1035
1036    unsafe fn create_shader_clear_program(
1037        gl: &glow::Context,
1038        es: bool,
1039    ) -> Option<ShaderClearProgram> {
1040        let program = unsafe { gl.create_program() }.expect("Could not create shader program");
1041        let vertex = unsafe {
1042            Self::compile_shader(
1043                include_str!("./shaders/clear.vert"),
1044                gl,
1045                glow::VERTEX_SHADER,
1046                es,
1047            )?
1048        };
1049        let fragment = unsafe {
1050            Self::compile_shader(
1051                include_str!("./shaders/clear.frag"),
1052                gl,
1053                glow::FRAGMENT_SHADER,
1054                es,
1055            )?
1056        };
1057        unsafe { gl.attach_shader(program, vertex) };
1058        unsafe { gl.attach_shader(program, fragment) };
1059        unsafe { gl.link_program(program) };
1060
1061        let linked_ok = unsafe { gl.get_program_link_status(program) };
1062        let msg = unsafe { gl.get_program_info_log(program) };
1063        if !msg.is_empty() {
1064            log::error!("Shader link error: {msg}");
1065        }
1066        if !linked_ok {
1067            return None;
1068        }
1069
1070        let color_uniform_location = unsafe { gl.get_uniform_location(program, "color") }
1071            .expect("Could not find color uniform in shader clear shader");
1072        unsafe { gl.delete_shader(vertex) };
1073        unsafe { gl.delete_shader(fragment) };
1074
1075        Some(ShaderClearProgram {
1076            program,
1077            color_uniform_location,
1078        })
1079    }
1080}
1081
1082impl crate::Adapter for super::Adapter {
1083    type A = super::Api;
1084
1085    unsafe fn open(
1086        &self,
1087        features: wgt::Features,
1088        _limits: &wgt::Limits,
1089        _memory_hints: &wgt::MemoryHints,
1090    ) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
1091        let gl = &self.shared.context.lock();
1092        unsafe { gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1) };
1093        unsafe { gl.pixel_store_i32(glow::PACK_ALIGNMENT, 1) };
1094        let main_vao =
1095            unsafe { gl.create_vertex_array() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
1096        unsafe { gl.bind_vertex_array(Some(main_vao)) };
1097
1098        let zero_buffer =
1099            unsafe { gl.create_buffer() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
1100        unsafe { gl.bind_buffer(glow::COPY_READ_BUFFER, Some(zero_buffer)) };
1101        let zeroes = vec![0u8; super::ZERO_BUFFER_SIZE];
1102        unsafe { gl.buffer_data_u8_slice(glow::COPY_READ_BUFFER, &zeroes, glow::STATIC_DRAW) };
1103
1104        // Compile the shader program we use for doing manual clears to work around Mesa fastclear
1105        // bug.
1106
1107        let shader_clear_program = if self
1108            .shared
1109            .workarounds
1110            .contains(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR)
1111        {
1112            Some(unsafe {
1113                Self::create_shader_clear_program(gl, self.shared.es)
1114                    .ok_or(crate::DeviceError::Lost)?
1115            })
1116        } else {
1117            // If we don't need the workaround, don't waste time and resources compiling the clear program
1118            None
1119        };
1120
1121        Ok(crate::OpenDevice {
1122            device: super::Device {
1123                shared: Arc::clone(&self.shared),
1124                main_vao,
1125                #[cfg(all(native, feature = "renderdoc"))]
1126                render_doc: Default::default(),
1127                counters: Default::default(),
1128            },
1129            queue: super::Queue {
1130                shared: Arc::clone(&self.shared),
1131                features,
1132                draw_fbo: unsafe { gl.create_framebuffer() }
1133                    .map_err(|_| crate::DeviceError::OutOfMemory)?,
1134                copy_fbo: unsafe { gl.create_framebuffer() }
1135                    .map_err(|_| crate::DeviceError::OutOfMemory)?,
1136                shader_clear_program,
1137                zero_buffer,
1138                temp_query_results: Mutex::new(Vec::new()),
1139                draw_buffer_count: AtomicU8::new(1),
1140                current_index_buffer: Mutex::new(None),
1141            },
1142        })
1143    }
1144
1145    unsafe fn texture_format_capabilities(
1146        &self,
1147        format: wgt::TextureFormat,
1148    ) -> crate::TextureFormatCapabilities {
1149        use crate::TextureFormatCapabilities as Tfc;
1150        use wgt::TextureFormat as Tf;
1151
1152        let sample_count = {
1153            let max_samples = self.shared.max_msaa_samples;
1154            if max_samples >= 16 {
1155                Tfc::MULTISAMPLE_X2
1156                    | Tfc::MULTISAMPLE_X4
1157                    | Tfc::MULTISAMPLE_X8
1158                    | Tfc::MULTISAMPLE_X16
1159            } else if max_samples >= 8 {
1160                Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4 | Tfc::MULTISAMPLE_X8
1161            } else {
1162                // The lowest supported level in GLE3.0/WebGL2 is 4X
1163                // (see GL_MAX_SAMPLES in https://registry.khronos.org/OpenGL-Refpages/es3.0/html/glGet.xhtml).
1164                // On some platforms, like iOS Safari, `get_parameter_i32(MAX_SAMPLES)` returns 0,
1165                // so we always fall back to supporting 4x here.
1166                Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4
1167            }
1168        };
1169
1170        // Base types are pulled from the table in the OpenGLES 3.0 spec in section 3.8.
1171        //
1172        // The storage types are based on table 8.26, in section
1173        // "TEXTURE IMAGE LOADS AND STORES" of OpenGLES-3.2 spec.
1174        let empty = Tfc::empty();
1175        let base = Tfc::COPY_SRC | Tfc::COPY_DST;
1176        let unfilterable = base | Tfc::SAMPLED;
1177        let depth = base | Tfc::SAMPLED | sample_count | Tfc::DEPTH_STENCIL_ATTACHMENT;
1178        let filterable = unfilterable | Tfc::SAMPLED_LINEAR;
1179        let renderable =
1180            unfilterable | Tfc::COLOR_ATTACHMENT | sample_count | Tfc::MULTISAMPLE_RESOLVE;
1181        let filterable_renderable = filterable | renderable | Tfc::COLOR_ATTACHMENT_BLEND;
1182        let storage =
1183            base | Tfc::STORAGE_READ_WRITE | Tfc::STORAGE_READ_ONLY | Tfc::STORAGE_WRITE_ONLY;
1184
1185        let feature_fn = |f, caps| {
1186            if self.shared.features.contains(f) {
1187                caps
1188            } else {
1189                empty
1190            }
1191        };
1192
1193        let bcn_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_BC, filterable);
1194        let etc2_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ETC2, filterable);
1195        let astc_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC, filterable);
1196        let astc_hdr_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR, filterable);
1197
1198        let private_caps_fn = |f, caps| {
1199            if self.shared.private_caps.contains(f) {
1200                caps
1201            } else {
1202                empty
1203            }
1204        };
1205
1206        let half_float_renderable = private_caps_fn(
1207            super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT,
1208            Tfc::COLOR_ATTACHMENT
1209                | Tfc::COLOR_ATTACHMENT_BLEND
1210                | sample_count
1211                | Tfc::MULTISAMPLE_RESOLVE,
1212        );
1213
1214        let float_renderable = private_caps_fn(
1215            super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
1216            Tfc::COLOR_ATTACHMENT
1217                | Tfc::COLOR_ATTACHMENT_BLEND
1218                | sample_count
1219                | Tfc::MULTISAMPLE_RESOLVE,
1220        );
1221
1222        let texture_float_linear = feature_fn(wgt::Features::FLOAT32_FILTERABLE, filterable);
1223
1224        let image_atomic = feature_fn(wgt::Features::TEXTURE_ATOMIC, Tfc::STORAGE_ATOMIC);
1225        let image_64_atomic = feature_fn(wgt::Features::TEXTURE_INT64_ATOMIC, Tfc::STORAGE_ATOMIC);
1226
1227        // UNORM gets full filterable+renderable; SNORM splits because
1228        // `EXT_texture_norm16` marks only UNORM as color-renderable.
1229        // Storage rides on a separate cap (desktop GL >= 4.2 core / pre-4.2
1230        // `GL_ARB_shader_image_load_store`, GLES `GL_NV_image_formats`).
1231        let norm16_unorm = private_caps_fn(
1232            super::PrivateCapabilities::TEXTURE_FORMAT_NORM16,
1233            filterable_renderable,
1234        );
1235        let norm16_snorm = if self
1236            .shared
1237            .private_caps
1238            .contains(super::PrivateCapabilities::TEXTURE_FORMAT_SNORM16_RENDERABLE)
1239        {
1240            norm16_unorm
1241        } else {
1242            private_caps_fn(
1243                super::PrivateCapabilities::TEXTURE_FORMAT_NORM16,
1244                filterable,
1245            )
1246        };
1247        let norm16_storage = private_caps_fn(
1248            super::PrivateCapabilities::TEXTURE_FORMAT_NORM16_STORAGE,
1249            storage,
1250        );
1251
1252        match format {
1253            Tf::R8Unorm => filterable_renderable,
1254            Tf::R8Snorm => filterable,
1255            Tf::R8Uint => renderable,
1256            Tf::R8Sint => renderable,
1257            Tf::R16Uint => renderable,
1258            Tf::R16Sint => renderable,
1259            Tf::R16Unorm => norm16_unorm | norm16_storage,
1260            Tf::R16Snorm => norm16_snorm | norm16_storage,
1261            Tf::R16Float => filterable | half_float_renderable,
1262            Tf::Rg8Unorm => filterable_renderable,
1263            Tf::Rg8Snorm => filterable,
1264            Tf::Rg8Uint => renderable,
1265            Tf::Rg8Sint => renderable,
1266            Tf::R32Uint => renderable | storage | image_atomic,
1267            Tf::R32Sint => renderable | storage | image_atomic,
1268            Tf::R32Float => unfilterable | storage | float_renderable | texture_float_linear,
1269            Tf::Rg16Uint => renderable,
1270            Tf::Rg16Sint => renderable,
1271            Tf::Rg16Unorm => norm16_unorm | norm16_storage,
1272            Tf::Rg16Snorm => norm16_snorm | norm16_storage,
1273            Tf::Rg16Float => filterable | half_float_renderable,
1274            Tf::Rgba8Unorm => filterable_renderable | storage,
1275            Tf::Rgba8UnormSrgb => filterable_renderable,
1276            Tf::Bgra8Unorm | Tf::Bgra8UnormSrgb => filterable_renderable,
1277            Tf::Rgba8Snorm => filterable | storage,
1278            Tf::Rgba8Uint => renderable | storage,
1279            Tf::Rgba8Sint => renderable | storage,
1280            Tf::Rgb10a2Uint => renderable,
1281            Tf::Rgb10a2Unorm => filterable_renderable,
1282            Tf::Rg11b10Ufloat => filterable | float_renderable,
1283            Tf::R64Uint => image_64_atomic,
1284            Tf::Rg32Uint => renderable,
1285            Tf::Rg32Sint => renderable,
1286            Tf::Rg32Float => unfilterable | float_renderable | texture_float_linear,
1287            Tf::Rgba16Uint => renderable | storage,
1288            Tf::Rgba16Sint => renderable | storage,
1289            Tf::Rgba16Unorm => norm16_unorm | norm16_storage,
1290            Tf::Rgba16Snorm => norm16_snorm | norm16_storage,
1291            Tf::Rgba16Float => filterable | storage | half_float_renderable,
1292            Tf::Rgba32Uint => renderable | storage,
1293            Tf::Rgba32Sint => renderable | storage,
1294            Tf::Rgba32Float => unfilterable | storage | float_renderable | texture_float_linear,
1295            Tf::Stencil8
1296            | Tf::Depth16Unorm
1297            | Tf::Depth32Float
1298            | Tf::Depth32FloatStencil8
1299            | Tf::Depth24Plus
1300            | Tf::Depth24PlusStencil8 => depth,
1301            Tf::NV12 => empty,
1302            Tf::P010 => empty,
1303            Tf::Rgb9e5Ufloat => filterable,
1304            Tf::Bc1RgbaUnorm
1305            | Tf::Bc1RgbaUnormSrgb
1306            | Tf::Bc2RgbaUnorm
1307            | Tf::Bc2RgbaUnormSrgb
1308            | Tf::Bc3RgbaUnorm
1309            | Tf::Bc3RgbaUnormSrgb
1310            | Tf::Bc4RUnorm
1311            | Tf::Bc4RSnorm
1312            | Tf::Bc5RgUnorm
1313            | Tf::Bc5RgSnorm
1314            | Tf::Bc6hRgbFloat
1315            | Tf::Bc6hRgbUfloat
1316            | Tf::Bc7RgbaUnorm
1317            | Tf::Bc7RgbaUnormSrgb => bcn_features,
1318            Tf::Etc2Rgb8Unorm
1319            | Tf::Etc2Rgb8UnormSrgb
1320            | Tf::Etc2Rgb8A1Unorm
1321            | Tf::Etc2Rgb8A1UnormSrgb
1322            | Tf::Etc2Rgba8Unorm
1323            | Tf::Etc2Rgba8UnormSrgb
1324            | Tf::EacR11Unorm
1325            | Tf::EacR11Snorm
1326            | Tf::EacRg11Unorm
1327            | Tf::EacRg11Snorm => etc2_features,
1328            Tf::Astc {
1329                block: _,
1330                channel: AstcChannel::Unorm | AstcChannel::UnormSrgb,
1331            } => astc_features,
1332            Tf::Astc {
1333                block: _,
1334                channel: AstcChannel::Hdr,
1335            } => astc_hdr_features,
1336        }
1337    }
1338
1339    unsafe fn surface_capabilities(
1340        &self,
1341        surface: &super::Surface,
1342    ) -> Option<crate::SurfaceCapabilities> {
1343        #[cfg(webgl)]
1344        if self.shared.context.webgl2_context != surface.webgl2_context {
1345            return None;
1346        }
1347
1348        if surface.presentable {
1349            // There is no extended-range or wide-gamut path in the GLES
1350            // backend; everything is presented as sRGB.
1351            let format_caps = |format: wgt::TextureFormat| wgt::SurfaceFormatCapabilities {
1352                format,
1353                color_spaces: wgt::SurfaceColorSpaces::SRGB,
1354            };
1355            let mut formats = vec![
1356                format_caps(wgt::TextureFormat::Rgba8Unorm),
1357                #[cfg(native)]
1358                format_caps(wgt::TextureFormat::Bgra8Unorm),
1359            ];
1360            if surface.supports_srgb() {
1361                formats.extend([
1362                    format_caps(wgt::TextureFormat::Rgba8UnormSrgb),
1363                    #[cfg(native)]
1364                    format_caps(wgt::TextureFormat::Bgra8UnormSrgb),
1365                ])
1366            }
1367            if self
1368                .shared
1369                .private_caps
1370                .contains(super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT)
1371            {
1372                formats.push(format_caps(wgt::TextureFormat::Rgba16Float))
1373            }
1374
1375            Some(crate::SurfaceCapabilities {
1376                formats,
1377                present_modes: if cfg!(windows) {
1378                    vec![wgt::PresentMode::Fifo, wgt::PresentMode::Immediate]
1379                } else {
1380                    vec![wgt::PresentMode::Fifo] //TODO
1381                },
1382                composite_alpha_modes: vec![wgt::CompositeAlphaMode::Opaque], //TODO
1383                maximum_frame_latency: 2..=2, //TODO, unused currently
1384                current_extent: None,
1385                usage: wgt::TextureUses::COLOR_TARGET,
1386            })
1387        } else {
1388            None
1389        }
1390    }
1391
1392    unsafe fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
1393        wgt::PresentationTimestamp::INVALID_TIMESTAMP
1394    }
1395
1396    fn get_ordered_buffer_usages(&self) -> wgt::BufferUses {
1397        wgt::BufferUses::INCLUSIVE | wgt::BufferUses::MAP_WRITE
1398    }
1399
1400    // Don't put barriers between inclusive uses
1401    fn get_ordered_texture_usages(&self) -> wgt::TextureUses {
1402        wgt::TextureUses::INCLUSIVE
1403            | wgt::TextureUses::COLOR_TARGET
1404            | wgt::TextureUses::DEPTH_WRITE
1405            | wgt::TextureUses::STENCIL_WRITE
1406    }
1407}
1408
1409impl super::AdapterShared {
1410    pub(super) unsafe fn get_buffer_sub_data(
1411        &self,
1412        gl: &glow::Context,
1413        target: u32,
1414        offset: i32,
1415        dst_data: &mut [u8],
1416    ) {
1417        if self
1418            .private_caps
1419            .contains(super::PrivateCapabilities::GET_BUFFER_SUB_DATA)
1420        {
1421            unsafe { gl.get_buffer_sub_data(target, offset, dst_data) };
1422        } else {
1423            log::error!("Fake map");
1424            let length = dst_data.len();
1425            // glMapBufferRange throws an error if length is 0.
1426            if length != 0 {
1427                let buffer_mapping =
1428                    unsafe { gl.map_buffer_range(target, offset, length as _, glow::MAP_READ_BIT) };
1429
1430                unsafe {
1431                    core::ptr::copy_nonoverlapping(buffer_mapping, dst_data.as_mut_ptr(), length)
1432                };
1433
1434                unsafe { gl.unmap_buffer(target) };
1435            }
1436        }
1437    }
1438}
1439
1440#[cfg(send_sync)]
1441unsafe impl Sync for super::Adapter {}
1442#[cfg(send_sync)]
1443unsafe impl Send for super::Adapter {}
1444
1445#[cfg(test)]
1446mod tests {
1447    use super::super::Adapter;
1448
1449    #[test]
1450    fn test_version_parse() {
1451        Adapter::parse_version("1").unwrap_err();
1452        Adapter::parse_version("1.").unwrap_err();
1453        Adapter::parse_version("1 h3l1o. W0rld").unwrap_err();
1454        Adapter::parse_version("1. h3l1o. W0rld").unwrap_err();
1455        Adapter::parse_version("1.2.3").unwrap_err();
1456
1457        assert_eq!(Adapter::parse_version("OpenGL ES 3.1").unwrap(), (3, 1));
1458        assert_eq!(
1459            Adapter::parse_version("OpenGL ES 2.0 Google Nexus").unwrap(),
1460            (2, 0)
1461        );
1462        assert_eq!(Adapter::parse_version("GLSL ES 1.1").unwrap(), (1, 1));
1463        assert_eq!(
1464            Adapter::parse_version("OpenGL ES GLSL ES 3.20").unwrap(),
1465            (3, 2)
1466        );
1467        assert_eq!(
1468            // WebGL 2.0 should parse as OpenGL ES 3.0
1469            Adapter::parse_version("WebGL 2.0 (OpenGL ES 3.0 Chromium)").unwrap(),
1470            (3, 0)
1471        );
1472        assert_eq!(
1473            Adapter::parse_version("WebGL GLSL ES 3.00 (OpenGL ES GLSL ES 3.0 Chromium)").unwrap(),
1474            (3, 0)
1475        );
1476    }
1477}