wgpu_hal/gles/
adapter.rs

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