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