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_components: {
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_components
766                    60
767                } else {
768                    max_varying_components
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            },
899        })
900    }
901
902    unsafe fn compile_shader(
903        source: &str,
904        gl: &glow::Context,
905        shader_type: u32,
906        es: bool,
907    ) -> Option<glow::Shader> {
908        let source = if es {
909            format!("#version 300 es\nprecision lowp float;\n{source}")
910        } else {
911            let version = gl.version();
912            if version.major == 3 && version.minor == 0 {
913                // OpenGL 3.0 only supports this format
914                format!("#version 130\n{source}")
915            } else {
916                // OpenGL 3.1+ support this format
917                format!("#version 140\n{source}")
918            }
919        };
920        let shader = unsafe { gl.create_shader(shader_type) }.expect("Could not create shader");
921        unsafe { gl.shader_source(shader, &source) };
922        unsafe { gl.compile_shader(shader) };
923
924        if !unsafe { gl.get_shader_compile_status(shader) } {
925            let msg = unsafe { gl.get_shader_info_log(shader) };
926            if !msg.is_empty() {
927                log::error!("\tShader compile error: {msg}");
928            }
929            unsafe { gl.delete_shader(shader) };
930            None
931        } else {
932            Some(shader)
933        }
934    }
935
936    unsafe fn create_shader_clear_program(
937        gl: &glow::Context,
938        es: bool,
939    ) -> Option<ShaderClearProgram> {
940        let program = unsafe { gl.create_program() }.expect("Could not create shader program");
941        let vertex = unsafe {
942            Self::compile_shader(
943                include_str!("./shaders/clear.vert"),
944                gl,
945                glow::VERTEX_SHADER,
946                es,
947            )?
948        };
949        let fragment = unsafe {
950            Self::compile_shader(
951                include_str!("./shaders/clear.frag"),
952                gl,
953                glow::FRAGMENT_SHADER,
954                es,
955            )?
956        };
957        unsafe { gl.attach_shader(program, vertex) };
958        unsafe { gl.attach_shader(program, fragment) };
959        unsafe { gl.link_program(program) };
960
961        let linked_ok = unsafe { gl.get_program_link_status(program) };
962        let msg = unsafe { gl.get_program_info_log(program) };
963        if !msg.is_empty() {
964            log::error!("Shader link error: {msg}");
965        }
966        if !linked_ok {
967            return None;
968        }
969
970        let color_uniform_location = unsafe { gl.get_uniform_location(program, "color") }
971            .expect("Could not find color uniform in shader clear shader");
972        unsafe { gl.delete_shader(vertex) };
973        unsafe { gl.delete_shader(fragment) };
974
975        Some(ShaderClearProgram {
976            program,
977            color_uniform_location,
978        })
979    }
980}
981
982impl crate::Adapter for super::Adapter {
983    type A = super::Api;
984
985    unsafe fn open(
986        &self,
987        features: wgt::Features,
988        _limits: &wgt::Limits,
989        _memory_hints: &wgt::MemoryHints,
990    ) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
991        let gl = &self.shared.context.lock();
992        unsafe { gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1) };
993        unsafe { gl.pixel_store_i32(glow::PACK_ALIGNMENT, 1) };
994        let main_vao =
995            unsafe { gl.create_vertex_array() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
996        unsafe { gl.bind_vertex_array(Some(main_vao)) };
997
998        let zero_buffer =
999            unsafe { gl.create_buffer() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
1000        unsafe { gl.bind_buffer(glow::COPY_READ_BUFFER, Some(zero_buffer)) };
1001        let zeroes = vec![0u8; super::ZERO_BUFFER_SIZE];
1002        unsafe { gl.buffer_data_u8_slice(glow::COPY_READ_BUFFER, &zeroes, glow::STATIC_DRAW) };
1003
1004        // Compile the shader program we use for doing manual clears to work around Mesa fastclear
1005        // bug.
1006
1007        let shader_clear_program = if self
1008            .shared
1009            .workarounds
1010            .contains(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR)
1011        {
1012            Some(unsafe {
1013                Self::create_shader_clear_program(gl, self.shared.es)
1014                    .ok_or(crate::DeviceError::Lost)?
1015            })
1016        } else {
1017            // If we don't need the workaround, don't waste time and resources compiling the clear program
1018            None
1019        };
1020
1021        Ok(crate::OpenDevice {
1022            device: super::Device {
1023                shared: Arc::clone(&self.shared),
1024                main_vao,
1025                #[cfg(all(native, feature = "renderdoc"))]
1026                render_doc: Default::default(),
1027                counters: Default::default(),
1028            },
1029            queue: super::Queue {
1030                shared: Arc::clone(&self.shared),
1031                features,
1032                draw_fbo: unsafe { gl.create_framebuffer() }
1033                    .map_err(|_| crate::DeviceError::OutOfMemory)?,
1034                copy_fbo: unsafe { gl.create_framebuffer() }
1035                    .map_err(|_| crate::DeviceError::OutOfMemory)?,
1036                shader_clear_program,
1037                zero_buffer,
1038                temp_query_results: Mutex::new(Vec::new()),
1039                draw_buffer_count: AtomicU8::new(1),
1040                current_index_buffer: Mutex::new(None),
1041            },
1042        })
1043    }
1044
1045    unsafe fn texture_format_capabilities(
1046        &self,
1047        format: wgt::TextureFormat,
1048    ) -> crate::TextureFormatCapabilities {
1049        use crate::TextureFormatCapabilities as Tfc;
1050        use wgt::TextureFormat as Tf;
1051
1052        let sample_count = {
1053            let max_samples = self.shared.max_msaa_samples;
1054            if max_samples >= 16 {
1055                Tfc::MULTISAMPLE_X2
1056                    | Tfc::MULTISAMPLE_X4
1057                    | Tfc::MULTISAMPLE_X8
1058                    | Tfc::MULTISAMPLE_X16
1059            } else if max_samples >= 8 {
1060                Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4 | Tfc::MULTISAMPLE_X8
1061            } else {
1062                // The lowest supported level in GLE3.0/WebGL2 is 4X
1063                // (see GL_MAX_SAMPLES in https://registry.khronos.org/OpenGL-Refpages/es3.0/html/glGet.xhtml).
1064                // On some platforms, like iOS Safari, `get_parameter_i32(MAX_SAMPLES)` returns 0,
1065                // so we always fall back to supporting 4x here.
1066                Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4
1067            }
1068        };
1069
1070        // Base types are pulled from the table in the OpenGLES 3.0 spec in section 3.8.
1071        //
1072        // The storage types are based on table 8.26, in section
1073        // "TEXTURE IMAGE LOADS AND STORES" of OpenGLES-3.2 spec.
1074        let empty = Tfc::empty();
1075        let base = Tfc::COPY_SRC | Tfc::COPY_DST;
1076        let unfilterable = base | Tfc::SAMPLED;
1077        let depth = base | Tfc::SAMPLED | sample_count | Tfc::DEPTH_STENCIL_ATTACHMENT;
1078        let filterable = unfilterable | Tfc::SAMPLED_LINEAR;
1079        let renderable =
1080            unfilterable | Tfc::COLOR_ATTACHMENT | sample_count | Tfc::MULTISAMPLE_RESOLVE;
1081        let filterable_renderable = filterable | renderable | Tfc::COLOR_ATTACHMENT_BLEND;
1082        let storage =
1083            base | Tfc::STORAGE_READ_WRITE | Tfc::STORAGE_READ_ONLY | Tfc::STORAGE_WRITE_ONLY;
1084
1085        let feature_fn = |f, caps| {
1086            if self.shared.features.contains(f) {
1087                caps
1088            } else {
1089                empty
1090            }
1091        };
1092
1093        let bcn_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_BC, filterable);
1094        let etc2_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ETC2, filterable);
1095        let astc_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC, filterable);
1096        let astc_hdr_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR, filterable);
1097
1098        let private_caps_fn = |f, caps| {
1099            if self.shared.private_caps.contains(f) {
1100                caps
1101            } else {
1102                empty
1103            }
1104        };
1105
1106        let half_float_renderable = private_caps_fn(
1107            super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT,
1108            Tfc::COLOR_ATTACHMENT
1109                | Tfc::COLOR_ATTACHMENT_BLEND
1110                | sample_count
1111                | Tfc::MULTISAMPLE_RESOLVE,
1112        );
1113
1114        let float_renderable = private_caps_fn(
1115            super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
1116            Tfc::COLOR_ATTACHMENT
1117                | Tfc::COLOR_ATTACHMENT_BLEND
1118                | sample_count
1119                | Tfc::MULTISAMPLE_RESOLVE,
1120        );
1121
1122        let texture_float_linear = feature_fn(wgt::Features::FLOAT32_FILTERABLE, filterable);
1123
1124        let image_atomic = feature_fn(wgt::Features::TEXTURE_ATOMIC, Tfc::STORAGE_ATOMIC);
1125        let image_64_atomic = feature_fn(wgt::Features::TEXTURE_INT64_ATOMIC, Tfc::STORAGE_ATOMIC);
1126
1127        match format {
1128            Tf::R8Unorm => filterable_renderable,
1129            Tf::R8Snorm => filterable,
1130            Tf::R8Uint => renderable,
1131            Tf::R8Sint => renderable,
1132            Tf::R16Uint => renderable,
1133            Tf::R16Sint => renderable,
1134            Tf::R16Unorm => empty,
1135            Tf::R16Snorm => empty,
1136            Tf::R16Float => filterable | half_float_renderable,
1137            Tf::Rg8Unorm => filterable_renderable,
1138            Tf::Rg8Snorm => filterable,
1139            Tf::Rg8Uint => renderable,
1140            Tf::Rg8Sint => renderable,
1141            Tf::R32Uint => renderable | storage | image_atomic,
1142            Tf::R32Sint => renderable | storage | image_atomic,
1143            Tf::R32Float => unfilterable | storage | float_renderable | texture_float_linear,
1144            Tf::Rg16Uint => renderable,
1145            Tf::Rg16Sint => renderable,
1146            Tf::Rg16Unorm => empty,
1147            Tf::Rg16Snorm => empty,
1148            Tf::Rg16Float => filterable | half_float_renderable,
1149            Tf::Rgba8Unorm => filterable_renderable | storage,
1150            Tf::Rgba8UnormSrgb => filterable_renderable,
1151            Tf::Bgra8Unorm | Tf::Bgra8UnormSrgb => filterable_renderable,
1152            Tf::Rgba8Snorm => filterable | storage,
1153            Tf::Rgba8Uint => renderable | storage,
1154            Tf::Rgba8Sint => renderable | storage,
1155            Tf::Rgb10a2Uint => renderable,
1156            Tf::Rgb10a2Unorm => filterable_renderable,
1157            Tf::Rg11b10Ufloat => filterable | float_renderable,
1158            Tf::R64Uint => image_64_atomic,
1159            Tf::Rg32Uint => renderable,
1160            Tf::Rg32Sint => renderable,
1161            Tf::Rg32Float => unfilterable | float_renderable | texture_float_linear,
1162            Tf::Rgba16Uint => renderable | storage,
1163            Tf::Rgba16Sint => renderable | storage,
1164            Tf::Rgba16Unorm => empty,
1165            Tf::Rgba16Snorm => empty,
1166            Tf::Rgba16Float => filterable | storage | half_float_renderable,
1167            Tf::Rgba32Uint => renderable | storage,
1168            Tf::Rgba32Sint => renderable | storage,
1169            Tf::Rgba32Float => unfilterable | storage | float_renderable | texture_float_linear,
1170            Tf::Stencil8
1171            | Tf::Depth16Unorm
1172            | Tf::Depth32Float
1173            | Tf::Depth32FloatStencil8
1174            | Tf::Depth24Plus
1175            | Tf::Depth24PlusStencil8 => depth,
1176            Tf::NV12 => empty,
1177            Tf::P010 => empty,
1178            Tf::Rgb9e5Ufloat => filterable,
1179            Tf::Bc1RgbaUnorm
1180            | Tf::Bc1RgbaUnormSrgb
1181            | Tf::Bc2RgbaUnorm
1182            | Tf::Bc2RgbaUnormSrgb
1183            | Tf::Bc3RgbaUnorm
1184            | Tf::Bc3RgbaUnormSrgb
1185            | Tf::Bc4RUnorm
1186            | Tf::Bc4RSnorm
1187            | Tf::Bc5RgUnorm
1188            | Tf::Bc5RgSnorm
1189            | Tf::Bc6hRgbFloat
1190            | Tf::Bc6hRgbUfloat
1191            | Tf::Bc7RgbaUnorm
1192            | Tf::Bc7RgbaUnormSrgb => bcn_features,
1193            Tf::Etc2Rgb8Unorm
1194            | Tf::Etc2Rgb8UnormSrgb
1195            | Tf::Etc2Rgb8A1Unorm
1196            | Tf::Etc2Rgb8A1UnormSrgb
1197            | Tf::Etc2Rgba8Unorm
1198            | Tf::Etc2Rgba8UnormSrgb
1199            | Tf::EacR11Unorm
1200            | Tf::EacR11Snorm
1201            | Tf::EacRg11Unorm
1202            | Tf::EacRg11Snorm => etc2_features,
1203            Tf::Astc {
1204                block: _,
1205                channel: AstcChannel::Unorm | AstcChannel::UnormSrgb,
1206            } => astc_features,
1207            Tf::Astc {
1208                block: _,
1209                channel: AstcChannel::Hdr,
1210            } => astc_hdr_features,
1211        }
1212    }
1213
1214    unsafe fn surface_capabilities(
1215        &self,
1216        surface: &super::Surface,
1217    ) -> Option<crate::SurfaceCapabilities> {
1218        #[cfg(webgl)]
1219        if self.shared.context.webgl2_context != surface.webgl2_context {
1220            return None;
1221        }
1222
1223        if surface.presentable {
1224            let mut formats = vec![
1225                wgt::TextureFormat::Rgba8Unorm,
1226                #[cfg(native)]
1227                wgt::TextureFormat::Bgra8Unorm,
1228            ];
1229            if surface.supports_srgb() {
1230                formats.extend([
1231                    wgt::TextureFormat::Rgba8UnormSrgb,
1232                    #[cfg(native)]
1233                    wgt::TextureFormat::Bgra8UnormSrgb,
1234                ])
1235            }
1236            if self
1237                .shared
1238                .private_caps
1239                .contains(super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT)
1240            {
1241                formats.push(wgt::TextureFormat::Rgba16Float)
1242            }
1243
1244            Some(crate::SurfaceCapabilities {
1245                formats,
1246                present_modes: if cfg!(windows) {
1247                    vec![wgt::PresentMode::Fifo, wgt::PresentMode::Immediate]
1248                } else {
1249                    vec![wgt::PresentMode::Fifo] //TODO
1250                },
1251                composite_alpha_modes: vec![wgt::CompositeAlphaMode::Opaque], //TODO
1252                maximum_frame_latency: 2..=2, //TODO, unused currently
1253                current_extent: None,
1254                usage: wgt::TextureUses::COLOR_TARGET,
1255            })
1256        } else {
1257            None
1258        }
1259    }
1260
1261    unsafe fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
1262        wgt::PresentationTimestamp::INVALID_TIMESTAMP
1263    }
1264}
1265
1266impl super::AdapterShared {
1267    pub(super) unsafe fn get_buffer_sub_data(
1268        &self,
1269        gl: &glow::Context,
1270        target: u32,
1271        offset: i32,
1272        dst_data: &mut [u8],
1273    ) {
1274        if self
1275            .private_caps
1276            .contains(super::PrivateCapabilities::GET_BUFFER_SUB_DATA)
1277        {
1278            unsafe { gl.get_buffer_sub_data(target, offset, dst_data) };
1279        } else {
1280            log::error!("Fake map");
1281            let length = dst_data.len();
1282            let buffer_mapping =
1283                unsafe { gl.map_buffer_range(target, offset, length as _, glow::MAP_READ_BIT) };
1284
1285            unsafe {
1286                core::ptr::copy_nonoverlapping(buffer_mapping, dst_data.as_mut_ptr(), length)
1287            };
1288
1289            unsafe { gl.unmap_buffer(target) };
1290        }
1291    }
1292}
1293
1294#[cfg(send_sync)]
1295unsafe impl Sync for super::Adapter {}
1296#[cfg(send_sync)]
1297unsafe impl Send for super::Adapter {}
1298
1299#[cfg(test)]
1300mod tests {
1301    use super::super::Adapter;
1302
1303    #[test]
1304    fn test_version_parse() {
1305        Adapter::parse_version("1").unwrap_err();
1306        Adapter::parse_version("1.").unwrap_err();
1307        Adapter::parse_version("1 h3l1o. W0rld").unwrap_err();
1308        Adapter::parse_version("1. h3l1o. W0rld").unwrap_err();
1309        Adapter::parse_version("1.2.3").unwrap_err();
1310
1311        assert_eq!(Adapter::parse_version("OpenGL ES 3.1").unwrap(), (3, 1));
1312        assert_eq!(
1313            Adapter::parse_version("OpenGL ES 2.0 Google Nexus").unwrap(),
1314            (2, 0)
1315        );
1316        assert_eq!(Adapter::parse_version("GLSL ES 1.1").unwrap(), (1, 1));
1317        assert_eq!(
1318            Adapter::parse_version("OpenGL ES GLSL ES 3.20").unwrap(),
1319            (3, 2)
1320        );
1321        assert_eq!(
1322            // WebGL 2.0 should parse as OpenGL ES 3.0
1323            Adapter::parse_version("WebGL 2.0 (OpenGL ES 3.0 Chromium)").unwrap(),
1324            (3, 0)
1325        );
1326        assert_eq!(
1327            Adapter::parse_version("WebGL GLSL ES 3.00 (OpenGL ES GLSL ES 3.0 Chromium)").unwrap(),
1328            (3, 0)
1329        );
1330    }
1331}