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