wgpu_hal/gles/
adapter.rs

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