Skip to main content

wgpu_hal/gles/
adapter.rs

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