Skip to main content

wgpu_hal/gles/
adapter.rs

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