wgpu_hal/gles/
adapter.rs

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