wgpu_hal/gles/
adapter.rs

1use glow::HasContext;
2use std::sync::Arc;
3use wgt::AstcChannel;
4
5use crate::auxil::db;
6
7// https://webgl2fundamentals.org/webgl/lessons/webgl-data-textures.html
8
9const GL_UNMASKED_VENDOR_WEBGL: u32 = 0x9245;
10const GL_UNMASKED_RENDERER_WEBGL: u32 = 0x9246;
11
12impl super::Adapter {
13    /// According to the OpenGL specification, the version information is
14    /// expected to follow the following syntax:
15    ///
16    /// ~~~bnf
17    /// <major>       ::= <number>
18    /// <minor>       ::= <number>
19    /// <revision>    ::= <number>
20    /// <vendor-info> ::= <string>
21    /// <release>     ::= <major> "." <minor> ["." <release>]
22    /// <version>     ::= <release> [" " <vendor-info>]
23    /// ~~~
24    ///
25    /// Note that this function is intentionally lenient in regards to parsing,
26    /// and will try to recover at least the first two version numbers without
27    /// resulting in an `Err`.
28    /// # Notes
29    /// `WebGL 2` version returned as `OpenGL ES 3.0`
30    fn parse_version(mut src: &str) -> Result<(u8, u8), crate::InstanceError> {
31        let webgl_sig = "WebGL ";
32        // According to the WebGL specification
33        // VERSION  WebGL<space>1.0<space><vendor-specific information>
34        // SHADING_LANGUAGE_VERSION WebGL<space>GLSL<space>ES<space>1.0<space><vendor-specific information>
35        let is_webgl = src.starts_with(webgl_sig);
36        if is_webgl {
37            let pos = src.rfind(webgl_sig).unwrap_or(0);
38            src = &src[pos + webgl_sig.len()..];
39        } else {
40            let es_sig = " ES ";
41            match src.rfind(es_sig) {
42                Some(pos) => {
43                    src = &src[pos + es_sig.len()..];
44                }
45                None => {
46                    log::warn!("ES not found in '{}'", src);
47                    return Err(crate::InstanceError);
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        let (version, _vendor_info) = match src.find(' ') {
62            Some(i) => (&src[..i], src[i + 1..].to_string()),
63            None => (src, String::new()),
64        };
65
66        // TODO: make this even more lenient so that we can also accept
67        // `<major> "." <minor> [<???>]`
68        let mut it = version.split('.');
69        let major = it.next().and_then(|s| s.parse().ok());
70        let minor = it.next().and_then(|s| {
71            let trimmed = if s.starts_with('0') {
72                "0"
73            } else {
74                s.trim_end_matches('0')
75            };
76            trimmed.parse().ok()
77        });
78
79        match (major, minor) {
80            (Some(major), Some(minor)) => Ok((
81                // Return WebGL 2.0 version as OpenGL ES 3.0
82                if is_webgl && !is_glsl {
83                    major + 1
84                } else {
85                    major
86                },
87                minor,
88            )),
89            _ => {
90                log::warn!("Unable to extract the version from '{}'", version);
91                Err(crate::InstanceError)
92            }
93        }
94    }
95
96    fn make_info(vendor_orig: String, renderer_orig: String) -> wgt::AdapterInfo {
97        let vendor = vendor_orig.to_lowercase();
98        let renderer = renderer_orig.to_lowercase();
99
100        // opengl has no way to discern device_type, so we can try to infer it from the renderer string
101        let strings_that_imply_integrated = [
102            " xpress", // space here is on purpose so we don't match express
103            "amd renoir",
104            "radeon hd 4200",
105            "radeon hd 4250",
106            "radeon hd 4290",
107            "radeon hd 4270",
108            "radeon hd 4225",
109            "radeon hd 3100",
110            "radeon hd 3200",
111            "radeon hd 3000",
112            "radeon hd 3300",
113            "radeon(tm) r4 graphics",
114            "radeon(tm) r5 graphics",
115            "radeon(tm) r6 graphics",
116            "radeon(tm) r7 graphics",
117            "radeon r7 graphics",
118            "nforce", // all nvidia nforce are integrated
119            "tegra",  // all nvidia tegra are integrated
120            "shield", // all nvidia shield are integrated
121            "igp",
122            "mali",
123            "intel",
124            "v3d",
125            "apple m", // all apple m are integrated
126        ];
127        let strings_that_imply_cpu = ["mesa offscreen", "swiftshader", "llvmpipe"];
128
129        //TODO: handle Intel Iris XE as discreet
130        let inferred_device_type = if vendor.contains("qualcomm")
131            || vendor.contains("intel")
132            || strings_that_imply_integrated
133                .iter()
134                .any(|&s| renderer.contains(s))
135        {
136            wgt::DeviceType::IntegratedGpu
137        } else if strings_that_imply_cpu.iter().any(|&s| renderer.contains(s)) {
138            wgt::DeviceType::Cpu
139        } else {
140            // At this point the Device type is Unknown.
141            // It's most likely DiscreteGpu, but we do not know for sure.
142            // Use "Other" to avoid possibly making incorrect assumptions.
143            // Note that if this same device is available under some other API (ex: Vulkan),
144            // It will mostly likely get a different device type (probably DiscreteGpu).
145            wgt::DeviceType::Other
146        };
147
148        // source: Sascha Willems at Vulkan
149        let vendor_id = if vendor.contains("amd") {
150            db::amd::VENDOR
151        } else if vendor.contains("imgtec") {
152            db::imgtec::VENDOR
153        } else if vendor.contains("nvidia") {
154            db::nvidia::VENDOR
155        } else if vendor.contains("arm") {
156            db::arm::VENDOR
157        } else if vendor.contains("qualcomm") {
158            db::qualcomm::VENDOR
159        } else if vendor.contains("intel") {
160            db::intel::VENDOR
161        } else if vendor.contains("broadcom") {
162            db::broadcom::VENDOR
163        } else if vendor.contains("mesa") {
164            db::mesa::VENDOR
165        } else if vendor.contains("apple") {
166            db::apple::VENDOR
167        } else {
168            0
169        };
170
171        wgt::AdapterInfo {
172            name: renderer_orig,
173            vendor: vendor_id,
174            device: 0,
175            device_type: inferred_device_type,
176            driver: String::new(),
177            driver_info: String::new(),
178            backend: wgt::Backend::Gl,
179        }
180    }
181
182    pub(super) unsafe fn expose(
183        context: super::AdapterContext,
184    ) -> Option<crate::ExposedAdapter<super::Api>> {
185        let gl = context.lock();
186        let extensions = gl.supported_extensions();
187
188        let (vendor_const, renderer_const) = if extensions.contains("WEBGL_debug_renderer_info") {
189            // emscripten doesn't enable "WEBGL_debug_renderer_info" extension by default. so, we do it manually.
190            // See https://github.com/gfx-rs/wgpu/issues/3245 for context
191            #[cfg(target_os = "emscripten")]
192            if unsafe { super::emscripten::enable_extension("WEBGL_debug_renderer_info\0") } {
193                (GL_UNMASKED_VENDOR_WEBGL, GL_UNMASKED_RENDERER_WEBGL)
194            } else {
195                (glow::VENDOR, glow::RENDERER)
196            }
197            // glow already enables WEBGL_debug_renderer_info on wasm32-unknown-unknown target by default.
198            #[cfg(not(target_os = "emscripten"))]
199            (GL_UNMASKED_VENDOR_WEBGL, GL_UNMASKED_RENDERER_WEBGL)
200        } else {
201            (glow::VENDOR, glow::RENDERER)
202        };
203
204        let (vendor, renderer) = {
205            let vendor = unsafe { gl.get_parameter_string(vendor_const) };
206            let renderer = unsafe { gl.get_parameter_string(renderer_const) };
207
208            (vendor, renderer)
209        };
210        let version = unsafe { gl.get_parameter_string(glow::VERSION) };
211        log::info!("Vendor: {}", vendor);
212        log::info!("Renderer: {}", renderer);
213        log::info!("Version: {}", version);
214
215        log::debug!("Extensions: {:#?}", extensions);
216
217        let ver = Self::parse_version(&version).ok()?;
218        if ver < (3, 0) {
219            log::warn!(
220                "Returned GLES context is {}.{}, when 3.0+ was requested",
221                ver.0,
222                ver.1
223            );
224            return None;
225        }
226
227        let supports_storage = ver >= (3, 1);
228        let supports_work_group_params = ver >= (3, 1);
229
230        let shading_language_version = {
231            let sl_version = unsafe { gl.get_parameter_string(glow::SHADING_LANGUAGE_VERSION) };
232            log::info!("SL version: {}", &sl_version);
233            let (sl_major, sl_minor) = Self::parse_version(&sl_version).ok()?;
234            let value = sl_major as u16 * 100 + sl_minor as u16 * 10;
235            naga::back::glsl::Version::Embedded {
236                version: value,
237                is_webgl: cfg!(target_arch = "wasm32"),
238            }
239        };
240
241        // ANGLE provides renderer strings like: "ANGLE (Apple, Apple M1 Pro, OpenGL 4.1)"
242        let is_angle = renderer.contains("ANGLE");
243
244        let vertex_shader_storage_blocks = if supports_storage {
245            (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_SHADER_STORAGE_BLOCKS) } as u32)
246        } else {
247            0
248        };
249        let fragment_shader_storage_blocks = if supports_storage {
250            (unsafe { gl.get_parameter_i32(glow::MAX_FRAGMENT_SHADER_STORAGE_BLOCKS) } as u32)
251        } else {
252            0
253        };
254        let vertex_shader_storage_textures = if supports_storage {
255            (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_IMAGE_UNIFORMS) } as u32)
256        } else {
257            0
258        };
259        let fragment_shader_storage_textures = if supports_storage {
260            (unsafe { gl.get_parameter_i32(glow::MAX_FRAGMENT_IMAGE_UNIFORMS) } as u32)
261        } else {
262            0
263        };
264        let max_storage_block_size = if supports_storage {
265            (unsafe { gl.get_parameter_i32(glow::MAX_SHADER_STORAGE_BLOCK_SIZE) } as u32)
266        } else {
267            0
268        };
269        let max_element_index = unsafe { gl.get_parameter_i32(glow::MAX_ELEMENT_INDEX) } as u32;
270
271        // WORKAROUND: In order to work around an issue with GL on RPI4 and similar, we ignore a
272        // zero vertex ssbo count if there are vertex sstos. (more info:
273        // https://github.com/gfx-rs/wgpu/pull/1607#issuecomment-874938961) The hardware does not
274        // want us to write to these SSBOs, but GLES cannot express that. We detect this case and
275        // disable writing to SSBOs.
276        let vertex_ssbo_false_zero =
277            vertex_shader_storage_blocks == 0 && vertex_shader_storage_textures != 0;
278        if vertex_ssbo_false_zero {
279            // We only care about fragment here as the 0 is a lie.
280            log::warn!("Max vertex shader SSBO == 0 and SSTO != 0. Interpreting as false zero.");
281        }
282
283        let max_storage_buffers_per_shader_stage = if vertex_shader_storage_blocks == 0 {
284            fragment_shader_storage_blocks
285        } else {
286            vertex_shader_storage_blocks.min(fragment_shader_storage_blocks)
287        };
288        let max_storage_textures_per_shader_stage = if vertex_shader_storage_textures == 0 {
289            fragment_shader_storage_textures
290        } else {
291            vertex_shader_storage_textures.min(fragment_shader_storage_textures)
292        };
293
294        let mut downlevel_flags = wgt::DownlevelFlags::empty()
295            | wgt::DownlevelFlags::NON_POWER_OF_TWO_MIPMAPPED_TEXTURES
296            | wgt::DownlevelFlags::CUBE_ARRAY_TEXTURES
297            | wgt::DownlevelFlags::COMPARISON_SAMPLERS;
298        downlevel_flags.set(wgt::DownlevelFlags::COMPUTE_SHADERS, ver >= (3, 1));
299        downlevel_flags.set(
300            wgt::DownlevelFlags::FRAGMENT_WRITABLE_STORAGE,
301            max_storage_block_size != 0,
302        );
303        downlevel_flags.set(wgt::DownlevelFlags::INDIRECT_EXECUTION, ver >= (3, 1));
304        //TODO: we can actually support positive `base_vertex` in the same way
305        // as we emulate the `start_instance`. But we can't deal with negatives...
306        downlevel_flags.set(wgt::DownlevelFlags::BASE_VERTEX, ver >= (3, 2));
307        downlevel_flags.set(
308            wgt::DownlevelFlags::INDEPENDENT_BLEND,
309            ver >= (3, 2) || extensions.contains("GL_EXT_draw_buffers_indexed"),
310        );
311        downlevel_flags.set(
312            wgt::DownlevelFlags::VERTEX_STORAGE,
313            max_storage_block_size != 0
314                && max_storage_buffers_per_shader_stage != 0
315                && (vertex_shader_storage_blocks != 0 || vertex_ssbo_false_zero),
316        );
317        downlevel_flags.set(wgt::DownlevelFlags::FRAGMENT_STORAGE, supports_storage);
318        if extensions.contains("EXT_texture_filter_anisotropic") {
319            let max_aniso =
320                unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_MAX_ANISOTROPY_EXT) } as u32;
321            downlevel_flags.set(wgt::DownlevelFlags::ANISOTROPIC_FILTERING, max_aniso >= 16);
322        }
323        downlevel_flags.set(
324            wgt::DownlevelFlags::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED,
325            !(cfg!(target_arch = "wasm32") || is_angle),
326        );
327        // see https://registry.khronos.org/webgl/specs/latest/2.0/#BUFFER_OBJECT_BINDING
328        downlevel_flags.set(
329            wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER,
330            !cfg!(target_arch = "wasm32"),
331        );
332        downlevel_flags.set(
333            wgt::DownlevelFlags::UNRESTRICTED_EXTERNAL_TEXTURE_COPIES,
334            !cfg!(target_arch = "wasm32"),
335        );
336        downlevel_flags.set(
337            wgt::DownlevelFlags::FULL_DRAW_INDEX_UINT32,
338            max_element_index == u32::MAX,
339        );
340        downlevel_flags.set(
341            wgt::DownlevelFlags::MULTISAMPLED_SHADING,
342            ver >= (3, 2) || extensions.contains("OES_sample_variables"),
343        );
344
345        let mut features = wgt::Features::empty()
346            | wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES
347            | wgt::Features::CLEAR_TEXTURE
348            | wgt::Features::PUSH_CONSTANTS;
349        features.set(
350            wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER | wgt::Features::ADDRESS_MODE_CLAMP_TO_ZERO,
351            extensions.contains("GL_EXT_texture_border_clamp"),
352        );
353        features.set(
354            wgt::Features::DEPTH_CLIP_CONTROL,
355            extensions.contains("GL_EXT_depth_clamp"),
356        );
357        features.set(
358            wgt::Features::VERTEX_WRITABLE_STORAGE,
359            downlevel_flags.contains(wgt::DownlevelFlags::VERTEX_STORAGE)
360                && vertex_shader_storage_textures != 0,
361        );
362        features.set(
363            wgt::Features::MULTIVIEW,
364            extensions.contains("OVR_multiview2"),
365        );
366        features.set(
367            wgt::Features::SHADER_PRIMITIVE_INDEX,
368            ver >= (3, 2) || extensions.contains("OES_geometry_shader"),
369        );
370        features.set(wgt::Features::SHADER_EARLY_DEPTH_TEST, ver >= (3, 1));
371        let gles_bcn_exts = [
372            "GL_EXT_texture_compression_s3tc_srgb",
373            "GL_EXT_texture_compression_rgtc",
374            "GL_EXT_texture_compression_bptc",
375        ];
376        let webgl_bcn_exts = [
377            "WEBGL_compressed_texture_s3tc",
378            "WEBGL_compressed_texture_s3tc_srgb",
379            "EXT_texture_compression_rgtc",
380            "EXT_texture_compression_bptc",
381        ];
382        let bcn_exts = if cfg!(target_arch = "wasm32") {
383            &webgl_bcn_exts[..]
384        } else {
385            &gles_bcn_exts[..]
386        };
387        features.set(
388            wgt::Features::TEXTURE_COMPRESSION_BC,
389            bcn_exts.iter().all(|&ext| extensions.contains(ext)),
390        );
391        features.set(
392            wgt::Features::TEXTURE_COMPRESSION_ETC2,
393            // This is a part of GLES-3 but not WebGL2 core
394            !cfg!(target_arch = "wasm32") || extensions.contains("WEBGL_compressed_texture_etc"),
395        );
396        // `OES_texture_compression_astc` provides 2D + 3D, LDR + HDR support
397        if extensions.contains("WEBGL_compressed_texture_astc")
398            || extensions.contains("GL_OES_texture_compression_astc")
399        {
400            #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
401            {
402                if context
403                    .glow_context
404                    .compressed_texture_astc_supports_ldr_profile()
405                {
406                    features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC);
407                }
408                if context
409                    .glow_context
410                    .compressed_texture_astc_supports_hdr_profile()
411                {
412                    features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR);
413                }
414            }
415
416            #[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))]
417            {
418                features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC);
419                features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR);
420            }
421        } else {
422            features.set(
423                wgt::Features::TEXTURE_COMPRESSION_ASTC,
424                extensions.contains("GL_KHR_texture_compression_astc_ldr"),
425            );
426            features.set(
427                wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR,
428                extensions.contains("GL_KHR_texture_compression_astc_hdr"),
429            );
430        }
431
432        let mut private_caps = super::PrivateCapabilities::empty();
433        private_caps.set(
434            super::PrivateCapabilities::BUFFER_ALLOCATION,
435            extensions.contains("GL_EXT_buffer_storage"),
436        );
437        private_caps.set(
438            super::PrivateCapabilities::SHADER_BINDING_LAYOUT,
439            ver >= (3, 1),
440        );
441        private_caps.set(
442            super::PrivateCapabilities::SHADER_TEXTURE_SHADOW_LOD,
443            extensions.contains("GL_EXT_texture_shadow_lod"),
444        );
445        private_caps.set(super::PrivateCapabilities::MEMORY_BARRIERS, ver >= (3, 1));
446        private_caps.set(
447            super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT,
448            ver >= (3, 1),
449        );
450        private_caps.set(
451            super::PrivateCapabilities::INDEX_BUFFER_ROLE_CHANGE,
452            !cfg!(target_arch = "wasm32"),
453        );
454        private_caps.set(
455            super::PrivateCapabilities::CAN_DISABLE_DRAW_BUFFER,
456            !cfg!(target_arch = "wasm32"),
457        );
458        private_caps.set(
459            super::PrivateCapabilities::GET_BUFFER_SUB_DATA,
460            cfg!(target_arch = "wasm32"),
461        );
462        let color_buffer_float = extensions.contains("GL_EXT_color_buffer_float")
463            || extensions.contains("EXT_color_buffer_float");
464        let color_buffer_half_float = extensions.contains("GL_EXT_color_buffer_half_float");
465        private_caps.set(
466            super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT,
467            color_buffer_half_float || color_buffer_float,
468        );
469        private_caps.set(
470            super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
471            color_buffer_float,
472        );
473        private_caps.set(
474            super::PrivateCapabilities::TEXTURE_FLOAT_LINEAR,
475            extensions.contains("OES_texture_float_linear"),
476        );
477
478        let max_texture_size = unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_SIZE) } as u32;
479        let max_texture_3d_size = unsafe { gl.get_parameter_i32(glow::MAX_3D_TEXTURE_SIZE) } as u32;
480
481        let min_uniform_buffer_offset_alignment =
482            (unsafe { gl.get_parameter_i32(glow::UNIFORM_BUFFER_OFFSET_ALIGNMENT) } as u32);
483        let min_storage_buffer_offset_alignment = if ver >= (3, 1) {
484            (unsafe { gl.get_parameter_i32(glow::SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT) } as u32)
485        } else {
486            256
487        };
488        let max_uniform_buffers_per_shader_stage =
489            unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_UNIFORM_BLOCKS) }
490                .min(unsafe { gl.get_parameter_i32(glow::MAX_FRAGMENT_UNIFORM_BLOCKS) })
491                as u32;
492
493        let max_compute_workgroups_per_dimension = if supports_work_group_params {
494            unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 0) }
495                .min(unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 1) })
496                .min(unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 2) })
497                as u32
498        } else {
499            0
500        };
501
502        let limits = wgt::Limits {
503            max_texture_dimension_1d: max_texture_size,
504            max_texture_dimension_2d: max_texture_size,
505            max_texture_dimension_3d: max_texture_3d_size,
506            max_texture_array_layers: unsafe {
507                gl.get_parameter_i32(glow::MAX_ARRAY_TEXTURE_LAYERS)
508            } as u32,
509            max_bind_groups: crate::MAX_BIND_GROUPS as u32,
510            max_bindings_per_bind_group: 65535,
511            max_dynamic_uniform_buffers_per_pipeline_layout: max_uniform_buffers_per_shader_stage,
512            max_dynamic_storage_buffers_per_pipeline_layout: max_storage_buffers_per_shader_stage,
513            max_sampled_textures_per_shader_stage: super::MAX_TEXTURE_SLOTS as u32,
514            max_samplers_per_shader_stage: super::MAX_SAMPLERS as u32,
515            max_storage_buffers_per_shader_stage,
516            max_storage_textures_per_shader_stage,
517            max_uniform_buffers_per_shader_stage,
518            max_uniform_buffer_binding_size: unsafe {
519                gl.get_parameter_i32(glow::MAX_UNIFORM_BLOCK_SIZE)
520            } as u32,
521            max_storage_buffer_binding_size: if ver >= (3, 1) {
522                unsafe { gl.get_parameter_i32(glow::MAX_SHADER_STORAGE_BLOCK_SIZE) }
523            } else {
524                0
525            } as u32,
526            max_vertex_buffers: if private_caps
527                .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
528            {
529                (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_BINDINGS) } as u32)
530            } else {
531                16 // should this be different?
532            },
533            max_vertex_attributes: (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIBS) }
534                as u32)
535                .min(super::MAX_VERTEX_ATTRIBUTES as u32),
536            max_vertex_buffer_array_stride: if private_caps
537                .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
538            {
539                (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) } as u32)
540            } else {
541                !0
542            },
543            max_push_constant_size: super::MAX_PUSH_CONSTANTS as u32 * 4,
544            min_uniform_buffer_offset_alignment,
545            min_storage_buffer_offset_alignment,
546            max_inter_stage_shader_components: unsafe {
547                gl.get_parameter_i32(glow::MAX_VARYING_COMPONENTS)
548            } as u32,
549            max_compute_workgroup_storage_size: if supports_work_group_params {
550                (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_SHARED_MEMORY_SIZE) } as u32)
551            } else {
552                0
553            },
554            max_compute_invocations_per_workgroup: if supports_work_group_params {
555                (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_WORK_GROUP_INVOCATIONS) } as u32)
556            } else {
557                0
558            },
559            max_compute_workgroup_size_x: if supports_work_group_params {
560                (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 0) }
561                    as u32)
562            } else {
563                0
564            },
565            max_compute_workgroup_size_y: if supports_work_group_params {
566                (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 1) }
567                    as u32)
568            } else {
569                0
570            },
571            max_compute_workgroup_size_z: if supports_work_group_params {
572                (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 2) }
573                    as u32)
574            } else {
575                0
576            },
577            max_compute_workgroups_per_dimension,
578            max_buffer_size: i32::MAX as u64,
579        };
580
581        let mut workarounds = super::Workarounds::empty();
582
583        workarounds.set(
584            super::Workarounds::EMULATE_BUFFER_MAP,
585            cfg!(target_arch = "wasm32"),
586        );
587
588        let r = renderer.to_lowercase();
589        // Check for Mesa sRGB clear bug. See
590        // [`super::PrivateCapabilities::MESA_I915_SRGB_SHADER_CLEAR`].
591        if context.is_owned()
592            && r.contains("mesa")
593            && r.contains("intel")
594            && r.split(&[' ', '(', ')'][..])
595                .any(|substr| substr.len() == 3 && substr.chars().nth(2) == Some('l'))
596        {
597            log::warn!(
598                "Detected skylake derivative running on mesa i915. Clears to srgb textures will \
599                use manual shader clears."
600            );
601            workarounds.set(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR, true);
602        }
603
604        let downlevel_defaults = wgt::DownlevelLimits {};
605
606        // Drop the GL guard so we can move the context into AdapterShared
607        // ( on Wasm the gl handle is just a ref so we tell clippy to allow
608        // dropping the ref )
609        #[allow(clippy::drop_ref)]
610        drop(gl);
611
612        Some(crate::ExposedAdapter {
613            adapter: super::Adapter {
614                shared: Arc::new(super::AdapterShared {
615                    context,
616                    private_caps,
617                    workarounds,
618                    features,
619                    shading_language_version,
620                    max_texture_size,
621                    next_shader_id: Default::default(),
622                    program_cache: Default::default(),
623                }),
624            },
625            info: Self::make_info(vendor, renderer),
626            features,
627            capabilities: crate::Capabilities {
628                limits,
629                downlevel: wgt::DownlevelCapabilities {
630                    flags: downlevel_flags,
631                    limits: downlevel_defaults,
632                    shader_model: wgt::ShaderModel::Sm5,
633                },
634                alignments: crate::Alignments {
635                    buffer_copy_offset: wgt::BufferSize::new(4).unwrap(),
636                    buffer_copy_pitch: wgt::BufferSize::new(4).unwrap(),
637                },
638            },
639        })
640    }
641
642    unsafe fn create_shader_clear_program(
643        gl: &glow::Context,
644    ) -> (glow::Program, glow::UniformLocation) {
645        let program = unsafe { gl.create_program() }.expect("Could not create shader program");
646        let vertex =
647            unsafe { gl.create_shader(glow::VERTEX_SHADER) }.expect("Could not create shader");
648        unsafe { gl.shader_source(vertex, include_str!("./shaders/clear.vert")) };
649        unsafe { gl.compile_shader(vertex) };
650        let fragment =
651            unsafe { gl.create_shader(glow::FRAGMENT_SHADER) }.expect("Could not create shader");
652        unsafe { gl.shader_source(fragment, include_str!("./shaders/clear.frag")) };
653        unsafe { gl.compile_shader(fragment) };
654        unsafe { gl.attach_shader(program, vertex) };
655        unsafe { gl.attach_shader(program, fragment) };
656        unsafe { gl.link_program(program) };
657        let color_uniform_location = unsafe { gl.get_uniform_location(program, "color") }
658            .expect("Could not find color uniform in shader clear shader");
659        unsafe { gl.delete_shader(vertex) };
660        unsafe { gl.delete_shader(fragment) };
661
662        (program, color_uniform_location)
663    }
664}
665
666impl crate::Adapter<super::Api> for super::Adapter {
667    unsafe fn open(
668        &self,
669        features: wgt::Features,
670        _limits: &wgt::Limits,
671    ) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
672        let gl = &self.shared.context.lock();
673        unsafe { gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1) };
674        unsafe { gl.pixel_store_i32(glow::PACK_ALIGNMENT, 1) };
675        let main_vao =
676            unsafe { gl.create_vertex_array() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
677        unsafe { gl.bind_vertex_array(Some(main_vao)) };
678
679        let zero_buffer =
680            unsafe { gl.create_buffer() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
681        unsafe { gl.bind_buffer(glow::COPY_READ_BUFFER, Some(zero_buffer)) };
682        let zeroes = vec![0u8; super::ZERO_BUFFER_SIZE];
683        unsafe { gl.buffer_data_u8_slice(glow::COPY_READ_BUFFER, &zeroes, glow::STATIC_DRAW) };
684
685        // Compile the shader program we use for doing manual clears to work around Mesa fastclear
686        // bug.
687        let (shader_clear_program, shader_clear_program_color_uniform_location) =
688            unsafe { Self::create_shader_clear_program(gl) };
689
690        Ok(crate::OpenDevice {
691            device: super::Device {
692                shared: Arc::clone(&self.shared),
693                main_vao,
694                #[cfg(all(not(target_arch = "wasm32"), feature = "renderdoc"))]
695                render_doc: Default::default(),
696            },
697            queue: super::Queue {
698                shared: Arc::clone(&self.shared),
699                features,
700                draw_fbo: unsafe { gl.create_framebuffer() }
701                    .map_err(|_| crate::DeviceError::OutOfMemory)?,
702                copy_fbo: unsafe { gl.create_framebuffer() }
703                    .map_err(|_| crate::DeviceError::OutOfMemory)?,
704                shader_clear_program,
705                shader_clear_program_color_uniform_location,
706                zero_buffer,
707                temp_query_results: Vec::new(),
708                draw_buffer_count: 1,
709                current_index_buffer: None,
710            },
711        })
712    }
713
714    unsafe fn texture_format_capabilities(
715        &self,
716        format: wgt::TextureFormat,
717    ) -> crate::TextureFormatCapabilities {
718        use crate::TextureFormatCapabilities as Tfc;
719        use wgt::TextureFormat as Tf;
720
721        let sample_count = {
722            let max_samples = unsafe {
723                self.shared
724                    .context
725                    .lock()
726                    .get_parameter_i32(glow::MAX_SAMPLES)
727            };
728            if max_samples >= 16 {
729                Tfc::MULTISAMPLE_X2
730                    | Tfc::MULTISAMPLE_X4
731                    | Tfc::MULTISAMPLE_X8
732                    | Tfc::MULTISAMPLE_X16
733            } else if max_samples >= 8 {
734                Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4 | Tfc::MULTISAMPLE_X8
735            } else {
736                // The lowest supported level in GLE3.0/WebGL2 is 4X
737                // (see GL_MAX_SAMPLES in https://registry.khronos.org/OpenGL-Refpages/es3.0/html/glGet.xhtml).
738                // On some platforms, like iOS Safari, `get_parameter_i32(MAX_SAMPLES)` returns 0,
739                // so we always fall back to supporting 4x here.
740                Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4
741            }
742        };
743
744        // Base types are pulled from the table in the OpenGLES 3.0 spec in section 3.8.
745        //
746        // The storage types are based on table 8.26, in section
747        // "TEXTURE IMAGE LOADS AND STORES" of OpenGLES-3.2 spec.
748        let empty = Tfc::empty();
749        let base = Tfc::COPY_SRC | Tfc::COPY_DST;
750        let unfilterable = base | Tfc::SAMPLED;
751        let depth = base | Tfc::SAMPLED | sample_count | Tfc::DEPTH_STENCIL_ATTACHMENT;
752        let filterable = unfilterable | Tfc::SAMPLED_LINEAR;
753        let renderable =
754            unfilterable | Tfc::COLOR_ATTACHMENT | sample_count | Tfc::MULTISAMPLE_RESOLVE;
755        let filterable_renderable = filterable | renderable | Tfc::COLOR_ATTACHMENT_BLEND;
756        let storage = base | Tfc::STORAGE | Tfc::STORAGE_READ_WRITE;
757
758        let feature_fn = |f, caps| {
759            if self.shared.features.contains(f) {
760                caps
761            } else {
762                empty
763            }
764        };
765
766        let bcn_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_BC, filterable);
767        let etc2_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ETC2, filterable);
768        let astc_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC, filterable);
769        let astc_hdr_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR, filterable);
770
771        let private_caps_fn = |f, caps| {
772            if self.shared.private_caps.contains(f) {
773                caps
774            } else {
775                empty
776            }
777        };
778
779        let half_float_renderable = private_caps_fn(
780            super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT,
781            Tfc::COLOR_ATTACHMENT
782                | Tfc::COLOR_ATTACHMENT_BLEND
783                | sample_count
784                | Tfc::MULTISAMPLE_RESOLVE,
785        );
786
787        let float_renderable = private_caps_fn(
788            super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
789            Tfc::COLOR_ATTACHMENT
790                | Tfc::COLOR_ATTACHMENT_BLEND
791                | sample_count
792                | Tfc::MULTISAMPLE_RESOLVE,
793        );
794
795        let texture_float_linear =
796            private_caps_fn(super::PrivateCapabilities::TEXTURE_FLOAT_LINEAR, filterable);
797
798        match format {
799            Tf::R8Unorm => filterable_renderable,
800            Tf::R8Snorm => filterable,
801            Tf::R8Uint => renderable,
802            Tf::R8Sint => renderable,
803            Tf::R16Uint => renderable,
804            Tf::R16Sint => renderable,
805            Tf::R16Unorm => empty,
806            Tf::R16Snorm => empty,
807            Tf::R16Float => filterable | half_float_renderable,
808            Tf::Rg8Unorm => filterable_renderable,
809            Tf::Rg8Snorm => filterable,
810            Tf::Rg8Uint => renderable,
811            Tf::Rg8Sint => renderable,
812            Tf::R32Uint => renderable | storage,
813            Tf::R32Sint => renderable | storage,
814            Tf::R32Float => unfilterable | storage | float_renderable | texture_float_linear,
815            Tf::Rg16Uint => renderable,
816            Tf::Rg16Sint => renderable,
817            Tf::Rg16Unorm => empty,
818            Tf::Rg16Snorm => empty,
819            Tf::Rg16Float => filterable | half_float_renderable,
820            Tf::Rgba8Unorm | Tf::Rgba8UnormSrgb => filterable_renderable | storage,
821            Tf::Bgra8Unorm | Tf::Bgra8UnormSrgb => filterable_renderable,
822            Tf::Rgba8Snorm => filterable,
823            Tf::Rgba8Uint => renderable | storage,
824            Tf::Rgba8Sint => renderable | storage,
825            Tf::Rgb10a2Unorm => filterable_renderable,
826            Tf::Rg11b10Float => filterable | float_renderable,
827            Tf::Rg32Uint => renderable,
828            Tf::Rg32Sint => renderable,
829            Tf::Rg32Float => unfilterable | float_renderable | texture_float_linear,
830            Tf::Rgba16Uint => renderable | storage,
831            Tf::Rgba16Sint => renderable | storage,
832            Tf::Rgba16Unorm => empty,
833            Tf::Rgba16Snorm => empty,
834            Tf::Rgba16Float => filterable | storage | half_float_renderable,
835            Tf::Rgba32Uint => renderable | storage,
836            Tf::Rgba32Sint => renderable | storage,
837            Tf::Rgba32Float => unfilterable | storage | float_renderable | texture_float_linear,
838            Tf::Stencil8
839            | Tf::Depth16Unorm
840            | Tf::Depth32Float
841            | Tf::Depth32FloatStencil8
842            | Tf::Depth24Plus
843            | Tf::Depth24PlusStencil8 => depth,
844            Tf::Rgb9e5Ufloat => filterable,
845            Tf::Bc1RgbaUnorm
846            | Tf::Bc1RgbaUnormSrgb
847            | Tf::Bc2RgbaUnorm
848            | Tf::Bc2RgbaUnormSrgb
849            | Tf::Bc3RgbaUnorm
850            | Tf::Bc3RgbaUnormSrgb
851            | Tf::Bc4RUnorm
852            | Tf::Bc4RSnorm
853            | Tf::Bc5RgUnorm
854            | Tf::Bc5RgSnorm
855            | Tf::Bc6hRgbFloat
856            | Tf::Bc6hRgbUfloat
857            | Tf::Bc7RgbaUnorm
858            | Tf::Bc7RgbaUnormSrgb => bcn_features,
859            Tf::Etc2Rgb8Unorm
860            | Tf::Etc2Rgb8UnormSrgb
861            | Tf::Etc2Rgb8A1Unorm
862            | Tf::Etc2Rgb8A1UnormSrgb
863            | Tf::Etc2Rgba8Unorm
864            | Tf::Etc2Rgba8UnormSrgb
865            | Tf::EacR11Unorm
866            | Tf::EacR11Snorm
867            | Tf::EacRg11Unorm
868            | Tf::EacRg11Snorm => etc2_features,
869            Tf::Astc {
870                block: _,
871                channel: AstcChannel::Unorm | AstcChannel::UnormSrgb,
872            } => astc_features,
873            Tf::Astc {
874                block: _,
875                channel: AstcChannel::Hdr,
876            } => astc_hdr_features,
877        }
878    }
879
880    unsafe fn surface_capabilities(
881        &self,
882        surface: &super::Surface,
883    ) -> Option<crate::SurfaceCapabilities> {
884        if surface.presentable {
885            let mut formats = vec![
886                wgt::TextureFormat::Rgba8Unorm,
887                #[cfg(not(target_arch = "wasm32"))]
888                wgt::TextureFormat::Bgra8Unorm,
889            ];
890            if surface.supports_srgb() {
891                formats.extend([
892                    wgt::TextureFormat::Rgba8UnormSrgb,
893                    #[cfg(not(target_arch = "wasm32"))]
894                    wgt::TextureFormat::Bgra8UnormSrgb,
895                ])
896            }
897            if self
898                .shared
899                .private_caps
900                .contains(super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT)
901            {
902                formats.push(wgt::TextureFormat::Rgba16Float)
903            }
904
905            Some(crate::SurfaceCapabilities {
906                formats,
907                present_modes: vec![wgt::PresentMode::Fifo], //TODO
908                composite_alpha_modes: vec![wgt::CompositeAlphaMode::Opaque], //TODO
909                swap_chain_sizes: 2..=2,
910                current_extent: None,
911                extents: wgt::Extent3d {
912                    width: 4,
913                    height: 4,
914                    depth_or_array_layers: 1,
915                }..=wgt::Extent3d {
916                    width: self.shared.max_texture_size,
917                    height: self.shared.max_texture_size,
918                    depth_or_array_layers: 1,
919                },
920                usage: crate::TextureUses::COLOR_TARGET,
921            })
922        } else {
923            None
924        }
925    }
926
927    unsafe fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
928        wgt::PresentationTimestamp::INVALID_TIMESTAMP
929    }
930}
931
932impl super::AdapterShared {
933    pub(super) unsafe fn get_buffer_sub_data(
934        &self,
935        gl: &glow::Context,
936        target: u32,
937        offset: i32,
938        dst_data: &mut [u8],
939    ) {
940        if self
941            .private_caps
942            .contains(super::PrivateCapabilities::GET_BUFFER_SUB_DATA)
943        {
944            unsafe { gl.get_buffer_sub_data(target, offset, dst_data) };
945        } else {
946            log::error!("Fake map");
947            let length = dst_data.len();
948            let buffer_mapping =
949                unsafe { gl.map_buffer_range(target, offset, length as _, glow::MAP_READ_BIT) };
950
951            unsafe { std::ptr::copy_nonoverlapping(buffer_mapping, dst_data.as_mut_ptr(), length) };
952
953            unsafe { gl.unmap_buffer(target) };
954        }
955    }
956}
957
958#[cfg(all(
959    target_arch = "wasm32",
960    feature = "fragile-send-sync-non-atomic-wasm",
961    not(target_feature = "atomics")
962))]
963unsafe impl Sync for super::Adapter {}
964#[cfg(all(
965    target_arch = "wasm32",
966    feature = "fragile-send-sync-non-atomic-wasm",
967    not(target_feature = "atomics")
968))]
969unsafe impl Send for super::Adapter {}
970
971#[cfg(test)]
972mod tests {
973    use super::super::Adapter;
974
975    #[test]
976    fn test_version_parse() {
977        let error = Err(crate::InstanceError);
978        assert_eq!(Adapter::parse_version("1"), error);
979        assert_eq!(Adapter::parse_version("1."), error);
980        assert_eq!(Adapter::parse_version("1 h3l1o. W0rld"), error);
981        assert_eq!(Adapter::parse_version("1. h3l1o. W0rld"), error);
982        assert_eq!(Adapter::parse_version("1.2.3"), error);
983        assert_eq!(Adapter::parse_version("OpenGL ES 3.1"), Ok((3, 1)));
984        assert_eq!(
985            Adapter::parse_version("OpenGL ES 2.0 Google Nexus"),
986            Ok((2, 0))
987        );
988        assert_eq!(Adapter::parse_version("GLSL ES 1.1"), Ok((1, 1)));
989        assert_eq!(Adapter::parse_version("OpenGL ES GLSL ES 3.20"), Ok((3, 2)));
990        assert_eq!(
991            // WebGL 2.0 should parse as OpenGL ES 3.0
992            Adapter::parse_version("WebGL 2.0 (OpenGL ES 3.0 Chromium)"),
993            Ok((3, 0))
994        );
995        assert_eq!(
996            Adapter::parse_version("WebGL GLSL ES 3.00 (OpenGL ES GLSL ES 3.0 Chromium)"),
997            Ok((3, 0))
998        );
999    }
1000}