wgpu_hal/gles/
adapter.rs

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