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_components: {
759 let max_varying_components =
763 unsafe { gl.get_parameter_i32(glow::MAX_VARYING_COMPONENTS) } as u32;
764 if max_varying_components == 0 {
765 60
767 } else {
768 max_varying_components
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 },
899 })
900 }
901
902 unsafe fn compile_shader(
903 source: &str,
904 gl: &glow::Context,
905 shader_type: u32,
906 es: bool,
907 ) -> Option<glow::Shader> {
908 let source = if es {
909 format!("#version 300 es\nprecision lowp float;\n{source}")
910 } else {
911 let version = gl.version();
912 if version.major == 3 && version.minor == 0 {
913 format!("#version 130\n{source}")
915 } else {
916 format!("#version 140\n{source}")
918 }
919 };
920 let shader = unsafe { gl.create_shader(shader_type) }.expect("Could not create shader");
921 unsafe { gl.shader_source(shader, &source) };
922 unsafe { gl.compile_shader(shader) };
923
924 if !unsafe { gl.get_shader_compile_status(shader) } {
925 let msg = unsafe { gl.get_shader_info_log(shader) };
926 if !msg.is_empty() {
927 log::error!("\tShader compile error: {msg}");
928 }
929 unsafe { gl.delete_shader(shader) };
930 None
931 } else {
932 Some(shader)
933 }
934 }
935
936 unsafe fn create_shader_clear_program(
937 gl: &glow::Context,
938 es: bool,
939 ) -> Option<ShaderClearProgram> {
940 let program = unsafe { gl.create_program() }.expect("Could not create shader program");
941 let vertex = unsafe {
942 Self::compile_shader(
943 include_str!("./shaders/clear.vert"),
944 gl,
945 glow::VERTEX_SHADER,
946 es,
947 )?
948 };
949 let fragment = unsafe {
950 Self::compile_shader(
951 include_str!("./shaders/clear.frag"),
952 gl,
953 glow::FRAGMENT_SHADER,
954 es,
955 )?
956 };
957 unsafe { gl.attach_shader(program, vertex) };
958 unsafe { gl.attach_shader(program, fragment) };
959 unsafe { gl.link_program(program) };
960
961 let linked_ok = unsafe { gl.get_program_link_status(program) };
962 let msg = unsafe { gl.get_program_info_log(program) };
963 if !msg.is_empty() {
964 log::error!("Shader link error: {msg}");
965 }
966 if !linked_ok {
967 return None;
968 }
969
970 let color_uniform_location = unsafe { gl.get_uniform_location(program, "color") }
971 .expect("Could not find color uniform in shader clear shader");
972 unsafe { gl.delete_shader(vertex) };
973 unsafe { gl.delete_shader(fragment) };
974
975 Some(ShaderClearProgram {
976 program,
977 color_uniform_location,
978 })
979 }
980}
981
982impl crate::Adapter for super::Adapter {
983 type A = super::Api;
984
985 unsafe fn open(
986 &self,
987 features: wgt::Features,
988 _limits: &wgt::Limits,
989 _memory_hints: &wgt::MemoryHints,
990 ) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
991 let gl = &self.shared.context.lock();
992 unsafe { gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1) };
993 unsafe { gl.pixel_store_i32(glow::PACK_ALIGNMENT, 1) };
994 let main_vao =
995 unsafe { gl.create_vertex_array() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
996 unsafe { gl.bind_vertex_array(Some(main_vao)) };
997
998 let zero_buffer =
999 unsafe { gl.create_buffer() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
1000 unsafe { gl.bind_buffer(glow::COPY_READ_BUFFER, Some(zero_buffer)) };
1001 let zeroes = vec![0u8; super::ZERO_BUFFER_SIZE];
1002 unsafe { gl.buffer_data_u8_slice(glow::COPY_READ_BUFFER, &zeroes, glow::STATIC_DRAW) };
1003
1004 let shader_clear_program = if self
1008 .shared
1009 .workarounds
1010 .contains(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR)
1011 {
1012 Some(unsafe {
1013 Self::create_shader_clear_program(gl, self.shared.es)
1014 .ok_or(crate::DeviceError::Lost)?
1015 })
1016 } else {
1017 None
1019 };
1020
1021 Ok(crate::OpenDevice {
1022 device: super::Device {
1023 shared: Arc::clone(&self.shared),
1024 main_vao,
1025 #[cfg(all(native, feature = "renderdoc"))]
1026 render_doc: Default::default(),
1027 counters: Default::default(),
1028 },
1029 queue: super::Queue {
1030 shared: Arc::clone(&self.shared),
1031 features,
1032 draw_fbo: unsafe { gl.create_framebuffer() }
1033 .map_err(|_| crate::DeviceError::OutOfMemory)?,
1034 copy_fbo: unsafe { gl.create_framebuffer() }
1035 .map_err(|_| crate::DeviceError::OutOfMemory)?,
1036 shader_clear_program,
1037 zero_buffer,
1038 temp_query_results: Mutex::new(Vec::new()),
1039 draw_buffer_count: AtomicU8::new(1),
1040 current_index_buffer: Mutex::new(None),
1041 },
1042 })
1043 }
1044
1045 unsafe fn texture_format_capabilities(
1046 &self,
1047 format: wgt::TextureFormat,
1048 ) -> crate::TextureFormatCapabilities {
1049 use crate::TextureFormatCapabilities as Tfc;
1050 use wgt::TextureFormat as Tf;
1051
1052 let sample_count = {
1053 let max_samples = self.shared.max_msaa_samples;
1054 if max_samples >= 16 {
1055 Tfc::MULTISAMPLE_X2
1056 | Tfc::MULTISAMPLE_X4
1057 | Tfc::MULTISAMPLE_X8
1058 | Tfc::MULTISAMPLE_X16
1059 } else if max_samples >= 8 {
1060 Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4 | Tfc::MULTISAMPLE_X8
1061 } else {
1062 Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4
1067 }
1068 };
1069
1070 let empty = Tfc::empty();
1075 let base = Tfc::COPY_SRC | Tfc::COPY_DST;
1076 let unfilterable = base | Tfc::SAMPLED;
1077 let depth = base | Tfc::SAMPLED | sample_count | Tfc::DEPTH_STENCIL_ATTACHMENT;
1078 let filterable = unfilterable | Tfc::SAMPLED_LINEAR;
1079 let renderable =
1080 unfilterable | Tfc::COLOR_ATTACHMENT | sample_count | Tfc::MULTISAMPLE_RESOLVE;
1081 let filterable_renderable = filterable | renderable | Tfc::COLOR_ATTACHMENT_BLEND;
1082 let storage =
1083 base | Tfc::STORAGE_READ_WRITE | Tfc::STORAGE_READ_ONLY | Tfc::STORAGE_WRITE_ONLY;
1084
1085 let feature_fn = |f, caps| {
1086 if self.shared.features.contains(f) {
1087 caps
1088 } else {
1089 empty
1090 }
1091 };
1092
1093 let bcn_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_BC, filterable);
1094 let etc2_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ETC2, filterable);
1095 let astc_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC, filterable);
1096 let astc_hdr_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR, filterable);
1097
1098 let private_caps_fn = |f, caps| {
1099 if self.shared.private_caps.contains(f) {
1100 caps
1101 } else {
1102 empty
1103 }
1104 };
1105
1106 let half_float_renderable = private_caps_fn(
1107 super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT,
1108 Tfc::COLOR_ATTACHMENT
1109 | Tfc::COLOR_ATTACHMENT_BLEND
1110 | sample_count
1111 | Tfc::MULTISAMPLE_RESOLVE,
1112 );
1113
1114 let float_renderable = private_caps_fn(
1115 super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
1116 Tfc::COLOR_ATTACHMENT
1117 | Tfc::COLOR_ATTACHMENT_BLEND
1118 | sample_count
1119 | Tfc::MULTISAMPLE_RESOLVE,
1120 );
1121
1122 let texture_float_linear = feature_fn(wgt::Features::FLOAT32_FILTERABLE, filterable);
1123
1124 let image_atomic = feature_fn(wgt::Features::TEXTURE_ATOMIC, Tfc::STORAGE_ATOMIC);
1125 let image_64_atomic = feature_fn(wgt::Features::TEXTURE_INT64_ATOMIC, Tfc::STORAGE_ATOMIC);
1126
1127 match format {
1128 Tf::R8Unorm => filterable_renderable,
1129 Tf::R8Snorm => filterable,
1130 Tf::R8Uint => renderable,
1131 Tf::R8Sint => renderable,
1132 Tf::R16Uint => renderable,
1133 Tf::R16Sint => renderable,
1134 Tf::R16Unorm => empty,
1135 Tf::R16Snorm => empty,
1136 Tf::R16Float => filterable | half_float_renderable,
1137 Tf::Rg8Unorm => filterable_renderable,
1138 Tf::Rg8Snorm => filterable,
1139 Tf::Rg8Uint => renderable,
1140 Tf::Rg8Sint => renderable,
1141 Tf::R32Uint => renderable | storage | image_atomic,
1142 Tf::R32Sint => renderable | storage | image_atomic,
1143 Tf::R32Float => unfilterable | storage | float_renderable | texture_float_linear,
1144 Tf::Rg16Uint => renderable,
1145 Tf::Rg16Sint => renderable,
1146 Tf::Rg16Unorm => empty,
1147 Tf::Rg16Snorm => empty,
1148 Tf::Rg16Float => filterable | half_float_renderable,
1149 Tf::Rgba8Unorm => filterable_renderable | storage,
1150 Tf::Rgba8UnormSrgb => filterable_renderable,
1151 Tf::Bgra8Unorm | Tf::Bgra8UnormSrgb => filterable_renderable,
1152 Tf::Rgba8Snorm => filterable | storage,
1153 Tf::Rgba8Uint => renderable | storage,
1154 Tf::Rgba8Sint => renderable | storage,
1155 Tf::Rgb10a2Uint => renderable,
1156 Tf::Rgb10a2Unorm => filterable_renderable,
1157 Tf::Rg11b10Ufloat => filterable | float_renderable,
1158 Tf::R64Uint => image_64_atomic,
1159 Tf::Rg32Uint => renderable,
1160 Tf::Rg32Sint => renderable,
1161 Tf::Rg32Float => unfilterable | float_renderable | texture_float_linear,
1162 Tf::Rgba16Uint => renderable | storage,
1163 Tf::Rgba16Sint => renderable | storage,
1164 Tf::Rgba16Unorm => empty,
1165 Tf::Rgba16Snorm => empty,
1166 Tf::Rgba16Float => filterable | storage | half_float_renderable,
1167 Tf::Rgba32Uint => renderable | storage,
1168 Tf::Rgba32Sint => renderable | storage,
1169 Tf::Rgba32Float => unfilterable | storage | float_renderable | texture_float_linear,
1170 Tf::Stencil8
1171 | Tf::Depth16Unorm
1172 | Tf::Depth32Float
1173 | Tf::Depth32FloatStencil8
1174 | Tf::Depth24Plus
1175 | Tf::Depth24PlusStencil8 => depth,
1176 Tf::NV12 => empty,
1177 Tf::P010 => empty,
1178 Tf::Rgb9e5Ufloat => filterable,
1179 Tf::Bc1RgbaUnorm
1180 | Tf::Bc1RgbaUnormSrgb
1181 | Tf::Bc2RgbaUnorm
1182 | Tf::Bc2RgbaUnormSrgb
1183 | Tf::Bc3RgbaUnorm
1184 | Tf::Bc3RgbaUnormSrgb
1185 | Tf::Bc4RUnorm
1186 | Tf::Bc4RSnorm
1187 | Tf::Bc5RgUnorm
1188 | Tf::Bc5RgSnorm
1189 | Tf::Bc6hRgbFloat
1190 | Tf::Bc6hRgbUfloat
1191 | Tf::Bc7RgbaUnorm
1192 | Tf::Bc7RgbaUnormSrgb => bcn_features,
1193 Tf::Etc2Rgb8Unorm
1194 | Tf::Etc2Rgb8UnormSrgb
1195 | Tf::Etc2Rgb8A1Unorm
1196 | Tf::Etc2Rgb8A1UnormSrgb
1197 | Tf::Etc2Rgba8Unorm
1198 | Tf::Etc2Rgba8UnormSrgb
1199 | Tf::EacR11Unorm
1200 | Tf::EacR11Snorm
1201 | Tf::EacRg11Unorm
1202 | Tf::EacRg11Snorm => etc2_features,
1203 Tf::Astc {
1204 block: _,
1205 channel: AstcChannel::Unorm | AstcChannel::UnormSrgb,
1206 } => astc_features,
1207 Tf::Astc {
1208 block: _,
1209 channel: AstcChannel::Hdr,
1210 } => astc_hdr_features,
1211 }
1212 }
1213
1214 unsafe fn surface_capabilities(
1215 &self,
1216 surface: &super::Surface,
1217 ) -> Option<crate::SurfaceCapabilities> {
1218 #[cfg(webgl)]
1219 if self.shared.context.webgl2_context != surface.webgl2_context {
1220 return None;
1221 }
1222
1223 if surface.presentable {
1224 let mut formats = vec![
1225 wgt::TextureFormat::Rgba8Unorm,
1226 #[cfg(native)]
1227 wgt::TextureFormat::Bgra8Unorm,
1228 ];
1229 if surface.supports_srgb() {
1230 formats.extend([
1231 wgt::TextureFormat::Rgba8UnormSrgb,
1232 #[cfg(native)]
1233 wgt::TextureFormat::Bgra8UnormSrgb,
1234 ])
1235 }
1236 if self
1237 .shared
1238 .private_caps
1239 .contains(super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT)
1240 {
1241 formats.push(wgt::TextureFormat::Rgba16Float)
1242 }
1243
1244 Some(crate::SurfaceCapabilities {
1245 formats,
1246 present_modes: if cfg!(windows) {
1247 vec![wgt::PresentMode::Fifo, wgt::PresentMode::Immediate]
1248 } else {
1249 vec![wgt::PresentMode::Fifo] },
1251 composite_alpha_modes: vec![wgt::CompositeAlphaMode::Opaque], maximum_frame_latency: 2..=2, current_extent: None,
1254 usage: wgt::TextureUses::COLOR_TARGET,
1255 })
1256 } else {
1257 None
1258 }
1259 }
1260
1261 unsafe fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
1262 wgt::PresentationTimestamp::INVALID_TIMESTAMP
1263 }
1264}
1265
1266impl super::AdapterShared {
1267 pub(super) unsafe fn get_buffer_sub_data(
1268 &self,
1269 gl: &glow::Context,
1270 target: u32,
1271 offset: i32,
1272 dst_data: &mut [u8],
1273 ) {
1274 if self
1275 .private_caps
1276 .contains(super::PrivateCapabilities::GET_BUFFER_SUB_DATA)
1277 {
1278 unsafe { gl.get_buffer_sub_data(target, offset, dst_data) };
1279 } else {
1280 log::error!("Fake map");
1281 let length = dst_data.len();
1282 let buffer_mapping =
1283 unsafe { gl.map_buffer_range(target, offset, length as _, glow::MAP_READ_BIT) };
1284
1285 unsafe {
1286 core::ptr::copy_nonoverlapping(buffer_mapping, dst_data.as_mut_ptr(), length)
1287 };
1288
1289 unsafe { gl.unmap_buffer(target) };
1290 }
1291 }
1292}
1293
1294#[cfg(send_sync)]
1295unsafe impl Sync for super::Adapter {}
1296#[cfg(send_sync)]
1297unsafe impl Send for super::Adapter {}
1298
1299#[cfg(test)]
1300mod tests {
1301 use super::super::Adapter;
1302
1303 #[test]
1304 fn test_version_parse() {
1305 Adapter::parse_version("1").unwrap_err();
1306 Adapter::parse_version("1.").unwrap_err();
1307 Adapter::parse_version("1 h3l1o. W0rld").unwrap_err();
1308 Adapter::parse_version("1. h3l1o. W0rld").unwrap_err();
1309 Adapter::parse_version("1.2.3").unwrap_err();
1310
1311 assert_eq!(Adapter::parse_version("OpenGL ES 3.1").unwrap(), (3, 1));
1312 assert_eq!(
1313 Adapter::parse_version("OpenGL ES 2.0 Google Nexus").unwrap(),
1314 (2, 0)
1315 );
1316 assert_eq!(Adapter::parse_version("GLSL ES 1.1").unwrap(), (1, 1));
1317 assert_eq!(
1318 Adapter::parse_version("OpenGL ES GLSL ES 3.20").unwrap(),
1319 (3, 2)
1320 );
1321 assert_eq!(
1322 Adapter::parse_version("WebGL 2.0 (OpenGL ES 3.0 Chromium)").unwrap(),
1324 (3, 0)
1325 );
1326 assert_eq!(
1327 Adapter::parse_version("WebGL GLSL ES 3.00 (OpenGL ES GLSL ES 3.0 Chromium)").unwrap(),
1328 (3, 0)
1329 );
1330 }
1331}