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