Skip to main content

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