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