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 fn parse_version(mut src: &str) -> Result<(u8, u8), crate::InstanceError> {
23 let webgl_sig = "WebGL ";
24 let is_webgl = src.starts_with(webgl_sig);
28 if is_webgl {
29 let pos = src.rfind(webgl_sig).unwrap_or(0);
30 src = &src[pos + webgl_sig.len()..];
31 } else {
32 let es_sig = " ES ";
33 match src.rfind(es_sig) {
34 Some(pos) => {
35 src = &src[pos + es_sig.len()..];
36 }
37 None => {
38 return Err(crate::InstanceError::new(format!(
39 "OpenGL version {src:?} does not contain 'ES'"
40 )));
41 }
42 }
43 };
44
45 let glsl_es_sig = "GLSL ES ";
46 let is_glsl = match src.find(glsl_es_sig) {
47 Some(pos) => {
48 src = &src[pos + glsl_es_sig.len()..];
49 true
50 }
51 None => false,
52 };
53
54 Self::parse_full_version(src).map(|(major, minor)| {
55 (
56 if is_webgl && !is_glsl {
58 major + 1
59 } else {
60 major
61 },
62 minor,
63 )
64 })
65 }
66
67 pub(super) fn parse_full_version(src: &str) -> Result<(u8, u8), crate::InstanceError> {
83 let (version, _vendor_info) = match src.find(' ') {
84 Some(i) => (&src[..i], src[i + 1..].to_owned()),
85 None => (src, String::new()),
86 };
87
88 let mut it = version.split('.');
91 let major = it.next().and_then(|s| s.parse().ok());
92 let minor = it.next().and_then(|s| {
93 let trimmed = if s.starts_with('0') {
94 "0"
95 } else {
96 s.trim_end_matches('0')
97 };
98 trimmed.parse().ok()
99 });
100
101 match (major, minor) {
102 (Some(major), Some(minor)) => Ok((major, minor)),
103 _ => Err(crate::InstanceError::new(format!(
104 "unable to extract OpenGL version from {version:?}"
105 ))),
106 }
107 }
108
109 fn make_info(vendor_orig: String, renderer_orig: String, version: String) -> wgt::AdapterInfo {
110 let vendor = vendor_orig.to_lowercase();
111 let renderer = renderer_orig.to_lowercase();
112
113 let strings_that_imply_integrated = [
115 " xpress", "amd renoir",
117 "radeon hd 4200",
118 "radeon hd 4250",
119 "radeon hd 4290",
120 "radeon hd 4270",
121 "radeon hd 4225",
122 "radeon hd 3100",
123 "radeon hd 3200",
124 "radeon hd 3000",
125 "radeon hd 3300",
126 "radeon(tm) r4 graphics",
127 "radeon(tm) r5 graphics",
128 "radeon(tm) r6 graphics",
129 "radeon(tm) r7 graphics",
130 "radeon r7 graphics",
131 "nforce", "tegra", "shield", "igp",
135 "mali",
136 "intel",
137 "v3d",
138 "apple m", ];
140 let strings_that_imply_cpu = ["mesa offscreen", "swiftshader", "llvmpipe"];
141
142 let inferred_device_type = if vendor.contains("qualcomm")
144 || vendor.contains("intel")
145 || strings_that_imply_integrated
146 .iter()
147 .any(|&s| renderer.contains(s))
148 {
149 wgt::DeviceType::IntegratedGpu
150 } else if strings_that_imply_cpu.iter().any(|&s| renderer.contains(s)) {
151 wgt::DeviceType::Cpu
152 } else {
153 wgt::DeviceType::Other
159 };
160
161 let vendor_id = if vendor.contains("amd") {
163 db::amd::VENDOR
164 } else if vendor.contains("imgtec") {
165 db::imgtec::VENDOR
166 } else if vendor.contains("nvidia") {
167 db::nvidia::VENDOR
168 } else if vendor.contains("arm") {
169 db::arm::VENDOR
170 } else if vendor.contains("qualcomm") {
171 db::qualcomm::VENDOR
172 } else if vendor.contains("intel") {
173 db::intel::VENDOR
174 } else if vendor.contains("broadcom") {
175 db::broadcom::VENDOR
176 } else if vendor.contains("mesa") {
177 db::mesa::VENDOR
178 } else if vendor.contains("apple") {
179 db::apple::VENDOR
180 } else {
181 0
182 };
183
184 wgt::AdapterInfo {
185 name: renderer_orig,
186 vendor: vendor_id,
187 device: 0,
188 device_type: inferred_device_type,
189 driver: "".to_owned(),
190 device_pci_bus_id: String::new(),
191 driver_info: version,
192 backend: wgt::Backend::Gl,
193 subgroup_min_size: wgt::MINIMUM_SUBGROUP_MIN_SIZE,
194 subgroup_max_size: wgt::MAXIMUM_SUBGROUP_MAX_SIZE,
195 transient_saves_memory: false,
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 = supported((3, 1), (4, 3));
317 let supports_compute = supported((3, 1), (4, 3));
319 let supports_work_group_params = supports_compute;
320
321 let is_angle = renderer.contains("ANGLE");
323
324 let vertex_shader_storage_blocks = if supports_storage {
325 let value =
326 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_SHADER_STORAGE_BLOCKS) } as u32);
327
328 if value == 0 && extensions.contains("GL_ARB_shader_storage_buffer_object") {
329 let new = (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_SHADER_STORAGE_BLOCKS) }
332 as u32);
333 log::debug!("Max vertex shader storage blocks is zero, but GL_ARB_shader_storage_buffer_object is specified. Assuming the compute value {new}");
334 new
335 } else {
336 value
337 }
338 } else {
339 0
340 };
341 let fragment_shader_storage_blocks = if supports_storage {
342 (unsafe { gl.get_parameter_i32(glow::MAX_FRAGMENT_SHADER_STORAGE_BLOCKS) } as u32)
343 } else {
344 0
345 };
346 let vertex_shader_storage_textures = if supports_storage {
347 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_IMAGE_UNIFORMS) } as u32)
348 } else {
349 0
350 };
351 let fragment_shader_storage_textures = if supports_storage {
352 (unsafe { gl.get_parameter_i32(glow::MAX_FRAGMENT_IMAGE_UNIFORMS) } as u32)
353 } else {
354 0
355 };
356 let max_storage_block_size = if supports_storage {
357 (unsafe { gl.get_parameter_i32(glow::MAX_SHADER_STORAGE_BLOCK_SIZE) } as u32)
358 } else {
359 0
360 };
361 let max_element_index = unsafe { gl.get_parameter_i32(glow::MAX_ELEMENT_INDEX) } as u32;
362
363 let vertex_ssbo_false_zero =
369 vertex_shader_storage_blocks == 0 && vertex_shader_storage_textures != 0;
370 if vertex_ssbo_false_zero {
371 log::debug!("Max vertex shader SSBO == 0 and SSTO != 0. Interpreting as false zero.");
373 }
374
375 let max_storage_buffers_per_shader_stage = if vertex_shader_storage_blocks == 0 {
376 fragment_shader_storage_blocks
377 } else {
378 vertex_shader_storage_blocks.min(fragment_shader_storage_blocks)
379 };
380 let max_storage_textures_per_shader_stage = if vertex_shader_storage_textures == 0 {
381 fragment_shader_storage_textures
382 } else {
383 vertex_shader_storage_textures.min(fragment_shader_storage_textures)
384 };
385 let indirect_execution = supported((3, 1), (4, 3))
387 || (extensions.contains("GL_ARB_draw_indirect") && supports_compute);
388 let supports_cube_array = supported((3, 2), (4, 0))
389 || (supported((3, 1), (4, 0)) && extensions.contains("GL_EXT_texture_cube_map_array"));
390
391 let mut downlevel_flags = wgt::DownlevelFlags::empty()
392 | wgt::DownlevelFlags::NON_POWER_OF_TWO_MIPMAPPED_TEXTURES
393 | wgt::DownlevelFlags::COMPARISON_SAMPLERS
394 | wgt::DownlevelFlags::SHADER_F16_IN_F32;
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 mut features = wgt::Features::empty()
452 | wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES
453 | wgt::Features::CLEAR_TEXTURE
454 | wgt::Features::IMMEDIATES
455 | wgt::Features::DEPTH32FLOAT_STENCIL8;
456 features.set(
457 wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER | wgt::Features::ADDRESS_MODE_CLAMP_TO_ZERO,
458 extensions.contains("GL_EXT_texture_border_clamp")
459 || extensions.contains("GL_ARB_texture_border_clamp"),
460 );
461 features.set(
462 wgt::Features::DEPTH_CLIP_CONTROL,
463 extensions.contains("GL_EXT_depth_clamp") || extensions.contains("GL_ARB_depth_clamp"),
464 );
465 features.set(
466 wgt::Features::VERTEX_WRITABLE_STORAGE,
467 downlevel_flags.contains(wgt::DownlevelFlags::VERTEX_STORAGE)
468 && vertex_shader_storage_textures != 0,
469 );
470 features.set(
471 wgt::Features::MULTIVIEW,
472 extensions.contains("OVR_multiview2") || extensions.contains("GL_OVR_multiview2"),
473 );
474 features.set(
475 wgt::Features::DUAL_SOURCE_BLENDING,
476 extensions.contains("GL_EXT_blend_func_extended")
477 || extensions.contains("GL_ARB_blend_func_extended"),
478 );
479 features.set(
480 wgt::Features::CLIP_DISTANCES,
481 full_ver.is_some() || extensions.contains("GL_EXT_clip_cull_distance"),
482 );
483 features.set(
484 wgt::Features::SHADER_PRIMITIVE_INDEX,
485 supported((3, 2), (3, 2))
486 || extensions.contains("OES_geometry_shader")
487 || extensions.contains("GL_ARB_geometry_shader4"),
488 );
489 features.set(
490 wgt::Features::SHADER_EARLY_DEPTH_TEST,
491 supported((3, 1), (4, 2)) || extensions.contains("GL_ARB_shader_image_load_store"),
492 );
493 if extensions.contains("GL_ARB_timer_query") {
494 features.set(wgt::Features::TIMESTAMP_QUERY, true);
495 features.set(wgt::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS, true);
496 features.set(wgt::Features::TIMESTAMP_QUERY_INSIDE_PASSES, true);
497 }
498 let gl_bcn_exts = [
499 "GL_EXT_texture_compression_s3tc",
500 "GL_EXT_texture_compression_rgtc",
501 "GL_ARB_texture_compression_bptc",
502 ];
503 let gles_bcn_exts = [
504 "GL_EXT_texture_compression_s3tc_srgb",
505 "GL_EXT_texture_compression_rgtc",
506 "GL_EXT_texture_compression_bptc",
507 ];
508 let webgl_bcn_exts = [
509 "WEBGL_compressed_texture_s3tc",
510 "WEBGL_compressed_texture_s3tc_srgb",
511 "EXT_texture_compression_rgtc",
512 "EXT_texture_compression_bptc",
513 ];
514 let bcn_exts = if cfg!(any(webgl, Emscripten)) {
515 &webgl_bcn_exts[..]
516 } else if es_ver.is_some() {
517 &gles_bcn_exts[..]
518 } else {
519 &gl_bcn_exts[..]
520 };
521 features.set(
522 wgt::Features::TEXTURE_COMPRESSION_BC,
523 bcn_exts.iter().all(|&ext| extensions.contains(ext)),
524 );
525 features.set(
526 wgt::Features::TEXTURE_COMPRESSION_BC_SLICED_3D,
527 bcn_exts.iter().all(|&ext| extensions.contains(ext)), );
529 let has_etc = if cfg!(any(webgl, Emscripten)) {
530 extensions.contains("WEBGL_compressed_texture_etc")
531 } else {
532 es_ver.is_some() || extensions.contains("GL_ARB_ES3_compatibility")
533 };
534 features.set(wgt::Features::TEXTURE_COMPRESSION_ETC2, has_etc);
535
536 if extensions.contains("WEBGL_compressed_texture_astc")
538 || extensions.contains("GL_OES_texture_compression_astc")
539 {
540 #[cfg(webgl)]
541 {
542 if context
543 .glow_context
544 .compressed_texture_astc_supports_ldr_profile()
545 {
546 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC);
547 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D);
548 }
549 if context
550 .glow_context
551 .compressed_texture_astc_supports_hdr_profile()
552 {
553 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR);
554 }
555 }
556
557 #[cfg(any(native, Emscripten))]
558 {
559 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC);
560 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D);
561 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR);
562 }
563 } else {
564 features.set(
565 wgt::Features::TEXTURE_COMPRESSION_ASTC,
566 extensions.contains("GL_KHR_texture_compression_astc_ldr"),
567 );
568 features.set(
569 wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D,
570 extensions.contains("GL_KHR_texture_compression_astc_ldr")
571 && extensions.contains("GL_KHR_texture_compression_astc_sliced_3d"),
572 );
573 features.set(
574 wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR,
575 extensions.contains("GL_KHR_texture_compression_astc_hdr"),
576 );
577 }
578
579 features.set(
580 wgt::Features::FLOAT32_FILTERABLE,
581 extensions.contains("GL_ARB_color_buffer_float")
582 || extensions.contains("GL_EXT_color_buffer_float")
583 || extensions.contains("OES_texture_float_linear"),
584 );
585
586 if es_ver.is_none() {
587 features |= wgt::Features::POLYGON_MODE_LINE | wgt::Features::POLYGON_MODE_POINT;
588 }
589
590 let mut private_caps = super::PrivateCapabilities::empty();
593 private_caps.set(
594 super::PrivateCapabilities::BUFFER_ALLOCATION,
595 extensions.contains("GL_EXT_buffer_storage")
596 || extensions.contains("GL_ARB_buffer_storage"),
597 );
598 private_caps.set(
599 super::PrivateCapabilities::SHADER_BINDING_LAYOUT,
600 supports_compute,
601 );
602 private_caps.set(
603 super::PrivateCapabilities::SHADER_TEXTURE_SHADOW_LOD,
604 extensions.contains("GL_EXT_texture_shadow_lod"),
605 );
606 private_caps.set(
607 super::PrivateCapabilities::MEMORY_BARRIERS,
608 supported((3, 1), (4, 2)),
609 );
610 private_caps.set(
611 super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT,
612 supported((3, 1), (4, 3)) || extensions.contains("GL_ARB_vertex_attrib_binding"),
613 );
614 private_caps.set(
615 super::PrivateCapabilities::INDEX_BUFFER_ROLE_CHANGE,
616 !cfg!(any(webgl, Emscripten)),
617 );
618 private_caps.set(
619 super::PrivateCapabilities::GET_BUFFER_SUB_DATA,
620 cfg!(any(webgl, Emscripten)) || full_ver.is_some(),
621 );
622 let color_buffer_float = extensions.contains("GL_EXT_color_buffer_float")
623 || extensions.contains("GL_ARB_color_buffer_float")
624 || extensions.contains("EXT_color_buffer_float");
625 let color_buffer_half_float = extensions.contains("GL_EXT_color_buffer_half_float")
626 || extensions.contains("GL_ARB_half_float_pixel");
627 private_caps.set(
628 super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT,
629 color_buffer_half_float || color_buffer_float,
630 );
631 private_caps.set(
632 super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
633 color_buffer_float,
634 );
635 private_caps.set(super::PrivateCapabilities::QUERY_BUFFERS, query_buffers);
636 private_caps.set(super::PrivateCapabilities::QUERY_64BIT, full_ver.is_some());
637 private_caps.set(
638 super::PrivateCapabilities::TEXTURE_STORAGE,
639 supported((3, 0), (4, 2)),
640 );
641 let is_mali = renderer.to_lowercase().contains("mali");
642 let debug_fns_enabled = match backend_options.debug_fns {
643 wgt::GlDebugFns::Auto => gl.supports_debug() && !is_mali,
644 wgt::GlDebugFns::ForceEnabled => gl.supports_debug(),
645 wgt::GlDebugFns::Disabled => false,
646 };
647 private_caps.set(super::PrivateCapabilities::DEBUG_FNS, debug_fns_enabled);
648 private_caps.set(
649 super::PrivateCapabilities::INVALIDATE_FRAMEBUFFER,
650 supported((3, 0), (4, 3)),
651 );
652 if let Some(full_ver) = full_ver {
653 let supported =
654 full_ver >= (4, 2) && extensions.contains("GL_ARB_shader_draw_parameters");
655 private_caps.set(
656 super::PrivateCapabilities::FULLY_FEATURED_INSTANCING,
657 supported,
658 );
659 features.set(wgt::Features::INDIRECT_FIRST_INSTANCE, supported);
666 }
667
668 let max_texture_size = unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_SIZE) } as u32;
669 let max_texture_3d_size = unsafe { gl.get_parameter_i32(glow::MAX_3D_TEXTURE_SIZE) } as u32;
670
671 let min_uniform_buffer_offset_alignment =
672 (unsafe { gl.get_parameter_i32(glow::UNIFORM_BUFFER_OFFSET_ALIGNMENT) } as u32);
673 let min_storage_buffer_offset_alignment = if supports_storage {
674 (unsafe { gl.get_parameter_i32(glow::SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT) } as u32)
675 } else {
676 256
677 };
678 let max_uniform_buffers_per_shader_stage =
679 unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_UNIFORM_BLOCKS) }
680 .min(unsafe { gl.get_parameter_i32(glow::MAX_FRAGMENT_UNIFORM_BLOCKS) })
681 as u32;
682
683 let max_compute_workgroups_per_dimension = if supports_work_group_params {
684 unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 0) }
685 .min(unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 1) })
686 .min(unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 2) })
687 as u32
688 } else {
689 0
690 };
691
692 let max_color_attachments = unsafe {
693 gl.get_parameter_i32(glow::MAX_COLOR_ATTACHMENTS)
694 .min(gl.get_parameter_i32(glow::MAX_DRAW_BUFFERS)) as u32
695 };
696
697 let max_color_attachment_bytes_per_sample =
699 max_color_attachments * wgt::TextureFormat::MAX_TARGET_PIXEL_BYTE_COST;
700
701 let limits = crate::auxil::apply_hal_limits(wgt::Limits {
702 max_texture_dimension_1d: max_texture_size,
703 max_texture_dimension_2d: max_texture_size,
704 max_texture_dimension_3d: max_texture_3d_size,
705 max_texture_array_layers: unsafe {
706 gl.get_parameter_i32(glow::MAX_ARRAY_TEXTURE_LAYERS)
707 } as u32,
708 max_bind_groups: crate::MAX_BIND_GROUPS as u32,
709 max_bindings_per_bind_group: 65535,
710 max_dynamic_uniform_buffers_per_pipeline_layout: max_uniform_buffers_per_shader_stage,
711 max_dynamic_storage_buffers_per_pipeline_layout: max_storage_buffers_per_shader_stage,
712 max_sampled_textures_per_shader_stage: super::MAX_TEXTURE_SLOTS as u32,
713 max_samplers_per_shader_stage: super::MAX_SAMPLERS as u32,
714 max_storage_buffers_per_shader_stage,
715 max_storage_textures_per_shader_stage,
716 max_uniform_buffers_per_shader_stage,
717 max_binding_array_elements_per_shader_stage: 0,
718 max_binding_array_sampler_elements_per_shader_stage: 0,
719 max_uniform_buffer_binding_size: unsafe {
720 gl.get_parameter_i32(glow::MAX_UNIFORM_BLOCK_SIZE)
721 } as u32,
722 max_storage_buffer_binding_size: if supports_storage {
723 unsafe { gl.get_parameter_i32(glow::MAX_SHADER_STORAGE_BLOCK_SIZE) }
724 } else {
725 0
726 } as u32,
727 max_vertex_buffers: if private_caps
728 .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
729 {
730 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_BINDINGS) } as u32)
731 } else {
732 16 },
734 max_vertex_attributes: (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIBS) }
735 as u32)
736 .min(super::MAX_VERTEX_ATTRIBUTES as u32),
737 max_vertex_buffer_array_stride: if private_caps
738 .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
739 {
740 if let Some(full_ver) = full_ver {
741 if full_ver >= (4, 4) {
742 let value =
744 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) })
745 as u32;
746
747 if value == 0 {
748 log::debug!("Max vertex attribute stride is 0. Assuming it is the OpenGL minimum spec 2048");
752 2048
753 } else {
754 value
755 }
756 } else {
757 log::debug!("Max vertex attribute stride unknown. Assuming it is the OpenGL minimum spec 2048");
758 2048
759 }
760 } else {
761 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) }) as u32
762 }
763 } else {
764 !0
765 },
766 max_immediate_size: super::MAX_IMMEDIATES as u32 * 4,
767 min_uniform_buffer_offset_alignment,
768 min_storage_buffer_offset_alignment,
769 max_inter_stage_shader_variables: {
770 let max_varying_components =
774 unsafe { gl.get_parameter_i32(glow::MAX_VARYING_COMPONENTS) } as u32;
775 if max_varying_components == 0 {
776 15
778 } else {
779 max_varying_components / 4
780 }
781 },
782 max_color_attachments,
783 max_color_attachment_bytes_per_sample,
784 max_compute_workgroup_storage_size: if supports_work_group_params {
785 (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_SHARED_MEMORY_SIZE) } as u32)
786 } else {
787 0
788 },
789 max_compute_invocations_per_workgroup: if supports_work_group_params {
790 (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_WORK_GROUP_INVOCATIONS) } as u32)
791 } else {
792 0
793 },
794 max_compute_workgroup_size_x: if supports_work_group_params {
795 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 0) }
796 as u32)
797 } else {
798 0
799 },
800 max_compute_workgroup_size_y: if supports_work_group_params {
801 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 1) }
802 as u32)
803 } else {
804 0
805 },
806 max_compute_workgroup_size_z: if supports_work_group_params {
807 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 2) }
808 as u32)
809 } else {
810 0
811 },
812 max_compute_workgroups_per_dimension,
813 max_buffer_size: i32::MAX as u64,
814 max_non_sampler_bindings: u32::MAX,
815
816 max_task_mesh_workgroup_total_count: 0,
817 max_task_mesh_workgroups_per_dimension: 0,
818 max_task_invocations_per_workgroup: 0,
819 max_task_invocations_per_dimension: 0,
820 max_mesh_invocations_per_workgroup: 0,
821 max_mesh_invocations_per_dimension: 0,
822 max_task_payload_size: 0,
823 max_mesh_output_vertices: 0,
824 max_mesh_output_primitives: 0,
825 max_mesh_output_layers: 0,
826 max_mesh_multiview_view_count: 0,
827
828 max_blas_primitive_count: 0,
829 max_blas_geometry_count: 0,
830 max_tlas_instance_count: 0,
831 max_acceleration_structures_per_shader_stage: 0,
832
833 max_multiview_view_count: 0,
834 });
835
836 let mut workarounds = super::Workarounds::empty();
837
838 workarounds.set(
839 super::Workarounds::EMULATE_BUFFER_MAP,
840 cfg!(any(webgl, Emscripten)),
841 );
842
843 let r = renderer.to_lowercase();
844 if context.is_owned()
847 && r.contains("mesa")
848 && r.contains("intel")
849 && r.split(&[' ', '(', ')'][..])
850 .any(|substr| substr.len() == 3 && substr.chars().nth(2) == Some('l'))
851 {
852 log::debug!(
853 "Detected skylake derivative running on mesa i915. Clears to srgb textures will \
854 use manual shader clears."
855 );
856 workarounds.set(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR, true);
857 }
858
859 let downlevel_defaults = wgt::DownlevelLimits {};
860 let max_samples = unsafe { gl.get_parameter_i32(glow::MAX_SAMPLES) };
861
862 #[cfg_attr(target_arch = "wasm32", allow(dropping_references))]
866 drop(gl);
867
868 Some(crate::ExposedAdapter {
869 adapter: super::Adapter {
870 shared: Arc::new(super::AdapterShared {
871 context,
872 private_caps,
873 workarounds,
874 features,
875 limits: limits.clone(),
876 options: backend_options,
877 shading_language_version,
878 next_shader_id: Default::default(),
879 program_cache: Default::default(),
880 es: es_ver.is_some(),
881 max_msaa_samples: max_samples,
882 }),
883 },
884 info: Self::make_info(vendor, renderer, version),
885 features,
886 capabilities: crate::Capabilities {
887 limits,
888 downlevel: wgt::DownlevelCapabilities {
889 flags: downlevel_flags,
890 limits: downlevel_defaults,
891 shader_model: wgt::ShaderModel::Sm5,
892 },
893 alignments: crate::Alignments {
894 buffer_copy_offset: wgt::BufferSize::new(4).unwrap(),
895 buffer_copy_pitch: wgt::BufferSize::new(4).unwrap(),
896 uniform_bounds_check_alignment: wgt::BufferSize::new(1).unwrap(),
906 raw_tlas_instance_size: 0,
907 ray_tracing_scratch_buffer_alignment: 0,
908 },
909 cooperative_matrix_properties: Vec::new(),
910 },
911 })
912 }
913
914 unsafe fn compile_shader(
915 source: &str,
916 gl: &glow::Context,
917 shader_type: u32,
918 es: bool,
919 ) -> Option<glow::Shader> {
920 let source = if es {
921 format!("#version 300 es\nprecision lowp float;\n{source}")
922 } else {
923 let version = gl.version();
924 if version.major == 3 && version.minor == 0 {
925 format!("#version 130\n{source}")
927 } else {
928 format!("#version 140\n{source}")
930 }
931 };
932 let shader = unsafe { gl.create_shader(shader_type) }.expect("Could not create shader");
933 unsafe { gl.shader_source(shader, &source) };
934 unsafe { gl.compile_shader(shader) };
935
936 if !unsafe { gl.get_shader_compile_status(shader) } {
937 let msg = unsafe { gl.get_shader_info_log(shader) };
938 if !msg.is_empty() {
939 log::error!("\tShader compile error: {msg}");
940 }
941 unsafe { gl.delete_shader(shader) };
942 None
943 } else {
944 Some(shader)
945 }
946 }
947
948 unsafe fn create_shader_clear_program(
949 gl: &glow::Context,
950 es: bool,
951 ) -> Option<ShaderClearProgram> {
952 let program = unsafe { gl.create_program() }.expect("Could not create shader program");
953 let vertex = unsafe {
954 Self::compile_shader(
955 include_str!("./shaders/clear.vert"),
956 gl,
957 glow::VERTEX_SHADER,
958 es,
959 )?
960 };
961 let fragment = unsafe {
962 Self::compile_shader(
963 include_str!("./shaders/clear.frag"),
964 gl,
965 glow::FRAGMENT_SHADER,
966 es,
967 )?
968 };
969 unsafe { gl.attach_shader(program, vertex) };
970 unsafe { gl.attach_shader(program, fragment) };
971 unsafe { gl.link_program(program) };
972
973 let linked_ok = unsafe { gl.get_program_link_status(program) };
974 let msg = unsafe { gl.get_program_info_log(program) };
975 if !msg.is_empty() {
976 log::error!("Shader link error: {msg}");
977 }
978 if !linked_ok {
979 return None;
980 }
981
982 let color_uniform_location = unsafe { gl.get_uniform_location(program, "color") }
983 .expect("Could not find color uniform in shader clear shader");
984 unsafe { gl.delete_shader(vertex) };
985 unsafe { gl.delete_shader(fragment) };
986
987 Some(ShaderClearProgram {
988 program,
989 color_uniform_location,
990 })
991 }
992}
993
994impl crate::Adapter for super::Adapter {
995 type A = super::Api;
996
997 unsafe fn open(
998 &self,
999 features: wgt::Features,
1000 _limits: &wgt::Limits,
1001 _memory_hints: &wgt::MemoryHints,
1002 ) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
1003 let gl = &self.shared.context.lock();
1004 unsafe { gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1) };
1005 unsafe { gl.pixel_store_i32(glow::PACK_ALIGNMENT, 1) };
1006 let main_vao =
1007 unsafe { gl.create_vertex_array() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
1008 unsafe { gl.bind_vertex_array(Some(main_vao)) };
1009
1010 let zero_buffer =
1011 unsafe { gl.create_buffer() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
1012 unsafe { gl.bind_buffer(glow::COPY_READ_BUFFER, Some(zero_buffer)) };
1013 let zeroes = vec![0u8; super::ZERO_BUFFER_SIZE];
1014 unsafe { gl.buffer_data_u8_slice(glow::COPY_READ_BUFFER, &zeroes, glow::STATIC_DRAW) };
1015
1016 let shader_clear_program = if self
1020 .shared
1021 .workarounds
1022 .contains(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR)
1023 {
1024 Some(unsafe {
1025 Self::create_shader_clear_program(gl, self.shared.es)
1026 .ok_or(crate::DeviceError::Lost)?
1027 })
1028 } else {
1029 None
1031 };
1032
1033 Ok(crate::OpenDevice {
1034 device: super::Device {
1035 shared: Arc::clone(&self.shared),
1036 main_vao,
1037 #[cfg(all(native, feature = "renderdoc"))]
1038 render_doc: Default::default(),
1039 counters: Default::default(),
1040 },
1041 queue: super::Queue {
1042 shared: Arc::clone(&self.shared),
1043 features,
1044 draw_fbo: unsafe { gl.create_framebuffer() }
1045 .map_err(|_| crate::DeviceError::OutOfMemory)?,
1046 copy_fbo: unsafe { gl.create_framebuffer() }
1047 .map_err(|_| crate::DeviceError::OutOfMemory)?,
1048 shader_clear_program,
1049 zero_buffer,
1050 temp_query_results: Mutex::new(Vec::new()),
1051 draw_buffer_count: AtomicU8::new(1),
1052 current_index_buffer: Mutex::new(None),
1053 },
1054 })
1055 }
1056
1057 unsafe fn texture_format_capabilities(
1058 &self,
1059 format: wgt::TextureFormat,
1060 ) -> crate::TextureFormatCapabilities {
1061 use crate::TextureFormatCapabilities as Tfc;
1062 use wgt::TextureFormat as Tf;
1063
1064 let sample_count = {
1065 let max_samples = self.shared.max_msaa_samples;
1066 if max_samples >= 16 {
1067 Tfc::MULTISAMPLE_X2
1068 | Tfc::MULTISAMPLE_X4
1069 | Tfc::MULTISAMPLE_X8
1070 | Tfc::MULTISAMPLE_X16
1071 } else if max_samples >= 8 {
1072 Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4 | Tfc::MULTISAMPLE_X8
1073 } else {
1074 Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4
1079 }
1080 };
1081
1082 let empty = Tfc::empty();
1087 let base = Tfc::COPY_SRC | Tfc::COPY_DST;
1088 let unfilterable = base | Tfc::SAMPLED;
1089 let depth = base | Tfc::SAMPLED | sample_count | Tfc::DEPTH_STENCIL_ATTACHMENT;
1090 let filterable = unfilterable | Tfc::SAMPLED_LINEAR;
1091 let renderable =
1092 unfilterable | Tfc::COLOR_ATTACHMENT | sample_count | Tfc::MULTISAMPLE_RESOLVE;
1093 let filterable_renderable = filterable | renderable | Tfc::COLOR_ATTACHMENT_BLEND;
1094 let storage =
1095 base | Tfc::STORAGE_READ_WRITE | Tfc::STORAGE_READ_ONLY | Tfc::STORAGE_WRITE_ONLY;
1096
1097 let feature_fn = |f, caps| {
1098 if self.shared.features.contains(f) {
1099 caps
1100 } else {
1101 empty
1102 }
1103 };
1104
1105 let bcn_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_BC, filterable);
1106 let etc2_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ETC2, filterable);
1107 let astc_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC, filterable);
1108 let astc_hdr_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR, filterable);
1109
1110 let private_caps_fn = |f, caps| {
1111 if self.shared.private_caps.contains(f) {
1112 caps
1113 } else {
1114 empty
1115 }
1116 };
1117
1118 let half_float_renderable = private_caps_fn(
1119 super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT,
1120 Tfc::COLOR_ATTACHMENT
1121 | Tfc::COLOR_ATTACHMENT_BLEND
1122 | sample_count
1123 | Tfc::MULTISAMPLE_RESOLVE,
1124 );
1125
1126 let float_renderable = private_caps_fn(
1127 super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
1128 Tfc::COLOR_ATTACHMENT
1129 | Tfc::COLOR_ATTACHMENT_BLEND
1130 | sample_count
1131 | Tfc::MULTISAMPLE_RESOLVE,
1132 );
1133
1134 let texture_float_linear = feature_fn(wgt::Features::FLOAT32_FILTERABLE, filterable);
1135
1136 let image_atomic = feature_fn(wgt::Features::TEXTURE_ATOMIC, Tfc::STORAGE_ATOMIC);
1137 let image_64_atomic = feature_fn(wgt::Features::TEXTURE_INT64_ATOMIC, Tfc::STORAGE_ATOMIC);
1138
1139 match format {
1140 Tf::R8Unorm => filterable_renderable,
1141 Tf::R8Snorm => filterable,
1142 Tf::R8Uint => renderable,
1143 Tf::R8Sint => renderable,
1144 Tf::R16Uint => renderable,
1145 Tf::R16Sint => renderable,
1146 Tf::R16Unorm => empty,
1147 Tf::R16Snorm => empty,
1148 Tf::R16Float => filterable | half_float_renderable,
1149 Tf::Rg8Unorm => filterable_renderable,
1150 Tf::Rg8Snorm => filterable,
1151 Tf::Rg8Uint => renderable,
1152 Tf::Rg8Sint => renderable,
1153 Tf::R32Uint => renderable | storage | image_atomic,
1154 Tf::R32Sint => renderable | storage | image_atomic,
1155 Tf::R32Float => unfilterable | storage | float_renderable | texture_float_linear,
1156 Tf::Rg16Uint => renderable,
1157 Tf::Rg16Sint => renderable,
1158 Tf::Rg16Unorm => empty,
1159 Tf::Rg16Snorm => empty,
1160 Tf::Rg16Float => filterable | half_float_renderable,
1161 Tf::Rgba8Unorm => filterable_renderable | storage,
1162 Tf::Rgba8UnormSrgb => filterable_renderable,
1163 Tf::Bgra8Unorm | Tf::Bgra8UnormSrgb => filterable_renderable,
1164 Tf::Rgba8Snorm => filterable | storage,
1165 Tf::Rgba8Uint => renderable | storage,
1166 Tf::Rgba8Sint => renderable | storage,
1167 Tf::Rgb10a2Uint => renderable,
1168 Tf::Rgb10a2Unorm => filterable_renderable,
1169 Tf::Rg11b10Ufloat => filterable | float_renderable,
1170 Tf::R64Uint => image_64_atomic,
1171 Tf::Rg32Uint => renderable,
1172 Tf::Rg32Sint => renderable,
1173 Tf::Rg32Float => unfilterable | float_renderable | texture_float_linear,
1174 Tf::Rgba16Uint => renderable | storage,
1175 Tf::Rgba16Sint => renderable | storage,
1176 Tf::Rgba16Unorm => empty,
1177 Tf::Rgba16Snorm => empty,
1178 Tf::Rgba16Float => filterable | storage | half_float_renderable,
1179 Tf::Rgba32Uint => renderable | storage,
1180 Tf::Rgba32Sint => renderable | storage,
1181 Tf::Rgba32Float => unfilterable | storage | float_renderable | texture_float_linear,
1182 Tf::Stencil8
1183 | Tf::Depth16Unorm
1184 | Tf::Depth32Float
1185 | Tf::Depth32FloatStencil8
1186 | Tf::Depth24Plus
1187 | Tf::Depth24PlusStencil8 => depth,
1188 Tf::NV12 => empty,
1189 Tf::P010 => empty,
1190 Tf::Rgb9e5Ufloat => filterable,
1191 Tf::Bc1RgbaUnorm
1192 | Tf::Bc1RgbaUnormSrgb
1193 | Tf::Bc2RgbaUnorm
1194 | Tf::Bc2RgbaUnormSrgb
1195 | Tf::Bc3RgbaUnorm
1196 | Tf::Bc3RgbaUnormSrgb
1197 | Tf::Bc4RUnorm
1198 | Tf::Bc4RSnorm
1199 | Tf::Bc5RgUnorm
1200 | Tf::Bc5RgSnorm
1201 | Tf::Bc6hRgbFloat
1202 | Tf::Bc6hRgbUfloat
1203 | Tf::Bc7RgbaUnorm
1204 | Tf::Bc7RgbaUnormSrgb => bcn_features,
1205 Tf::Etc2Rgb8Unorm
1206 | Tf::Etc2Rgb8UnormSrgb
1207 | Tf::Etc2Rgb8A1Unorm
1208 | Tf::Etc2Rgb8A1UnormSrgb
1209 | Tf::Etc2Rgba8Unorm
1210 | Tf::Etc2Rgba8UnormSrgb
1211 | Tf::EacR11Unorm
1212 | Tf::EacR11Snorm
1213 | Tf::EacRg11Unorm
1214 | Tf::EacRg11Snorm => etc2_features,
1215 Tf::Astc {
1216 block: _,
1217 channel: AstcChannel::Unorm | AstcChannel::UnormSrgb,
1218 } => astc_features,
1219 Tf::Astc {
1220 block: _,
1221 channel: AstcChannel::Hdr,
1222 } => astc_hdr_features,
1223 }
1224 }
1225
1226 unsafe fn surface_capabilities(
1227 &self,
1228 surface: &super::Surface,
1229 ) -> Option<crate::SurfaceCapabilities> {
1230 #[cfg(webgl)]
1231 if self.shared.context.webgl2_context != surface.webgl2_context {
1232 return None;
1233 }
1234
1235 if surface.presentable {
1236 let mut formats = vec![
1237 wgt::TextureFormat::Rgba8Unorm,
1238 #[cfg(native)]
1239 wgt::TextureFormat::Bgra8Unorm,
1240 ];
1241 if surface.supports_srgb() {
1242 formats.extend([
1243 wgt::TextureFormat::Rgba8UnormSrgb,
1244 #[cfg(native)]
1245 wgt::TextureFormat::Bgra8UnormSrgb,
1246 ])
1247 }
1248 if self
1249 .shared
1250 .private_caps
1251 .contains(super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT)
1252 {
1253 formats.push(wgt::TextureFormat::Rgba16Float)
1254 }
1255
1256 Some(crate::SurfaceCapabilities {
1257 formats,
1258 present_modes: if cfg!(windows) {
1259 vec![wgt::PresentMode::Fifo, wgt::PresentMode::Immediate]
1260 } else {
1261 vec![wgt::PresentMode::Fifo] },
1263 composite_alpha_modes: vec![wgt::CompositeAlphaMode::Opaque], maximum_frame_latency: 2..=2, current_extent: None,
1266 usage: wgt::TextureUses::COLOR_TARGET,
1267 })
1268 } else {
1269 None
1270 }
1271 }
1272
1273 unsafe fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
1274 wgt::PresentationTimestamp::INVALID_TIMESTAMP
1275 }
1276}
1277
1278impl super::AdapterShared {
1279 pub(super) unsafe fn get_buffer_sub_data(
1280 &self,
1281 gl: &glow::Context,
1282 target: u32,
1283 offset: i32,
1284 dst_data: &mut [u8],
1285 ) {
1286 if self
1287 .private_caps
1288 .contains(super::PrivateCapabilities::GET_BUFFER_SUB_DATA)
1289 {
1290 unsafe { gl.get_buffer_sub_data(target, offset, dst_data) };
1291 } else {
1292 log::error!("Fake map");
1293 let length = dst_data.len();
1294 let buffer_mapping =
1295 unsafe { gl.map_buffer_range(target, offset, length as _, glow::MAP_READ_BIT) };
1296
1297 unsafe {
1298 core::ptr::copy_nonoverlapping(buffer_mapping, dst_data.as_mut_ptr(), length)
1299 };
1300
1301 unsafe { gl.unmap_buffer(target) };
1302 }
1303 }
1304}
1305
1306#[cfg(send_sync)]
1307unsafe impl Sync for super::Adapter {}
1308#[cfg(send_sync)]
1309unsafe impl Send for super::Adapter {}
1310
1311#[cfg(test)]
1312mod tests {
1313 use super::super::Adapter;
1314
1315 #[test]
1316 fn test_version_parse() {
1317 Adapter::parse_version("1").unwrap_err();
1318 Adapter::parse_version("1.").unwrap_err();
1319 Adapter::parse_version("1 h3l1o. W0rld").unwrap_err();
1320 Adapter::parse_version("1. h3l1o. W0rld").unwrap_err();
1321 Adapter::parse_version("1.2.3").unwrap_err();
1322
1323 assert_eq!(Adapter::parse_version("OpenGL ES 3.1").unwrap(), (3, 1));
1324 assert_eq!(
1325 Adapter::parse_version("OpenGL ES 2.0 Google Nexus").unwrap(),
1326 (2, 0)
1327 );
1328 assert_eq!(Adapter::parse_version("GLSL ES 1.1").unwrap(), (1, 1));
1329 assert_eq!(
1330 Adapter::parse_version("OpenGL ES GLSL ES 3.20").unwrap(),
1331 (3, 2)
1332 );
1333 assert_eq!(
1334 Adapter::parse_version("WebGL 2.0 (OpenGL ES 3.0 Chromium)").unwrap(),
1336 (3, 0)
1337 );
1338 assert_eq!(
1339 Adapter::parse_version("WebGL GLSL ES 3.00 (OpenGL ES GLSL ES 3.0 Chromium)").unwrap(),
1340 (3, 0)
1341 );
1342 }
1343}