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))
684                .min(crate::MAX_COLOR_ATTACHMENTS as i32) as u32
685        };
686
687        // 16 bytes per sample is the maximum size of a color attachment.
688        let max_color_attachment_bytes_per_sample =
689            max_color_attachments * wgt::TextureFormat::MAX_TARGET_PIXEL_BYTE_COST;
690
691        let limits = wgt::Limits {
692            max_texture_dimension_1d: max_texture_size,
693            max_texture_dimension_2d: max_texture_size,
694            max_texture_dimension_3d: max_texture_3d_size,
695            max_texture_array_layers: unsafe {
696                gl.get_parameter_i32(glow::MAX_ARRAY_TEXTURE_LAYERS)
697            } as u32,
698            max_bind_groups: crate::MAX_BIND_GROUPS as u32,
699            max_bindings_per_bind_group: 65535,
700            max_dynamic_uniform_buffers_per_pipeline_layout: max_uniform_buffers_per_shader_stage,
701            max_dynamic_storage_buffers_per_pipeline_layout: max_storage_buffers_per_shader_stage,
702            max_sampled_textures_per_shader_stage: super::MAX_TEXTURE_SLOTS as u32,
703            max_samplers_per_shader_stage: super::MAX_SAMPLERS as u32,
704            max_storage_buffers_per_shader_stage,
705            max_storage_textures_per_shader_stage,
706            max_uniform_buffers_per_shader_stage,
707            max_binding_array_elements_per_shader_stage: 0,
708            max_binding_array_sampler_elements_per_shader_stage: 0,
709            max_uniform_buffer_binding_size: unsafe {
710                gl.get_parameter_i32(glow::MAX_UNIFORM_BLOCK_SIZE)
711            } as u32,
712            max_storage_buffer_binding_size: if supports_storage {
713                unsafe { gl.get_parameter_i32(glow::MAX_SHADER_STORAGE_BLOCK_SIZE) }
714            } else {
715                0
716            } as u32,
717            max_vertex_buffers: if private_caps
718                .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
719            {
720                (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_BINDINGS) } as u32)
721            } else {
722                16 // should this be different?
723            }
724            .min(crate::MAX_VERTEX_BUFFERS as u32),
725            max_vertex_attributes: (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIBS) }
726                as u32)
727                .min(super::MAX_VERTEX_ATTRIBUTES as u32),
728            max_vertex_buffer_array_stride: if private_caps
729                .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
730            {
731                if let Some(full_ver) = full_ver {
732                    if full_ver >= (4, 4) {
733                        // We can query `GL_MAX_VERTEX_ATTRIB_STRIDE` in OpenGL 4.4+
734                        let value =
735                            (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) })
736                                as u32;
737
738                        if value == 0 {
739                            // This should be at least 2048, but the driver for AMD Radeon HD 5870 on
740                            // Windows doesn't recognize `GL_MAX_VERTEX_ATTRIB_STRIDE`.
741
742                            log::debug!("Max vertex attribute stride is 0. Assuming it is the OpenGL minimum spec 2048");
743                            2048
744                        } else {
745                            value
746                        }
747                    } else {
748                        log::debug!("Max vertex attribute stride unknown. Assuming it is the OpenGL minimum spec 2048");
749                        2048
750                    }
751                } else {
752                    (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) }) as u32
753                }
754            } else {
755                !0
756            },
757            max_immediate_size: super::MAX_IMMEDIATES as u32 * 4,
758            min_uniform_buffer_offset_alignment,
759            min_storage_buffer_offset_alignment,
760            max_inter_stage_shader_components: {
761                // MAX_VARYING_COMPONENTS may return 0, because it is deprecated since OpenGL 3.2 core,
762                // and an OpenGL Context with the core profile and with forward-compatibility=true,
763                // will make deprecated constants unavailable.
764                let max_varying_components =
765                    unsafe { gl.get_parameter_i32(glow::MAX_VARYING_COMPONENTS) } as u32;
766                if max_varying_components == 0 {
767                    // default value for max_inter_stage_shader_components
768                    60
769                } else {
770                    max_varying_components
771                }
772            },
773            max_color_attachments,
774            max_color_attachment_bytes_per_sample,
775            max_compute_workgroup_storage_size: if supports_work_group_params {
776                (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_SHARED_MEMORY_SIZE) } as u32)
777            } else {
778                0
779            },
780            max_compute_invocations_per_workgroup: if supports_work_group_params {
781                (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_WORK_GROUP_INVOCATIONS) } as u32)
782            } else {
783                0
784            },
785            max_compute_workgroup_size_x: if supports_work_group_params {
786                (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 0) }
787                    as u32)
788            } else {
789                0
790            },
791            max_compute_workgroup_size_y: if supports_work_group_params {
792                (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 1) }
793                    as u32)
794            } else {
795                0
796            },
797            max_compute_workgroup_size_z: if supports_work_group_params {
798                (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 2) }
799                    as u32)
800            } else {
801                0
802            },
803            max_compute_workgroups_per_dimension,
804            max_buffer_size: i32::MAX as u64,
805            max_non_sampler_bindings: u32::MAX,
806
807            max_task_workgroup_total_count: 0,
808            max_task_workgroups_per_dimension: 0,
809            max_mesh_multiview_view_count: 0,
810            max_mesh_output_layers: 0,
811
812            max_blas_primitive_count: 0,
813            max_blas_geometry_count: 0,
814            max_tlas_instance_count: 0,
815            max_acceleration_structures_per_shader_stage: 0,
816
817            max_multiview_view_count: 0,
818        };
819
820        let mut workarounds = super::Workarounds::empty();
821
822        workarounds.set(
823            super::Workarounds::EMULATE_BUFFER_MAP,
824            cfg!(any(webgl, Emscripten)),
825        );
826
827        let r = renderer.to_lowercase();
828        // Check for Mesa sRGB clear bug. See
829        // [`super::PrivateCapabilities::MESA_I915_SRGB_SHADER_CLEAR`].
830        if context.is_owned()
831            && r.contains("mesa")
832            && r.contains("intel")
833            && r.split(&[' ', '(', ')'][..])
834                .any(|substr| substr.len() == 3 && substr.chars().nth(2) == Some('l'))
835        {
836            log::debug!(
837                "Detected skylake derivative running on mesa i915. Clears to srgb textures will \
838                use manual shader clears."
839            );
840            workarounds.set(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR, true);
841        }
842
843        let downlevel_defaults = wgt::DownlevelLimits {};
844        let max_samples = unsafe { gl.get_parameter_i32(glow::MAX_SAMPLES) };
845
846        // Drop the GL guard so we can move the context into AdapterShared
847        // ( on Wasm the gl handle is just a ref so we tell clippy to allow
848        // dropping the ref )
849        #[cfg_attr(target_arch = "wasm32", allow(dropping_references))]
850        drop(gl);
851
852        Some(crate::ExposedAdapter {
853            adapter: super::Adapter {
854                shared: Arc::new(super::AdapterShared {
855                    context,
856                    private_caps,
857                    workarounds,
858                    features,
859                    limits: limits.clone(),
860                    options: backend_options,
861                    shading_language_version,
862                    next_shader_id: Default::default(),
863                    program_cache: Default::default(),
864                    es: es_ver.is_some(),
865                    max_msaa_samples: max_samples,
866                }),
867            },
868            info: Self::make_info(vendor, renderer, version),
869            features,
870            capabilities: crate::Capabilities {
871                limits,
872                downlevel: wgt::DownlevelCapabilities {
873                    flags: downlevel_flags,
874                    limits: downlevel_defaults,
875                    shader_model: wgt::ShaderModel::Sm5,
876                },
877                alignments: crate::Alignments {
878                    buffer_copy_offset: wgt::BufferSize::new(4).unwrap(),
879                    buffer_copy_pitch: wgt::BufferSize::new(4).unwrap(),
880                    // #6151: `wgpu_hal::gles` doesn't ask Naga to inject bounds
881                    // checks in GLSL, and it doesn't request extensions like
882                    // `KHR_robust_buffer_access_behavior` that would provide
883                    // them, so we can't really implement the checks promised by
884                    // [`crate::BufferBinding`].
885                    //
886                    // Since this is a pre-existing condition, for the time
887                    // being, provide 1 as the value here, to cause as little
888                    // trouble as possible.
889                    uniform_bounds_check_alignment: wgt::BufferSize::new(1).unwrap(),
890                    raw_tlas_instance_size: 0,
891                    ray_tracing_scratch_buffer_alignment: 0,
892                },
893            },
894        })
895    }
896
897    unsafe fn compile_shader(
898        source: &str,
899        gl: &glow::Context,
900        shader_type: u32,
901        es: bool,
902    ) -> Option<glow::Shader> {
903        let source = if es {
904            format!("#version 300 es\nprecision lowp float;\n{source}")
905        } else {
906            let version = gl.version();
907            if version.major == 3 && version.minor == 0 {
908                // OpenGL 3.0 only supports this format
909                format!("#version 130\n{source}")
910            } else {
911                // OpenGL 3.1+ support this format
912                format!("#version 140\n{source}")
913            }
914        };
915        let shader = unsafe { gl.create_shader(shader_type) }.expect("Could not create shader");
916        unsafe { gl.shader_source(shader, &source) };
917        unsafe { gl.compile_shader(shader) };
918
919        if !unsafe { gl.get_shader_compile_status(shader) } {
920            let msg = unsafe { gl.get_shader_info_log(shader) };
921            if !msg.is_empty() {
922                log::error!("\tShader compile error: {msg}");
923            }
924            unsafe { gl.delete_shader(shader) };
925            None
926        } else {
927            Some(shader)
928        }
929    }
930
931    unsafe fn create_shader_clear_program(
932        gl: &glow::Context,
933        es: bool,
934    ) -> Option<ShaderClearProgram> {
935        let program = unsafe { gl.create_program() }.expect("Could not create shader program");
936        let vertex = unsafe {
937            Self::compile_shader(
938                include_str!("./shaders/clear.vert"),
939                gl,
940                glow::VERTEX_SHADER,
941                es,
942            )?
943        };
944        let fragment = unsafe {
945            Self::compile_shader(
946                include_str!("./shaders/clear.frag"),
947                gl,
948                glow::FRAGMENT_SHADER,
949                es,
950            )?
951        };
952        unsafe { gl.attach_shader(program, vertex) };
953        unsafe { gl.attach_shader(program, fragment) };
954        unsafe { gl.link_program(program) };
955
956        let linked_ok = unsafe { gl.get_program_link_status(program) };
957        let msg = unsafe { gl.get_program_info_log(program) };
958        if !msg.is_empty() {
959            log::error!("Shader link error: {msg}");
960        }
961        if !linked_ok {
962            return None;
963        }
964
965        let color_uniform_location = unsafe { gl.get_uniform_location(program, "color") }
966            .expect("Could not find color uniform in shader clear shader");
967        unsafe { gl.delete_shader(vertex) };
968        unsafe { gl.delete_shader(fragment) };
969
970        Some(ShaderClearProgram {
971            program,
972            color_uniform_location,
973        })
974    }
975}
976
977impl crate::Adapter for super::Adapter {
978    type A = super::Api;
979
980    unsafe fn open(
981        &self,
982        features: wgt::Features,
983        _limits: &wgt::Limits,
984        _memory_hints: &wgt::MemoryHints,
985    ) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
986        let gl = &self.shared.context.lock();
987        unsafe { gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1) };
988        unsafe { gl.pixel_store_i32(glow::PACK_ALIGNMENT, 1) };
989        let main_vao =
990            unsafe { gl.create_vertex_array() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
991        unsafe { gl.bind_vertex_array(Some(main_vao)) };
992
993        let zero_buffer =
994            unsafe { gl.create_buffer() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
995        unsafe { gl.bind_buffer(glow::COPY_READ_BUFFER, Some(zero_buffer)) };
996        let zeroes = vec![0u8; super::ZERO_BUFFER_SIZE];
997        unsafe { gl.buffer_data_u8_slice(glow::COPY_READ_BUFFER, &zeroes, glow::STATIC_DRAW) };
998
999        // Compile the shader program we use for doing manual clears to work around Mesa fastclear
1000        // bug.
1001
1002        let shader_clear_program = if self
1003            .shared
1004            .workarounds
1005            .contains(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR)
1006        {
1007            Some(unsafe {
1008                Self::create_shader_clear_program(gl, self.shared.es)
1009                    .ok_or(crate::DeviceError::Lost)?
1010            })
1011        } else {
1012            // If we don't need the workaround, don't waste time and resources compiling the clear program
1013            None
1014        };
1015
1016        Ok(crate::OpenDevice {
1017            device: super::Device {
1018                shared: Arc::clone(&self.shared),
1019                main_vao,
1020                #[cfg(all(native, feature = "renderdoc"))]
1021                render_doc: Default::default(),
1022                counters: Default::default(),
1023            },
1024            queue: super::Queue {
1025                shared: Arc::clone(&self.shared),
1026                features,
1027                draw_fbo: unsafe { gl.create_framebuffer() }
1028                    .map_err(|_| crate::DeviceError::OutOfMemory)?,
1029                copy_fbo: unsafe { gl.create_framebuffer() }
1030                    .map_err(|_| crate::DeviceError::OutOfMemory)?,
1031                shader_clear_program,
1032                zero_buffer,
1033                temp_query_results: Mutex::new(Vec::new()),
1034                draw_buffer_count: AtomicU8::new(1),
1035                current_index_buffer: Mutex::new(None),
1036            },
1037        })
1038    }
1039
1040    unsafe fn texture_format_capabilities(
1041        &self,
1042        format: wgt::TextureFormat,
1043    ) -> crate::TextureFormatCapabilities {
1044        use crate::TextureFormatCapabilities as Tfc;
1045        use wgt::TextureFormat as Tf;
1046
1047        let sample_count = {
1048            let max_samples = self.shared.max_msaa_samples;
1049            if max_samples >= 16 {
1050                Tfc::MULTISAMPLE_X2
1051                    | Tfc::MULTISAMPLE_X4
1052                    | Tfc::MULTISAMPLE_X8
1053                    | Tfc::MULTISAMPLE_X16
1054            } else if max_samples >= 8 {
1055                Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4 | Tfc::MULTISAMPLE_X8
1056            } else {
1057                // The lowest supported level in GLE3.0/WebGL2 is 4X
1058                // (see GL_MAX_SAMPLES in https://registry.khronos.org/OpenGL-Refpages/es3.0/html/glGet.xhtml).
1059                // On some platforms, like iOS Safari, `get_parameter_i32(MAX_SAMPLES)` returns 0,
1060                // so we always fall back to supporting 4x here.
1061                Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4
1062            }
1063        };
1064
1065        // Base types are pulled from the table in the OpenGLES 3.0 spec in section 3.8.
1066        //
1067        // The storage types are based on table 8.26, in section
1068        // "TEXTURE IMAGE LOADS AND STORES" of OpenGLES-3.2 spec.
1069        let empty = Tfc::empty();
1070        let base = Tfc::COPY_SRC | Tfc::COPY_DST;
1071        let unfilterable = base | Tfc::SAMPLED;
1072        let depth = base | Tfc::SAMPLED | sample_count | Tfc::DEPTH_STENCIL_ATTACHMENT;
1073        let filterable = unfilterable | Tfc::SAMPLED_LINEAR;
1074        let renderable =
1075            unfilterable | Tfc::COLOR_ATTACHMENT | sample_count | Tfc::MULTISAMPLE_RESOLVE;
1076        let filterable_renderable = filterable | renderable | Tfc::COLOR_ATTACHMENT_BLEND;
1077        let storage =
1078            base | Tfc::STORAGE_READ_WRITE | Tfc::STORAGE_READ_ONLY | Tfc::STORAGE_WRITE_ONLY;
1079
1080        let feature_fn = |f, caps| {
1081            if self.shared.features.contains(f) {
1082                caps
1083            } else {
1084                empty
1085            }
1086        };
1087
1088        let bcn_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_BC, filterable);
1089        let etc2_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ETC2, filterable);
1090        let astc_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC, filterable);
1091        let astc_hdr_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR, filterable);
1092
1093        let private_caps_fn = |f, caps| {
1094            if self.shared.private_caps.contains(f) {
1095                caps
1096            } else {
1097                empty
1098            }
1099        };
1100
1101        let half_float_renderable = private_caps_fn(
1102            super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT,
1103            Tfc::COLOR_ATTACHMENT
1104                | Tfc::COLOR_ATTACHMENT_BLEND
1105                | sample_count
1106                | Tfc::MULTISAMPLE_RESOLVE,
1107        );
1108
1109        let float_renderable = private_caps_fn(
1110            super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
1111            Tfc::COLOR_ATTACHMENT
1112                | Tfc::COLOR_ATTACHMENT_BLEND
1113                | sample_count
1114                | Tfc::MULTISAMPLE_RESOLVE,
1115        );
1116
1117        let texture_float_linear = feature_fn(wgt::Features::FLOAT32_FILTERABLE, filterable);
1118
1119        let image_atomic = feature_fn(wgt::Features::TEXTURE_ATOMIC, Tfc::STORAGE_ATOMIC);
1120        let image_64_atomic = feature_fn(wgt::Features::TEXTURE_INT64_ATOMIC, Tfc::STORAGE_ATOMIC);
1121
1122        match format {
1123            Tf::R8Unorm => filterable_renderable,
1124            Tf::R8Snorm => filterable,
1125            Tf::R8Uint => renderable,
1126            Tf::R8Sint => renderable,
1127            Tf::R16Uint => renderable,
1128            Tf::R16Sint => renderable,
1129            Tf::R16Unorm => empty,
1130            Tf::R16Snorm => empty,
1131            Tf::R16Float => filterable | half_float_renderable,
1132            Tf::Rg8Unorm => filterable_renderable,
1133            Tf::Rg8Snorm => filterable,
1134            Tf::Rg8Uint => renderable,
1135            Tf::Rg8Sint => renderable,
1136            Tf::R32Uint => renderable | storage | image_atomic,
1137            Tf::R32Sint => renderable | storage | image_atomic,
1138            Tf::R32Float => unfilterable | storage | float_renderable | texture_float_linear,
1139            Tf::Rg16Uint => renderable,
1140            Tf::Rg16Sint => renderable,
1141            Tf::Rg16Unorm => empty,
1142            Tf::Rg16Snorm => empty,
1143            Tf::Rg16Float => filterable | half_float_renderable,
1144            Tf::Rgba8Unorm => filterable_renderable | storage,
1145            Tf::Rgba8UnormSrgb => filterable_renderable,
1146            Tf::Bgra8Unorm | Tf::Bgra8UnormSrgb => filterable_renderable,
1147            Tf::Rgba8Snorm => filterable | storage,
1148            Tf::Rgba8Uint => renderable | storage,
1149            Tf::Rgba8Sint => renderable | storage,
1150            Tf::Rgb10a2Uint => renderable,
1151            Tf::Rgb10a2Unorm => filterable_renderable,
1152            Tf::Rg11b10Ufloat => filterable | float_renderable,
1153            Tf::R64Uint => image_64_atomic,
1154            Tf::Rg32Uint => renderable,
1155            Tf::Rg32Sint => renderable,
1156            Tf::Rg32Float => unfilterable | float_renderable | texture_float_linear,
1157            Tf::Rgba16Uint => renderable | storage,
1158            Tf::Rgba16Sint => renderable | storage,
1159            Tf::Rgba16Unorm => empty,
1160            Tf::Rgba16Snorm => empty,
1161            Tf::Rgba16Float => filterable | storage | half_float_renderable,
1162            Tf::Rgba32Uint => renderable | storage,
1163            Tf::Rgba32Sint => renderable | storage,
1164            Tf::Rgba32Float => unfilterable | storage | float_renderable | texture_float_linear,
1165            Tf::Stencil8
1166            | Tf::Depth16Unorm
1167            | Tf::Depth32Float
1168            | Tf::Depth32FloatStencil8
1169            | Tf::Depth24Plus
1170            | Tf::Depth24PlusStencil8 => depth,
1171            Tf::NV12 => empty,
1172            Tf::P010 => empty,
1173            Tf::Rgb9e5Ufloat => filterable,
1174            Tf::Bc1RgbaUnorm
1175            | Tf::Bc1RgbaUnormSrgb
1176            | Tf::Bc2RgbaUnorm
1177            | Tf::Bc2RgbaUnormSrgb
1178            | Tf::Bc3RgbaUnorm
1179            | Tf::Bc3RgbaUnormSrgb
1180            | Tf::Bc4RUnorm
1181            | Tf::Bc4RSnorm
1182            | Tf::Bc5RgUnorm
1183            | Tf::Bc5RgSnorm
1184            | Tf::Bc6hRgbFloat
1185            | Tf::Bc6hRgbUfloat
1186            | Tf::Bc7RgbaUnorm
1187            | Tf::Bc7RgbaUnormSrgb => bcn_features,
1188            Tf::Etc2Rgb8Unorm
1189            | Tf::Etc2Rgb8UnormSrgb
1190            | Tf::Etc2Rgb8A1Unorm
1191            | Tf::Etc2Rgb8A1UnormSrgb
1192            | Tf::Etc2Rgba8Unorm
1193            | Tf::Etc2Rgba8UnormSrgb
1194            | Tf::EacR11Unorm
1195            | Tf::EacR11Snorm
1196            | Tf::EacRg11Unorm
1197            | Tf::EacRg11Snorm => etc2_features,
1198            Tf::Astc {
1199                block: _,
1200                channel: AstcChannel::Unorm | AstcChannel::UnormSrgb,
1201            } => astc_features,
1202            Tf::Astc {
1203                block: _,
1204                channel: AstcChannel::Hdr,
1205            } => astc_hdr_features,
1206        }
1207    }
1208
1209    unsafe fn surface_capabilities(
1210        &self,
1211        surface: &super::Surface,
1212    ) -> Option<crate::SurfaceCapabilities> {
1213        #[cfg(webgl)]
1214        if self.shared.context.webgl2_context != surface.webgl2_context {
1215            return None;
1216        }
1217
1218        if surface.presentable {
1219            let mut formats = vec![
1220                wgt::TextureFormat::Rgba8Unorm,
1221                #[cfg(native)]
1222                wgt::TextureFormat::Bgra8Unorm,
1223            ];
1224            if surface.supports_srgb() {
1225                formats.extend([
1226                    wgt::TextureFormat::Rgba8UnormSrgb,
1227                    #[cfg(native)]
1228                    wgt::TextureFormat::Bgra8UnormSrgb,
1229                ])
1230            }
1231            if self
1232                .shared
1233                .private_caps
1234                .contains(super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT)
1235            {
1236                formats.push(wgt::TextureFormat::Rgba16Float)
1237            }
1238
1239            Some(crate::SurfaceCapabilities {
1240                formats,
1241                present_modes: if cfg!(windows) {
1242                    vec![wgt::PresentMode::Fifo, wgt::PresentMode::Immediate]
1243                } else {
1244                    vec![wgt::PresentMode::Fifo] //TODO
1245                },
1246                composite_alpha_modes: vec![wgt::CompositeAlphaMode::Opaque], //TODO
1247                maximum_frame_latency: 2..=2, //TODO, unused currently
1248                current_extent: None,
1249                usage: wgt::TextureUses::COLOR_TARGET,
1250            })
1251        } else {
1252            None
1253        }
1254    }
1255
1256    unsafe fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
1257        wgt::PresentationTimestamp::INVALID_TIMESTAMP
1258    }
1259}
1260
1261impl super::AdapterShared {
1262    pub(super) unsafe fn get_buffer_sub_data(
1263        &self,
1264        gl: &glow::Context,
1265        target: u32,
1266        offset: i32,
1267        dst_data: &mut [u8],
1268    ) {
1269        if self
1270            .private_caps
1271            .contains(super::PrivateCapabilities::GET_BUFFER_SUB_DATA)
1272        {
1273            unsafe { gl.get_buffer_sub_data(target, offset, dst_data) };
1274        } else {
1275            log::error!("Fake map");
1276            let length = dst_data.len();
1277            let buffer_mapping =
1278                unsafe { gl.map_buffer_range(target, offset, length as _, glow::MAP_READ_BIT) };
1279
1280            unsafe {
1281                core::ptr::copy_nonoverlapping(buffer_mapping, dst_data.as_mut_ptr(), length)
1282            };
1283
1284            unsafe { gl.unmap_buffer(target) };
1285        }
1286    }
1287}
1288
1289#[cfg(send_sync)]
1290unsafe impl Sync for super::Adapter {}
1291#[cfg(send_sync)]
1292unsafe impl Send for super::Adapter {}
1293
1294#[cfg(test)]
1295mod tests {
1296    use super::super::Adapter;
1297
1298    #[test]
1299    fn test_version_parse() {
1300        Adapter::parse_version("1").unwrap_err();
1301        Adapter::parse_version("1.").unwrap_err();
1302        Adapter::parse_version("1 h3l1o. W0rld").unwrap_err();
1303        Adapter::parse_version("1. h3l1o. W0rld").unwrap_err();
1304        Adapter::parse_version("1.2.3").unwrap_err();
1305
1306        assert_eq!(Adapter::parse_version("OpenGL ES 3.1").unwrap(), (3, 1));
1307        assert_eq!(
1308            Adapter::parse_version("OpenGL ES 2.0 Google Nexus").unwrap(),
1309            (2, 0)
1310        );
1311        assert_eq!(Adapter::parse_version("GLSL ES 1.1").unwrap(), (1, 1));
1312        assert_eq!(
1313            Adapter::parse_version("OpenGL ES GLSL ES 3.20").unwrap(),
1314            (3, 2)
1315        );
1316        assert_eq!(
1317            // WebGL 2.0 should parse as OpenGL ES 3.0
1318            Adapter::parse_version("WebGL 2.0 (OpenGL ES 3.0 Chromium)").unwrap(),
1319            (3, 0)
1320        );
1321        assert_eq!(
1322            Adapter::parse_version("WebGL GLSL ES 3.00 (OpenGL ES GLSL ES 3.0 Chromium)").unwrap(),
1323            (3, 0)
1324        );
1325    }
1326}