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