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