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 let is_mali = renderer.to_lowercase().contains("mali");
637 let debug_fns_enabled = match backend_options.debug_fns {
638 wgt::GlDebugFns::Auto => gl.supports_debug() && !is_mali,
639 wgt::GlDebugFns::ForceEnabled => gl.supports_debug(),
640 wgt::GlDebugFns::Disabled => false,
641 };
642 private_caps.set(super::PrivateCapabilities::DEBUG_FNS, debug_fns_enabled);
643 private_caps.set(
644 super::PrivateCapabilities::INVALIDATE_FRAMEBUFFER,
645 supported((3, 0), (4, 3)),
646 );
647 if let Some(full_ver) = full_ver {
648 let supported =
649 full_ver >= (4, 2) && extensions.contains("GL_ARB_shader_draw_parameters");
650 private_caps.set(
651 super::PrivateCapabilities::FULLY_FEATURED_INSTANCING,
652 supported,
653 );
654 features.set(wgt::Features::INDIRECT_FIRST_INSTANCE, supported);
661 }
662
663 let max_texture_size = unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_SIZE) } as u32;
664 let max_texture_3d_size = unsafe { gl.get_parameter_i32(glow::MAX_3D_TEXTURE_SIZE) } as u32;
665
666 let min_uniform_buffer_offset_alignment =
667 (unsafe { gl.get_parameter_i32(glow::UNIFORM_BUFFER_OFFSET_ALIGNMENT) } as u32);
668 let min_storage_buffer_offset_alignment = if supports_storage {
669 (unsafe { gl.get_parameter_i32(glow::SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT) } as u32)
670 } else {
671 256
672 };
673 let max_uniform_buffers_per_shader_stage =
674 unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_UNIFORM_BLOCKS) }
675 .min(unsafe { gl.get_parameter_i32(glow::MAX_FRAGMENT_UNIFORM_BLOCKS) })
676 as u32;
677
678 let max_compute_workgroups_per_dimension = if supports_work_group_params {
679 unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 0) }
680 .min(unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 1) })
681 .min(unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 2) })
682 as u32
683 } else {
684 0
685 };
686
687 let max_color_attachments = unsafe {
688 gl.get_parameter_i32(glow::MAX_COLOR_ATTACHMENTS)
689 .min(gl.get_parameter_i32(glow::MAX_DRAW_BUFFERS)) as u32
690 };
691
692 let max_color_attachment_bytes_per_sample =
694 max_color_attachments * wgt::TextureFormat::MAX_TARGET_PIXEL_BYTE_COST;
695
696 let limits = crate::auxil::apply_hal_limits(wgt::Limits {
697 max_texture_dimension_1d: max_texture_size,
698 max_texture_dimension_2d: max_texture_size,
699 max_texture_dimension_3d: max_texture_3d_size,
700 max_texture_array_layers: unsafe {
701 gl.get_parameter_i32(glow::MAX_ARRAY_TEXTURE_LAYERS)
702 } as u32,
703 max_bind_groups: crate::MAX_BIND_GROUPS as u32,
704 max_bindings_per_bind_group: 65535,
705 max_dynamic_uniform_buffers_per_pipeline_layout: max_uniform_buffers_per_shader_stage,
706 max_dynamic_storage_buffers_per_pipeline_layout: max_storage_buffers_per_shader_stage,
707 max_sampled_textures_per_shader_stage: super::MAX_TEXTURE_SLOTS as u32,
708 max_samplers_per_shader_stage: super::MAX_SAMPLERS as u32,
709 max_storage_buffers_per_shader_stage,
710 max_storage_textures_per_shader_stage,
711 max_uniform_buffers_per_shader_stage,
712 max_binding_array_elements_per_shader_stage: 0,
713 max_binding_array_sampler_elements_per_shader_stage: 0,
714 max_uniform_buffer_binding_size: unsafe {
715 gl.get_parameter_i32(glow::MAX_UNIFORM_BLOCK_SIZE)
716 } as u32,
717 max_storage_buffer_binding_size: if supports_storage {
718 unsafe { gl.get_parameter_i32(glow::MAX_SHADER_STORAGE_BLOCK_SIZE) }
719 } else {
720 0
721 } as u32,
722 max_vertex_buffers: if private_caps
723 .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
724 {
725 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_BINDINGS) } as u32)
726 } else {
727 16 },
729 max_vertex_attributes: (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIBS) }
730 as u32)
731 .min(super::MAX_VERTEX_ATTRIBUTES as u32),
732 max_vertex_buffer_array_stride: if private_caps
733 .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
734 {
735 if let Some(full_ver) = full_ver {
736 if full_ver >= (4, 4) {
737 let value =
739 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) })
740 as u32;
741
742 if value == 0 {
743 log::debug!("Max vertex attribute stride is 0. Assuming it is the OpenGL minimum spec 2048");
747 2048
748 } else {
749 value
750 }
751 } else {
752 log::debug!("Max vertex attribute stride unknown. Assuming it is the OpenGL minimum spec 2048");
753 2048
754 }
755 } else {
756 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) }) as u32
757 }
758 } else {
759 !0
760 },
761 max_immediate_size: super::MAX_IMMEDIATES as u32 * 4,
762 min_uniform_buffer_offset_alignment,
763 min_storage_buffer_offset_alignment,
764 max_inter_stage_shader_variables: {
765 let max_varying_components =
769 unsafe { gl.get_parameter_i32(glow::MAX_VARYING_COMPONENTS) } as u32;
770 if max_varying_components == 0 {
771 15
773 } else {
774 max_varying_components / 4
775 }
776 },
777 max_color_attachments,
778 max_color_attachment_bytes_per_sample,
779 max_compute_workgroup_storage_size: if supports_work_group_params {
780 (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_SHARED_MEMORY_SIZE) } as u32)
781 } else {
782 0
783 },
784 max_compute_invocations_per_workgroup: if supports_work_group_params {
785 (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_WORK_GROUP_INVOCATIONS) } as u32)
786 } else {
787 0
788 },
789 max_compute_workgroup_size_x: if supports_work_group_params {
790 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 0) }
791 as u32)
792 } else {
793 0
794 },
795 max_compute_workgroup_size_y: if supports_work_group_params {
796 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 1) }
797 as u32)
798 } else {
799 0
800 },
801 max_compute_workgroup_size_z: if supports_work_group_params {
802 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 2) }
803 as u32)
804 } else {
805 0
806 },
807 max_compute_workgroups_per_dimension,
808 max_buffer_size: i32::MAX as u64,
809 max_non_sampler_bindings: u32::MAX,
810
811 max_task_mesh_workgroup_total_count: 0,
812 max_task_mesh_workgroups_per_dimension: 0,
813 max_task_invocations_per_workgroup: 0,
814 max_task_invocations_per_dimension: 0,
815 max_mesh_invocations_per_workgroup: 0,
816 max_mesh_invocations_per_dimension: 0,
817 max_task_payload_size: 0,
818 max_mesh_output_vertices: 0,
819 max_mesh_output_primitives: 0,
820 max_mesh_output_layers: 0,
821 max_mesh_multiview_view_count: 0,
822
823 max_blas_primitive_count: 0,
824 max_blas_geometry_count: 0,
825 max_tlas_instance_count: 0,
826 max_acceleration_structures_per_shader_stage: 0,
827
828 max_multiview_view_count: 0,
829 });
830
831 let mut workarounds = super::Workarounds::empty();
832
833 workarounds.set(
834 super::Workarounds::EMULATE_BUFFER_MAP,
835 cfg!(any(webgl, Emscripten)),
836 );
837
838 let r = renderer.to_lowercase();
839 if context.is_owned()
842 && r.contains("mesa")
843 && r.contains("intel")
844 && r.split(&[' ', '(', ')'][..])
845 .any(|substr| substr.len() == 3 && substr.chars().nth(2) == Some('l'))
846 {
847 log::debug!(
848 "Detected skylake derivative running on mesa i915. Clears to srgb textures will \
849 use manual shader clears."
850 );
851 workarounds.set(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR, true);
852 }
853
854 let downlevel_defaults = wgt::DownlevelLimits {};
855 let max_samples = unsafe { gl.get_parameter_i32(glow::MAX_SAMPLES) };
856
857 #[cfg_attr(target_arch = "wasm32", allow(dropping_references))]
861 drop(gl);
862
863 Some(crate::ExposedAdapter {
864 adapter: super::Adapter {
865 shared: Arc::new(super::AdapterShared {
866 context,
867 private_caps,
868 workarounds,
869 features,
870 limits: limits.clone(),
871 options: backend_options,
872 shading_language_version,
873 next_shader_id: Default::default(),
874 program_cache: Default::default(),
875 es: es_ver.is_some(),
876 max_msaa_samples: max_samples,
877 }),
878 },
879 info: Self::make_info(vendor, renderer, version),
880 features,
881 capabilities: crate::Capabilities {
882 limits,
883 downlevel: wgt::DownlevelCapabilities {
884 flags: downlevel_flags,
885 limits: downlevel_defaults,
886 shader_model: wgt::ShaderModel::Sm5,
887 },
888 alignments: crate::Alignments {
889 buffer_copy_offset: wgt::BufferSize::new(4).unwrap(),
890 buffer_copy_pitch: wgt::BufferSize::new(4).unwrap(),
891 uniform_bounds_check_alignment: wgt::BufferSize::new(1).unwrap(),
901 raw_tlas_instance_size: 0,
902 ray_tracing_scratch_buffer_alignment: 0,
903 },
904 cooperative_matrix_properties: Vec::new(),
905 },
906 })
907 }
908
909 unsafe fn compile_shader(
910 source: &str,
911 gl: &glow::Context,
912 shader_type: u32,
913 es: bool,
914 ) -> Option<glow::Shader> {
915 let source = if es {
916 format!("#version 300 es\nprecision lowp float;\n{source}")
917 } else {
918 let version = gl.version();
919 if version.major == 3 && version.minor == 0 {
920 format!("#version 130\n{source}")
922 } else {
923 format!("#version 140\n{source}")
925 }
926 };
927 let shader = unsafe { gl.create_shader(shader_type) }.expect("Could not create shader");
928 unsafe { gl.shader_source(shader, &source) };
929 unsafe { gl.compile_shader(shader) };
930
931 if !unsafe { gl.get_shader_compile_status(shader) } {
932 let msg = unsafe { gl.get_shader_info_log(shader) };
933 if !msg.is_empty() {
934 log::error!("\tShader compile error: {msg}");
935 }
936 unsafe { gl.delete_shader(shader) };
937 None
938 } else {
939 Some(shader)
940 }
941 }
942
943 unsafe fn create_shader_clear_program(
944 gl: &glow::Context,
945 es: bool,
946 ) -> Option<ShaderClearProgram> {
947 let program = unsafe { gl.create_program() }.expect("Could not create shader program");
948 let vertex = unsafe {
949 Self::compile_shader(
950 include_str!("./shaders/clear.vert"),
951 gl,
952 glow::VERTEX_SHADER,
953 es,
954 )?
955 };
956 let fragment = unsafe {
957 Self::compile_shader(
958 include_str!("./shaders/clear.frag"),
959 gl,
960 glow::FRAGMENT_SHADER,
961 es,
962 )?
963 };
964 unsafe { gl.attach_shader(program, vertex) };
965 unsafe { gl.attach_shader(program, fragment) };
966 unsafe { gl.link_program(program) };
967
968 let linked_ok = unsafe { gl.get_program_link_status(program) };
969 let msg = unsafe { gl.get_program_info_log(program) };
970 if !msg.is_empty() {
971 log::error!("Shader link error: {msg}");
972 }
973 if !linked_ok {
974 return None;
975 }
976
977 let color_uniform_location = unsafe { gl.get_uniform_location(program, "color") }
978 .expect("Could not find color uniform in shader clear shader");
979 unsafe { gl.delete_shader(vertex) };
980 unsafe { gl.delete_shader(fragment) };
981
982 Some(ShaderClearProgram {
983 program,
984 color_uniform_location,
985 })
986 }
987}
988
989impl crate::Adapter for super::Adapter {
990 type A = super::Api;
991
992 unsafe fn open(
993 &self,
994 features: wgt::Features,
995 _limits: &wgt::Limits,
996 _memory_hints: &wgt::MemoryHints,
997 ) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
998 let gl = &self.shared.context.lock();
999 unsafe { gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1) };
1000 unsafe { gl.pixel_store_i32(glow::PACK_ALIGNMENT, 1) };
1001 let main_vao =
1002 unsafe { gl.create_vertex_array() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
1003 unsafe { gl.bind_vertex_array(Some(main_vao)) };
1004
1005 let zero_buffer =
1006 unsafe { gl.create_buffer() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
1007 unsafe { gl.bind_buffer(glow::COPY_READ_BUFFER, Some(zero_buffer)) };
1008 let zeroes = vec![0u8; super::ZERO_BUFFER_SIZE];
1009 unsafe { gl.buffer_data_u8_slice(glow::COPY_READ_BUFFER, &zeroes, glow::STATIC_DRAW) };
1010
1011 let shader_clear_program = if self
1015 .shared
1016 .workarounds
1017 .contains(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR)
1018 {
1019 Some(unsafe {
1020 Self::create_shader_clear_program(gl, self.shared.es)
1021 .ok_or(crate::DeviceError::Lost)?
1022 })
1023 } else {
1024 None
1026 };
1027
1028 Ok(crate::OpenDevice {
1029 device: super::Device {
1030 shared: Arc::clone(&self.shared),
1031 main_vao,
1032 #[cfg(all(native, feature = "renderdoc"))]
1033 render_doc: Default::default(),
1034 counters: Default::default(),
1035 },
1036 queue: super::Queue {
1037 shared: Arc::clone(&self.shared),
1038 features,
1039 draw_fbo: unsafe { gl.create_framebuffer() }
1040 .map_err(|_| crate::DeviceError::OutOfMemory)?,
1041 copy_fbo: unsafe { gl.create_framebuffer() }
1042 .map_err(|_| crate::DeviceError::OutOfMemory)?,
1043 shader_clear_program,
1044 zero_buffer,
1045 temp_query_results: Mutex::new(Vec::new()),
1046 draw_buffer_count: AtomicU8::new(1),
1047 current_index_buffer: Mutex::new(None),
1048 },
1049 })
1050 }
1051
1052 unsafe fn texture_format_capabilities(
1053 &self,
1054 format: wgt::TextureFormat,
1055 ) -> crate::TextureFormatCapabilities {
1056 use crate::TextureFormatCapabilities as Tfc;
1057 use wgt::TextureFormat as Tf;
1058
1059 let sample_count = {
1060 let max_samples = self.shared.max_msaa_samples;
1061 if max_samples >= 16 {
1062 Tfc::MULTISAMPLE_X2
1063 | Tfc::MULTISAMPLE_X4
1064 | Tfc::MULTISAMPLE_X8
1065 | Tfc::MULTISAMPLE_X16
1066 } else if max_samples >= 8 {
1067 Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4 | Tfc::MULTISAMPLE_X8
1068 } else {
1069 Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4
1074 }
1075 };
1076
1077 let empty = Tfc::empty();
1082 let base = Tfc::COPY_SRC | Tfc::COPY_DST;
1083 let unfilterable = base | Tfc::SAMPLED;
1084 let depth = base | Tfc::SAMPLED | sample_count | Tfc::DEPTH_STENCIL_ATTACHMENT;
1085 let filterable = unfilterable | Tfc::SAMPLED_LINEAR;
1086 let renderable =
1087 unfilterable | Tfc::COLOR_ATTACHMENT | sample_count | Tfc::MULTISAMPLE_RESOLVE;
1088 let filterable_renderable = filterable | renderable | Tfc::COLOR_ATTACHMENT_BLEND;
1089 let storage =
1090 base | Tfc::STORAGE_READ_WRITE | Tfc::STORAGE_READ_ONLY | Tfc::STORAGE_WRITE_ONLY;
1091
1092 let feature_fn = |f, caps| {
1093 if self.shared.features.contains(f) {
1094 caps
1095 } else {
1096 empty
1097 }
1098 };
1099
1100 let bcn_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_BC, filterable);
1101 let etc2_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ETC2, filterable);
1102 let astc_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC, filterable);
1103 let astc_hdr_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR, filterable);
1104
1105 let private_caps_fn = |f, caps| {
1106 if self.shared.private_caps.contains(f) {
1107 caps
1108 } else {
1109 empty
1110 }
1111 };
1112
1113 let half_float_renderable = private_caps_fn(
1114 super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT,
1115 Tfc::COLOR_ATTACHMENT
1116 | Tfc::COLOR_ATTACHMENT_BLEND
1117 | sample_count
1118 | Tfc::MULTISAMPLE_RESOLVE,
1119 );
1120
1121 let float_renderable = private_caps_fn(
1122 super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
1123 Tfc::COLOR_ATTACHMENT
1124 | Tfc::COLOR_ATTACHMENT_BLEND
1125 | sample_count
1126 | Tfc::MULTISAMPLE_RESOLVE,
1127 );
1128
1129 let texture_float_linear = feature_fn(wgt::Features::FLOAT32_FILTERABLE, filterable);
1130
1131 let image_atomic = feature_fn(wgt::Features::TEXTURE_ATOMIC, Tfc::STORAGE_ATOMIC);
1132 let image_64_atomic = feature_fn(wgt::Features::TEXTURE_INT64_ATOMIC, Tfc::STORAGE_ATOMIC);
1133
1134 match format {
1135 Tf::R8Unorm => filterable_renderable,
1136 Tf::R8Snorm => filterable,
1137 Tf::R8Uint => renderable,
1138 Tf::R8Sint => renderable,
1139 Tf::R16Uint => renderable,
1140 Tf::R16Sint => renderable,
1141 Tf::R16Unorm => empty,
1142 Tf::R16Snorm => empty,
1143 Tf::R16Float => filterable | half_float_renderable,
1144 Tf::Rg8Unorm => filterable_renderable,
1145 Tf::Rg8Snorm => filterable,
1146 Tf::Rg8Uint => renderable,
1147 Tf::Rg8Sint => renderable,
1148 Tf::R32Uint => renderable | storage | image_atomic,
1149 Tf::R32Sint => renderable | storage | image_atomic,
1150 Tf::R32Float => unfilterable | storage | float_renderable | texture_float_linear,
1151 Tf::Rg16Uint => renderable,
1152 Tf::Rg16Sint => renderable,
1153 Tf::Rg16Unorm => empty,
1154 Tf::Rg16Snorm => empty,
1155 Tf::Rg16Float => filterable | half_float_renderable,
1156 Tf::Rgba8Unorm => filterable_renderable | storage,
1157 Tf::Rgba8UnormSrgb => filterable_renderable,
1158 Tf::Bgra8Unorm | Tf::Bgra8UnormSrgb => filterable_renderable,
1159 Tf::Rgba8Snorm => filterable | storage,
1160 Tf::Rgba8Uint => renderable | storage,
1161 Tf::Rgba8Sint => renderable | storage,
1162 Tf::Rgb10a2Uint => renderable,
1163 Tf::Rgb10a2Unorm => filterable_renderable,
1164 Tf::Rg11b10Ufloat => filterable | float_renderable,
1165 Tf::R64Uint => image_64_atomic,
1166 Tf::Rg32Uint => renderable,
1167 Tf::Rg32Sint => renderable,
1168 Tf::Rg32Float => unfilterable | float_renderable | texture_float_linear,
1169 Tf::Rgba16Uint => renderable | storage,
1170 Tf::Rgba16Sint => renderable | storage,
1171 Tf::Rgba16Unorm => empty,
1172 Tf::Rgba16Snorm => empty,
1173 Tf::Rgba16Float => filterable | storage | half_float_renderable,
1174 Tf::Rgba32Uint => renderable | storage,
1175 Tf::Rgba32Sint => renderable | storage,
1176 Tf::Rgba32Float => unfilterable | storage | float_renderable | texture_float_linear,
1177 Tf::Stencil8
1178 | Tf::Depth16Unorm
1179 | Tf::Depth32Float
1180 | Tf::Depth32FloatStencil8
1181 | Tf::Depth24Plus
1182 | Tf::Depth24PlusStencil8 => depth,
1183 Tf::NV12 => empty,
1184 Tf::P010 => empty,
1185 Tf::Rgb9e5Ufloat => filterable,
1186 Tf::Bc1RgbaUnorm
1187 | Tf::Bc1RgbaUnormSrgb
1188 | Tf::Bc2RgbaUnorm
1189 | Tf::Bc2RgbaUnormSrgb
1190 | Tf::Bc3RgbaUnorm
1191 | Tf::Bc3RgbaUnormSrgb
1192 | Tf::Bc4RUnorm
1193 | Tf::Bc4RSnorm
1194 | Tf::Bc5RgUnorm
1195 | Tf::Bc5RgSnorm
1196 | Tf::Bc6hRgbFloat
1197 | Tf::Bc6hRgbUfloat
1198 | Tf::Bc7RgbaUnorm
1199 | Tf::Bc7RgbaUnormSrgb => bcn_features,
1200 Tf::Etc2Rgb8Unorm
1201 | Tf::Etc2Rgb8UnormSrgb
1202 | Tf::Etc2Rgb8A1Unorm
1203 | Tf::Etc2Rgb8A1UnormSrgb
1204 | Tf::Etc2Rgba8Unorm
1205 | Tf::Etc2Rgba8UnormSrgb
1206 | Tf::EacR11Unorm
1207 | Tf::EacR11Snorm
1208 | Tf::EacRg11Unorm
1209 | Tf::EacRg11Snorm => etc2_features,
1210 Tf::Astc {
1211 block: _,
1212 channel: AstcChannel::Unorm | AstcChannel::UnormSrgb,
1213 } => astc_features,
1214 Tf::Astc {
1215 block: _,
1216 channel: AstcChannel::Hdr,
1217 } => astc_hdr_features,
1218 }
1219 }
1220
1221 unsafe fn surface_capabilities(
1222 &self,
1223 surface: &super::Surface,
1224 ) -> Option<crate::SurfaceCapabilities> {
1225 #[cfg(webgl)]
1226 if self.shared.context.webgl2_context != surface.webgl2_context {
1227 return None;
1228 }
1229
1230 if surface.presentable {
1231 let mut formats = vec![
1232 wgt::TextureFormat::Rgba8Unorm,
1233 #[cfg(native)]
1234 wgt::TextureFormat::Bgra8Unorm,
1235 ];
1236 if surface.supports_srgb() {
1237 formats.extend([
1238 wgt::TextureFormat::Rgba8UnormSrgb,
1239 #[cfg(native)]
1240 wgt::TextureFormat::Bgra8UnormSrgb,
1241 ])
1242 }
1243 if self
1244 .shared
1245 .private_caps
1246 .contains(super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT)
1247 {
1248 formats.push(wgt::TextureFormat::Rgba16Float)
1249 }
1250
1251 Some(crate::SurfaceCapabilities {
1252 formats,
1253 present_modes: if cfg!(windows) {
1254 vec![wgt::PresentMode::Fifo, wgt::PresentMode::Immediate]
1255 } else {
1256 vec![wgt::PresentMode::Fifo] },
1258 composite_alpha_modes: vec![wgt::CompositeAlphaMode::Opaque], maximum_frame_latency: 2..=2, current_extent: None,
1261 usage: wgt::TextureUses::COLOR_TARGET,
1262 })
1263 } else {
1264 None
1265 }
1266 }
1267
1268 unsafe fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
1269 wgt::PresentationTimestamp::INVALID_TIMESTAMP
1270 }
1271}
1272
1273impl super::AdapterShared {
1274 pub(super) unsafe fn get_buffer_sub_data(
1275 &self,
1276 gl: &glow::Context,
1277 target: u32,
1278 offset: i32,
1279 dst_data: &mut [u8],
1280 ) {
1281 if self
1282 .private_caps
1283 .contains(super::PrivateCapabilities::GET_BUFFER_SUB_DATA)
1284 {
1285 unsafe { gl.get_buffer_sub_data(target, offset, dst_data) };
1286 } else {
1287 log::error!("Fake map");
1288 let length = dst_data.len();
1289 let buffer_mapping =
1290 unsafe { gl.map_buffer_range(target, offset, length as _, glow::MAP_READ_BIT) };
1291
1292 unsafe {
1293 core::ptr::copy_nonoverlapping(buffer_mapping, dst_data.as_mut_ptr(), length)
1294 };
1295
1296 unsafe { gl.unmap_buffer(target) };
1297 }
1298 }
1299}
1300
1301#[cfg(send_sync)]
1302unsafe impl Sync for super::Adapter {}
1303#[cfg(send_sync)]
1304unsafe impl Send for super::Adapter {}
1305
1306#[cfg(test)]
1307mod tests {
1308 use super::super::Adapter;
1309
1310 #[test]
1311 fn test_version_parse() {
1312 Adapter::parse_version("1").unwrap_err();
1313 Adapter::parse_version("1.").unwrap_err();
1314 Adapter::parse_version("1 h3l1o. W0rld").unwrap_err();
1315 Adapter::parse_version("1. h3l1o. W0rld").unwrap_err();
1316 Adapter::parse_version("1.2.3").unwrap_err();
1317
1318 assert_eq!(Adapter::parse_version("OpenGL ES 3.1").unwrap(), (3, 1));
1319 assert_eq!(
1320 Adapter::parse_version("OpenGL ES 2.0 Google Nexus").unwrap(),
1321 (2, 0)
1322 );
1323 assert_eq!(Adapter::parse_version("GLSL ES 1.1").unwrap(), (1, 1));
1324 assert_eq!(
1325 Adapter::parse_version("OpenGL ES GLSL ES 3.20").unwrap(),
1326 (3, 2)
1327 );
1328 assert_eq!(
1329 Adapter::parse_version("WebGL 2.0 (OpenGL ES 3.0 Chromium)").unwrap(),
1331 (3, 0)
1332 );
1333 assert_eq!(
1334 Adapter::parse_version("WebGL GLSL ES 3.00 (OpenGL ES GLSL ES 3.0 Chromium)").unwrap(),
1335 (3, 0)
1336 );
1337 }
1338}