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                    program_cache: Default::default(),
959                    es: es_ver.is_some(),
960                    max_msaa_samples: max_samples,
961                }),
962            },
963            info: Self::make_info(vendor, renderer, version),
964            features,
965            capabilities: crate::Capabilities {
966                limits,
967                downlevel: wgt::DownlevelCapabilities {
968                    flags: downlevel_flags,
969                    limits: downlevel_defaults,
970                    shader_model: wgt::ShaderModel::Sm5,
971                },
972                alignments: crate::Alignments {
973                    buffer_copy_offset: wgt::BufferSize::new(4).unwrap(),
974                    buffer_copy_pitch: wgt::BufferSize::new(4).unwrap(),
975                    // #6151: `wgpu_hal::gles` doesn't ask Naga to inject bounds
976                    // checks in GLSL, and it doesn't request extensions like
977                    // `KHR_robust_buffer_access_behavior` that would provide
978                    // them, so we can't really implement the checks promised by
979                    // [`crate::BufferBinding`].
980                    //
981                    // Since this is a pre-existing condition, for the time
982                    // being, provide 1 as the value here, to cause as little
983                    // trouble as possible.
984                    uniform_bounds_check_alignment: wgt::BufferSize::new(1).unwrap(),
985                    raw_tlas_instance_size: 0,
986                    ray_tracing_scratch_buffer_alignment: 0,
987                    ray_tracing_pipeline_group_data_size: 0,
988                    ray_tracing_pipeline_group_data_alignment: 0,
989                    ray_tracing_pipeline_data_offset_alignment: 0,
990                },
991                cooperative_matrix_properties: Vec::new(),
992            },
993        })
994    }
995
996    unsafe fn compile_shader(
997        source: &str,
998        gl: &glow::Context,
999        shader_type: u32,
1000        es: bool,
1001    ) -> Option<glow::Shader> {
1002        let source = if es {
1003            format!("#version 300 es\nprecision lowp float;\n{source}")
1004        } else {
1005            let version = gl.version();
1006            if version.major == 3 && version.minor == 0 {
1007                // OpenGL 3.0 only supports this format
1008                format!("#version 130\n{source}")
1009            } else {
1010                // OpenGL 3.1+ support this format
1011                format!("#version 140\n{source}")
1012            }
1013        };
1014        let shader = unsafe { gl.create_shader(shader_type) }.expect("Could not create shader");
1015        unsafe { gl.shader_source(shader, &source) };
1016        unsafe { gl.compile_shader(shader) };
1017
1018        if !unsafe { gl.get_shader_compile_status(shader) } {
1019            let msg = unsafe { gl.get_shader_info_log(shader) };
1020            if !msg.is_empty() {
1021                log::error!("\tShader compile error: {msg}");
1022            }
1023            unsafe { gl.delete_shader(shader) };
1024            None
1025        } else {
1026            Some(shader)
1027        }
1028    }
1029
1030    unsafe fn create_shader_clear_program(
1031        gl: &glow::Context,
1032        es: bool,
1033    ) -> Option<ShaderClearProgram> {
1034        let program = unsafe { gl.create_program() }.expect("Could not create shader program");
1035        let vertex = unsafe {
1036            Self::compile_shader(
1037                include_str!("./shaders/clear.vert"),
1038                gl,
1039                glow::VERTEX_SHADER,
1040                es,
1041            )?
1042        };
1043        let fragment = unsafe {
1044            Self::compile_shader(
1045                include_str!("./shaders/clear.frag"),
1046                gl,
1047                glow::FRAGMENT_SHADER,
1048                es,
1049            )?
1050        };
1051        unsafe { gl.attach_shader(program, vertex) };
1052        unsafe { gl.attach_shader(program, fragment) };
1053        unsafe { gl.link_program(program) };
1054
1055        let linked_ok = unsafe { gl.get_program_link_status(program) };
1056        let msg = unsafe { gl.get_program_info_log(program) };
1057        if !msg.is_empty() {
1058            log::error!("Shader link error: {msg}");
1059        }
1060        if !linked_ok {
1061            return None;
1062        }
1063
1064        let color_uniform_location = unsafe { gl.get_uniform_location(program, "color") }
1065            .expect("Could not find color uniform in shader clear shader");
1066        unsafe { gl.delete_shader(vertex) };
1067        unsafe { gl.delete_shader(fragment) };
1068
1069        Some(ShaderClearProgram {
1070            program,
1071            color_uniform_location,
1072        })
1073    }
1074}
1075
1076impl crate::Adapter for super::Adapter {
1077    type A = super::Api;
1078
1079    unsafe fn open(
1080        &self,
1081        features: wgt::Features,
1082        _limits: &wgt::Limits,
1083        _memory_hints: &wgt::MemoryHints,
1084    ) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
1085        let gl = &self.shared.context.lock();
1086        unsafe { gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1) };
1087        unsafe { gl.pixel_store_i32(glow::PACK_ALIGNMENT, 1) };
1088        let main_vao =
1089            unsafe { gl.create_vertex_array() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
1090        unsafe { gl.bind_vertex_array(Some(main_vao)) };
1091
1092        let zero_buffer =
1093            unsafe { gl.create_buffer() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
1094        unsafe { gl.bind_buffer(glow::COPY_READ_BUFFER, Some(zero_buffer)) };
1095        let zeroes = vec![0u8; super::ZERO_BUFFER_SIZE];
1096        unsafe { gl.buffer_data_u8_slice(glow::COPY_READ_BUFFER, &zeroes, glow::STATIC_DRAW) };
1097
1098        // Compile the shader program we use for doing manual clears to work around Mesa fastclear
1099        // bug.
1100
1101        let shader_clear_program = if self
1102            .shared
1103            .workarounds
1104            .contains(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR)
1105        {
1106            Some(unsafe {
1107                Self::create_shader_clear_program(gl, self.shared.es)
1108                    .ok_or(crate::DeviceError::Lost)?
1109            })
1110        } else {
1111            // If we don't need the workaround, don't waste time and resources compiling the clear program
1112            None
1113        };
1114
1115        Ok(crate::OpenDevice {
1116            device: super::Device {
1117                shared: Arc::clone(&self.shared),
1118                main_vao,
1119                #[cfg(all(native, feature = "renderdoc"))]
1120                render_doc: Default::default(),
1121                counters: Default::default(),
1122            },
1123            queue: super::Queue {
1124                shared: Arc::clone(&self.shared),
1125                features,
1126                draw_fbo: unsafe { gl.create_framebuffer() }
1127                    .map_err(|_| crate::DeviceError::OutOfMemory)?,
1128                copy_fbo: unsafe { gl.create_framebuffer() }
1129                    .map_err(|_| crate::DeviceError::OutOfMemory)?,
1130                shader_clear_program,
1131                zero_buffer,
1132                temp_query_results: Mutex::new(Vec::new()),
1133                draw_buffer_count: AtomicU8::new(1),
1134                current_index_buffer: Mutex::new(None),
1135            },
1136        })
1137    }
1138
1139    unsafe fn texture_format_capabilities(
1140        &self,
1141        format: wgt::TextureFormat,
1142    ) -> crate::TextureFormatCapabilities {
1143        use crate::TextureFormatCapabilities as Tfc;
1144        use wgt::TextureFormat as Tf;
1145
1146        let sample_count = {
1147            let max_samples = self.shared.max_msaa_samples;
1148            if max_samples >= 16 {
1149                Tfc::MULTISAMPLE_X2
1150                    | Tfc::MULTISAMPLE_X4
1151                    | Tfc::MULTISAMPLE_X8
1152                    | Tfc::MULTISAMPLE_X16
1153            } else if max_samples >= 8 {
1154                Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4 | Tfc::MULTISAMPLE_X8
1155            } else {
1156                // The lowest supported level in GLE3.0/WebGL2 is 4X
1157                // (see GL_MAX_SAMPLES in https://registry.khronos.org/OpenGL-Refpages/es3.0/html/glGet.xhtml).
1158                // On some platforms, like iOS Safari, `get_parameter_i32(MAX_SAMPLES)` returns 0,
1159                // so we always fall back to supporting 4x here.
1160                Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4
1161            }
1162        };
1163
1164        // Base types are pulled from the table in the OpenGLES 3.0 spec in section 3.8.
1165        //
1166        // The storage types are based on table 8.26, in section
1167        // "TEXTURE IMAGE LOADS AND STORES" of OpenGLES-3.2 spec.
1168        let empty = Tfc::empty();
1169        let base = Tfc::COPY_SRC | Tfc::COPY_DST;
1170        let unfilterable = base | Tfc::SAMPLED;
1171        let depth = base | Tfc::SAMPLED | sample_count | Tfc::DEPTH_STENCIL_ATTACHMENT;
1172        let filterable = unfilterable | Tfc::SAMPLED_LINEAR;
1173        let renderable =
1174            unfilterable | Tfc::COLOR_ATTACHMENT | sample_count | Tfc::MULTISAMPLE_RESOLVE;
1175        let filterable_renderable = filterable | renderable | Tfc::COLOR_ATTACHMENT_BLEND;
1176        let storage =
1177            base | Tfc::STORAGE_READ_WRITE | Tfc::STORAGE_READ_ONLY | Tfc::STORAGE_WRITE_ONLY;
1178
1179        let feature_fn = |f, caps| {
1180            if self.shared.features.contains(f) {
1181                caps
1182            } else {
1183                empty
1184            }
1185        };
1186
1187        let bcn_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_BC, filterable);
1188        let etc2_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ETC2, filterable);
1189        let astc_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC, filterable);
1190        let astc_hdr_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR, filterable);
1191
1192        let private_caps_fn = |f, caps| {
1193            if self.shared.private_caps.contains(f) {
1194                caps
1195            } else {
1196                empty
1197            }
1198        };
1199
1200        let half_float_renderable = private_caps_fn(
1201            super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT,
1202            Tfc::COLOR_ATTACHMENT
1203                | Tfc::COLOR_ATTACHMENT_BLEND
1204                | sample_count
1205                | Tfc::MULTISAMPLE_RESOLVE,
1206        );
1207
1208        let float_renderable = private_caps_fn(
1209            super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
1210            Tfc::COLOR_ATTACHMENT
1211                | Tfc::COLOR_ATTACHMENT_BLEND
1212                | sample_count
1213                | Tfc::MULTISAMPLE_RESOLVE,
1214        );
1215
1216        let texture_float_linear = feature_fn(wgt::Features::FLOAT32_FILTERABLE, filterable);
1217
1218        let image_atomic = feature_fn(wgt::Features::TEXTURE_ATOMIC, Tfc::STORAGE_ATOMIC);
1219        let image_64_atomic = feature_fn(wgt::Features::TEXTURE_INT64_ATOMIC, Tfc::STORAGE_ATOMIC);
1220
1221        // UNORM gets full filterable+renderable; SNORM splits because
1222        // `EXT_texture_norm16` marks only UNORM as color-renderable.
1223        // Storage rides on a separate cap (desktop GL >= 4.2 core / pre-4.2
1224        // `GL_ARB_shader_image_load_store`, GLES `GL_NV_image_formats`).
1225        let norm16_unorm = private_caps_fn(
1226            super::PrivateCapabilities::TEXTURE_FORMAT_NORM16,
1227            filterable_renderable,
1228        );
1229        let norm16_snorm = if self
1230            .shared
1231            .private_caps
1232            .contains(super::PrivateCapabilities::TEXTURE_FORMAT_SNORM16_RENDERABLE)
1233        {
1234            norm16_unorm
1235        } else {
1236            private_caps_fn(
1237                super::PrivateCapabilities::TEXTURE_FORMAT_NORM16,
1238                filterable,
1239            )
1240        };
1241        let norm16_storage = private_caps_fn(
1242            super::PrivateCapabilities::TEXTURE_FORMAT_NORM16_STORAGE,
1243            storage,
1244        );
1245
1246        match format {
1247            Tf::R8Unorm => filterable_renderable,
1248            Tf::R8Snorm => filterable,
1249            Tf::R8Uint => renderable,
1250            Tf::R8Sint => renderable,
1251            Tf::R16Uint => renderable,
1252            Tf::R16Sint => renderable,
1253            Tf::R16Unorm => norm16_unorm | norm16_storage,
1254            Tf::R16Snorm => norm16_snorm | norm16_storage,
1255            Tf::R16Float => filterable | half_float_renderable,
1256            Tf::Rg8Unorm => filterable_renderable,
1257            Tf::Rg8Snorm => filterable,
1258            Tf::Rg8Uint => renderable,
1259            Tf::Rg8Sint => renderable,
1260            Tf::R32Uint => renderable | storage | image_atomic,
1261            Tf::R32Sint => renderable | storage | image_atomic,
1262            Tf::R32Float => unfilterable | storage | float_renderable | texture_float_linear,
1263            Tf::Rg16Uint => renderable,
1264            Tf::Rg16Sint => renderable,
1265            Tf::Rg16Unorm => norm16_unorm | norm16_storage,
1266            Tf::Rg16Snorm => norm16_snorm | norm16_storage,
1267            Tf::Rg16Float => filterable | half_float_renderable,
1268            Tf::Rgba8Unorm => filterable_renderable | storage,
1269            Tf::Rgba8UnormSrgb => filterable_renderable,
1270            Tf::Bgra8Unorm | Tf::Bgra8UnormSrgb => filterable_renderable,
1271            Tf::Rgba8Snorm => filterable | storage,
1272            Tf::Rgba8Uint => renderable | storage,
1273            Tf::Rgba8Sint => renderable | storage,
1274            Tf::Rgb10a2Uint => renderable,
1275            Tf::Rgb10a2Unorm => filterable_renderable,
1276            Tf::Rg11b10Ufloat => filterable | float_renderable,
1277            Tf::R64Uint => image_64_atomic,
1278            Tf::Rg32Uint => renderable,
1279            Tf::Rg32Sint => renderable,
1280            Tf::Rg32Float => unfilterable | float_renderable | texture_float_linear,
1281            Tf::Rgba16Uint => renderable | storage,
1282            Tf::Rgba16Sint => renderable | storage,
1283            Tf::Rgba16Unorm => norm16_unorm | norm16_storage,
1284            Tf::Rgba16Snorm => norm16_snorm | norm16_storage,
1285            Tf::Rgba16Float => filterable | storage | half_float_renderable,
1286            Tf::Rgba32Uint => renderable | storage,
1287            Tf::Rgba32Sint => renderable | storage,
1288            Tf::Rgba32Float => unfilterable | storage | float_renderable | texture_float_linear,
1289            Tf::Stencil8
1290            | Tf::Depth16Unorm
1291            | Tf::Depth32Float
1292            | Tf::Depth32FloatStencil8
1293            | Tf::Depth24Plus
1294            | Tf::Depth24PlusStencil8 => depth,
1295            Tf::NV12 => empty,
1296            Tf::P010 => empty,
1297            Tf::Rgb9e5Ufloat => filterable,
1298            Tf::Bc1RgbaUnorm
1299            | Tf::Bc1RgbaUnormSrgb
1300            | Tf::Bc2RgbaUnorm
1301            | Tf::Bc2RgbaUnormSrgb
1302            | Tf::Bc3RgbaUnorm
1303            | Tf::Bc3RgbaUnormSrgb
1304            | Tf::Bc4RUnorm
1305            | Tf::Bc4RSnorm
1306            | Tf::Bc5RgUnorm
1307            | Tf::Bc5RgSnorm
1308            | Tf::Bc6hRgbFloat
1309            | Tf::Bc6hRgbUfloat
1310            | Tf::Bc7RgbaUnorm
1311            | Tf::Bc7RgbaUnormSrgb => bcn_features,
1312            Tf::Etc2Rgb8Unorm
1313            | Tf::Etc2Rgb8UnormSrgb
1314            | Tf::Etc2Rgb8A1Unorm
1315            | Tf::Etc2Rgb8A1UnormSrgb
1316            | Tf::Etc2Rgba8Unorm
1317            | Tf::Etc2Rgba8UnormSrgb
1318            | Tf::EacR11Unorm
1319            | Tf::EacR11Snorm
1320            | Tf::EacRg11Unorm
1321            | Tf::EacRg11Snorm => etc2_features,
1322            Tf::Astc {
1323                block: _,
1324                channel: AstcChannel::Unorm | AstcChannel::UnormSrgb,
1325            } => astc_features,
1326            Tf::Astc {
1327                block: _,
1328                channel: AstcChannel::Hdr,
1329            } => astc_hdr_features,
1330        }
1331    }
1332
1333    unsafe fn surface_capabilities(
1334        &self,
1335        surface: &super::Surface,
1336    ) -> Option<crate::SurfaceCapabilities> {
1337        #[cfg(webgl)]
1338        if self.shared.context.webgl2_context != surface.webgl2_context {
1339            return None;
1340        }
1341
1342        if surface.presentable {
1343            // There is no extended-range or wide-gamut path in the GLES
1344            // backend; everything is presented as sRGB.
1345            let format_caps = |format: wgt::TextureFormat| wgt::SurfaceFormatCapabilities {
1346                format,
1347                color_spaces: wgt::SurfaceColorSpaces::SRGB,
1348            };
1349            let mut formats = vec![
1350                format_caps(wgt::TextureFormat::Rgba8Unorm),
1351                #[cfg(native)]
1352                format_caps(wgt::TextureFormat::Bgra8Unorm),
1353            ];
1354            if surface.supports_srgb() {
1355                formats.extend([
1356                    format_caps(wgt::TextureFormat::Rgba8UnormSrgb),
1357                    #[cfg(native)]
1358                    format_caps(wgt::TextureFormat::Bgra8UnormSrgb),
1359                ])
1360            }
1361            if self
1362                .shared
1363                .private_caps
1364                .contains(super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT)
1365            {
1366                formats.push(format_caps(wgt::TextureFormat::Rgba16Float))
1367            }
1368
1369            Some(crate::SurfaceCapabilities {
1370                formats,
1371                present_modes: if cfg!(windows) {
1372                    vec![wgt::PresentMode::Fifo, wgt::PresentMode::Immediate]
1373                } else {
1374                    vec![wgt::PresentMode::Fifo] //TODO
1375                },
1376                composite_alpha_modes: vec![wgt::CompositeAlphaMode::Opaque], //TODO
1377                maximum_frame_latency: 2..=2, //TODO, unused currently
1378                current_extent: None,
1379                usage: wgt::TextureUses::COLOR_TARGET,
1380            })
1381        } else {
1382            None
1383        }
1384    }
1385
1386    unsafe fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
1387        wgt::PresentationTimestamp::INVALID_TIMESTAMP
1388    }
1389
1390    fn get_ordered_buffer_usages(&self) -> wgt::BufferUses {
1391        wgt::BufferUses::INCLUSIVE | wgt::BufferUses::MAP_WRITE
1392    }
1393
1394    // Don't put barriers between inclusive uses
1395    fn get_ordered_texture_usages(&self) -> wgt::TextureUses {
1396        wgt::TextureUses::INCLUSIVE
1397            | wgt::TextureUses::COLOR_TARGET
1398            | wgt::TextureUses::DEPTH_STENCIL_WRITE
1399    }
1400}
1401
1402impl super::AdapterShared {
1403    pub(super) unsafe fn get_buffer_sub_data(
1404        &self,
1405        gl: &glow::Context,
1406        target: u32,
1407        offset: i32,
1408        dst_data: &mut [u8],
1409    ) {
1410        if self
1411            .private_caps
1412            .contains(super::PrivateCapabilities::GET_BUFFER_SUB_DATA)
1413        {
1414            unsafe { gl.get_buffer_sub_data(target, offset, dst_data) };
1415        } else {
1416            log::error!("Fake map");
1417            let length = dst_data.len();
1418            // glMapBufferRange throws an error if length is 0.
1419            if length != 0 {
1420                let buffer_mapping =
1421                    unsafe { gl.map_buffer_range(target, offset, length as _, glow::MAP_READ_BIT) };
1422
1423                unsafe {
1424                    core::ptr::copy_nonoverlapping(buffer_mapping, dst_data.as_mut_ptr(), length)
1425                };
1426
1427                unsafe { gl.unmap_buffer(target) };
1428            }
1429        }
1430    }
1431}
1432
1433#[cfg(send_sync)]
1434unsafe impl Sync for super::Adapter {}
1435#[cfg(send_sync)]
1436unsafe impl Send for super::Adapter {}
1437
1438#[cfg(test)]
1439mod tests {
1440    use super::super::Adapter;
1441
1442    #[test]
1443    fn test_version_parse() {
1444        Adapter::parse_version("1").unwrap_err();
1445        Adapter::parse_version("1.").unwrap_err();
1446        Adapter::parse_version("1 h3l1o. W0rld").unwrap_err();
1447        Adapter::parse_version("1. h3l1o. W0rld").unwrap_err();
1448        Adapter::parse_version("1.2.3").unwrap_err();
1449
1450        assert_eq!(Adapter::parse_version("OpenGL ES 3.1").unwrap(), (3, 1));
1451        assert_eq!(
1452            Adapter::parse_version("OpenGL ES 2.0 Google Nexus").unwrap(),
1453            (2, 0)
1454        );
1455        assert_eq!(Adapter::parse_version("GLSL ES 1.1").unwrap(), (1, 1));
1456        assert_eq!(
1457            Adapter::parse_version("OpenGL ES GLSL ES 3.20").unwrap(),
1458            (3, 2)
1459        );
1460        assert_eq!(
1461            // WebGL 2.0 should parse as OpenGL ES 3.0
1462            Adapter::parse_version("WebGL 2.0 (OpenGL ES 3.0 Chromium)").unwrap(),
1463            (3, 0)
1464        );
1465        assert_eq!(
1466            Adapter::parse_version("WebGL GLSL ES 3.00 (OpenGL ES GLSL ES 3.0 Chromium)").unwrap(),
1467            (3, 0)
1468        );
1469    }
1470}