wgpu_hal/gles/
adapter.rs

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