1use alloc::{borrow::ToOwned as _, format, string::String, sync::Arc, vec, vec::Vec};
2use core::sync::atomic::AtomicU8;
3
4use glow::HasContext;
5use parking_lot::Mutex;
6use wgt::AstcChannel;
7
8use crate::auxil::db;
9use crate::gles::ShaderClearProgram;
10
11const GL_UNMASKED_VENDOR_WEBGL: u32 = 0x9245;
14const GL_UNMASKED_RENDERER_WEBGL: u32 = 0x9246;
15
16impl super::Adapter {
17 pub fn get_glsl_version(&self) -> naga::back::glsl::Version {
18 self.shared.shading_language_version
19 }
20
21 fn parse_version(mut src: &str) -> Result<(u8, u8), crate::InstanceError> {
27 let webgl_sig = "WebGL ";
28 let is_webgl = src.starts_with(webgl_sig);
32 if is_webgl {
33 let pos = src.rfind(webgl_sig).unwrap_or(0);
34 src = &src[pos + webgl_sig.len()..];
35 } else {
36 let es_sig = " ES ";
37 match src.rfind(es_sig) {
38 Some(pos) => {
39 src = &src[pos + es_sig.len()..];
40 }
41 None => {
42 return Err(crate::InstanceError::new(format!(
43 "OpenGL version {src:?} does not contain 'ES'"
44 )));
45 }
46 }
47 };
48
49 let glsl_es_sig = "GLSL ES ";
50 let is_glsl = match src.find(glsl_es_sig) {
51 Some(pos) => {
52 src = &src[pos + glsl_es_sig.len()..];
53 true
54 }
55 None => false,
56 };
57
58 Self::parse_full_version(src).map(|(major, minor)| {
59 (
60 if is_webgl && !is_glsl {
62 major + 1
63 } else {
64 major
65 },
66 minor,
67 )
68 })
69 }
70
71 pub(super) fn parse_full_version(src: &str) -> Result<(u8, u8), crate::InstanceError> {
87 let (version, _vendor_info) = match src.find(' ') {
88 Some(i) => (&src[..i], src[i + 1..].to_owned()),
89 None => (src, String::new()),
90 };
91
92 let mut it = version.split('.');
95 let major = it.next().and_then(|s| s.parse().ok());
96 let minor = it.next().and_then(|s| {
97 let trimmed = if s.starts_with('0') {
98 "0"
99 } else {
100 s.trim_end_matches('0')
101 };
102 trimmed.parse().ok()
103 });
104
105 match (major, minor) {
106 (Some(major), Some(minor)) => Ok((major, minor)),
107 _ => Err(crate::InstanceError::new(format!(
108 "unable to extract OpenGL version from {version:?}"
109 ))),
110 }
111 }
112
113 fn make_info(vendor_orig: String, renderer_orig: String, version: String) -> wgt::AdapterInfo {
114 let vendor = vendor_orig.to_lowercase();
115 let renderer = renderer_orig.to_lowercase();
116
117 let strings_that_imply_integrated = [
119 " xpress", "amd renoir",
121 "radeon hd 4200",
122 "radeon hd 4250",
123 "radeon hd 4290",
124 "radeon hd 4270",
125 "radeon hd 4225",
126 "radeon hd 3100",
127 "radeon hd 3200",
128 "radeon hd 3000",
129 "radeon hd 3300",
130 "radeon(tm) r4 graphics",
131 "radeon(tm) r5 graphics",
132 "radeon(tm) r6 graphics",
133 "radeon(tm) r7 graphics",
134 "radeon r7 graphics",
135 "nforce", "tegra", "shield", "igp",
139 "mali",
140 "intel",
141 "v3d",
142 "apple m", ];
144 let strings_that_imply_cpu = ["mesa offscreen", "swiftshader", "llvmpipe"];
145
146 let inferred_device_type = if vendor.contains("qualcomm")
148 || vendor.contains("intel")
149 || strings_that_imply_integrated
150 .iter()
151 .any(|&s| renderer.contains(s))
152 {
153 wgt::DeviceType::IntegratedGpu
154 } else if strings_that_imply_cpu.iter().any(|&s| renderer.contains(s)) {
155 wgt::DeviceType::Cpu
156 } else {
157 wgt::DeviceType::Other
163 };
164
165 let vendor_id = if vendor.contains("amd") {
167 db::amd::VENDOR
168 } else if vendor.contains("imgtec") {
169 db::imgtec::VENDOR
170 } else if vendor.contains("nvidia") {
171 db::nvidia::VENDOR
172 } else if vendor.contains("arm") {
173 db::arm::VENDOR
174 } else if vendor.contains("qualcomm") {
175 db::qualcomm::VENDOR
176 } else if vendor.contains("intel") {
177 db::intel::VENDOR
178 } else if vendor.contains("broadcom") {
179 db::broadcom::VENDOR
180 } else if vendor.contains("mesa") {
181 db::mesa::VENDOR
182 } else if vendor.contains("apple") {
183 db::apple::VENDOR
184 } else {
185 0
186 };
187
188 wgt::AdapterInfo {
189 name: renderer_orig,
190 vendor: vendor_id,
191 driver_info: version,
192 ..wgt::AdapterInfo::new(inferred_device_type, wgt::Backend::Gl)
193 }
194 }
195
196 pub(super) unsafe fn expose(
197 context: super::AdapterContext,
198 backend_options: wgt::GlBackendOptions,
199 ) -> Option<crate::ExposedAdapter<super::Api>> {
200 let gl = context.lock();
201 let extensions = gl.supported_extensions();
202
203 let (vendor_const, renderer_const) = if extensions.contains("WEBGL_debug_renderer_info") {
204 #[cfg(Emscripten)]
207 if unsafe {
208 super::emscripten::enable_extension(c"WEBGL_debug_renderer_info".to_str().unwrap())
209 } {
210 (GL_UNMASKED_VENDOR_WEBGL, GL_UNMASKED_RENDERER_WEBGL)
211 } else {
212 (glow::VENDOR, glow::RENDERER)
213 }
214 #[cfg(not(Emscripten))]
216 (GL_UNMASKED_VENDOR_WEBGL, GL_UNMASKED_RENDERER_WEBGL)
217 } else {
218 (glow::VENDOR, glow::RENDERER)
219 };
220
221 let vendor = unsafe { gl.get_parameter_string(vendor_const) };
222 let renderer = unsafe { gl.get_parameter_string(renderer_const) };
223 let version = unsafe { gl.get_parameter_string(glow::VERSION) };
224 log::debug!("Vendor: {vendor}");
225 log::debug!("Renderer: {renderer}");
226 log::debug!("Version: {version}");
227
228 let full_ver = Self::parse_full_version(&version).ok();
229 let es_ver = full_ver.map_or_else(|| Self::parse_version(&version).ok(), |_| None);
230
231 if let Some(full_ver) = full_ver {
232 let core_profile = (full_ver >= (3, 2)).then(|| unsafe {
233 gl.get_parameter_i32(glow::CONTEXT_PROFILE_MASK)
234 & glow::CONTEXT_CORE_PROFILE_BIT as i32
235 != 0
236 });
237 log::trace!(
238 "Profile: {}",
239 core_profile
240 .map(|core_profile| if core_profile {
241 "Core"
242 } else {
243 "Compatibility"
244 })
245 .unwrap_or("Legacy")
246 );
247 }
248
249 if es_ver.is_none() && full_ver.is_none() {
250 log::warn!("Unable to parse OpenGL version");
251 return None;
252 }
253
254 if let Some(es_ver) = es_ver {
255 if es_ver < (3, 0) {
256 log::warn!(
257 "Returned GLES context is {}.{}, when 3.0+ was requested",
258 es_ver.0,
259 es_ver.1
260 );
261 return None;
262 }
263 }
264
265 if let Some(full_ver) = full_ver {
266 if full_ver < (3, 3) {
267 log::warn!(
268 "Returned GL context is {}.{}, when 3.3+ is needed",
269 full_ver.0,
270 full_ver.1
271 );
272 return None;
273 }
274 }
275
276 let shading_language_version = {
277 let sl_version = unsafe { gl.get_parameter_string(glow::SHADING_LANGUAGE_VERSION) };
278 log::debug!("SL version: {}", &sl_version);
279 if full_ver.is_some() {
280 let (sl_major, sl_minor) = Self::parse_full_version(&sl_version).ok()?;
281 let mut value = sl_major as u16 * 100 + sl_minor as u16 * 10;
282 if value > 450 {
284 value = 450;
285 }
286 naga::back::glsl::Version::Desktop(value)
287 } else {
288 let (sl_major, sl_minor) = Self::parse_version(&sl_version).ok()?;
289 let value = sl_major as u16 * 100 + sl_minor as u16 * 10;
290 naga::back::glsl::Version::Embedded {
291 version: value,
292 is_webgl: cfg!(any(webgl, Emscripten)),
293 }
294 }
295 };
296
297 log::debug!("Supported GL Extensions: {extensions:#?}");
298
299 let supported = |(req_es_major, req_es_minor), (req_full_major, req_full_minor)| {
300 let es_supported = es_ver
301 .map(|es_ver| es_ver >= (req_es_major, req_es_minor))
302 .unwrap_or_default();
303
304 let full_supported = full_ver
305 .map(|full_ver| full_ver >= (req_full_major, req_full_minor))
306 .unwrap_or_default();
307
308 es_supported || full_supported
309 };
310
311 let supports_storage =
312 supported((3, 1), (4, 3)) || extensions.contains("GL_ARB_shader_storage_buffer_object");
313 let supports_compute =
314 supported((3, 1), (4, 3)) || extensions.contains("GL_ARB_compute_shader");
315 let supports_work_group_params = supports_compute;
316
317 let is_angle = renderer.contains("ANGLE");
319
320 let vertex_shader_storage_blocks = if supports_storage {
321 let value =
322 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_SHADER_STORAGE_BLOCKS) } as u32);
323
324 if value == 0 && extensions.contains("GL_ARB_shader_storage_buffer_object") {
325 let new = (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_SHADER_STORAGE_BLOCKS) }
328 as u32);
329 log::debug!("Max vertex shader storage blocks is zero, but GL_ARB_shader_storage_buffer_object is specified. Assuming the compute value {new}");
330 new
331 } else {
332 value
333 }
334 } else {
335 0
336 };
337 let fragment_shader_storage_blocks = if supports_storage {
338 (unsafe { gl.get_parameter_i32(glow::MAX_FRAGMENT_SHADER_STORAGE_BLOCKS) } as u32)
339 } else {
340 0
341 };
342 let vertex_shader_storage_textures = if supports_storage {
343 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_IMAGE_UNIFORMS) } as u32)
344 } else {
345 0
346 };
347 let fragment_shader_storage_textures = if supports_storage {
348 (unsafe { gl.get_parameter_i32(glow::MAX_FRAGMENT_IMAGE_UNIFORMS) } as u32)
349 } else {
350 0
351 };
352 let max_storage_block_size = if supports_storage {
353 (unsafe { gl.get_parameter_i32(glow::MAX_SHADER_STORAGE_BLOCK_SIZE) } as u32)
354 } else {
355 0
356 };
357 let max_element_index = unsafe { gl.get_parameter_i32(glow::MAX_ELEMENT_INDEX) } as u32;
358
359 let vertex_ssbo_false_zero =
365 vertex_shader_storage_blocks == 0 && vertex_shader_storage_textures != 0;
366 if vertex_ssbo_false_zero {
367 log::debug!("Max vertex shader SSBO == 0 and SSTO != 0. Interpreting as false zero.");
369 }
370
371 let max_storage_buffers_per_shader_stage = if vertex_shader_storage_blocks == 0 {
372 fragment_shader_storage_blocks
373 } else {
374 vertex_shader_storage_blocks.min(fragment_shader_storage_blocks)
375 };
376 let max_storage_textures_per_shader_stage = if vertex_shader_storage_textures == 0 {
377 fragment_shader_storage_textures
378 } else {
379 vertex_shader_storage_textures.min(fragment_shader_storage_textures)
380 };
381 let indirect_execution = supported((3, 1), (4, 3))
383 || (extensions.contains("GL_ARB_draw_indirect") && supports_compute);
384 let supports_cube_array = supported((3, 2), (4, 0))
385 || (supported((3, 1), (4, 0)) && extensions.contains("GL_EXT_texture_cube_map_array"));
386
387 let mut downlevel_flags = wgt::DownlevelFlags::empty()
388 | wgt::DownlevelFlags::NON_POWER_OF_TWO_MIPMAPPED_TEXTURES
389 | wgt::DownlevelFlags::COMPARISON_SAMPLERS
390 | wgt::DownlevelFlags::SHADER_F16_IN_F32
391 | wgt::DownlevelFlags::MSL2_1;
392 downlevel_flags.set(
393 wgt::DownlevelFlags::CUBE_ARRAY_TEXTURES,
394 supports_cube_array,
395 );
396 downlevel_flags.set(wgt::DownlevelFlags::COMPUTE_SHADERS, supports_compute);
397 downlevel_flags.set(
398 wgt::DownlevelFlags::FRAGMENT_WRITABLE_STORAGE,
399 max_storage_block_size != 0,
400 );
401 downlevel_flags.set(wgt::DownlevelFlags::INDIRECT_EXECUTION, indirect_execution);
402 downlevel_flags.set(wgt::DownlevelFlags::BASE_VERTEX, supported((3, 2), (3, 2)));
403 downlevel_flags.set(
404 wgt::DownlevelFlags::INDEPENDENT_BLEND,
405 supported((3, 2), (4, 0)) || extensions.contains("GL_EXT_draw_buffers_indexed"),
406 );
407 downlevel_flags.set(
408 wgt::DownlevelFlags::VERTEX_STORAGE,
409 max_storage_block_size != 0
410 && max_storage_buffers_per_shader_stage != 0
411 && (vertex_shader_storage_blocks != 0 || vertex_ssbo_false_zero),
412 );
413 downlevel_flags.set(wgt::DownlevelFlags::FRAGMENT_STORAGE, supports_storage);
414 if extensions.contains("EXT_texture_filter_anisotropic")
415 || extensions.contains("GL_EXT_texture_filter_anisotropic")
416 {
417 let max_aniso =
418 unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_MAX_ANISOTROPY_EXT) } as u32;
419 downlevel_flags.set(wgt::DownlevelFlags::ANISOTROPIC_FILTERING, max_aniso >= 16);
420 }
421 downlevel_flags.set(
422 wgt::DownlevelFlags::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED,
423 !(cfg!(any(webgl, Emscripten)) || is_angle),
424 );
425 downlevel_flags.set(
427 wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER,
428 !cfg!(any(webgl, Emscripten)),
429 );
430 downlevel_flags.set(
431 wgt::DownlevelFlags::UNRESTRICTED_EXTERNAL_TEXTURE_COPIES,
432 !cfg!(any(webgl, Emscripten)),
433 );
434 downlevel_flags.set(
435 wgt::DownlevelFlags::FULL_DRAW_INDEX_UINT32,
436 max_element_index == u32::MAX,
437 );
438 downlevel_flags.set(
439 wgt::DownlevelFlags::MULTISAMPLED_SHADING,
440 supported((3, 2), (4, 0)) || extensions.contains("OES_sample_variables"),
441 );
442 let query_buffers = extensions.contains("GL_ARB_query_buffer_object")
443 || extensions.contains("GL_AMD_query_buffer_object");
444 if query_buffers {
445 downlevel_flags.set(wgt::DownlevelFlags::NONBLOCKING_QUERY_RESOLVE, true);
446 }
447
448 let supports_16bit_norm = if es_ver.is_some() {
451 extensions.contains("GL_EXT_texture_norm16")
452 || extensions.contains("EXT_texture_norm16")
453 } else {
454 true
455 };
456 let supports_16bit_snorm_renderable = supports_16bit_norm
462 && (extensions.contains("GL_EXT_render_snorm")
463 || extensions.contains("EXT_render_snorm"));
464 let supports_16bit_norm_storage = supports_16bit_norm
476 && if es_ver.is_some() {
477 extensions.contains("GL_NV_image_formats")
478 } else {
479 full_ver.is_some_and(|v| v >= (4, 2))
480 || extensions.contains("GL_ARB_shader_image_load_store")
481 };
482
483 let mut features = wgt::Features::empty()
484 | wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES
485 | wgt::Features::CLEAR_TEXTURE
486 | wgt::Features::IMMEDIATES
487 | wgt::Features::DEPTH32FLOAT_STENCIL8
488 | wgt::Features::PASSTHROUGH_SHADERS;
489 features.set(
490 wgt::Features::TEXTURE_FORMAT_16BIT_NORM,
491 supports_16bit_norm,
492 );
493 features.set(
494 wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER | wgt::Features::ADDRESS_MODE_CLAMP_TO_ZERO,
495 extensions.contains("GL_EXT_texture_border_clamp")
496 || extensions.contains("GL_ARB_texture_border_clamp"),
497 );
498 features.set(
499 wgt::Features::DEPTH_CLIP_CONTROL,
500 extensions.contains("GL_EXT_depth_clamp") || extensions.contains("GL_ARB_depth_clamp"),
501 );
502 features.set(
503 wgt::Features::VERTEX_WRITABLE_STORAGE,
504 downlevel_flags.contains(wgt::DownlevelFlags::VERTEX_STORAGE)
505 && vertex_shader_storage_textures != 0,
506 );
507 features.set(
508 wgt::Features::MULTIVIEW,
509 extensions.contains("OVR_multiview2") || extensions.contains("GL_OVR_multiview2"),
510 );
511 features.set(
512 wgt::Features::DUAL_SOURCE_BLENDING,
513 extensions.contains("GL_EXT_blend_func_extended")
514 || extensions.contains("GL_ARB_blend_func_extended"),
515 );
516 features.set(
517 wgt::Features::CLIP_DISTANCES,
518 full_ver.is_some() || extensions.contains("GL_EXT_clip_cull_distance"),
519 );
520 features.set(
521 wgt::Features::PRIMITIVE_INDEX,
522 supported((3, 2), (3, 2))
523 || extensions.contains("OES_geometry_shader")
524 || extensions.contains("GL_ARB_geometry_shader4"),
525 );
526 features.set(
527 wgt::Features::SHADER_EARLY_DEPTH_TEST,
528 supported((3, 1), (4, 2)) || extensions.contains("GL_ARB_shader_image_load_store"),
529 );
530 if extensions.contains("GL_ARB_timer_query") {
531 features.set(wgt::Features::TIMESTAMP_QUERY, true);
532 features.set(wgt::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS, true);
533 features.set(wgt::Features::TIMESTAMP_QUERY_INSIDE_PASSES, true);
534 }
535 let gl_bcn_exts = [
536 "GL_EXT_texture_compression_s3tc",
537 "GL_EXT_texture_compression_rgtc",
538 "GL_ARB_texture_compression_bptc",
539 ];
540 let gles_bcn_exts = [
541 "GL_EXT_texture_compression_s3tc_srgb",
542 "GL_EXT_texture_compression_rgtc",
543 "GL_EXT_texture_compression_bptc",
544 ];
545 let webgl_bcn_exts = [
546 "WEBGL_compressed_texture_s3tc",
547 "WEBGL_compressed_texture_s3tc_srgb",
548 "EXT_texture_compression_rgtc",
549 "EXT_texture_compression_bptc",
550 ];
551 let bcn_exts = if cfg!(any(webgl, Emscripten)) {
552 &webgl_bcn_exts[..]
553 } else if es_ver.is_some() {
554 &gles_bcn_exts[..]
555 } else {
556 &gl_bcn_exts[..]
557 };
558 features.set(
559 wgt::Features::TEXTURE_COMPRESSION_BC,
560 bcn_exts.iter().all(|&ext| extensions.contains(ext)),
561 );
562 features.set(
563 wgt::Features::TEXTURE_COMPRESSION_BC_SLICED_3D,
564 bcn_exts.iter().all(|&ext| extensions.contains(ext)), );
566 let has_etc = if cfg!(any(webgl, Emscripten)) {
567 extensions.contains("WEBGL_compressed_texture_etc")
568 } else {
569 es_ver.is_some() || extensions.contains("GL_ARB_ES3_compatibility")
570 };
571 features.set(wgt::Features::TEXTURE_COMPRESSION_ETC2, has_etc);
572
573 if extensions.contains("WEBGL_compressed_texture_astc")
575 || extensions.contains("GL_OES_texture_compression_astc")
576 {
577 #[cfg(webgl)]
578 {
579 if context
580 .glow_context
581 .compressed_texture_astc_supports_ldr_profile()
582 {
583 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC);
584 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D);
585 }
586 if context
587 .glow_context
588 .compressed_texture_astc_supports_hdr_profile()
589 {
590 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR);
591 }
592 }
593
594 #[cfg(any(native, Emscripten))]
595 {
596 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC);
597 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D);
598 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR);
599 }
600 } else {
601 features.set(
602 wgt::Features::TEXTURE_COMPRESSION_ASTC,
603 extensions.contains("GL_KHR_texture_compression_astc_ldr"),
604 );
605 features.set(
606 wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D,
607 extensions.contains("GL_KHR_texture_compression_astc_ldr")
608 && extensions.contains("GL_KHR_texture_compression_astc_sliced_3d"),
609 );
610 features.set(
611 wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR,
612 extensions.contains("GL_KHR_texture_compression_astc_hdr"),
613 );
614 }
615
616 downlevel_flags.set(
617 wgt::DownlevelFlags::TEXTURE_COMPRESSION,
618 features.contains(wgt::Features::TEXTURE_COMPRESSION_BC)
619 || features.contains(
620 wgt::Features::TEXTURE_COMPRESSION_ETC2
621 | wgt::Features::TEXTURE_COMPRESSION_ASTC,
622 ),
623 );
624
625 features.set(
626 wgt::Features::FLOAT32_FILTERABLE,
627 extensions.contains("GL_ARB_color_buffer_float")
628 || extensions.contains("GL_EXT_color_buffer_float")
629 || extensions.contains("OES_texture_float_linear"),
630 );
631
632 if es_ver.is_none() {
633 features |= wgt::Features::POLYGON_MODE_LINE | wgt::Features::POLYGON_MODE_POINT;
634 }
635
636 let mut private_caps = super::PrivateCapabilities::empty();
639 private_caps.set(
640 super::PrivateCapabilities::BUFFER_ALLOCATION,
641 extensions.contains("GL_EXT_buffer_storage")
642 || extensions.contains("GL_ARB_buffer_storage"),
643 );
644 private_caps.set(
645 super::PrivateCapabilities::SHADER_BINDING_LAYOUT,
646 supports_compute,
647 );
648 private_caps.set(
649 super::PrivateCapabilities::SHADER_TEXTURE_SHADOW_LOD,
650 extensions.contains("GL_EXT_texture_shadow_lod"),
651 );
652 private_caps.set(
653 super::PrivateCapabilities::MEMORY_BARRIERS,
654 supported((3, 1), (4, 2)),
655 );
656 private_caps.set(
657 super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT,
658 supported((3, 1), (4, 3)) || extensions.contains("GL_ARB_vertex_attrib_binding"),
659 );
660 private_caps.set(
661 super::PrivateCapabilities::INDEX_BUFFER_ROLE_CHANGE,
662 !cfg!(any(webgl, Emscripten)),
663 );
664 private_caps.set(
665 super::PrivateCapabilities::GET_BUFFER_SUB_DATA,
666 cfg!(any(webgl, Emscripten)) || full_ver.is_some(),
667 );
668 let color_buffer_float = extensions.contains("GL_EXT_color_buffer_float")
669 || extensions.contains("GL_ARB_color_buffer_float")
670 || extensions.contains("EXT_color_buffer_float");
671 let color_buffer_half_float = extensions.contains("GL_EXT_color_buffer_half_float")
672 || extensions.contains("GL_ARB_half_float_pixel");
673 private_caps.set(
674 super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT,
675 color_buffer_half_float || color_buffer_float,
676 );
677 private_caps.set(
678 super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
679 color_buffer_float,
680 );
681 private_caps.set(super::PrivateCapabilities::QUERY_BUFFERS, query_buffers);
682 private_caps.set(super::PrivateCapabilities::QUERY_64BIT, full_ver.is_some());
683 private_caps.set(
684 super::PrivateCapabilities::TEXTURE_STORAGE,
685 supported((3, 0), (4, 2)),
686 );
687 let is_mali = renderer.to_lowercase().contains("mali");
688 let debug_fns_enabled = match backend_options.debug_fns {
689 wgt::GlDebugFns::Auto => gl.supports_debug() && !is_mali,
690 wgt::GlDebugFns::ForceEnabled => gl.supports_debug(),
691 wgt::GlDebugFns::Disabled => false,
692 };
693 private_caps.set(super::PrivateCapabilities::DEBUG_FNS, debug_fns_enabled);
694 private_caps.set(
695 super::PrivateCapabilities::INVALIDATE_FRAMEBUFFER,
696 supported((3, 0), (4, 3)),
697 );
698 if let Some(full_ver) = full_ver {
699 let supported =
700 full_ver >= (4, 2) && extensions.contains("GL_ARB_shader_draw_parameters");
701 private_caps.set(
702 super::PrivateCapabilities::FULLY_FEATURED_INSTANCING,
703 supported,
704 );
705 features.set(wgt::Features::INDIRECT_FIRST_INSTANCE, supported);
712 }
713 private_caps.set(
714 super::PrivateCapabilities::MULTISAMPLED_RENDER_TO_TEXTURE,
715 extensions.contains("GL_EXT_multisampled_render_to_texture"),
716 );
717 private_caps.set(
718 super::PrivateCapabilities::TEXTURE_FORMAT_NORM16,
719 supports_16bit_norm,
720 );
721 private_caps.set(
722 super::PrivateCapabilities::TEXTURE_FORMAT_SNORM16_RENDERABLE,
723 supports_16bit_snorm_renderable,
724 );
725 private_caps.set(
726 super::PrivateCapabilities::TEXTURE_FORMAT_NORM16_STORAGE,
727 supports_16bit_norm_storage,
728 );
729
730 if supports_storage {
733 features |= wgt::Features::MEMORY_DECORATION_COHERENT
734 | wgt::Features::MEMORY_DECORATION_VOLATILE;
735 }
736
737 let max_texture_size = unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_SIZE) } as u32;
738 let max_texture_3d_size = unsafe { gl.get_parameter_i32(glow::MAX_3D_TEXTURE_SIZE) } as u32;
739
740 let min_uniform_buffer_offset_alignment =
741 (unsafe { gl.get_parameter_i32(glow::UNIFORM_BUFFER_OFFSET_ALIGNMENT) } as u32);
742 let min_storage_buffer_offset_alignment = if supports_storage {
743 (unsafe { gl.get_parameter_i32(glow::SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT) } as u32)
744 } else {
745 256
746 };
747 let max_uniform_buffers_per_shader_stage =
748 unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_UNIFORM_BLOCKS) }
749 .min(unsafe { gl.get_parameter_i32(glow::MAX_FRAGMENT_UNIFORM_BLOCKS) })
750 as u32;
751
752 let max_compute_workgroups_per_dimension = if supports_work_group_params {
753 unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 0) }
754 .min(unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 1) })
755 .min(unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 2) })
756 as u32
757 } else {
758 0
759 };
760
761 let max_color_attachments = unsafe {
762 gl.get_parameter_i32(glow::MAX_COLOR_ATTACHMENTS)
763 .min(gl.get_parameter_i32(glow::MAX_DRAW_BUFFERS)) as u32
764 };
765
766 let max_color_attachment_bytes_per_sample =
768 max_color_attachments * wgt::TextureFormat::MAX_TARGET_PIXEL_BYTE_COST;
769
770 let limits = crate::auxil::adjust_raw_limits(wgt::Limits {
771 max_texture_dimension_1d: max_texture_size,
772 max_texture_dimension_2d: max_texture_size,
773 max_texture_dimension_3d: max_texture_3d_size,
774 max_texture_array_layers: unsafe {
775 gl.get_parameter_i32(glow::MAX_ARRAY_TEXTURE_LAYERS)
776 } as u32,
777 max_bind_groups: u32::MAX,
778 max_bind_groups_plus_vertex_buffers: u32::MAX,
780 max_bindings_per_bind_group: u32::MAX,
782 max_dynamic_uniform_buffers_per_pipeline_layout: max_uniform_buffers_per_shader_stage,
783 max_dynamic_storage_buffers_per_pipeline_layout: max_storage_buffers_per_shader_stage,
784 max_sampled_textures_per_shader_stage: super::MAX_TEXTURE_SLOTS as u32,
785 max_samplers_per_shader_stage: super::MAX_SAMPLERS as u32,
786 max_storage_buffers_per_shader_stage,
787 max_storage_textures_per_shader_stage,
788 max_uniform_buffers_per_shader_stage,
789 max_binding_array_elements_per_shader_stage: 0,
790 max_binding_array_sampler_elements_per_shader_stage: 0,
791 max_binding_array_acceleration_structure_elements_per_shader_stage: 0,
792 max_uniform_buffer_binding_size: unsafe {
793 gl.get_parameter_i32(glow::MAX_UNIFORM_BLOCK_SIZE)
794 } as u64,
795 max_storage_buffer_binding_size: if supports_storage {
796 unsafe { gl.get_parameter_i32(glow::MAX_SHADER_STORAGE_BLOCK_SIZE) }
797 } else {
798 0
799 } as u64,
800 max_vertex_buffers: if private_caps
801 .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
802 {
803 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_BINDINGS) } as u32)
804 } else {
805 16 },
807 max_vertex_attributes: (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIBS) }
808 as u32)
809 .min(super::MAX_VERTEX_ATTRIBUTES as u32),
810 max_vertex_buffer_array_stride: if private_caps
811 .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
812 {
813 if let Some(full_ver) = full_ver {
814 if full_ver >= (4, 4) {
815 let value =
817 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) })
818 as u32;
819
820 if value == 0 {
821 log::debug!("Max vertex attribute stride is 0. Assuming it is the OpenGL minimum spec 2048");
825 2048
826 } else {
827 value
828 }
829 } else {
830 log::debug!("Max vertex attribute stride unknown. Assuming it is the OpenGL minimum spec 2048");
831 2048
832 }
833 } else {
834 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) }) as u32
835 }
836 } else {
837 !0
838 },
839 max_immediate_size: super::MAX_IMMEDIATES as u32 * 4,
840 min_uniform_buffer_offset_alignment,
841 min_storage_buffer_offset_alignment,
842 max_inter_stage_shader_variables: {
843 let max_varying_components =
847 unsafe { gl.get_parameter_i32(glow::MAX_VARYING_COMPONENTS) } as u32;
848 if max_varying_components == 0 {
849 15
851 } else {
852 max_varying_components / 4
853 }
854 },
855 max_color_attachments,
856 max_color_attachment_bytes_per_sample,
857 max_compute_workgroup_storage_size: if supports_work_group_params {
858 (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_SHARED_MEMORY_SIZE) } as u32)
859 } else {
860 0
861 },
862 max_compute_invocations_per_workgroup: if supports_work_group_params {
863 (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_WORK_GROUP_INVOCATIONS) } as u32)
864 } else {
865 0
866 },
867 max_compute_workgroup_size_x: if supports_work_group_params {
868 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 0) }
869 as u32)
870 } else {
871 0
872 },
873 max_compute_workgroup_size_y: if supports_work_group_params {
874 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 1) }
875 as u32)
876 } else {
877 0
878 },
879 max_compute_workgroup_size_z: if supports_work_group_params {
880 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 2) }
881 as u32)
882 } else {
883 0
884 },
885 max_compute_workgroups_per_dimension,
886 max_buffer_size: i32::MAX as u64,
887 max_non_sampler_bindings: u32::MAX,
888
889 max_task_workgroup_total_count: 0,
890 max_task_workgroups_per_dimension: 0,
891 max_mesh_workgroup_total_count: 0,
892 max_mesh_workgroups_per_dimension: 0,
893 max_task_invocations_per_workgroup: 0,
894 max_task_invocations_per_dimension: 0,
895 max_mesh_invocations_per_workgroup: 0,
896 max_mesh_invocations_per_dimension: 0,
897 max_task_payload_size: 0,
898 max_mesh_output_vertices: 0,
899 max_mesh_output_primitives: 0,
900 max_mesh_output_layers: 0,
901 max_mesh_multiview_view_count: 0,
902
903 max_blas_primitive_count: 0,
904 max_blas_geometry_count: 0,
905 max_tlas_instance_count: 0,
906 max_acceleration_structures_per_shader_stage: 0,
907 max_buffers_and_acceleration_structures_per_shader_stage: u32::MAX,
908
909 max_multiview_view_count: 0,
910
911 max_ray_dispatch_count: 0,
912 max_ray_recursion_depth: 0,
913 });
914
915 let mut workarounds = super::Workarounds::empty();
916
917 workarounds.set(
918 super::Workarounds::EMULATE_BUFFER_MAP,
919 cfg!(any(webgl, Emscripten)),
920 );
921
922 let r = renderer.to_lowercase();
923 if context.is_owned()
926 && r.contains("mesa")
927 && r.contains("intel")
928 && r.split(&[' ', '(', ')'][..])
929 .any(|substr| substr.len() == 3 && substr.chars().nth(2) == Some('l'))
930 {
931 log::debug!(
932 "Detected skylake derivative running on mesa i915. Clears to srgb textures will \
933 use manual shader clears."
934 );
935 workarounds.set(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR, true);
936 }
937
938 let downlevel_defaults = wgt::DownlevelLimits {};
939 let max_samples = unsafe { gl.get_parameter_i32(glow::MAX_SAMPLES) };
940
941 #[cfg_attr(target_arch = "wasm32", allow(dropping_references))]
945 drop(gl);
946
947 Some(crate::ExposedAdapter {
948 adapter: super::Adapter {
949 shared: Arc::new(super::AdapterShared {
950 context,
951 private_caps,
952 workarounds,
953 features,
954 limits: limits.clone(),
955 options: backend_options,
956 shading_language_version,
957 next_shader_id: Default::default(),
958 unread_links: Default::default(),
959 program_cache: Default::default(),
960 es: es_ver.is_some(),
961 max_msaa_samples: max_samples,
962 }),
963 },
964 info: Self::make_info(vendor, renderer, version),
965 features,
966 capabilities: crate::Capabilities {
967 limits,
968 downlevel: wgt::DownlevelCapabilities {
969 flags: downlevel_flags,
970 limits: downlevel_defaults,
971 shader_model: wgt::ShaderModel::Sm5,
972 },
973 alignments: crate::Alignments {
974 buffer_copy_offset: wgt::BufferSize::new(4).unwrap(),
975 buffer_copy_pitch: wgt::BufferSize::new(4).unwrap(),
976 uniform_bounds_check_alignment: wgt::BufferSize::new(1).unwrap(),
986 raw_tlas_instance_size: 0,
987 ray_tracing_scratch_buffer_alignment: 0,
988 ray_tracing_pipeline_group_data_size: 0,
989 ray_tracing_pipeline_group_data_alignment: 0,
990 ray_tracing_pipeline_data_offset_alignment: 0,
991 },
992 cooperative_matrix_properties: Vec::new(),
993 },
994 })
995 }
996
997 unsafe fn compile_shader(
998 source: &str,
999 gl: &glow::Context,
1000 shader_type: u32,
1001 es: bool,
1002 ) -> Option<glow::Shader> {
1003 let source = if es {
1004 format!("#version 300 es\nprecision lowp float;\n{source}")
1005 } else {
1006 let version = gl.version();
1007 if version.major == 3 && version.minor == 0 {
1008 format!("#version 130\n{source}")
1010 } else {
1011 format!("#version 140\n{source}")
1013 }
1014 };
1015 let shader = unsafe { gl.create_shader(shader_type) }.expect("Could not create shader");
1016 unsafe { gl.shader_source(shader, &source) };
1017 unsafe { gl.compile_shader(shader) };
1018
1019 if !unsafe { gl.get_shader_compile_status(shader) } {
1020 let msg = unsafe { gl.get_shader_info_log(shader) };
1021 if !msg.is_empty() {
1022 log::error!("\tShader compile error: {msg}");
1023 }
1024 unsafe { gl.delete_shader(shader) };
1025 None
1026 } else {
1027 Some(shader)
1028 }
1029 }
1030
1031 unsafe fn create_shader_clear_program(
1032 gl: &glow::Context,
1033 es: bool,
1034 ) -> Option<ShaderClearProgram> {
1035 let program = unsafe { gl.create_program() }.expect("Could not create shader program");
1036 let vertex = unsafe {
1037 Self::compile_shader(
1038 include_str!("./shaders/clear.vert"),
1039 gl,
1040 glow::VERTEX_SHADER,
1041 es,
1042 )?
1043 };
1044 let fragment = unsafe {
1045 Self::compile_shader(
1046 include_str!("./shaders/clear.frag"),
1047 gl,
1048 glow::FRAGMENT_SHADER,
1049 es,
1050 )?
1051 };
1052 unsafe { gl.attach_shader(program, vertex) };
1053 unsafe { gl.attach_shader(program, fragment) };
1054 unsafe { gl.link_program(program) };
1055
1056 let linked_ok = unsafe { gl.get_program_link_status(program) };
1057 let msg = unsafe { gl.get_program_info_log(program) };
1058 if !msg.is_empty() {
1059 log::error!("Shader link error: {msg}");
1060 }
1061 if !linked_ok {
1062 return None;
1063 }
1064
1065 let color_uniform_location = unsafe { gl.get_uniform_location(program, "color") }
1066 .expect("Could not find color uniform in shader clear shader");
1067 unsafe { gl.delete_shader(vertex) };
1068 unsafe { gl.delete_shader(fragment) };
1069
1070 Some(ShaderClearProgram {
1071 program,
1072 color_uniform_location,
1073 })
1074 }
1075}
1076
1077impl crate::Adapter for super::Adapter {
1078 type A = super::Api;
1079
1080 unsafe fn open(
1081 &self,
1082 features: wgt::Features,
1083 _limits: &wgt::Limits,
1084 _memory_hints: &wgt::MemoryHints,
1085 ) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
1086 let gl = &self.shared.context.lock();
1087 unsafe { gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1) };
1088 unsafe { gl.pixel_store_i32(glow::PACK_ALIGNMENT, 1) };
1089 let main_vao =
1090 unsafe { gl.create_vertex_array() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
1091 unsafe { gl.bind_vertex_array(Some(main_vao)) };
1092
1093 let zero_buffer =
1094 unsafe { gl.create_buffer() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
1095 unsafe { gl.bind_buffer(glow::COPY_READ_BUFFER, Some(zero_buffer)) };
1096 let zeroes = vec![0u8; super::ZERO_BUFFER_SIZE];
1097 unsafe { gl.buffer_data_u8_slice(glow::COPY_READ_BUFFER, &zeroes, glow::STATIC_DRAW) };
1098
1099 let shader_clear_program = if self
1103 .shared
1104 .workarounds
1105 .contains(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR)
1106 {
1107 Some(unsafe {
1108 Self::create_shader_clear_program(gl, self.shared.es)
1109 .ok_or(crate::DeviceError::Lost)?
1110 })
1111 } else {
1112 None
1114 };
1115
1116 Ok(crate::OpenDevice {
1117 device: super::Device {
1118 shared: Arc::clone(&self.shared),
1119 main_vao,
1120 #[cfg(all(native, feature = "renderdoc"))]
1121 render_doc: Default::default(),
1122 counters: Default::default(),
1123 },
1124 queue: super::Queue {
1125 shared: Arc::clone(&self.shared),
1126 features,
1127 draw_fbo: unsafe { gl.create_framebuffer() }
1128 .map_err(|_| crate::DeviceError::OutOfMemory)?,
1129 copy_fbo: unsafe { gl.create_framebuffer() }
1130 .map_err(|_| crate::DeviceError::OutOfMemory)?,
1131 shader_clear_program,
1132 zero_buffer,
1133 temp_query_results: Mutex::new(Vec::new()),
1134 draw_buffer_count: AtomicU8::new(1),
1135 current_index_buffer: Mutex::new(None),
1136 },
1137 })
1138 }
1139
1140 unsafe fn texture_format_capabilities(
1141 &self,
1142 format: wgt::TextureFormat,
1143 ) -> crate::TextureFormatCapabilities {
1144 use crate::TextureFormatCapabilities as Tfc;
1145 use wgt::TextureFormat as Tf;
1146
1147 let sample_count = {
1148 let max_samples = self.shared.max_msaa_samples;
1149 if max_samples >= 16 {
1150 Tfc::MULTISAMPLE_X2
1151 | Tfc::MULTISAMPLE_X4
1152 | Tfc::MULTISAMPLE_X8
1153 | Tfc::MULTISAMPLE_X16
1154 } else if max_samples >= 8 {
1155 Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4 | Tfc::MULTISAMPLE_X8
1156 } else {
1157 Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4
1162 }
1163 };
1164
1165 let empty = Tfc::empty();
1170 let base = Tfc::COPY_SRC | Tfc::COPY_DST;
1171 let unfilterable = base | Tfc::SAMPLED;
1172 let depth = base | Tfc::SAMPLED | sample_count | Tfc::DEPTH_STENCIL_ATTACHMENT;
1173 let filterable = unfilterable | Tfc::SAMPLED_LINEAR;
1174 let renderable =
1175 unfilterable | Tfc::COLOR_ATTACHMENT | sample_count | Tfc::MULTISAMPLE_RESOLVE;
1176 let filterable_renderable = filterable | renderable | Tfc::COLOR_ATTACHMENT_BLEND;
1177 let storage =
1178 base | Tfc::STORAGE_READ_WRITE | Tfc::STORAGE_READ_ONLY | Tfc::STORAGE_WRITE_ONLY;
1179
1180 let feature_fn = |f, caps| {
1181 if self.shared.features.contains(f) {
1182 caps
1183 } else {
1184 empty
1185 }
1186 };
1187
1188 let bcn_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_BC, filterable);
1189 let etc2_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ETC2, filterable);
1190 let astc_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC, filterable);
1191 let astc_hdr_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR, filterable);
1192
1193 let private_caps_fn = |f, caps| {
1194 if self.shared.private_caps.contains(f) {
1195 caps
1196 } else {
1197 empty
1198 }
1199 };
1200
1201 let half_float_renderable = private_caps_fn(
1202 super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT,
1203 Tfc::COLOR_ATTACHMENT
1204 | Tfc::COLOR_ATTACHMENT_BLEND
1205 | sample_count
1206 | Tfc::MULTISAMPLE_RESOLVE,
1207 );
1208
1209 let float_renderable = private_caps_fn(
1210 super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
1211 Tfc::COLOR_ATTACHMENT
1212 | Tfc::COLOR_ATTACHMENT_BLEND
1213 | sample_count
1214 | Tfc::MULTISAMPLE_RESOLVE,
1215 );
1216
1217 let texture_float_linear = feature_fn(wgt::Features::FLOAT32_FILTERABLE, filterable);
1218
1219 let image_atomic = feature_fn(wgt::Features::TEXTURE_ATOMIC, Tfc::STORAGE_ATOMIC);
1220 let image_64_atomic = feature_fn(wgt::Features::TEXTURE_INT64_ATOMIC, Tfc::STORAGE_ATOMIC);
1221
1222 let norm16_unorm = private_caps_fn(
1227 super::PrivateCapabilities::TEXTURE_FORMAT_NORM16,
1228 filterable_renderable,
1229 );
1230 let norm16_snorm = if self
1231 .shared
1232 .private_caps
1233 .contains(super::PrivateCapabilities::TEXTURE_FORMAT_SNORM16_RENDERABLE)
1234 {
1235 norm16_unorm
1236 } else {
1237 private_caps_fn(
1238 super::PrivateCapabilities::TEXTURE_FORMAT_NORM16,
1239 filterable,
1240 )
1241 };
1242 let norm16_storage = private_caps_fn(
1243 super::PrivateCapabilities::TEXTURE_FORMAT_NORM16_STORAGE,
1244 storage,
1245 );
1246
1247 match format {
1248 Tf::R8Unorm => filterable_renderable,
1249 Tf::R8Snorm => filterable,
1250 Tf::R8Uint => renderable,
1251 Tf::R8Sint => renderable,
1252 Tf::R16Uint => renderable,
1253 Tf::R16Sint => renderable,
1254 Tf::R16Unorm => norm16_unorm | norm16_storage,
1255 Tf::R16Snorm => norm16_snorm | norm16_storage,
1256 Tf::R16Float => filterable | half_float_renderable,
1257 Tf::Rg8Unorm => filterable_renderable,
1258 Tf::Rg8Snorm => filterable,
1259 Tf::Rg8Uint => renderable,
1260 Tf::Rg8Sint => renderable,
1261 Tf::R32Uint => renderable | storage | image_atomic,
1262 Tf::R32Sint => renderable | storage | image_atomic,
1263 Tf::R32Float => unfilterable | storage | float_renderable | texture_float_linear,
1264 Tf::Rg16Uint => renderable,
1265 Tf::Rg16Sint => renderable,
1266 Tf::Rg16Unorm => norm16_unorm | norm16_storage,
1267 Tf::Rg16Snorm => norm16_snorm | norm16_storage,
1268 Tf::Rg16Float => filterable | half_float_renderable,
1269 Tf::Rgba8Unorm => filterable_renderable | storage,
1270 Tf::Rgba8UnormSrgb => filterable_renderable,
1271 Tf::Bgra8Unorm | Tf::Bgra8UnormSrgb => filterable_renderable,
1272 Tf::Rgba8Snorm => filterable | storage,
1273 Tf::Rgba8Uint => renderable | storage,
1274 Tf::Rgba8Sint => renderable | storage,
1275 Tf::Rgb10a2Uint => renderable,
1276 Tf::Rgb10a2Unorm => filterable_renderable,
1277 Tf::Rg11b10Ufloat => filterable | float_renderable,
1278 Tf::R64Uint => image_64_atomic,
1279 Tf::Rg32Uint => renderable,
1280 Tf::Rg32Sint => renderable,
1281 Tf::Rg32Float => unfilterable | float_renderable | texture_float_linear,
1282 Tf::Rgba16Uint => renderable | storage,
1283 Tf::Rgba16Sint => renderable | storage,
1284 Tf::Rgba16Unorm => norm16_unorm | norm16_storage,
1285 Tf::Rgba16Snorm => norm16_snorm | norm16_storage,
1286 Tf::Rgba16Float => filterable | storage | half_float_renderable,
1287 Tf::Rgba32Uint => renderable | storage,
1288 Tf::Rgba32Sint => renderable | storage,
1289 Tf::Rgba32Float => unfilterable | storage | float_renderable | texture_float_linear,
1290 Tf::Stencil8
1291 | Tf::Depth16Unorm
1292 | Tf::Depth32Float
1293 | Tf::Depth32FloatStencil8
1294 | Tf::Depth24Plus
1295 | Tf::Depth24PlusStencil8 => depth,
1296 Tf::NV12 => empty,
1297 Tf::P010 => empty,
1298 Tf::Rgb9e5Ufloat => filterable,
1299 Tf::Bc1RgbaUnorm
1300 | Tf::Bc1RgbaUnormSrgb
1301 | Tf::Bc2RgbaUnorm
1302 | Tf::Bc2RgbaUnormSrgb
1303 | Tf::Bc3RgbaUnorm
1304 | Tf::Bc3RgbaUnormSrgb
1305 | Tf::Bc4RUnorm
1306 | Tf::Bc4RSnorm
1307 | Tf::Bc5RgUnorm
1308 | Tf::Bc5RgSnorm
1309 | Tf::Bc6hRgbFloat
1310 | Tf::Bc6hRgbUfloat
1311 | Tf::Bc7RgbaUnorm
1312 | Tf::Bc7RgbaUnormSrgb => bcn_features,
1313 Tf::Etc2Rgb8Unorm
1314 | Tf::Etc2Rgb8UnormSrgb
1315 | Tf::Etc2Rgb8A1Unorm
1316 | Tf::Etc2Rgb8A1UnormSrgb
1317 | Tf::Etc2Rgba8Unorm
1318 | Tf::Etc2Rgba8UnormSrgb
1319 | Tf::EacR11Unorm
1320 | Tf::EacR11Snorm
1321 | Tf::EacRg11Unorm
1322 | Tf::EacRg11Snorm => etc2_features,
1323 Tf::Astc {
1324 block: _,
1325 channel: AstcChannel::Unorm | AstcChannel::UnormSrgb,
1326 } => astc_features,
1327 Tf::Astc {
1328 block: _,
1329 channel: AstcChannel::Hdr,
1330 } => astc_hdr_features,
1331 }
1332 }
1333
1334 unsafe fn surface_capabilities(
1335 &self,
1336 surface: &super::Surface,
1337 ) -> Option<crate::SurfaceCapabilities> {
1338 #[cfg(webgl)]
1339 if self.shared.context.webgl2_context != surface.webgl2_context {
1340 return None;
1341 }
1342
1343 if surface.presentable {
1344 let format_caps = |format: wgt::TextureFormat| wgt::SurfaceFormatCapabilities {
1347 format,
1348 color_spaces: wgt::SurfaceColorSpaces::SRGB,
1349 };
1350 let mut formats = vec![
1351 format_caps(wgt::TextureFormat::Rgba8Unorm),
1352 #[cfg(native)]
1353 format_caps(wgt::TextureFormat::Bgra8Unorm),
1354 ];
1355 if surface.supports_srgb() {
1356 formats.extend([
1357 format_caps(wgt::TextureFormat::Rgba8UnormSrgb),
1358 #[cfg(native)]
1359 format_caps(wgt::TextureFormat::Bgra8UnormSrgb),
1360 ])
1361 }
1362 if self
1363 .shared
1364 .private_caps
1365 .contains(super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT)
1366 {
1367 formats.push(format_caps(wgt::TextureFormat::Rgba16Float))
1368 }
1369
1370 Some(crate::SurfaceCapabilities {
1371 formats,
1372 present_modes: if cfg!(windows) {
1373 vec![wgt::PresentMode::Fifo, wgt::PresentMode::Immediate]
1374 } else {
1375 vec![wgt::PresentMode::Fifo] },
1377 composite_alpha_modes: vec![wgt::CompositeAlphaMode::Opaque], maximum_frame_latency: 2..=2, current_extent: None,
1380 #[cfg(webgl)]
1384 usage: wgt::TextureUses::COLOR_TARGET
1385 | wgt::TextureUses::COPY_SRC
1386 | wgt::TextureUses::COPY_DST
1387 | wgt::TextureUses::RESOURCE,
1388 #[cfg(not(webgl))]
1389 usage: wgt::TextureUses::COLOR_TARGET,
1390 })
1391 } else {
1392 None
1393 }
1394 }
1395
1396 unsafe fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
1397 wgt::PresentationTimestamp::INVALID_TIMESTAMP
1398 }
1399
1400 fn get_ordered_buffer_usages(&self) -> wgt::BufferUses {
1401 wgt::BufferUses::INCLUSIVE | wgt::BufferUses::MAP_WRITE
1402 }
1403
1404 fn get_ordered_texture_usages(&self) -> wgt::TextureUses {
1406 wgt::TextureUses::INCLUSIVE
1407 | wgt::TextureUses::COLOR_TARGET
1408 | wgt::TextureUses::DEPTH_STENCIL_WRITE
1409 }
1410}
1411
1412impl super::AdapterShared {
1413 pub(super) unsafe fn get_buffer_sub_data(
1414 &self,
1415 gl: &glow::Context,
1416 target: u32,
1417 offset: i32,
1418 dst_data: &mut [u8],
1419 ) {
1420 if self
1421 .private_caps
1422 .contains(super::PrivateCapabilities::GET_BUFFER_SUB_DATA)
1423 {
1424 unsafe { gl.get_buffer_sub_data(target, offset, dst_data) };
1425 } else {
1426 log::error!("Fake map");
1427 let length = dst_data.len();
1428 if length != 0 {
1430 let buffer_mapping =
1431 unsafe { gl.map_buffer_range(target, offset, length as _, glow::MAP_READ_BIT) };
1432
1433 unsafe {
1434 core::ptr::copy_nonoverlapping(buffer_mapping, dst_data.as_mut_ptr(), length)
1435 };
1436
1437 unsafe { gl.unmap_buffer(target) };
1438 }
1439 }
1440 }
1441}
1442
1443#[cfg(send_sync)]
1444unsafe impl Sync for super::Adapter {}
1445#[cfg(send_sync)]
1446unsafe impl Send for super::Adapter {}
1447
1448#[cfg(test)]
1449mod tests {
1450 use super::super::Adapter;
1451
1452 #[test]
1453 fn test_version_parse() {
1454 Adapter::parse_version("1").unwrap_err();
1455 Adapter::parse_version("1.").unwrap_err();
1456 Adapter::parse_version("1 h3l1o. W0rld").unwrap_err();
1457 Adapter::parse_version("1. h3l1o. W0rld").unwrap_err();
1458 Adapter::parse_version("1.2.3").unwrap_err();
1459
1460 assert_eq!(Adapter::parse_version("OpenGL ES 3.1").unwrap(), (3, 1));
1461 assert_eq!(
1462 Adapter::parse_version("OpenGL ES 2.0 Google Nexus").unwrap(),
1463 (2, 0)
1464 );
1465 assert_eq!(Adapter::parse_version("GLSL ES 1.1").unwrap(), (1, 1));
1466 assert_eq!(
1467 Adapter::parse_version("OpenGL ES GLSL ES 3.20").unwrap(),
1468 (3, 2)
1469 );
1470 assert_eq!(
1471 Adapter::parse_version("WebGL 2.0 (OpenGL ES 3.0 Chromium)").unwrap(),
1473 (3, 0)
1474 );
1475 assert_eq!(
1476 Adapter::parse_version("WebGL GLSL ES 3.00 (OpenGL ES GLSL ES 3.0 Chromium)").unwrap(),
1477 (3, 0)
1478 );
1479 }
1480}