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
389 let mut downlevel_flags = wgt::DownlevelFlags::empty()
390 | wgt::DownlevelFlags::NON_POWER_OF_TWO_MIPMAPPED_TEXTURES
391 | wgt::DownlevelFlags::CUBE_ARRAY_TEXTURES
392 | wgt::DownlevelFlags::COMPARISON_SAMPLERS
393 | wgt::DownlevelFlags::SHADER_F16_IN_F32;
394 downlevel_flags.set(wgt::DownlevelFlags::COMPUTE_SHADERS, supports_compute);
395 downlevel_flags.set(
396 wgt::DownlevelFlags::FRAGMENT_WRITABLE_STORAGE,
397 max_storage_block_size != 0,
398 );
399 downlevel_flags.set(wgt::DownlevelFlags::INDIRECT_EXECUTION, indirect_execution);
400 downlevel_flags.set(wgt::DownlevelFlags::BASE_VERTEX, supported((3, 2), (3, 2)));
401 downlevel_flags.set(
402 wgt::DownlevelFlags::INDEPENDENT_BLEND,
403 supported((3, 2), (4, 0)) || extensions.contains("GL_EXT_draw_buffers_indexed"),
404 );
405 downlevel_flags.set(
406 wgt::DownlevelFlags::VERTEX_STORAGE,
407 max_storage_block_size != 0
408 && max_storage_buffers_per_shader_stage != 0
409 && (vertex_shader_storage_blocks != 0 || vertex_ssbo_false_zero),
410 );
411 downlevel_flags.set(wgt::DownlevelFlags::FRAGMENT_STORAGE, supports_storage);
412 if extensions.contains("EXT_texture_filter_anisotropic")
413 || extensions.contains("GL_EXT_texture_filter_anisotropic")
414 {
415 let max_aniso =
416 unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_MAX_ANISOTROPY_EXT) } as u32;
417 downlevel_flags.set(wgt::DownlevelFlags::ANISOTROPIC_FILTERING, max_aniso >= 16);
418 }
419 downlevel_flags.set(
420 wgt::DownlevelFlags::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED,
421 !(cfg!(any(webgl, Emscripten)) || is_angle),
422 );
423 downlevel_flags.set(
425 wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER,
426 !cfg!(any(webgl, Emscripten)),
427 );
428 downlevel_flags.set(
429 wgt::DownlevelFlags::UNRESTRICTED_EXTERNAL_TEXTURE_COPIES,
430 !cfg!(any(webgl, Emscripten)),
431 );
432 downlevel_flags.set(
433 wgt::DownlevelFlags::FULL_DRAW_INDEX_UINT32,
434 max_element_index == u32::MAX,
435 );
436 downlevel_flags.set(
437 wgt::DownlevelFlags::MULTISAMPLED_SHADING,
438 supported((3, 2), (4, 0)) || extensions.contains("OES_sample_variables"),
439 );
440 let query_buffers = extensions.contains("GL_ARB_query_buffer_object")
441 || extensions.contains("GL_AMD_query_buffer_object");
442 if query_buffers {
443 downlevel_flags.set(wgt::DownlevelFlags::NONBLOCKING_QUERY_RESOLVE, true);
444 }
445
446 let mut features = wgt::Features::empty()
447 | wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES
448 | wgt::Features::CLEAR_TEXTURE
449 | wgt::Features::IMMEDIATES
450 | wgt::Features::DEPTH32FLOAT_STENCIL8;
451 features.set(
452 wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER | wgt::Features::ADDRESS_MODE_CLAMP_TO_ZERO,
453 extensions.contains("GL_EXT_texture_border_clamp")
454 || extensions.contains("GL_ARB_texture_border_clamp"),
455 );
456 features.set(
457 wgt::Features::DEPTH_CLIP_CONTROL,
458 extensions.contains("GL_EXT_depth_clamp") || extensions.contains("GL_ARB_depth_clamp"),
459 );
460 features.set(
461 wgt::Features::VERTEX_WRITABLE_STORAGE,
462 downlevel_flags.contains(wgt::DownlevelFlags::VERTEX_STORAGE)
463 && vertex_shader_storage_textures != 0,
464 );
465 features.set(
466 wgt::Features::MULTIVIEW,
467 extensions.contains("OVR_multiview2") || extensions.contains("GL_OVR_multiview2"),
468 );
469 features.set(
470 wgt::Features::DUAL_SOURCE_BLENDING,
471 extensions.contains("GL_EXT_blend_func_extended")
472 || extensions.contains("GL_ARB_blend_func_extended"),
473 );
474 features.set(
475 wgt::Features::CLIP_DISTANCES,
476 full_ver.is_some() || extensions.contains("GL_EXT_clip_cull_distance"),
477 );
478 features.set(
479 wgt::Features::SHADER_PRIMITIVE_INDEX,
480 supported((3, 2), (3, 2))
481 || extensions.contains("OES_geometry_shader")
482 || extensions.contains("GL_ARB_geometry_shader4"),
483 );
484 features.set(
485 wgt::Features::SHADER_EARLY_DEPTH_TEST,
486 supported((3, 1), (4, 2)) || extensions.contains("GL_ARB_shader_image_load_store"),
487 );
488 if extensions.contains("GL_ARB_timer_query") {
489 features.set(wgt::Features::TIMESTAMP_QUERY, true);
490 features.set(wgt::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS, true);
491 features.set(wgt::Features::TIMESTAMP_QUERY_INSIDE_PASSES, true);
492 }
493 let gl_bcn_exts = [
494 "GL_EXT_texture_compression_s3tc",
495 "GL_EXT_texture_compression_rgtc",
496 "GL_ARB_texture_compression_bptc",
497 ];
498 let gles_bcn_exts = [
499 "GL_EXT_texture_compression_s3tc_srgb",
500 "GL_EXT_texture_compression_rgtc",
501 "GL_EXT_texture_compression_bptc",
502 ];
503 let webgl_bcn_exts = [
504 "WEBGL_compressed_texture_s3tc",
505 "WEBGL_compressed_texture_s3tc_srgb",
506 "EXT_texture_compression_rgtc",
507 "EXT_texture_compression_bptc",
508 ];
509 let bcn_exts = if cfg!(any(webgl, Emscripten)) {
510 &webgl_bcn_exts[..]
511 } else if es_ver.is_some() {
512 &gles_bcn_exts[..]
513 } else {
514 &gl_bcn_exts[..]
515 };
516 features.set(
517 wgt::Features::TEXTURE_COMPRESSION_BC,
518 bcn_exts.iter().all(|&ext| extensions.contains(ext)),
519 );
520 features.set(
521 wgt::Features::TEXTURE_COMPRESSION_BC_SLICED_3D,
522 bcn_exts.iter().all(|&ext| extensions.contains(ext)), );
524 let has_etc = if cfg!(any(webgl, Emscripten)) {
525 extensions.contains("WEBGL_compressed_texture_etc")
526 } else {
527 es_ver.is_some() || extensions.contains("GL_ARB_ES3_compatibility")
528 };
529 features.set(wgt::Features::TEXTURE_COMPRESSION_ETC2, has_etc);
530
531 if extensions.contains("WEBGL_compressed_texture_astc")
533 || extensions.contains("GL_OES_texture_compression_astc")
534 {
535 #[cfg(webgl)]
536 {
537 if context
538 .glow_context
539 .compressed_texture_astc_supports_ldr_profile()
540 {
541 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC);
542 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D);
543 }
544 if context
545 .glow_context
546 .compressed_texture_astc_supports_hdr_profile()
547 {
548 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR);
549 }
550 }
551
552 #[cfg(any(native, Emscripten))]
553 {
554 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC);
555 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D);
556 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR);
557 }
558 } else {
559 features.set(
560 wgt::Features::TEXTURE_COMPRESSION_ASTC,
561 extensions.contains("GL_KHR_texture_compression_astc_ldr"),
562 );
563 features.set(
564 wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D,
565 extensions.contains("GL_KHR_texture_compression_astc_ldr")
566 && extensions.contains("GL_KHR_texture_compression_astc_sliced_3d"),
567 );
568 features.set(
569 wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR,
570 extensions.contains("GL_KHR_texture_compression_astc_hdr"),
571 );
572 }
573
574 features.set(
575 wgt::Features::FLOAT32_FILTERABLE,
576 extensions.contains("GL_ARB_color_buffer_float")
577 || extensions.contains("GL_EXT_color_buffer_float")
578 || extensions.contains("OES_texture_float_linear"),
579 );
580
581 if es_ver.is_none() {
582 features |= wgt::Features::POLYGON_MODE_LINE | wgt::Features::POLYGON_MODE_POINT;
583 }
584
585 let mut private_caps = super::PrivateCapabilities::empty();
588 private_caps.set(
589 super::PrivateCapabilities::BUFFER_ALLOCATION,
590 extensions.contains("GL_EXT_buffer_storage")
591 || extensions.contains("GL_ARB_buffer_storage"),
592 );
593 private_caps.set(
594 super::PrivateCapabilities::SHADER_BINDING_LAYOUT,
595 supports_compute,
596 );
597 private_caps.set(
598 super::PrivateCapabilities::SHADER_TEXTURE_SHADOW_LOD,
599 extensions.contains("GL_EXT_texture_shadow_lod"),
600 );
601 private_caps.set(
602 super::PrivateCapabilities::MEMORY_BARRIERS,
603 supported((3, 1), (4, 2)),
604 );
605 private_caps.set(
606 super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT,
607 supported((3, 1), (4, 3)) || extensions.contains("GL_ARB_vertex_attrib_binding"),
608 );
609 private_caps.set(
610 super::PrivateCapabilities::INDEX_BUFFER_ROLE_CHANGE,
611 !cfg!(any(webgl, Emscripten)),
612 );
613 private_caps.set(
614 super::PrivateCapabilities::GET_BUFFER_SUB_DATA,
615 cfg!(any(webgl, Emscripten)) || full_ver.is_some(),
616 );
617 let color_buffer_float = extensions.contains("GL_EXT_color_buffer_float")
618 || extensions.contains("GL_ARB_color_buffer_float")
619 || extensions.contains("EXT_color_buffer_float");
620 let color_buffer_half_float = extensions.contains("GL_EXT_color_buffer_half_float")
621 || extensions.contains("GL_ARB_half_float_pixel");
622 private_caps.set(
623 super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT,
624 color_buffer_half_float || color_buffer_float,
625 );
626 private_caps.set(
627 super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
628 color_buffer_float,
629 );
630 private_caps.set(super::PrivateCapabilities::QUERY_BUFFERS, query_buffers);
631 private_caps.set(super::PrivateCapabilities::QUERY_64BIT, full_ver.is_some());
632 private_caps.set(
633 super::PrivateCapabilities::TEXTURE_STORAGE,
634 supported((3, 0), (4, 2)),
635 );
636 private_caps.set(super::PrivateCapabilities::DEBUG_FNS, gl.supports_debug());
637 private_caps.set(
638 super::PrivateCapabilities::INVALIDATE_FRAMEBUFFER,
639 supported((3, 0), (4, 3)),
640 );
641 if let Some(full_ver) = full_ver {
642 let supported =
643 full_ver >= (4, 2) && extensions.contains("GL_ARB_shader_draw_parameters");
644 private_caps.set(
645 super::PrivateCapabilities::FULLY_FEATURED_INSTANCING,
646 supported,
647 );
648 features.set(wgt::Features::INDIRECT_FIRST_INSTANCE, supported);
655 }
656
657 let max_texture_size = unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_SIZE) } as u32;
658 let max_texture_3d_size = unsafe { gl.get_parameter_i32(glow::MAX_3D_TEXTURE_SIZE) } as u32;
659
660 let min_uniform_buffer_offset_alignment =
661 (unsafe { gl.get_parameter_i32(glow::UNIFORM_BUFFER_OFFSET_ALIGNMENT) } as u32);
662 let min_storage_buffer_offset_alignment = if supports_storage {
663 (unsafe { gl.get_parameter_i32(glow::SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT) } as u32)
664 } else {
665 256
666 };
667 let max_uniform_buffers_per_shader_stage =
668 unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_UNIFORM_BLOCKS) }
669 .min(unsafe { gl.get_parameter_i32(glow::MAX_FRAGMENT_UNIFORM_BLOCKS) })
670 as u32;
671
672 let max_compute_workgroups_per_dimension = if supports_work_group_params {
673 unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 0) }
674 .min(unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 1) })
675 .min(unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 2) })
676 as u32
677 } else {
678 0
679 };
680
681 let max_color_attachments = unsafe {
682 gl.get_parameter_i32(glow::MAX_COLOR_ATTACHMENTS)
683 .min(gl.get_parameter_i32(glow::MAX_DRAW_BUFFERS)) as u32
684 };
685
686 let max_color_attachment_bytes_per_sample =
688 max_color_attachments * wgt::TextureFormat::MAX_TARGET_PIXEL_BYTE_COST;
689
690 let limits = crate::auxil::apply_hal_limits(wgt::Limits {
691 max_texture_dimension_1d: max_texture_size,
692 max_texture_dimension_2d: max_texture_size,
693 max_texture_dimension_3d: max_texture_3d_size,
694 max_texture_array_layers: unsafe {
695 gl.get_parameter_i32(glow::MAX_ARRAY_TEXTURE_LAYERS)
696 } as u32,
697 max_bind_groups: crate::MAX_BIND_GROUPS as u32,
698 max_bindings_per_bind_group: 65535,
699 max_dynamic_uniform_buffers_per_pipeline_layout: max_uniform_buffers_per_shader_stage,
700 max_dynamic_storage_buffers_per_pipeline_layout: max_storage_buffers_per_shader_stage,
701 max_sampled_textures_per_shader_stage: super::MAX_TEXTURE_SLOTS as u32,
702 max_samplers_per_shader_stage: super::MAX_SAMPLERS as u32,
703 max_storage_buffers_per_shader_stage,
704 max_storage_textures_per_shader_stage,
705 max_uniform_buffers_per_shader_stage,
706 max_binding_array_elements_per_shader_stage: 0,
707 max_binding_array_sampler_elements_per_shader_stage: 0,
708 max_uniform_buffer_binding_size: unsafe {
709 gl.get_parameter_i32(glow::MAX_UNIFORM_BLOCK_SIZE)
710 } as u32,
711 max_storage_buffer_binding_size: if supports_storage {
712 unsafe { gl.get_parameter_i32(glow::MAX_SHADER_STORAGE_BLOCK_SIZE) }
713 } else {
714 0
715 } as u32,
716 max_vertex_buffers: if private_caps
717 .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
718 {
719 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_BINDINGS) } as u32)
720 } else {
721 16 },
723 max_vertex_attributes: (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIBS) }
724 as u32)
725 .min(super::MAX_VERTEX_ATTRIBUTES as u32),
726 max_vertex_buffer_array_stride: if private_caps
727 .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
728 {
729 if let Some(full_ver) = full_ver {
730 if full_ver >= (4, 4) {
731 let value =
733 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) })
734 as u32;
735
736 if value == 0 {
737 log::debug!("Max vertex attribute stride is 0. Assuming it is the OpenGL minimum spec 2048");
741 2048
742 } else {
743 value
744 }
745 } else {
746 log::debug!("Max vertex attribute stride unknown. Assuming it is the OpenGL minimum spec 2048");
747 2048
748 }
749 } else {
750 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) }) as u32
751 }
752 } else {
753 !0
754 },
755 max_immediate_size: super::MAX_IMMEDIATES as u32 * 4,
756 min_uniform_buffer_offset_alignment,
757 min_storage_buffer_offset_alignment,
758 max_inter_stage_shader_variables: {
759 let max_varying_components =
763 unsafe { gl.get_parameter_i32(glow::MAX_VARYING_COMPONENTS) } as u32;
764 if max_varying_components == 0 {
765 15
767 } else {
768 max_varying_components / 4
769 }
770 },
771 max_color_attachments,
772 max_color_attachment_bytes_per_sample,
773 max_compute_workgroup_storage_size: if supports_work_group_params {
774 (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_SHARED_MEMORY_SIZE) } as u32)
775 } else {
776 0
777 },
778 max_compute_invocations_per_workgroup: if supports_work_group_params {
779 (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_WORK_GROUP_INVOCATIONS) } as u32)
780 } else {
781 0
782 },
783 max_compute_workgroup_size_x: if supports_work_group_params {
784 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 0) }
785 as u32)
786 } else {
787 0
788 },
789 max_compute_workgroup_size_y: if supports_work_group_params {
790 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 1) }
791 as u32)
792 } else {
793 0
794 },
795 max_compute_workgroup_size_z: if supports_work_group_params {
796 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 2) }
797 as u32)
798 } else {
799 0
800 },
801 max_compute_workgroups_per_dimension,
802 max_buffer_size: i32::MAX as u64,
803 max_non_sampler_bindings: u32::MAX,
804
805 max_task_mesh_workgroup_total_count: 0,
806 max_task_mesh_workgroups_per_dimension: 0,
807 max_task_invocations_per_workgroup: 0,
808 max_task_invocations_per_dimension: 0,
809 max_mesh_invocations_per_workgroup: 0,
810 max_mesh_invocations_per_dimension: 0,
811 max_task_payload_size: 0,
812 max_mesh_output_vertices: 0,
813 max_mesh_output_primitives: 0,
814 max_mesh_output_layers: 0,
815 max_mesh_multiview_view_count: 0,
816
817 max_blas_primitive_count: 0,
818 max_blas_geometry_count: 0,
819 max_tlas_instance_count: 0,
820 max_acceleration_structures_per_shader_stage: 0,
821
822 max_multiview_view_count: 0,
823 });
824
825 let mut workarounds = super::Workarounds::empty();
826
827 workarounds.set(
828 super::Workarounds::EMULATE_BUFFER_MAP,
829 cfg!(any(webgl, Emscripten)),
830 );
831
832 let r = renderer.to_lowercase();
833 if context.is_owned()
836 && r.contains("mesa")
837 && r.contains("intel")
838 && r.split(&[' ', '(', ')'][..])
839 .any(|substr| substr.len() == 3 && substr.chars().nth(2) == Some('l'))
840 {
841 log::debug!(
842 "Detected skylake derivative running on mesa i915. Clears to srgb textures will \
843 use manual shader clears."
844 );
845 workarounds.set(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR, true);
846 }
847
848 let downlevel_defaults = wgt::DownlevelLimits {};
849 let max_samples = unsafe { gl.get_parameter_i32(glow::MAX_SAMPLES) };
850
851 #[cfg_attr(target_arch = "wasm32", allow(dropping_references))]
855 drop(gl);
856
857 Some(crate::ExposedAdapter {
858 adapter: super::Adapter {
859 shared: Arc::new(super::AdapterShared {
860 context,
861 private_caps,
862 workarounds,
863 features,
864 limits: limits.clone(),
865 options: backend_options,
866 shading_language_version,
867 next_shader_id: Default::default(),
868 program_cache: Default::default(),
869 es: es_ver.is_some(),
870 max_msaa_samples: max_samples,
871 }),
872 },
873 info: Self::make_info(vendor, renderer, version),
874 features,
875 capabilities: crate::Capabilities {
876 limits,
877 downlevel: wgt::DownlevelCapabilities {
878 flags: downlevel_flags,
879 limits: downlevel_defaults,
880 shader_model: wgt::ShaderModel::Sm5,
881 },
882 alignments: crate::Alignments {
883 buffer_copy_offset: wgt::BufferSize::new(4).unwrap(),
884 buffer_copy_pitch: wgt::BufferSize::new(4).unwrap(),
885 uniform_bounds_check_alignment: wgt::BufferSize::new(1).unwrap(),
895 raw_tlas_instance_size: 0,
896 ray_tracing_scratch_buffer_alignment: 0,
897 },
898 cooperative_matrix_properties: Vec::new(),
899 },
900 })
901 }
902
903 unsafe fn compile_shader(
904 source: &str,
905 gl: &glow::Context,
906 shader_type: u32,
907 es: bool,
908 ) -> Option<glow::Shader> {
909 let source = if es {
910 format!("#version 300 es\nprecision lowp float;\n{source}")
911 } else {
912 let version = gl.version();
913 if version.major == 3 && version.minor == 0 {
914 format!("#version 130\n{source}")
916 } else {
917 format!("#version 140\n{source}")
919 }
920 };
921 let shader = unsafe { gl.create_shader(shader_type) }.expect("Could not create shader");
922 unsafe { gl.shader_source(shader, &source) };
923 unsafe { gl.compile_shader(shader) };
924
925 if !unsafe { gl.get_shader_compile_status(shader) } {
926 let msg = unsafe { gl.get_shader_info_log(shader) };
927 if !msg.is_empty() {
928 log::error!("\tShader compile error: {msg}");
929 }
930 unsafe { gl.delete_shader(shader) };
931 None
932 } else {
933 Some(shader)
934 }
935 }
936
937 unsafe fn create_shader_clear_program(
938 gl: &glow::Context,
939 es: bool,
940 ) -> Option<ShaderClearProgram> {
941 let program = unsafe { gl.create_program() }.expect("Could not create shader program");
942 let vertex = unsafe {
943 Self::compile_shader(
944 include_str!("./shaders/clear.vert"),
945 gl,
946 glow::VERTEX_SHADER,
947 es,
948 )?
949 };
950 let fragment = unsafe {
951 Self::compile_shader(
952 include_str!("./shaders/clear.frag"),
953 gl,
954 glow::FRAGMENT_SHADER,
955 es,
956 )?
957 };
958 unsafe { gl.attach_shader(program, vertex) };
959 unsafe { gl.attach_shader(program, fragment) };
960 unsafe { gl.link_program(program) };
961
962 let linked_ok = unsafe { gl.get_program_link_status(program) };
963 let msg = unsafe { gl.get_program_info_log(program) };
964 if !msg.is_empty() {
965 log::error!("Shader link error: {msg}");
966 }
967 if !linked_ok {
968 return None;
969 }
970
971 let color_uniform_location = unsafe { gl.get_uniform_location(program, "color") }
972 .expect("Could not find color uniform in shader clear shader");
973 unsafe { gl.delete_shader(vertex) };
974 unsafe { gl.delete_shader(fragment) };
975
976 Some(ShaderClearProgram {
977 program,
978 color_uniform_location,
979 })
980 }
981}
982
983impl crate::Adapter for super::Adapter {
984 type A = super::Api;
985
986 unsafe fn open(
987 &self,
988 features: wgt::Features,
989 _limits: &wgt::Limits,
990 _memory_hints: &wgt::MemoryHints,
991 ) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
992 let gl = &self.shared.context.lock();
993 unsafe { gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1) };
994 unsafe { gl.pixel_store_i32(glow::PACK_ALIGNMENT, 1) };
995 let main_vao =
996 unsafe { gl.create_vertex_array() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
997 unsafe { gl.bind_vertex_array(Some(main_vao)) };
998
999 let zero_buffer =
1000 unsafe { gl.create_buffer() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
1001 unsafe { gl.bind_buffer(glow::COPY_READ_BUFFER, Some(zero_buffer)) };
1002 let zeroes = vec![0u8; super::ZERO_BUFFER_SIZE];
1003 unsafe { gl.buffer_data_u8_slice(glow::COPY_READ_BUFFER, &zeroes, glow::STATIC_DRAW) };
1004
1005 let shader_clear_program = if self
1009 .shared
1010 .workarounds
1011 .contains(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR)
1012 {
1013 Some(unsafe {
1014 Self::create_shader_clear_program(gl, self.shared.es)
1015 .ok_or(crate::DeviceError::Lost)?
1016 })
1017 } else {
1018 None
1020 };
1021
1022 Ok(crate::OpenDevice {
1023 device: super::Device {
1024 shared: Arc::clone(&self.shared),
1025 main_vao,
1026 #[cfg(all(native, feature = "renderdoc"))]
1027 render_doc: Default::default(),
1028 counters: Default::default(),
1029 },
1030 queue: super::Queue {
1031 shared: Arc::clone(&self.shared),
1032 features,
1033 draw_fbo: unsafe { gl.create_framebuffer() }
1034 .map_err(|_| crate::DeviceError::OutOfMemory)?,
1035 copy_fbo: unsafe { gl.create_framebuffer() }
1036 .map_err(|_| crate::DeviceError::OutOfMemory)?,
1037 shader_clear_program,
1038 zero_buffer,
1039 temp_query_results: Mutex::new(Vec::new()),
1040 draw_buffer_count: AtomicU8::new(1),
1041 current_index_buffer: Mutex::new(None),
1042 },
1043 })
1044 }
1045
1046 unsafe fn texture_format_capabilities(
1047 &self,
1048 format: wgt::TextureFormat,
1049 ) -> crate::TextureFormatCapabilities {
1050 use crate::TextureFormatCapabilities as Tfc;
1051 use wgt::TextureFormat as Tf;
1052
1053 let sample_count = {
1054 let max_samples = self.shared.max_msaa_samples;
1055 if max_samples >= 16 {
1056 Tfc::MULTISAMPLE_X2
1057 | Tfc::MULTISAMPLE_X4
1058 | Tfc::MULTISAMPLE_X8
1059 | Tfc::MULTISAMPLE_X16
1060 } else if max_samples >= 8 {
1061 Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4 | Tfc::MULTISAMPLE_X8
1062 } else {
1063 Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4
1068 }
1069 };
1070
1071 let empty = Tfc::empty();
1076 let base = Tfc::COPY_SRC | Tfc::COPY_DST;
1077 let unfilterable = base | Tfc::SAMPLED;
1078 let depth = base | Tfc::SAMPLED | sample_count | Tfc::DEPTH_STENCIL_ATTACHMENT;
1079 let filterable = unfilterable | Tfc::SAMPLED_LINEAR;
1080 let renderable =
1081 unfilterable | Tfc::COLOR_ATTACHMENT | sample_count | Tfc::MULTISAMPLE_RESOLVE;
1082 let filterable_renderable = filterable | renderable | Tfc::COLOR_ATTACHMENT_BLEND;
1083 let storage =
1084 base | Tfc::STORAGE_READ_WRITE | Tfc::STORAGE_READ_ONLY | Tfc::STORAGE_WRITE_ONLY;
1085
1086 let feature_fn = |f, caps| {
1087 if self.shared.features.contains(f) {
1088 caps
1089 } else {
1090 empty
1091 }
1092 };
1093
1094 let bcn_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_BC, filterable);
1095 let etc2_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ETC2, filterable);
1096 let astc_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC, filterable);
1097 let astc_hdr_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR, filterable);
1098
1099 let private_caps_fn = |f, caps| {
1100 if self.shared.private_caps.contains(f) {
1101 caps
1102 } else {
1103 empty
1104 }
1105 };
1106
1107 let half_float_renderable = private_caps_fn(
1108 super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT,
1109 Tfc::COLOR_ATTACHMENT
1110 | Tfc::COLOR_ATTACHMENT_BLEND
1111 | sample_count
1112 | Tfc::MULTISAMPLE_RESOLVE,
1113 );
1114
1115 let float_renderable = private_caps_fn(
1116 super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
1117 Tfc::COLOR_ATTACHMENT
1118 | Tfc::COLOR_ATTACHMENT_BLEND
1119 | sample_count
1120 | Tfc::MULTISAMPLE_RESOLVE,
1121 );
1122
1123 let texture_float_linear = feature_fn(wgt::Features::FLOAT32_FILTERABLE, filterable);
1124
1125 let image_atomic = feature_fn(wgt::Features::TEXTURE_ATOMIC, Tfc::STORAGE_ATOMIC);
1126 let image_64_atomic = feature_fn(wgt::Features::TEXTURE_INT64_ATOMIC, Tfc::STORAGE_ATOMIC);
1127
1128 match format {
1129 Tf::R8Unorm => filterable_renderable,
1130 Tf::R8Snorm => filterable,
1131 Tf::R8Uint => renderable,
1132 Tf::R8Sint => renderable,
1133 Tf::R16Uint => renderable,
1134 Tf::R16Sint => renderable,
1135 Tf::R16Unorm => empty,
1136 Tf::R16Snorm => empty,
1137 Tf::R16Float => filterable | half_float_renderable,
1138 Tf::Rg8Unorm => filterable_renderable,
1139 Tf::Rg8Snorm => filterable,
1140 Tf::Rg8Uint => renderable,
1141 Tf::Rg8Sint => renderable,
1142 Tf::R32Uint => renderable | storage | image_atomic,
1143 Tf::R32Sint => renderable | storage | image_atomic,
1144 Tf::R32Float => unfilterable | storage | float_renderable | texture_float_linear,
1145 Tf::Rg16Uint => renderable,
1146 Tf::Rg16Sint => renderable,
1147 Tf::Rg16Unorm => empty,
1148 Tf::Rg16Snorm => empty,
1149 Tf::Rg16Float => filterable | half_float_renderable,
1150 Tf::Rgba8Unorm => filterable_renderable | storage,
1151 Tf::Rgba8UnormSrgb => filterable_renderable,
1152 Tf::Bgra8Unorm | Tf::Bgra8UnormSrgb => filterable_renderable,
1153 Tf::Rgba8Snorm => filterable | storage,
1154 Tf::Rgba8Uint => renderable | storage,
1155 Tf::Rgba8Sint => renderable | storage,
1156 Tf::Rgb10a2Uint => renderable,
1157 Tf::Rgb10a2Unorm => filterable_renderable,
1158 Tf::Rg11b10Ufloat => filterable | float_renderable,
1159 Tf::R64Uint => image_64_atomic,
1160 Tf::Rg32Uint => renderable,
1161 Tf::Rg32Sint => renderable,
1162 Tf::Rg32Float => unfilterable | float_renderable | texture_float_linear,
1163 Tf::Rgba16Uint => renderable | storage,
1164 Tf::Rgba16Sint => renderable | storage,
1165 Tf::Rgba16Unorm => empty,
1166 Tf::Rgba16Snorm => empty,
1167 Tf::Rgba16Float => filterable | storage | half_float_renderable,
1168 Tf::Rgba32Uint => renderable | storage,
1169 Tf::Rgba32Sint => renderable | storage,
1170 Tf::Rgba32Float => unfilterable | storage | float_renderable | texture_float_linear,
1171 Tf::Stencil8
1172 | Tf::Depth16Unorm
1173 | Tf::Depth32Float
1174 | Tf::Depth32FloatStencil8
1175 | Tf::Depth24Plus
1176 | Tf::Depth24PlusStencil8 => depth,
1177 Tf::NV12 => empty,
1178 Tf::P010 => empty,
1179 Tf::Rgb9e5Ufloat => filterable,
1180 Tf::Bc1RgbaUnorm
1181 | Tf::Bc1RgbaUnormSrgb
1182 | Tf::Bc2RgbaUnorm
1183 | Tf::Bc2RgbaUnormSrgb
1184 | Tf::Bc3RgbaUnorm
1185 | Tf::Bc3RgbaUnormSrgb
1186 | Tf::Bc4RUnorm
1187 | Tf::Bc4RSnorm
1188 | Tf::Bc5RgUnorm
1189 | Tf::Bc5RgSnorm
1190 | Tf::Bc6hRgbFloat
1191 | Tf::Bc6hRgbUfloat
1192 | Tf::Bc7RgbaUnorm
1193 | Tf::Bc7RgbaUnormSrgb => bcn_features,
1194 Tf::Etc2Rgb8Unorm
1195 | Tf::Etc2Rgb8UnormSrgb
1196 | Tf::Etc2Rgb8A1Unorm
1197 | Tf::Etc2Rgb8A1UnormSrgb
1198 | Tf::Etc2Rgba8Unorm
1199 | Tf::Etc2Rgba8UnormSrgb
1200 | Tf::EacR11Unorm
1201 | Tf::EacR11Snorm
1202 | Tf::EacRg11Unorm
1203 | Tf::EacRg11Snorm => etc2_features,
1204 Tf::Astc {
1205 block: _,
1206 channel: AstcChannel::Unorm | AstcChannel::UnormSrgb,
1207 } => astc_features,
1208 Tf::Astc {
1209 block: _,
1210 channel: AstcChannel::Hdr,
1211 } => astc_hdr_features,
1212 }
1213 }
1214
1215 unsafe fn surface_capabilities(
1216 &self,
1217 surface: &super::Surface,
1218 ) -> Option<crate::SurfaceCapabilities> {
1219 #[cfg(webgl)]
1220 if self.shared.context.webgl2_context != surface.webgl2_context {
1221 return None;
1222 }
1223
1224 if surface.presentable {
1225 let mut formats = vec![
1226 wgt::TextureFormat::Rgba8Unorm,
1227 #[cfg(native)]
1228 wgt::TextureFormat::Bgra8Unorm,
1229 ];
1230 if surface.supports_srgb() {
1231 formats.extend([
1232 wgt::TextureFormat::Rgba8UnormSrgb,
1233 #[cfg(native)]
1234 wgt::TextureFormat::Bgra8UnormSrgb,
1235 ])
1236 }
1237 if self
1238 .shared
1239 .private_caps
1240 .contains(super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT)
1241 {
1242 formats.push(wgt::TextureFormat::Rgba16Float)
1243 }
1244
1245 Some(crate::SurfaceCapabilities {
1246 formats,
1247 present_modes: if cfg!(windows) {
1248 vec![wgt::PresentMode::Fifo, wgt::PresentMode::Immediate]
1249 } else {
1250 vec![wgt::PresentMode::Fifo] },
1252 composite_alpha_modes: vec![wgt::CompositeAlphaMode::Opaque], maximum_frame_latency: 2..=2, current_extent: None,
1255 usage: wgt::TextureUses::COLOR_TARGET,
1256 })
1257 } else {
1258 None
1259 }
1260 }
1261
1262 unsafe fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
1263 wgt::PresentationTimestamp::INVALID_TIMESTAMP
1264 }
1265}
1266
1267impl super::AdapterShared {
1268 pub(super) unsafe fn get_buffer_sub_data(
1269 &self,
1270 gl: &glow::Context,
1271 target: u32,
1272 offset: i32,
1273 dst_data: &mut [u8],
1274 ) {
1275 if self
1276 .private_caps
1277 .contains(super::PrivateCapabilities::GET_BUFFER_SUB_DATA)
1278 {
1279 unsafe { gl.get_buffer_sub_data(target, offset, dst_data) };
1280 } else {
1281 log::error!("Fake map");
1282 let length = dst_data.len();
1283 let buffer_mapping =
1284 unsafe { gl.map_buffer_range(target, offset, length as _, glow::MAP_READ_BIT) };
1285
1286 unsafe {
1287 core::ptr::copy_nonoverlapping(buffer_mapping, dst_data.as_mut_ptr(), length)
1288 };
1289
1290 unsafe { gl.unmap_buffer(target) };
1291 }
1292 }
1293}
1294
1295#[cfg(send_sync)]
1296unsafe impl Sync for super::Adapter {}
1297#[cfg(send_sync)]
1298unsafe impl Send for super::Adapter {}
1299
1300#[cfg(test)]
1301mod tests {
1302 use super::super::Adapter;
1303
1304 #[test]
1305 fn test_version_parse() {
1306 Adapter::parse_version("1").unwrap_err();
1307 Adapter::parse_version("1.").unwrap_err();
1308 Adapter::parse_version("1 h3l1o. W0rld").unwrap_err();
1309 Adapter::parse_version("1. h3l1o. W0rld").unwrap_err();
1310 Adapter::parse_version("1.2.3").unwrap_err();
1311
1312 assert_eq!(Adapter::parse_version("OpenGL ES 3.1").unwrap(), (3, 1));
1313 assert_eq!(
1314 Adapter::parse_version("OpenGL ES 2.0 Google Nexus").unwrap(),
1315 (2, 0)
1316 );
1317 assert_eq!(Adapter::parse_version("GLSL ES 1.1").unwrap(), (1, 1));
1318 assert_eq!(
1319 Adapter::parse_version("OpenGL ES GLSL ES 3.20").unwrap(),
1320 (3, 2)
1321 );
1322 assert_eq!(
1323 Adapter::parse_version("WebGL 2.0 (OpenGL ES 3.0 Chromium)").unwrap(),
1325 (3, 0)
1326 );
1327 assert_eq!(
1328 Adapter::parse_version("WebGL GLSL ES 3.00 (OpenGL ES GLSL ES 3.0 Chromium)").unwrap(),
1329 (3, 0)
1330 );
1331 }
1332}