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