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::COMPARISON_SAMPLERS
388            | wgt::DownlevelFlags::MSL2_1
389            | wgt::DownlevelFlags::NON_POWER_OF_TWO_MIPMAPPED_TEXTURES
390            | wgt::DownlevelFlags::SHADER_F16_IN_F32;
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::CLEAR_TEXTURE
491            | wgt::Features::DEPTH32FLOAT_STENCIL8
492            | wgt::Features::IMMEDIATES
493            | wgt::Features::PASSTHROUGH_SHADERS
494            | wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES
495            | wgt::Features::UNRESTRICTED_BYTES_PER_ROW;
496        features.set(
497            wgt::Features::TEXTURE_FORMAT_16BIT_NORM,
498            supports_16bit_norm,
499        );
500        features.set(
501            wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER | wgt::Features::ADDRESS_MODE_CLAMP_TO_ZERO,
502            extensions.contains("GL_EXT_texture_border_clamp")
503                || extensions.contains("GL_ARB_texture_border_clamp"),
504        );
505        features.set(
506            wgt::Features::DEPTH_CLIP_CONTROL,
507            extensions.contains("GL_EXT_depth_clamp") || extensions.contains("GL_ARB_depth_clamp"),
508        );
509        features.set(
510            wgt::Features::VERTEX_WRITABLE_STORAGE,
511            downlevel_flags.contains(wgt::DownlevelFlags::VERTEX_STORAGE)
512                && vertex_shader_storage_textures != 0,
513        );
514        features.set(
515            wgt::Features::MULTIVIEW,
516            extensions.contains("OVR_multiview2") || extensions.contains("GL_OVR_multiview2"),
517        );
518        features.set(
519            wgt::Features::DUAL_SOURCE_BLENDING,
520            extensions.contains("GL_EXT_blend_func_extended")
521                || extensions.contains("GL_ARB_blend_func_extended"),
522        );
523        features.set(
524            wgt::Features::CLIP_DISTANCES,
525            full_ver.is_some() || extensions.contains("GL_EXT_clip_cull_distance"),
526        );
527        features.set(
528            wgt::Features::PRIMITIVE_INDEX,
529            supported((3, 2), (3, 2))
530                || extensions.contains("OES_geometry_shader")
531                || extensions.contains("GL_ARB_geometry_shader4"),
532        );
533        features.set(
534            wgt::Features::SHADER_EARLY_DEPTH_TEST,
535            supported((3, 1), (4, 2)) || extensions.contains("GL_ARB_shader_image_load_store"),
536        );
537        if extensions.contains("GL_ARB_timer_query") {
538            features.set(wgt::Features::TIMESTAMP_QUERY, true);
539            features.set(wgt::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS, true);
540            features.set(wgt::Features::TIMESTAMP_QUERY_INSIDE_PASSES, true);
541        }
542        let gl_bcn_exts = [
543            "GL_EXT_texture_compression_s3tc",
544            "GL_EXT_texture_compression_rgtc",
545            "GL_ARB_texture_compression_bptc",
546        ];
547        let gles_bcn_exts = [
548            "GL_EXT_texture_compression_s3tc_srgb",
549            "GL_EXT_texture_compression_rgtc",
550            "GL_EXT_texture_compression_bptc",
551        ];
552        let webgl_bcn_exts = [
553            "WEBGL_compressed_texture_s3tc",
554            "WEBGL_compressed_texture_s3tc_srgb",
555            "EXT_texture_compression_rgtc",
556            "EXT_texture_compression_bptc",
557        ];
558        let bcn_exts = if cfg!(any(webgl, Emscripten)) {
559            &webgl_bcn_exts[..]
560        } else if es_ver.is_some() {
561            &gles_bcn_exts[..]
562        } else {
563            &gl_bcn_exts[..]
564        };
565        features.set(
566            wgt::Features::TEXTURE_COMPRESSION_BC,
567            bcn_exts.iter().all(|&ext| extensions.contains(ext)),
568        );
569        features.set(
570            wgt::Features::TEXTURE_COMPRESSION_BC_SLICED_3D,
571            bcn_exts.iter().all(|&ext| extensions.contains(ext)), // BC guaranteed Sliced 3D
572        );
573        let has_etc = if cfg!(any(webgl, Emscripten)) {
574            extensions.contains("WEBGL_compressed_texture_etc")
575        } else {
576            es_ver.is_some() || extensions.contains("GL_ARB_ES3_compatibility")
577        };
578        features.set(wgt::Features::TEXTURE_COMPRESSION_ETC2, has_etc);
579
580        // `OES_texture_compression_astc` provides 2D + 3D, LDR + HDR support
581        if extensions.contains("WEBGL_compressed_texture_astc")
582            || extensions.contains("GL_OES_texture_compression_astc")
583        {
584            #[cfg(webgl)]
585            {
586                if context
587                    .glow_context
588                    .compressed_texture_astc_supports_ldr_profile()
589                {
590                    features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC);
591                    features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D);
592                }
593                if context
594                    .glow_context
595                    .compressed_texture_astc_supports_hdr_profile()
596                {
597                    features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR);
598                }
599            }
600
601            #[cfg(any(native, Emscripten))]
602            {
603                features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC);
604                features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D);
605                features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR);
606            }
607        } else {
608            features.set(
609                wgt::Features::TEXTURE_COMPRESSION_ASTC,
610                extensions.contains("GL_KHR_texture_compression_astc_ldr"),
611            );
612            features.set(
613                wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D,
614                extensions.contains("GL_KHR_texture_compression_astc_ldr")
615                    && extensions.contains("GL_KHR_texture_compression_astc_sliced_3d"),
616            );
617            features.set(
618                wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR,
619                extensions.contains("GL_KHR_texture_compression_astc_hdr"),
620            );
621        }
622
623        downlevel_flags.set(
624            wgt::DownlevelFlags::TEXTURE_COMPRESSION,
625            features.contains(wgt::Features::TEXTURE_COMPRESSION_BC)
626                || features.contains(
627                    wgt::Features::TEXTURE_COMPRESSION_ETC2
628                        | wgt::Features::TEXTURE_COMPRESSION_ASTC,
629                ),
630        );
631
632        features.set(
633            wgt::Features::FLOAT32_FILTERABLE,
634            extensions.contains("GL_ARB_color_buffer_float")
635                || extensions.contains("GL_EXT_color_buffer_float")
636                || extensions.contains("OES_texture_float_linear"),
637        );
638
639        if es_ver.is_none() {
640            features |= wgt::Features::POLYGON_MODE_LINE | wgt::Features::POLYGON_MODE_POINT;
641        }
642
643        // We *might* be able to emulate bgra8unorm-storage but currently don't attempt to.
644
645        let mut private_caps = super::PrivateCapabilities::empty();
646        private_caps.set(
647            super::PrivateCapabilities::BUFFER_ALLOCATION,
648            extensions.contains("GL_EXT_buffer_storage")
649                || extensions.contains("GL_ARB_buffer_storage"),
650        );
651        private_caps.set(
652            super::PrivateCapabilities::SHADER_BINDING_LAYOUT,
653            supports_compute,
654        );
655        private_caps.set(
656            super::PrivateCapabilities::SHADER_TEXTURE_SHADOW_LOD,
657            extensions.contains("GL_EXT_texture_shadow_lod"),
658        );
659        private_caps.set(
660            super::PrivateCapabilities::MEMORY_BARRIERS,
661            supported((3, 1), (4, 2)),
662        );
663        private_caps.set(
664            super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT,
665            supported((3, 1), (4, 3)) || extensions.contains("GL_ARB_vertex_attrib_binding"),
666        );
667        private_caps.set(
668            super::PrivateCapabilities::INDEX_BUFFER_ROLE_CHANGE,
669            !cfg!(any(webgl, Emscripten)),
670        );
671        private_caps.set(
672            super::PrivateCapabilities::GET_BUFFER_SUB_DATA,
673            cfg!(any(webgl, Emscripten)) || full_ver.is_some(),
674        );
675        let color_buffer_float = extensions.contains("GL_EXT_color_buffer_float")
676            || extensions.contains("GL_ARB_color_buffer_float")
677            || extensions.contains("EXT_color_buffer_float");
678        let color_buffer_half_float = extensions.contains("GL_EXT_color_buffer_half_float")
679            || extensions.contains("GL_ARB_half_float_pixel");
680        private_caps.set(
681            super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT,
682            color_buffer_half_float || color_buffer_float,
683        );
684        private_caps.set(
685            super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
686            color_buffer_float,
687        );
688        private_caps.set(super::PrivateCapabilities::QUERY_BUFFERS, query_buffers);
689        private_caps.set(super::PrivateCapabilities::QUERY_64BIT, full_ver.is_some());
690        private_caps.set(
691            super::PrivateCapabilities::TEXTURE_STORAGE,
692            supported((3, 0), (4, 2)),
693        );
694        let is_mali = renderer.to_lowercase().contains("mali");
695        let debug_fns_enabled = match backend_options.debug_fns {
696            wgt::GlDebugFns::Auto => gl.supports_debug() && !is_mali,
697            wgt::GlDebugFns::ForceEnabled => gl.supports_debug(),
698            wgt::GlDebugFns::Disabled => false,
699        };
700        private_caps.set(super::PrivateCapabilities::DEBUG_FNS, debug_fns_enabled);
701        private_caps.set(
702            super::PrivateCapabilities::INVALIDATE_FRAMEBUFFER,
703            supported((3, 0), (4, 3)),
704        );
705        if let Some(full_ver) = full_ver {
706            let supported =
707                full_ver >= (4, 2) && extensions.contains("GL_ARB_shader_draw_parameters");
708            private_caps.set(
709                super::PrivateCapabilities::FULLY_FEATURED_INSTANCING,
710                supported,
711            );
712            // Desktop 4.2 and greater specify the first instance parameter.
713            //
714            // For all other versions, the behavior is undefined.
715            //
716            // We only support indirect first instance when we also have ARB_shader_draw_parameters as
717            // that's the only way to get gl_InstanceID to work correctly.
718            features.set(wgt::Features::INDIRECT_FIRST_INSTANCE, supported);
719        }
720        private_caps.set(
721            super::PrivateCapabilities::MULTISAMPLED_RENDER_TO_TEXTURE,
722            extensions.contains("GL_EXT_multisampled_render_to_texture"),
723        );
724        private_caps.set(
725            super::PrivateCapabilities::TEXTURE_FORMAT_NORM16,
726            supports_16bit_norm,
727        );
728        private_caps.set(
729            super::PrivateCapabilities::TEXTURE_FORMAT_SNORM16_RENDERABLE,
730            supports_16bit_snorm_renderable,
731        );
732        private_caps.set(
733            super::PrivateCapabilities::TEXTURE_FORMAT_NORM16_STORAGE,
734            supports_16bit_norm_storage,
735        );
736
737        // GLSL ES 3.10+ / GLSL 4.30+ natively support coherent/volatile qualifiers
738        // on storage buffers. These were introduced alongside storage buffer support.
739        if supports_storage {
740            features |= wgt::Features::MEMORY_DECORATION_COHERENT
741                | wgt::Features::MEMORY_DECORATION_VOLATILE;
742        }
743
744        let max_texture_size = unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_SIZE) } as u32;
745        let max_texture_3d_size = unsafe { gl.get_parameter_i32(glow::MAX_3D_TEXTURE_SIZE) } as u32;
746
747        let min_uniform_buffer_offset_alignment =
748            (unsafe { gl.get_parameter_i32(glow::UNIFORM_BUFFER_OFFSET_ALIGNMENT) } as u32);
749        let min_storage_buffer_offset_alignment = if supports_storage {
750            (unsafe { gl.get_parameter_i32(glow::SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT) } as u32)
751        } else {
752            256
753        };
754        let max_uniform_buffers_per_shader_stage =
755            unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_UNIFORM_BLOCKS) }
756                .min(unsafe { gl.get_parameter_i32(glow::MAX_FRAGMENT_UNIFORM_BLOCKS) })
757                as u32;
758
759        let max_compute_workgroups_per_dimension = if supports_work_group_params {
760            unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 0) }
761                .min(unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 1) })
762                .min(unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 2) })
763                as u32
764        } else {
765            0
766        };
767
768        let max_color_attachments = unsafe {
769            gl.get_parameter_i32(glow::MAX_COLOR_ATTACHMENTS)
770                .min(gl.get_parameter_i32(glow::MAX_DRAW_BUFFERS)) as u32
771        };
772
773        // 16 bytes per sample is the maximum size of a color attachment.
774        let max_color_attachment_bytes_per_sample =
775            max_color_attachments * wgt::TextureFormat::MAX_TARGET_PIXEL_BYTE_COST;
776
777        let limits = crate::auxil::adjust_raw_limits(wgt::Limits {
778            max_texture_dimension_1d: max_texture_size,
779            max_texture_dimension_2d: max_texture_size,
780            max_texture_dimension_3d: max_texture_3d_size,
781            max_texture_array_layers: unsafe {
782                gl.get_parameter_i32(glow::MAX_ARRAY_TEXTURE_LAYERS)
783            } as u32,
784            max_bind_groups: u32::MAX,
785            // No limit.
786            max_bind_groups_plus_vertex_buffers: u32::MAX,
787            // No limit.
788            max_bindings_per_bind_group: u32::MAX,
789            max_dynamic_uniform_buffers_per_pipeline_layout: max_uniform_buffers_per_shader_stage,
790            max_dynamic_storage_buffers_per_pipeline_layout: max_storage_buffers_per_shader_stage,
791            max_sampled_textures_per_shader_stage: super::MAX_TEXTURE_SLOTS as u32,
792            max_samplers_per_shader_stage: super::MAX_SAMPLERS as u32,
793            max_storage_buffers_per_shader_stage,
794            max_storage_buffers_in_vertex_stage: 0,
795            max_storage_buffers_in_fragment_stage: 0,
796            max_storage_textures_per_shader_stage,
797            max_storage_textures_in_vertex_stage: 0,
798            max_storage_textures_in_fragment_stage: 0,
799            max_uniform_buffers_per_shader_stage,
800            max_binding_array_elements_per_shader_stage: 0,
801            max_binding_array_sampler_elements_per_shader_stage: 0,
802            max_binding_array_acceleration_structure_elements_per_shader_stage: 0,
803            max_uniform_buffer_binding_size: unsafe {
804                gl.get_parameter_i32(glow::MAX_UNIFORM_BLOCK_SIZE)
805            } as u64,
806            max_storage_buffer_binding_size: if supports_storage {
807                unsafe { gl.get_parameter_i32(glow::MAX_SHADER_STORAGE_BLOCK_SIZE) }
808            } else {
809                0
810            } as u64,
811            max_vertex_buffers: if private_caps
812                .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
813            {
814                (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_BINDINGS) } as u32)
815            } else {
816                16 // should this be different?
817            },
818            max_vertex_attributes: (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIBS) }
819                as u32)
820                .min(super::MAX_VERTEX_ATTRIBUTES as u32),
821            max_vertex_buffer_array_stride: if private_caps
822                .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
823            {
824                if let Some(full_ver) = full_ver {
825                    if full_ver >= (4, 4) {
826                        // We can query `GL_MAX_VERTEX_ATTRIB_STRIDE` in OpenGL 4.4+
827                        let value =
828                            (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) })
829                                as u32;
830
831                        if value == 0 {
832                            // This should be at least 2048, but the driver for AMD Radeon HD 5870 on
833                            // Windows doesn't recognize `GL_MAX_VERTEX_ATTRIB_STRIDE`.
834
835                            log::debug!("Max vertex attribute stride is 0. Assuming it is the OpenGL minimum spec 2048");
836                            2048
837                        } else {
838                            value
839                        }
840                    } else {
841                        log::debug!("Max vertex attribute stride unknown. Assuming it is the OpenGL minimum spec 2048");
842                        2048
843                    }
844                } else {
845                    (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) }) as u32
846                }
847            } else {
848                !0
849            },
850            max_immediate_size: super::MAX_IMMEDIATES as u32 * 4,
851            min_uniform_buffer_offset_alignment,
852            min_storage_buffer_offset_alignment,
853            max_inter_stage_shader_variables: {
854                // MAX_VARYING_COMPONENTS may return 0, because it is deprecated since OpenGL 3.2 core,
855                // and an OpenGL Context with the core profile and with forward-compatibility=true,
856                // will make deprecated constants unavailable.
857                let max_varying_components =
858                    unsafe { gl.get_parameter_i32(glow::MAX_VARYING_COMPONENTS) } as u32;
859                if max_varying_components == 0 {
860                    // default value for max_inter_stage_shader_variables
861                    15
862                } else {
863                    max_varying_components / 4
864                }
865            },
866            max_color_attachments,
867            max_color_attachment_bytes_per_sample,
868            max_compute_workgroup_storage_size: if supports_work_group_params {
869                (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_SHARED_MEMORY_SIZE) } as u32)
870            } else {
871                0
872            },
873            max_compute_invocations_per_workgroup: if supports_work_group_params {
874                (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_WORK_GROUP_INVOCATIONS) } as u32)
875            } else {
876                0
877            },
878            max_compute_workgroup_size_x: if supports_work_group_params {
879                (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 0) }
880                    as u32)
881            } else {
882                0
883            },
884            max_compute_workgroup_size_y: if supports_work_group_params {
885                (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 1) }
886                    as u32)
887            } else {
888                0
889            },
890            max_compute_workgroup_size_z: if supports_work_group_params {
891                (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 2) }
892                    as u32)
893            } else {
894                0
895            },
896            max_compute_workgroups_per_dimension,
897            max_buffer_size: i32::MAX as u64,
898            max_non_sampler_bindings: u32::MAX,
899
900            max_task_workgroup_total_count: 0,
901            max_task_workgroups_per_dimension: 0,
902            max_mesh_workgroup_total_count: 0,
903            max_mesh_workgroups_per_dimension: 0,
904            max_task_invocations_per_workgroup: 0,
905            max_task_invocations_per_dimension: 0,
906            max_mesh_invocations_per_workgroup: 0,
907            max_mesh_invocations_per_dimension: 0,
908            max_task_payload_size: 0,
909            max_mesh_output_vertices: 0,
910            max_mesh_output_primitives: 0,
911            max_mesh_output_layers: 0,
912            max_mesh_multiview_view_count: 0,
913
914            max_blas_primitive_count: 0,
915            max_blas_geometry_count: 0,
916            max_tlas_instance_count: 0,
917            max_acceleration_structures_per_shader_stage: 0,
918            max_buffers_and_acceleration_structures_per_shader_stage: u32::MAX,
919
920            max_multiview_view_count: 0,
921
922            max_ray_dispatch_count: 0,
923            max_ray_recursion_depth: 0,
924        });
925
926        let mut workarounds = super::Workarounds::empty();
927
928        workarounds.set(
929            super::Workarounds::EMULATE_BUFFER_MAP,
930            cfg!(any(webgl, Emscripten)),
931        );
932
933        let r = renderer.to_lowercase();
934        // Check for Mesa sRGB clear bug. See
935        // [`super::PrivateCapabilities::MESA_I915_SRGB_SHADER_CLEAR`].
936        if context.is_owned()
937            && r.contains("mesa")
938            && r.contains("intel")
939            && r.split(&[' ', '(', ')'][..])
940                .any(|substr| substr.len() == 3 && substr.chars().nth(2) == Some('l'))
941        {
942            log::debug!(
943                "Detected skylake derivative running on mesa i915. Clears to srgb textures will \
944                use manual shader clears."
945            );
946            workarounds.set(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR, true);
947        }
948
949        let downlevel_defaults = wgt::DownlevelLimits {};
950        let max_samples = unsafe { gl.get_parameter_i32(glow::MAX_SAMPLES) };
951
952        // Drop the GL guard so we can move the context into AdapterShared
953        // ( on Wasm the gl handle is just a ref so we tell clippy to allow
954        // dropping the ref )
955        #[cfg_attr(target_family = "wasm", allow(dropping_references))]
956        drop(gl);
957
958        Some(crate::ExposedAdapter {
959            adapter: super::Adapter {
960                shared: Arc::new(super::AdapterShared {
961                    context,
962                    private_caps,
963                    workarounds,
964                    features,
965                    limits: limits.clone(),
966                    options: backend_options,
967                    shading_language_version,
968                    next_shader_id: Default::default(),
969                    program_cache: Default::default(),
970                    es: es_ver.is_some(),
971                    max_msaa_samples: max_samples,
972                }),
973            },
974            info: Self::make_info(vendor, renderer, version),
975            features,
976            capabilities: crate::Capabilities {
977                limits,
978                downlevel: wgt::DownlevelCapabilities {
979                    flags: downlevel_flags,
980                    limits: downlevel_defaults,
981                    shader_model: wgt::ShaderModel::Sm5,
982                },
983                alignments: crate::Alignments {
984                    buffer_copy_offset: wgt::BufferSize::new(4).unwrap(),
985                    buffer_copy_pitch: wgt::BufferSize::new(4).unwrap(),
986                    // #6151: `wgpu_hal::gles` doesn't ask Naga to inject bounds
987                    // checks in GLSL, and it doesn't request extensions like
988                    // `KHR_robust_buffer_access_behavior` that would provide
989                    // them, so we can't really implement the checks promised by
990                    // [`crate::BufferBinding`].
991                    //
992                    // Since this is a pre-existing condition, for the time
993                    // being, provide 1 as the value here, to cause as little
994                    // trouble as possible.
995                    uniform_bounds_check_alignment: wgt::BufferSize::new(1).unwrap(),
996                    raw_tlas_instance_size: 0,
997                    ray_tracing_scratch_buffer_alignment: 0,
998                    ray_tracing_pipeline_group_data_size: 0,
999                    ray_tracing_pipeline_group_data_alignment: 0,
1000                    ray_tracing_pipeline_data_offset_alignment: 0,
1001                },
1002                cooperative_matrix_properties: Vec::new(),
1003            },
1004        })
1005    }
1006
1007    unsafe fn compile_shader(
1008        source: &str,
1009        gl: &glow::Context,
1010        shader_type: u32,
1011        es: bool,
1012    ) -> Option<glow::Shader> {
1013        let source = if es {
1014            format!("#version 300 es\nprecision lowp float;\n{source}")
1015        } else {
1016            let version = gl.version();
1017            if version.major == 3 && version.minor == 0 {
1018                // OpenGL 3.0 only supports this format
1019                format!("#version 130\n{source}")
1020            } else {
1021                // OpenGL 3.1+ support this format
1022                format!("#version 140\n{source}")
1023            }
1024        };
1025        let shader = unsafe { gl.create_shader(shader_type) }.expect("Could not create shader");
1026        unsafe { gl.shader_source(shader, &source) };
1027        unsafe { gl.compile_shader(shader) };
1028
1029        if !unsafe { gl.get_shader_compile_status(shader) } {
1030            let msg = unsafe { gl.get_shader_info_log(shader) };
1031            if !msg.is_empty() {
1032                log::error!("\tShader compile error: {msg}");
1033            }
1034            unsafe { gl.delete_shader(shader) };
1035            None
1036        } else {
1037            Some(shader)
1038        }
1039    }
1040
1041    unsafe fn create_shader_clear_program(
1042        gl: &glow::Context,
1043        es: bool,
1044    ) -> Option<ShaderClearProgram> {
1045        let program = unsafe { gl.create_program() }.expect("Could not create shader program");
1046        let vertex = unsafe {
1047            Self::compile_shader(
1048                include_str!("./shaders/clear.vert"),
1049                gl,
1050                glow::VERTEX_SHADER,
1051                es,
1052            )?
1053        };
1054        let fragment = unsafe {
1055            Self::compile_shader(
1056                include_str!("./shaders/clear.frag"),
1057                gl,
1058                glow::FRAGMENT_SHADER,
1059                es,
1060            )?
1061        };
1062        unsafe { gl.attach_shader(program, vertex) };
1063        unsafe { gl.attach_shader(program, fragment) };
1064        unsafe { gl.link_program(program) };
1065
1066        let linked_ok = unsafe { gl.get_program_link_status(program) };
1067        let msg = unsafe { gl.get_program_info_log(program) };
1068        if !msg.is_empty() {
1069            log::error!("Shader link error: {msg}");
1070        }
1071        if !linked_ok {
1072            return None;
1073        }
1074
1075        let color_uniform_location = unsafe { gl.get_uniform_location(program, "color") }
1076            .expect("Could not find color uniform in shader clear shader");
1077        unsafe { gl.delete_shader(vertex) };
1078        unsafe { gl.delete_shader(fragment) };
1079
1080        Some(ShaderClearProgram {
1081            program,
1082            color_uniform_location,
1083        })
1084    }
1085}
1086
1087impl crate::Adapter for super::Adapter {
1088    type A = super::Api;
1089
1090    unsafe fn open(
1091        &self,
1092        features: wgt::Features,
1093        _limits: &wgt::Limits,
1094        _memory_hints: &wgt::MemoryHints,
1095    ) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
1096        let gl = &self.shared.context.lock();
1097        unsafe { gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1) };
1098        unsafe { gl.pixel_store_i32(glow::PACK_ALIGNMENT, 1) };
1099        let main_vao =
1100            unsafe { gl.create_vertex_array() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
1101        unsafe { gl.bind_vertex_array(Some(main_vao)) };
1102
1103        // Desktop GL defaults to non-seamless cube sampling; ES 3.0+ and other backends are seamless by spec.
1104        if !self.shared.es {
1105            unsafe { gl.enable(glow::TEXTURE_CUBE_MAP_SEAMLESS) };
1106        }
1107
1108        let zero_buffer =
1109            unsafe { gl.create_buffer() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
1110        unsafe { gl.bind_buffer(glow::COPY_READ_BUFFER, Some(zero_buffer)) };
1111        let zeroes = vec![0u8; super::ZERO_BUFFER_SIZE];
1112        unsafe { gl.buffer_data_u8_slice(glow::COPY_READ_BUFFER, &zeroes, glow::STATIC_DRAW) };
1113
1114        // Compile the shader program we use for doing manual clears to work around Mesa fastclear
1115        // bug.
1116
1117        let shader_clear_program = if self
1118            .shared
1119            .workarounds
1120            .contains(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR)
1121        {
1122            Some(unsafe {
1123                Self::create_shader_clear_program(gl, self.shared.es)
1124                    .ok_or(crate::DeviceError::Lost)?
1125            })
1126        } else {
1127            // If we don't need the workaround, don't waste time and resources compiling the clear program
1128            None
1129        };
1130
1131        Ok(crate::OpenDevice {
1132            device: super::Device {
1133                shared: Arc::clone(&self.shared),
1134                main_vao,
1135                #[cfg(all(native, feature = "renderdoc"))]
1136                render_doc: Default::default(),
1137                counters: Default::default(),
1138            },
1139            queue: super::Queue {
1140                shared: Arc::clone(&self.shared),
1141                features,
1142                draw_fbo: unsafe { gl.create_framebuffer() }
1143                    .map_err(|_| crate::DeviceError::OutOfMemory)?,
1144                copy_fbo: unsafe { gl.create_framebuffer() }
1145                    .map_err(|_| crate::DeviceError::OutOfMemory)?,
1146                shader_clear_program,
1147                zero_buffer,
1148                temp_query_results: Mutex::new(Vec::new()),
1149                draw_buffer_count: AtomicU8::new(1),
1150                current_index_buffer: Mutex::new(None),
1151            },
1152        })
1153    }
1154
1155    unsafe fn texture_format_capabilities(
1156        &self,
1157        format: wgt::TextureFormat,
1158    ) -> crate::TextureFormatCapabilities {
1159        use crate::TextureFormatCapabilities as Tfc;
1160        use wgt::TextureFormat as Tf;
1161
1162        let sample_count = {
1163            let max_samples = self.shared.max_msaa_samples;
1164            if max_samples >= 16 {
1165                Tfc::MULTISAMPLE_X2
1166                    | Tfc::MULTISAMPLE_X4
1167                    | Tfc::MULTISAMPLE_X8
1168                    | Tfc::MULTISAMPLE_X16
1169            } else if max_samples >= 8 {
1170                Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4 | Tfc::MULTISAMPLE_X8
1171            } else {
1172                // The lowest supported level in GLE3.0/WebGL2 is 4X
1173                // (see GL_MAX_SAMPLES in https://registry.khronos.org/OpenGL-Refpages/es3.0/html/glGet.xhtml).
1174                // On some platforms, like iOS Safari, `get_parameter_i32(MAX_SAMPLES)` returns 0,
1175                // so we always fall back to supporting 4x here.
1176                Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4
1177            }
1178        };
1179
1180        // Base types are pulled from the table in the OpenGLES 3.0 spec in section 3.8.
1181        //
1182        // The storage types are based on table 8.26, in section
1183        // "TEXTURE IMAGE LOADS AND STORES" of OpenGLES-3.2 spec.
1184        let empty = Tfc::empty();
1185        let base = Tfc::COPY_SRC | Tfc::COPY_DST;
1186        let unfilterable = base | Tfc::SAMPLED;
1187        let depth = base | Tfc::SAMPLED | sample_count | Tfc::DEPTH_STENCIL_ATTACHMENT;
1188        let filterable = unfilterable | Tfc::SAMPLED_LINEAR;
1189        let renderable =
1190            unfilterable | Tfc::COLOR_ATTACHMENT | sample_count | Tfc::MULTISAMPLE_RESOLVE;
1191        let filterable_renderable = filterable | renderable | Tfc::COLOR_ATTACHMENT_BLEND;
1192        let storage =
1193            base | Tfc::STORAGE_READ_WRITE | Tfc::STORAGE_READ_ONLY | Tfc::STORAGE_WRITE_ONLY;
1194
1195        let feature_fn = |f, caps| {
1196            if self.shared.features.contains(f) {
1197                caps
1198            } else {
1199                empty
1200            }
1201        };
1202
1203        let bcn_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_BC, filterable);
1204        let etc2_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ETC2, filterable);
1205        let astc_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC, filterable);
1206        let astc_hdr_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR, filterable);
1207
1208        let private_caps_fn = |f, caps| {
1209            if self.shared.private_caps.contains(f) {
1210                caps
1211            } else {
1212                empty
1213            }
1214        };
1215
1216        let half_float_renderable = private_caps_fn(
1217            super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT,
1218            Tfc::COLOR_ATTACHMENT
1219                | Tfc::COLOR_ATTACHMENT_BLEND
1220                | sample_count
1221                | Tfc::MULTISAMPLE_RESOLVE,
1222        );
1223
1224        let float_renderable = private_caps_fn(
1225            super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
1226            Tfc::COLOR_ATTACHMENT
1227                | Tfc::COLOR_ATTACHMENT_BLEND
1228                | sample_count
1229                | Tfc::MULTISAMPLE_RESOLVE,
1230        );
1231
1232        let texture_float_linear = feature_fn(wgt::Features::FLOAT32_FILTERABLE, filterable);
1233
1234        let image_atomic = feature_fn(wgt::Features::TEXTURE_ATOMIC, Tfc::STORAGE_ATOMIC);
1235        let image_64_atomic = feature_fn(wgt::Features::TEXTURE_INT64_ATOMIC, Tfc::STORAGE_ATOMIC);
1236
1237        // UNORM gets full filterable+renderable; SNORM splits because
1238        // `EXT_texture_norm16` marks only UNORM as color-renderable.
1239        // Storage rides on a separate cap (desktop GL >= 4.2 core / pre-4.2
1240        // `GL_ARB_shader_image_load_store`, GLES `GL_NV_image_formats`).
1241        let norm16_unorm = private_caps_fn(
1242            super::PrivateCapabilities::TEXTURE_FORMAT_NORM16,
1243            filterable_renderable,
1244        );
1245        let norm16_snorm = if self
1246            .shared
1247            .private_caps
1248            .contains(super::PrivateCapabilities::TEXTURE_FORMAT_SNORM16_RENDERABLE)
1249        {
1250            norm16_unorm
1251        } else {
1252            private_caps_fn(
1253                super::PrivateCapabilities::TEXTURE_FORMAT_NORM16,
1254                filterable,
1255            )
1256        };
1257        let norm16_storage = private_caps_fn(
1258            super::PrivateCapabilities::TEXTURE_FORMAT_NORM16_STORAGE,
1259            storage,
1260        );
1261
1262        match format {
1263            Tf::R8Unorm => filterable_renderable,
1264            Tf::R8Snorm => filterable,
1265            Tf::R8Uint => renderable,
1266            Tf::R8Sint => renderable,
1267            Tf::R16Uint => renderable,
1268            Tf::R16Sint => renderable,
1269            Tf::R16Unorm => norm16_unorm | norm16_storage,
1270            Tf::R16Snorm => norm16_snorm | norm16_storage,
1271            Tf::R16Float => filterable | half_float_renderable,
1272            Tf::Rg8Unorm => filterable_renderable,
1273            Tf::Rg8Snorm => filterable,
1274            Tf::Rg8Uint => renderable,
1275            Tf::Rg8Sint => renderable,
1276            Tf::R32Uint => renderable | storage | image_atomic,
1277            Tf::R32Sint => renderable | storage | image_atomic,
1278            Tf::R32Float => unfilterable | storage | float_renderable | texture_float_linear,
1279            Tf::Rg16Uint => renderable,
1280            Tf::Rg16Sint => renderable,
1281            Tf::Rg16Unorm => norm16_unorm | norm16_storage,
1282            Tf::Rg16Snorm => norm16_snorm | norm16_storage,
1283            Tf::Rg16Float => filterable | half_float_renderable,
1284            Tf::Rgba8Unorm => filterable_renderable | storage,
1285            Tf::Rgba8UnormSrgb => filterable_renderable,
1286            Tf::Bgra8Unorm | Tf::Bgra8UnormSrgb => filterable_renderable,
1287            Tf::Rgba8Snorm => filterable | storage,
1288            Tf::Rgba8Uint => renderable | storage,
1289            Tf::Rgba8Sint => renderable | storage,
1290            Tf::Rgb10a2Uint => renderable,
1291            Tf::Rgb10a2Unorm => filterable_renderable,
1292            Tf::Rg11b10Ufloat => filterable | float_renderable,
1293            Tf::R64Uint => image_64_atomic,
1294            Tf::Rg32Uint => renderable,
1295            Tf::Rg32Sint => renderable,
1296            Tf::Rg32Float => unfilterable | float_renderable | texture_float_linear,
1297            Tf::Rgba16Uint => renderable | storage,
1298            Tf::Rgba16Sint => renderable | storage,
1299            Tf::Rgba16Unorm => norm16_unorm | norm16_storage,
1300            Tf::Rgba16Snorm => norm16_snorm | norm16_storage,
1301            Tf::Rgba16Float => filterable | storage | half_float_renderable,
1302            Tf::Rgba32Uint => renderable | storage,
1303            Tf::Rgba32Sint => renderable | storage,
1304            Tf::Rgba32Float => unfilterable | storage | float_renderable | texture_float_linear,
1305            Tf::Stencil8
1306            | Tf::Depth16Unorm
1307            | Tf::Depth32Float
1308            | Tf::Depth32FloatStencil8
1309            | Tf::Depth24Plus
1310            | Tf::Depth24PlusStencil8 => depth,
1311            Tf::NV12 => empty,
1312            Tf::P010 => empty,
1313            Tf::Rgb9e5Ufloat => filterable,
1314            Tf::Bc1RgbaUnorm
1315            | Tf::Bc1RgbaUnormSrgb
1316            | Tf::Bc2RgbaUnorm
1317            | Tf::Bc2RgbaUnormSrgb
1318            | Tf::Bc3RgbaUnorm
1319            | Tf::Bc3RgbaUnormSrgb
1320            | Tf::Bc4RUnorm
1321            | Tf::Bc4RSnorm
1322            | Tf::Bc5RgUnorm
1323            | Tf::Bc5RgSnorm
1324            | Tf::Bc6hRgbFloat
1325            | Tf::Bc6hRgbUfloat
1326            | Tf::Bc7RgbaUnorm
1327            | Tf::Bc7RgbaUnormSrgb => bcn_features,
1328            Tf::Etc2Rgb8Unorm
1329            | Tf::Etc2Rgb8UnormSrgb
1330            | Tf::Etc2Rgb8A1Unorm
1331            | Tf::Etc2Rgb8A1UnormSrgb
1332            | Tf::Etc2Rgba8Unorm
1333            | Tf::Etc2Rgba8UnormSrgb
1334            | Tf::EacR11Unorm
1335            | Tf::EacR11Snorm
1336            | Tf::EacRg11Unorm
1337            | Tf::EacRg11Snorm => etc2_features,
1338            Tf::Astc {
1339                block: _,
1340                channel: AstcChannel::Unorm | AstcChannel::UnormSrgb,
1341            } => astc_features,
1342            Tf::Astc {
1343                block: _,
1344                channel: AstcChannel::Hdr,
1345            } => astc_hdr_features,
1346        }
1347    }
1348
1349    unsafe fn surface_capabilities(
1350        &self,
1351        surface: &super::Surface,
1352    ) -> Option<crate::SurfaceCapabilities> {
1353        #[cfg(webgl)]
1354        if self.shared.context.webgl2_context != surface.webgl2_context {
1355            return None;
1356        }
1357
1358        if surface.presentable {
1359            // There is no extended-range or wide-gamut path in the GLES
1360            // backend; everything is presented as sRGB.
1361            let format_caps = |format: wgt::TextureFormat| wgt::SurfaceFormatCapabilities {
1362                format,
1363                color_spaces: wgt::SurfaceColorSpaces::SRGB,
1364            };
1365            let mut formats = vec![
1366                format_caps(wgt::TextureFormat::Rgba8Unorm),
1367                #[cfg(native)]
1368                format_caps(wgt::TextureFormat::Bgra8Unorm),
1369            ];
1370            if surface.supports_srgb() {
1371                formats.extend([
1372                    format_caps(wgt::TextureFormat::Rgba8UnormSrgb),
1373                    #[cfg(native)]
1374                    format_caps(wgt::TextureFormat::Bgra8UnormSrgb),
1375                ])
1376            }
1377            if self
1378                .shared
1379                .private_caps
1380                .contains(super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT)
1381            {
1382                formats.push(format_caps(wgt::TextureFormat::Rgba16Float))
1383            }
1384
1385            Some(crate::SurfaceCapabilities {
1386                formats,
1387                present_modes: if cfg!(windows) {
1388                    vec![wgt::PresentMode::Fifo, wgt::PresentMode::Immediate]
1389                } else {
1390                    vec![wgt::PresentMode::Fifo] //TODO
1391                },
1392                composite_alpha_modes: vec![wgt::CompositeAlphaMode::Opaque], //TODO
1393                maximum_frame_latency: 2..=2, //TODO, unused currently
1394                current_extent: None,
1395                usage: wgt::TextureUses::COLOR_TARGET,
1396            })
1397        } else {
1398            None
1399        }
1400    }
1401
1402    unsafe fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
1403        wgt::PresentationTimestamp::INVALID_TIMESTAMP
1404    }
1405
1406    fn get_ordered_buffer_usages(&self) -> wgt::BufferUses {
1407        wgt::BufferUses::INCLUSIVE | wgt::BufferUses::MAP_WRITE
1408    }
1409
1410    // Don't put barriers between inclusive uses
1411    fn get_ordered_texture_usages(&self) -> wgt::TextureUses {
1412        wgt::TextureUses::INCLUSIVE
1413            | wgt::TextureUses::COLOR_TARGET
1414            | wgt::TextureUses::DEPTH_WRITE
1415            | wgt::TextureUses::STENCIL_WRITE
1416    }
1417}
1418
1419impl super::AdapterShared {
1420    pub(super) unsafe fn get_buffer_sub_data(
1421        &self,
1422        gl: &glow::Context,
1423        target: u32,
1424        offset: i32,
1425        dst_data: &mut [u8],
1426    ) {
1427        if self
1428            .private_caps
1429            .contains(super::PrivateCapabilities::GET_BUFFER_SUB_DATA)
1430        {
1431            unsafe { gl.get_buffer_sub_data(target, offset, dst_data) };
1432        } else {
1433            log::error!("Fake map");
1434            let length = dst_data.len();
1435            // glMapBufferRange throws an error if length is 0.
1436            if length != 0 {
1437                let buffer_mapping =
1438                    unsafe { gl.map_buffer_range(target, offset, length as _, glow::MAP_READ_BIT) };
1439
1440                unsafe {
1441                    core::ptr::copy_nonoverlapping(buffer_mapping, dst_data.as_mut_ptr(), length)
1442                };
1443
1444                unsafe { gl.unmap_buffer(target) };
1445            }
1446        }
1447    }
1448}
1449
1450#[cfg(send_sync)]
1451unsafe impl Sync for super::Adapter {}
1452#[cfg(send_sync)]
1453unsafe impl Send for super::Adapter {}
1454
1455#[cfg(test)]
1456mod tests {
1457    use super::super::Adapter;
1458
1459    #[test]
1460    fn test_version_parse() {
1461        Adapter::parse_version("1").unwrap_err();
1462        Adapter::parse_version("1.").unwrap_err();
1463        Adapter::parse_version("1 h3l1o. W0rld").unwrap_err();
1464        Adapter::parse_version("1. h3l1o. W0rld").unwrap_err();
1465        Adapter::parse_version("1.2.3").unwrap_err();
1466
1467        assert_eq!(Adapter::parse_version("OpenGL ES 3.1").unwrap(), (3, 1));
1468        assert_eq!(
1469            Adapter::parse_version("OpenGL ES 2.0 Google Nexus").unwrap(),
1470            (2, 0)
1471        );
1472        assert_eq!(Adapter::parse_version("GLSL ES 1.1").unwrap(), (1, 1));
1473        assert_eq!(
1474            Adapter::parse_version("OpenGL ES GLSL ES 3.20").unwrap(),
1475            (3, 2)
1476        );
1477        assert_eq!(
1478            // WebGL 2.0 should parse as OpenGL ES 3.0
1479            Adapter::parse_version("WebGL 2.0 (OpenGL ES 3.0 Chromium)").unwrap(),
1480            (3, 0)
1481        );
1482        assert_eq!(
1483            Adapter::parse_version("WebGL GLSL ES 3.00 (OpenGL ES GLSL ES 3.0 Chromium)").unwrap(),
1484            (3, 0)
1485        );
1486    }
1487}