wgpu_hal/gles/
adapter.rs

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