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 program_cache: Default::default(),
959 es: es_ver.is_some(),
960 max_msaa_samples: max_samples,
961 }),
962 },
963 info: Self::make_info(vendor, renderer, version),
964 features,
965 capabilities: crate::Capabilities {
966 limits,
967 downlevel: wgt::DownlevelCapabilities {
968 flags: downlevel_flags,
969 limits: downlevel_defaults,
970 shader_model: wgt::ShaderModel::Sm5,
971 },
972 alignments: crate::Alignments {
973 buffer_copy_offset: wgt::BufferSize::new(4).unwrap(),
974 buffer_copy_pitch: wgt::BufferSize::new(4).unwrap(),
975 uniform_bounds_check_alignment: wgt::BufferSize::new(1).unwrap(),
985 raw_tlas_instance_size: 0,
986 ray_tracing_scratch_buffer_alignment: 0,
987 ray_tracing_pipeline_group_data_size: 0,
988 ray_tracing_pipeline_group_data_alignment: 0,
989 ray_tracing_pipeline_data_offset_alignment: 0,
990 },
991 cooperative_matrix_properties: Vec::new(),
992 },
993 })
994 }
995
996 unsafe fn compile_shader(
997 source: &str,
998 gl: &glow::Context,
999 shader_type: u32,
1000 es: bool,
1001 ) -> Option<glow::Shader> {
1002 let source = if es {
1003 format!("#version 300 es\nprecision lowp float;\n{source}")
1004 } else {
1005 let version = gl.version();
1006 if version.major == 3 && version.minor == 0 {
1007 format!("#version 130\n{source}")
1009 } else {
1010 format!("#version 140\n{source}")
1012 }
1013 };
1014 let shader = unsafe { gl.create_shader(shader_type) }.expect("Could not create shader");
1015 unsafe { gl.shader_source(shader, &source) };
1016 unsafe { gl.compile_shader(shader) };
1017
1018 if !unsafe { gl.get_shader_compile_status(shader) } {
1019 let msg = unsafe { gl.get_shader_info_log(shader) };
1020 if !msg.is_empty() {
1021 log::error!("\tShader compile error: {msg}");
1022 }
1023 unsafe { gl.delete_shader(shader) };
1024 None
1025 } else {
1026 Some(shader)
1027 }
1028 }
1029
1030 unsafe fn create_shader_clear_program(
1031 gl: &glow::Context,
1032 es: bool,
1033 ) -> Option<ShaderClearProgram> {
1034 let program = unsafe { gl.create_program() }.expect("Could not create shader program");
1035 let vertex = unsafe {
1036 Self::compile_shader(
1037 include_str!("./shaders/clear.vert"),
1038 gl,
1039 glow::VERTEX_SHADER,
1040 es,
1041 )?
1042 };
1043 let fragment = unsafe {
1044 Self::compile_shader(
1045 include_str!("./shaders/clear.frag"),
1046 gl,
1047 glow::FRAGMENT_SHADER,
1048 es,
1049 )?
1050 };
1051 unsafe { gl.attach_shader(program, vertex) };
1052 unsafe { gl.attach_shader(program, fragment) };
1053 unsafe { gl.link_program(program) };
1054
1055 let linked_ok = unsafe { gl.get_program_link_status(program) };
1056 let msg = unsafe { gl.get_program_info_log(program) };
1057 if !msg.is_empty() {
1058 log::error!("Shader link error: {msg}");
1059 }
1060 if !linked_ok {
1061 return None;
1062 }
1063
1064 let color_uniform_location = unsafe { gl.get_uniform_location(program, "color") }
1065 .expect("Could not find color uniform in shader clear shader");
1066 unsafe { gl.delete_shader(vertex) };
1067 unsafe { gl.delete_shader(fragment) };
1068
1069 Some(ShaderClearProgram {
1070 program,
1071 color_uniform_location,
1072 })
1073 }
1074}
1075
1076impl crate::Adapter for super::Adapter {
1077 type A = super::Api;
1078
1079 unsafe fn open(
1080 &self,
1081 features: wgt::Features,
1082 _limits: &wgt::Limits,
1083 _memory_hints: &wgt::MemoryHints,
1084 ) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
1085 let gl = &self.shared.context.lock();
1086 unsafe { gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1) };
1087 unsafe { gl.pixel_store_i32(glow::PACK_ALIGNMENT, 1) };
1088 let main_vao =
1089 unsafe { gl.create_vertex_array() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
1090 unsafe { gl.bind_vertex_array(Some(main_vao)) };
1091
1092 let zero_buffer =
1093 unsafe { gl.create_buffer() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
1094 unsafe { gl.bind_buffer(glow::COPY_READ_BUFFER, Some(zero_buffer)) };
1095 let zeroes = vec![0u8; super::ZERO_BUFFER_SIZE];
1096 unsafe { gl.buffer_data_u8_slice(glow::COPY_READ_BUFFER, &zeroes, glow::STATIC_DRAW) };
1097
1098 let shader_clear_program = if self
1102 .shared
1103 .workarounds
1104 .contains(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR)
1105 {
1106 Some(unsafe {
1107 Self::create_shader_clear_program(gl, self.shared.es)
1108 .ok_or(crate::DeviceError::Lost)?
1109 })
1110 } else {
1111 None
1113 };
1114
1115 Ok(crate::OpenDevice {
1116 device: super::Device {
1117 shared: Arc::clone(&self.shared),
1118 main_vao,
1119 #[cfg(all(native, feature = "renderdoc"))]
1120 render_doc: Default::default(),
1121 counters: Default::default(),
1122 },
1123 queue: super::Queue {
1124 shared: Arc::clone(&self.shared),
1125 features,
1126 draw_fbo: unsafe { gl.create_framebuffer() }
1127 .map_err(|_| crate::DeviceError::OutOfMemory)?,
1128 copy_fbo: unsafe { gl.create_framebuffer() }
1129 .map_err(|_| crate::DeviceError::OutOfMemory)?,
1130 shader_clear_program,
1131 zero_buffer,
1132 temp_query_results: Mutex::new(Vec::new()),
1133 draw_buffer_count: AtomicU8::new(1),
1134 current_index_buffer: Mutex::new(None),
1135 },
1136 })
1137 }
1138
1139 unsafe fn texture_format_capabilities(
1140 &self,
1141 format: wgt::TextureFormat,
1142 ) -> crate::TextureFormatCapabilities {
1143 use crate::TextureFormatCapabilities as Tfc;
1144 use wgt::TextureFormat as Tf;
1145
1146 let sample_count = {
1147 let max_samples = self.shared.max_msaa_samples;
1148 if max_samples >= 16 {
1149 Tfc::MULTISAMPLE_X2
1150 | Tfc::MULTISAMPLE_X4
1151 | Tfc::MULTISAMPLE_X8
1152 | Tfc::MULTISAMPLE_X16
1153 } else if max_samples >= 8 {
1154 Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4 | Tfc::MULTISAMPLE_X8
1155 } else {
1156 Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4
1161 }
1162 };
1163
1164 let empty = Tfc::empty();
1169 let base = Tfc::COPY_SRC | Tfc::COPY_DST;
1170 let unfilterable = base | Tfc::SAMPLED;
1171 let depth = base | Tfc::SAMPLED | sample_count | Tfc::DEPTH_STENCIL_ATTACHMENT;
1172 let filterable = unfilterable | Tfc::SAMPLED_LINEAR;
1173 let renderable =
1174 unfilterable | Tfc::COLOR_ATTACHMENT | sample_count | Tfc::MULTISAMPLE_RESOLVE;
1175 let filterable_renderable = filterable | renderable | Tfc::COLOR_ATTACHMENT_BLEND;
1176 let storage =
1177 base | Tfc::STORAGE_READ_WRITE | Tfc::STORAGE_READ_ONLY | Tfc::STORAGE_WRITE_ONLY;
1178
1179 let feature_fn = |f, caps| {
1180 if self.shared.features.contains(f) {
1181 caps
1182 } else {
1183 empty
1184 }
1185 };
1186
1187 let bcn_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_BC, filterable);
1188 let etc2_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ETC2, filterable);
1189 let astc_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC, filterable);
1190 let astc_hdr_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR, filterable);
1191
1192 let private_caps_fn = |f, caps| {
1193 if self.shared.private_caps.contains(f) {
1194 caps
1195 } else {
1196 empty
1197 }
1198 };
1199
1200 let half_float_renderable = private_caps_fn(
1201 super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT,
1202 Tfc::COLOR_ATTACHMENT
1203 | Tfc::COLOR_ATTACHMENT_BLEND
1204 | sample_count
1205 | Tfc::MULTISAMPLE_RESOLVE,
1206 );
1207
1208 let float_renderable = private_caps_fn(
1209 super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
1210 Tfc::COLOR_ATTACHMENT
1211 | Tfc::COLOR_ATTACHMENT_BLEND
1212 | sample_count
1213 | Tfc::MULTISAMPLE_RESOLVE,
1214 );
1215
1216 let texture_float_linear = feature_fn(wgt::Features::FLOAT32_FILTERABLE, filterable);
1217
1218 let image_atomic = feature_fn(wgt::Features::TEXTURE_ATOMIC, Tfc::STORAGE_ATOMIC);
1219 let image_64_atomic = feature_fn(wgt::Features::TEXTURE_INT64_ATOMIC, Tfc::STORAGE_ATOMIC);
1220
1221 let norm16_unorm = private_caps_fn(
1226 super::PrivateCapabilities::TEXTURE_FORMAT_NORM16,
1227 filterable_renderable,
1228 );
1229 let norm16_snorm = if self
1230 .shared
1231 .private_caps
1232 .contains(super::PrivateCapabilities::TEXTURE_FORMAT_SNORM16_RENDERABLE)
1233 {
1234 norm16_unorm
1235 } else {
1236 private_caps_fn(
1237 super::PrivateCapabilities::TEXTURE_FORMAT_NORM16,
1238 filterable,
1239 )
1240 };
1241 let norm16_storage = private_caps_fn(
1242 super::PrivateCapabilities::TEXTURE_FORMAT_NORM16_STORAGE,
1243 storage,
1244 );
1245
1246 match format {
1247 Tf::R8Unorm => filterable_renderable,
1248 Tf::R8Snorm => filterable,
1249 Tf::R8Uint => renderable,
1250 Tf::R8Sint => renderable,
1251 Tf::R16Uint => renderable,
1252 Tf::R16Sint => renderable,
1253 Tf::R16Unorm => norm16_unorm | norm16_storage,
1254 Tf::R16Snorm => norm16_snorm | norm16_storage,
1255 Tf::R16Float => filterable | half_float_renderable,
1256 Tf::Rg8Unorm => filterable_renderable,
1257 Tf::Rg8Snorm => filterable,
1258 Tf::Rg8Uint => renderable,
1259 Tf::Rg8Sint => renderable,
1260 Tf::R32Uint => renderable | storage | image_atomic,
1261 Tf::R32Sint => renderable | storage | image_atomic,
1262 Tf::R32Float => unfilterable | storage | float_renderable | texture_float_linear,
1263 Tf::Rg16Uint => renderable,
1264 Tf::Rg16Sint => renderable,
1265 Tf::Rg16Unorm => norm16_unorm | norm16_storage,
1266 Tf::Rg16Snorm => norm16_snorm | norm16_storage,
1267 Tf::Rg16Float => filterable | half_float_renderable,
1268 Tf::Rgba8Unorm => filterable_renderable | storage,
1269 Tf::Rgba8UnormSrgb => filterable_renderable,
1270 Tf::Bgra8Unorm | Tf::Bgra8UnormSrgb => filterable_renderable,
1271 Tf::Rgba8Snorm => filterable | storage,
1272 Tf::Rgba8Uint => renderable | storage,
1273 Tf::Rgba8Sint => renderable | storage,
1274 Tf::Rgb10a2Uint => renderable,
1275 Tf::Rgb10a2Unorm => filterable_renderable,
1276 Tf::Rg11b10Ufloat => filterable | float_renderable,
1277 Tf::R64Uint => image_64_atomic,
1278 Tf::Rg32Uint => renderable,
1279 Tf::Rg32Sint => renderable,
1280 Tf::Rg32Float => unfilterable | float_renderable | texture_float_linear,
1281 Tf::Rgba16Uint => renderable | storage,
1282 Tf::Rgba16Sint => renderable | storage,
1283 Tf::Rgba16Unorm => norm16_unorm | norm16_storage,
1284 Tf::Rgba16Snorm => norm16_snorm | norm16_storage,
1285 Tf::Rgba16Float => filterable | storage | half_float_renderable,
1286 Tf::Rgba32Uint => renderable | storage,
1287 Tf::Rgba32Sint => renderable | storage,
1288 Tf::Rgba32Float => unfilterable | storage | float_renderable | texture_float_linear,
1289 Tf::Stencil8
1290 | Tf::Depth16Unorm
1291 | Tf::Depth32Float
1292 | Tf::Depth32FloatStencil8
1293 | Tf::Depth24Plus
1294 | Tf::Depth24PlusStencil8 => depth,
1295 Tf::NV12 => empty,
1296 Tf::P010 => empty,
1297 Tf::Rgb9e5Ufloat => filterable,
1298 Tf::Bc1RgbaUnorm
1299 | Tf::Bc1RgbaUnormSrgb
1300 | Tf::Bc2RgbaUnorm
1301 | Tf::Bc2RgbaUnormSrgb
1302 | Tf::Bc3RgbaUnorm
1303 | Tf::Bc3RgbaUnormSrgb
1304 | Tf::Bc4RUnorm
1305 | Tf::Bc4RSnorm
1306 | Tf::Bc5RgUnorm
1307 | Tf::Bc5RgSnorm
1308 | Tf::Bc6hRgbFloat
1309 | Tf::Bc6hRgbUfloat
1310 | Tf::Bc7RgbaUnorm
1311 | Tf::Bc7RgbaUnormSrgb => bcn_features,
1312 Tf::Etc2Rgb8Unorm
1313 | Tf::Etc2Rgb8UnormSrgb
1314 | Tf::Etc2Rgb8A1Unorm
1315 | Tf::Etc2Rgb8A1UnormSrgb
1316 | Tf::Etc2Rgba8Unorm
1317 | Tf::Etc2Rgba8UnormSrgb
1318 | Tf::EacR11Unorm
1319 | Tf::EacR11Snorm
1320 | Tf::EacRg11Unorm
1321 | Tf::EacRg11Snorm => etc2_features,
1322 Tf::Astc {
1323 block: _,
1324 channel: AstcChannel::Unorm | AstcChannel::UnormSrgb,
1325 } => astc_features,
1326 Tf::Astc {
1327 block: _,
1328 channel: AstcChannel::Hdr,
1329 } => astc_hdr_features,
1330 }
1331 }
1332
1333 unsafe fn surface_capabilities(
1334 &self,
1335 surface: &super::Surface,
1336 ) -> Option<crate::SurfaceCapabilities> {
1337 #[cfg(webgl)]
1338 if self.shared.context.webgl2_context != surface.webgl2_context {
1339 return None;
1340 }
1341
1342 if surface.presentable {
1343 let format_caps = |format: wgt::TextureFormat| wgt::SurfaceFormatCapabilities {
1346 format,
1347 color_spaces: wgt::SurfaceColorSpaces::SRGB,
1348 };
1349 let mut formats = vec![
1350 format_caps(wgt::TextureFormat::Rgba8Unorm),
1351 #[cfg(native)]
1352 format_caps(wgt::TextureFormat::Bgra8Unorm),
1353 ];
1354 if surface.supports_srgb() {
1355 formats.extend([
1356 format_caps(wgt::TextureFormat::Rgba8UnormSrgb),
1357 #[cfg(native)]
1358 format_caps(wgt::TextureFormat::Bgra8UnormSrgb),
1359 ])
1360 }
1361 if self
1362 .shared
1363 .private_caps
1364 .contains(super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT)
1365 {
1366 formats.push(format_caps(wgt::TextureFormat::Rgba16Float))
1367 }
1368
1369 Some(crate::SurfaceCapabilities {
1370 formats,
1371 present_modes: if cfg!(windows) {
1372 vec![wgt::PresentMode::Fifo, wgt::PresentMode::Immediate]
1373 } else {
1374 vec![wgt::PresentMode::Fifo] },
1376 composite_alpha_modes: vec![wgt::CompositeAlphaMode::Opaque], maximum_frame_latency: 2..=2, current_extent: None,
1379 usage: wgt::TextureUses::COLOR_TARGET,
1380 })
1381 } else {
1382 None
1383 }
1384 }
1385
1386 unsafe fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
1387 wgt::PresentationTimestamp::INVALID_TIMESTAMP
1388 }
1389
1390 fn get_ordered_buffer_usages(&self) -> wgt::BufferUses {
1391 wgt::BufferUses::INCLUSIVE | wgt::BufferUses::MAP_WRITE
1392 }
1393
1394 fn get_ordered_texture_usages(&self) -> wgt::TextureUses {
1396 wgt::TextureUses::INCLUSIVE
1397 | wgt::TextureUses::COLOR_TARGET
1398 | wgt::TextureUses::DEPTH_STENCIL_WRITE
1399 }
1400}
1401
1402impl super::AdapterShared {
1403 pub(super) unsafe fn get_buffer_sub_data(
1404 &self,
1405 gl: &glow::Context,
1406 target: u32,
1407 offset: i32,
1408 dst_data: &mut [u8],
1409 ) {
1410 if self
1411 .private_caps
1412 .contains(super::PrivateCapabilities::GET_BUFFER_SUB_DATA)
1413 {
1414 unsafe { gl.get_buffer_sub_data(target, offset, dst_data) };
1415 } else {
1416 log::error!("Fake map");
1417 let length = dst_data.len();
1418 if length != 0 {
1420 let buffer_mapping =
1421 unsafe { gl.map_buffer_range(target, offset, length as _, glow::MAP_READ_BIT) };
1422
1423 unsafe {
1424 core::ptr::copy_nonoverlapping(buffer_mapping, dst_data.as_mut_ptr(), length)
1425 };
1426
1427 unsafe { gl.unmap_buffer(target) };
1428 }
1429 }
1430 }
1431}
1432
1433#[cfg(send_sync)]
1434unsafe impl Sync for super::Adapter {}
1435#[cfg(send_sync)]
1436unsafe impl Send for super::Adapter {}
1437
1438#[cfg(test)]
1439mod tests {
1440 use super::super::Adapter;
1441
1442 #[test]
1443 fn test_version_parse() {
1444 Adapter::parse_version("1").unwrap_err();
1445 Adapter::parse_version("1.").unwrap_err();
1446 Adapter::parse_version("1 h3l1o. W0rld").unwrap_err();
1447 Adapter::parse_version("1. h3l1o. W0rld").unwrap_err();
1448 Adapter::parse_version("1.2.3").unwrap_err();
1449
1450 assert_eq!(Adapter::parse_version("OpenGL ES 3.1").unwrap(), (3, 1));
1451 assert_eq!(
1452 Adapter::parse_version("OpenGL ES 2.0 Google Nexus").unwrap(),
1453 (2, 0)
1454 );
1455 assert_eq!(Adapter::parse_version("GLSL ES 1.1").unwrap(), (1, 1));
1456 assert_eq!(
1457 Adapter::parse_version("OpenGL ES GLSL ES 3.20").unwrap(),
1458 (3, 2)
1459 );
1460 assert_eq!(
1461 Adapter::parse_version("WebGL 2.0 (OpenGL ES 3.0 Chromium)").unwrap(),
1463 (3, 0)
1464 );
1465 assert_eq!(
1466 Adapter::parse_version("WebGL GLSL ES 3.00 (OpenGL ES GLSL ES 3.0 Chromium)").unwrap(),
1467 (3, 0)
1468 );
1469 }
1470}