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