wgpu_hal/gles/
adapter.rs

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