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 driver_info: version,
188 ..wgt::AdapterInfo::new(inferred_device_type, wgt::Backend::Gl)
189 }
190 }
191
192 pub(super) unsafe fn expose(
193 context: super::AdapterContext,
194 backend_options: wgt::GlBackendOptions,
195 ) -> Option<crate::ExposedAdapter<super::Api>> {
196 let gl = context.lock();
197 let extensions = gl.supported_extensions();
198
199 let (vendor_const, renderer_const) = if extensions.contains("WEBGL_debug_renderer_info") {
200 #[cfg(Emscripten)]
203 if unsafe {
204 super::emscripten::enable_extension(c"WEBGL_debug_renderer_info".to_str().unwrap())
205 } {
206 (GL_UNMASKED_VENDOR_WEBGL, GL_UNMASKED_RENDERER_WEBGL)
207 } else {
208 (glow::VENDOR, glow::RENDERER)
209 }
210 #[cfg(not(Emscripten))]
212 (GL_UNMASKED_VENDOR_WEBGL, GL_UNMASKED_RENDERER_WEBGL)
213 } else {
214 (glow::VENDOR, glow::RENDERER)
215 };
216
217 let vendor = unsafe { gl.get_parameter_string(vendor_const) };
218 let renderer = unsafe { gl.get_parameter_string(renderer_const) };
219 let version = unsafe { gl.get_parameter_string(glow::VERSION) };
220 log::debug!("Vendor: {vendor}");
221 log::debug!("Renderer: {renderer}");
222 log::debug!("Version: {version}");
223
224 let full_ver = Self::parse_full_version(&version).ok();
225 let es_ver = full_ver.map_or_else(|| Self::parse_version(&version).ok(), |_| None);
226
227 if let Some(full_ver) = full_ver {
228 let core_profile = (full_ver >= (3, 2)).then(|| unsafe {
229 gl.get_parameter_i32(glow::CONTEXT_PROFILE_MASK)
230 & glow::CONTEXT_CORE_PROFILE_BIT as i32
231 != 0
232 });
233 log::trace!(
234 "Profile: {}",
235 core_profile
236 .map(|core_profile| if core_profile {
237 "Core"
238 } else {
239 "Compatibility"
240 })
241 .unwrap_or("Legacy")
242 );
243 }
244
245 if es_ver.is_none() && full_ver.is_none() {
246 log::warn!("Unable to parse OpenGL version");
247 return None;
248 }
249
250 if let Some(es_ver) = es_ver {
251 if es_ver < (3, 0) {
252 log::warn!(
253 "Returned GLES context is {}.{}, when 3.0+ was requested",
254 es_ver.0,
255 es_ver.1
256 );
257 return None;
258 }
259 }
260
261 if let Some(full_ver) = full_ver {
262 if full_ver < (3, 3) {
263 log::warn!(
264 "Returned GL context is {}.{}, when 3.3+ is needed",
265 full_ver.0,
266 full_ver.1
267 );
268 return None;
269 }
270 }
271
272 let shading_language_version = {
273 let sl_version = unsafe { gl.get_parameter_string(glow::SHADING_LANGUAGE_VERSION) };
274 log::debug!("SL version: {}", &sl_version);
275 if full_ver.is_some() {
276 let (sl_major, sl_minor) = Self::parse_full_version(&sl_version).ok()?;
277 let mut value = sl_major as u16 * 100 + sl_minor as u16 * 10;
278 if value > 450 {
280 value = 450;
281 }
282 naga::back::glsl::Version::Desktop(value)
283 } else {
284 let (sl_major, sl_minor) = Self::parse_version(&sl_version).ok()?;
285 let value = sl_major as u16 * 100 + sl_minor as u16 * 10;
286 naga::back::glsl::Version::Embedded {
287 version: value,
288 is_webgl: cfg!(any(webgl, Emscripten)),
289 }
290 }
291 };
292
293 log::debug!("Supported GL Extensions: {extensions:#?}");
294
295 let supported = |(req_es_major, req_es_minor), (req_full_major, req_full_minor)| {
296 let es_supported = es_ver
297 .map(|es_ver| es_ver >= (req_es_major, req_es_minor))
298 .unwrap_or_default();
299
300 let full_supported = full_ver
301 .map(|full_ver| full_ver >= (req_full_major, req_full_minor))
302 .unwrap_or_default();
303
304 es_supported || full_supported
305 };
306
307 let supports_storage =
308 supported((3, 1), (4, 3)) || extensions.contains("GL_ARB_shader_storage_buffer_object");
309 let supports_compute =
310 supported((3, 1), (4, 3)) || extensions.contains("GL_ARB_compute_shader");
311 let supports_work_group_params = supports_compute;
312
313 let is_angle = renderer.contains("ANGLE");
315
316 let vertex_shader_storage_blocks = if supports_storage {
317 let value =
318 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_SHADER_STORAGE_BLOCKS) } as u32);
319
320 if value == 0 && extensions.contains("GL_ARB_shader_storage_buffer_object") {
321 let new = (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_SHADER_STORAGE_BLOCKS) }
324 as u32);
325 log::debug!("Max vertex shader storage blocks is zero, but GL_ARB_shader_storage_buffer_object is specified. Assuming the compute value {new}");
326 new
327 } else {
328 value
329 }
330 } else {
331 0
332 };
333 let fragment_shader_storage_blocks = if supports_storage {
334 (unsafe { gl.get_parameter_i32(glow::MAX_FRAGMENT_SHADER_STORAGE_BLOCKS) } as u32)
335 } else {
336 0
337 };
338 let vertex_shader_storage_textures = if supports_storage {
339 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_IMAGE_UNIFORMS) } as u32)
340 } else {
341 0
342 };
343 let fragment_shader_storage_textures = if supports_storage {
344 (unsafe { gl.get_parameter_i32(glow::MAX_FRAGMENT_IMAGE_UNIFORMS) } as u32)
345 } else {
346 0
347 };
348 let max_storage_block_size = if supports_storage {
349 (unsafe { gl.get_parameter_i32(glow::MAX_SHADER_STORAGE_BLOCK_SIZE) } as u32)
350 } else {
351 0
352 };
353 let max_element_index = unsafe { gl.get_parameter_i32(glow::MAX_ELEMENT_INDEX) } as u32;
354
355 let vertex_ssbo_false_zero =
361 vertex_shader_storage_blocks == 0 && vertex_shader_storage_textures != 0;
362 if vertex_ssbo_false_zero {
363 log::debug!("Max vertex shader SSBO == 0 and SSTO != 0. Interpreting as false zero.");
365 }
366
367 let max_storage_buffers_per_shader_stage = if vertex_shader_storage_blocks == 0 {
368 fragment_shader_storage_blocks
369 } else {
370 vertex_shader_storage_blocks.min(fragment_shader_storage_blocks)
371 };
372 let max_storage_textures_per_shader_stage = if vertex_shader_storage_textures == 0 {
373 fragment_shader_storage_textures
374 } else {
375 vertex_shader_storage_textures.min(fragment_shader_storage_textures)
376 };
377 let indirect_execution = supported((3, 1), (4, 3))
379 || (extensions.contains("GL_ARB_draw_indirect") && supports_compute);
380 let supports_cube_array = supported((3, 2), (4, 0))
381 || (supported((3, 1), (4, 0)) && extensions.contains("GL_EXT_texture_cube_map_array"));
382
383 let mut downlevel_flags = wgt::DownlevelFlags::empty()
384 | wgt::DownlevelFlags::NON_POWER_OF_TWO_MIPMAPPED_TEXTURES
385 | wgt::DownlevelFlags::COMPARISON_SAMPLERS
386 | wgt::DownlevelFlags::SHADER_F16_IN_F32;
387 downlevel_flags.set(
388 wgt::DownlevelFlags::CUBE_ARRAY_TEXTURES,
389 supports_cube_array,
390 );
391 downlevel_flags.set(wgt::DownlevelFlags::COMPUTE_SHADERS, supports_compute);
392 downlevel_flags.set(
393 wgt::DownlevelFlags::FRAGMENT_WRITABLE_STORAGE,
394 max_storage_block_size != 0,
395 );
396 downlevel_flags.set(wgt::DownlevelFlags::INDIRECT_EXECUTION, indirect_execution);
397 downlevel_flags.set(wgt::DownlevelFlags::BASE_VERTEX, supported((3, 2), (3, 2)));
398 downlevel_flags.set(
399 wgt::DownlevelFlags::INDEPENDENT_BLEND,
400 supported((3, 2), (4, 0)) || extensions.contains("GL_EXT_draw_buffers_indexed"),
401 );
402 downlevel_flags.set(
403 wgt::DownlevelFlags::VERTEX_STORAGE,
404 max_storage_block_size != 0
405 && max_storage_buffers_per_shader_stage != 0
406 && (vertex_shader_storage_blocks != 0 || vertex_ssbo_false_zero),
407 );
408 downlevel_flags.set(wgt::DownlevelFlags::FRAGMENT_STORAGE, supports_storage);
409 if extensions.contains("EXT_texture_filter_anisotropic")
410 || extensions.contains("GL_EXT_texture_filter_anisotropic")
411 {
412 let max_aniso =
413 unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_MAX_ANISOTROPY_EXT) } as u32;
414 downlevel_flags.set(wgt::DownlevelFlags::ANISOTROPIC_FILTERING, max_aniso >= 16);
415 }
416 downlevel_flags.set(
417 wgt::DownlevelFlags::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED,
418 !(cfg!(any(webgl, Emscripten)) || is_angle),
419 );
420 downlevel_flags.set(
422 wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER,
423 !cfg!(any(webgl, Emscripten)),
424 );
425 downlevel_flags.set(
426 wgt::DownlevelFlags::UNRESTRICTED_EXTERNAL_TEXTURE_COPIES,
427 !cfg!(any(webgl, Emscripten)),
428 );
429 downlevel_flags.set(
430 wgt::DownlevelFlags::FULL_DRAW_INDEX_UINT32,
431 max_element_index == u32::MAX,
432 );
433 downlevel_flags.set(
434 wgt::DownlevelFlags::MULTISAMPLED_SHADING,
435 supported((3, 2), (4, 0)) || extensions.contains("OES_sample_variables"),
436 );
437 let query_buffers = extensions.contains("GL_ARB_query_buffer_object")
438 || extensions.contains("GL_AMD_query_buffer_object");
439 if query_buffers {
440 downlevel_flags.set(wgt::DownlevelFlags::NONBLOCKING_QUERY_RESOLVE, true);
441 }
442
443 let mut features = wgt::Features::empty()
444 | wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES
445 | wgt::Features::CLEAR_TEXTURE
446 | wgt::Features::IMMEDIATES
447 | wgt::Features::DEPTH32FLOAT_STENCIL8;
448 features.set(
449 wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER | wgt::Features::ADDRESS_MODE_CLAMP_TO_ZERO,
450 extensions.contains("GL_EXT_texture_border_clamp")
451 || extensions.contains("GL_ARB_texture_border_clamp"),
452 );
453 features.set(
454 wgt::Features::DEPTH_CLIP_CONTROL,
455 extensions.contains("GL_EXT_depth_clamp") || extensions.contains("GL_ARB_depth_clamp"),
456 );
457 features.set(
458 wgt::Features::VERTEX_WRITABLE_STORAGE,
459 downlevel_flags.contains(wgt::DownlevelFlags::VERTEX_STORAGE)
460 && vertex_shader_storage_textures != 0,
461 );
462 features.set(
463 wgt::Features::MULTIVIEW,
464 extensions.contains("OVR_multiview2") || extensions.contains("GL_OVR_multiview2"),
465 );
466 features.set(
467 wgt::Features::DUAL_SOURCE_BLENDING,
468 extensions.contains("GL_EXT_blend_func_extended")
469 || extensions.contains("GL_ARB_blend_func_extended"),
470 );
471 features.set(
472 wgt::Features::CLIP_DISTANCES,
473 full_ver.is_some() || extensions.contains("GL_EXT_clip_cull_distance"),
474 );
475 features.set(
476 wgt::Features::PRIMITIVE_INDEX,
477 supported((3, 2), (3, 2))
478 || extensions.contains("OES_geometry_shader")
479 || extensions.contains("GL_ARB_geometry_shader4"),
480 );
481 features.set(
482 wgt::Features::SHADER_EARLY_DEPTH_TEST,
483 supported((3, 1), (4, 2)) || extensions.contains("GL_ARB_shader_image_load_store"),
484 );
485 if extensions.contains("GL_ARB_timer_query") {
486 features.set(wgt::Features::TIMESTAMP_QUERY, true);
487 features.set(wgt::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS, true);
488 features.set(wgt::Features::TIMESTAMP_QUERY_INSIDE_PASSES, true);
489 }
490 let gl_bcn_exts = [
491 "GL_EXT_texture_compression_s3tc",
492 "GL_EXT_texture_compression_rgtc",
493 "GL_ARB_texture_compression_bptc",
494 ];
495 let gles_bcn_exts = [
496 "GL_EXT_texture_compression_s3tc_srgb",
497 "GL_EXT_texture_compression_rgtc",
498 "GL_EXT_texture_compression_bptc",
499 ];
500 let webgl_bcn_exts = [
501 "WEBGL_compressed_texture_s3tc",
502 "WEBGL_compressed_texture_s3tc_srgb",
503 "EXT_texture_compression_rgtc",
504 "EXT_texture_compression_bptc",
505 ];
506 let bcn_exts = if cfg!(any(webgl, Emscripten)) {
507 &webgl_bcn_exts[..]
508 } else if es_ver.is_some() {
509 &gles_bcn_exts[..]
510 } else {
511 &gl_bcn_exts[..]
512 };
513 features.set(
514 wgt::Features::TEXTURE_COMPRESSION_BC,
515 bcn_exts.iter().all(|&ext| extensions.contains(ext)),
516 );
517 features.set(
518 wgt::Features::TEXTURE_COMPRESSION_BC_SLICED_3D,
519 bcn_exts.iter().all(|&ext| extensions.contains(ext)), );
521 let has_etc = if cfg!(any(webgl, Emscripten)) {
522 extensions.contains("WEBGL_compressed_texture_etc")
523 } else {
524 es_ver.is_some() || extensions.contains("GL_ARB_ES3_compatibility")
525 };
526 features.set(wgt::Features::TEXTURE_COMPRESSION_ETC2, has_etc);
527
528 if extensions.contains("WEBGL_compressed_texture_astc")
530 || extensions.contains("GL_OES_texture_compression_astc")
531 {
532 #[cfg(webgl)]
533 {
534 if context
535 .glow_context
536 .compressed_texture_astc_supports_ldr_profile()
537 {
538 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC);
539 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D);
540 }
541 if context
542 .glow_context
543 .compressed_texture_astc_supports_hdr_profile()
544 {
545 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR);
546 }
547 }
548
549 #[cfg(any(native, Emscripten))]
550 {
551 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC);
552 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D);
553 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR);
554 }
555 } else {
556 features.set(
557 wgt::Features::TEXTURE_COMPRESSION_ASTC,
558 extensions.contains("GL_KHR_texture_compression_astc_ldr"),
559 );
560 features.set(
561 wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D,
562 extensions.contains("GL_KHR_texture_compression_astc_ldr")
563 && extensions.contains("GL_KHR_texture_compression_astc_sliced_3d"),
564 );
565 features.set(
566 wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR,
567 extensions.contains("GL_KHR_texture_compression_astc_hdr"),
568 );
569 }
570
571 features.set(
572 wgt::Features::FLOAT32_FILTERABLE,
573 extensions.contains("GL_ARB_color_buffer_float")
574 || extensions.contains("GL_EXT_color_buffer_float")
575 || extensions.contains("OES_texture_float_linear"),
576 );
577
578 if es_ver.is_none() {
579 features |= wgt::Features::POLYGON_MODE_LINE | wgt::Features::POLYGON_MODE_POINT;
580 }
581
582 let mut private_caps = super::PrivateCapabilities::empty();
585 private_caps.set(
586 super::PrivateCapabilities::BUFFER_ALLOCATION,
587 extensions.contains("GL_EXT_buffer_storage")
588 || extensions.contains("GL_ARB_buffer_storage"),
589 );
590 private_caps.set(
591 super::PrivateCapabilities::SHADER_BINDING_LAYOUT,
592 supports_compute,
593 );
594 private_caps.set(
595 super::PrivateCapabilities::SHADER_TEXTURE_SHADOW_LOD,
596 extensions.contains("GL_EXT_texture_shadow_lod"),
597 );
598 private_caps.set(
599 super::PrivateCapabilities::MEMORY_BARRIERS,
600 supported((3, 1), (4, 2)),
601 );
602 private_caps.set(
603 super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT,
604 supported((3, 1), (4, 3)) || extensions.contains("GL_ARB_vertex_attrib_binding"),
605 );
606 private_caps.set(
607 super::PrivateCapabilities::INDEX_BUFFER_ROLE_CHANGE,
608 !cfg!(any(webgl, Emscripten)),
609 );
610 private_caps.set(
611 super::PrivateCapabilities::GET_BUFFER_SUB_DATA,
612 cfg!(any(webgl, Emscripten)) || full_ver.is_some(),
613 );
614 let color_buffer_float = extensions.contains("GL_EXT_color_buffer_float")
615 || extensions.contains("GL_ARB_color_buffer_float")
616 || extensions.contains("EXT_color_buffer_float");
617 let color_buffer_half_float = extensions.contains("GL_EXT_color_buffer_half_float")
618 || extensions.contains("GL_ARB_half_float_pixel");
619 private_caps.set(
620 super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT,
621 color_buffer_half_float || color_buffer_float,
622 );
623 private_caps.set(
624 super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
625 color_buffer_float,
626 );
627 private_caps.set(super::PrivateCapabilities::QUERY_BUFFERS, query_buffers);
628 private_caps.set(super::PrivateCapabilities::QUERY_64BIT, full_ver.is_some());
629 private_caps.set(
630 super::PrivateCapabilities::TEXTURE_STORAGE,
631 supported((3, 0), (4, 2)),
632 );
633 let is_mali = renderer.to_lowercase().contains("mali");
634 let debug_fns_enabled = match backend_options.debug_fns {
635 wgt::GlDebugFns::Auto => gl.supports_debug() && !is_mali,
636 wgt::GlDebugFns::ForceEnabled => gl.supports_debug(),
637 wgt::GlDebugFns::Disabled => false,
638 };
639 private_caps.set(super::PrivateCapabilities::DEBUG_FNS, debug_fns_enabled);
640 private_caps.set(
641 super::PrivateCapabilities::INVALIDATE_FRAMEBUFFER,
642 supported((3, 0), (4, 3)),
643 );
644 if let Some(full_ver) = full_ver {
645 let supported =
646 full_ver >= (4, 2) && extensions.contains("GL_ARB_shader_draw_parameters");
647 private_caps.set(
648 super::PrivateCapabilities::FULLY_FEATURED_INSTANCING,
649 supported,
650 );
651 features.set(wgt::Features::INDIRECT_FIRST_INSTANCE, supported);
658 }
659 private_caps.set(
660 super::PrivateCapabilities::MULTISAMPLED_RENDER_TO_TEXTURE,
661 extensions.contains("GL_EXT_multisampled_render_to_texture"),
662 );
663
664 if supports_storage {
667 features |= wgt::Features::MEMORY_DECORATION_COHERENT
668 | wgt::Features::MEMORY_DECORATION_VOLATILE;
669 }
670
671 let max_texture_size = unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_SIZE) } as u32;
672 let max_texture_3d_size = unsafe { gl.get_parameter_i32(glow::MAX_3D_TEXTURE_SIZE) } as u32;
673
674 let min_uniform_buffer_offset_alignment =
675 (unsafe { gl.get_parameter_i32(glow::UNIFORM_BUFFER_OFFSET_ALIGNMENT) } as u32);
676 let min_storage_buffer_offset_alignment = if supports_storage {
677 (unsafe { gl.get_parameter_i32(glow::SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT) } as u32)
678 } else {
679 256
680 };
681 let max_uniform_buffers_per_shader_stage =
682 unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_UNIFORM_BLOCKS) }
683 .min(unsafe { gl.get_parameter_i32(glow::MAX_FRAGMENT_UNIFORM_BLOCKS) })
684 as u32;
685
686 let max_compute_workgroups_per_dimension = if supports_work_group_params {
687 unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 0) }
688 .min(unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 1) })
689 .min(unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 2) })
690 as u32
691 } else {
692 0
693 };
694
695 let max_color_attachments = unsafe {
696 gl.get_parameter_i32(glow::MAX_COLOR_ATTACHMENTS)
697 .min(gl.get_parameter_i32(glow::MAX_DRAW_BUFFERS)) as u32
698 };
699
700 let max_color_attachment_bytes_per_sample =
702 max_color_attachments * wgt::TextureFormat::MAX_TARGET_PIXEL_BYTE_COST;
703
704 let limits = crate::auxil::adjust_raw_limits(wgt::Limits {
705 max_texture_dimension_1d: max_texture_size,
706 max_texture_dimension_2d: max_texture_size,
707 max_texture_dimension_3d: max_texture_3d_size,
708 max_texture_array_layers: unsafe {
709 gl.get_parameter_i32(glow::MAX_ARRAY_TEXTURE_LAYERS)
710 } as u32,
711 max_bind_groups: crate::MAX_BIND_GROUPS as u32,
712 max_bindings_per_bind_group: u32::MAX,
714 max_dynamic_uniform_buffers_per_pipeline_layout: max_uniform_buffers_per_shader_stage,
715 max_dynamic_storage_buffers_per_pipeline_layout: max_storage_buffers_per_shader_stage,
716 max_sampled_textures_per_shader_stage: super::MAX_TEXTURE_SLOTS as u32,
717 max_samplers_per_shader_stage: super::MAX_SAMPLERS as u32,
718 max_storage_buffers_per_shader_stage,
719 max_storage_textures_per_shader_stage,
720 max_uniform_buffers_per_shader_stage,
721 max_binding_array_elements_per_shader_stage: 0,
722 max_binding_array_sampler_elements_per_shader_stage: 0,
723 max_binding_array_acceleration_structure_elements_per_shader_stage: 0,
724 max_uniform_buffer_binding_size: unsafe {
725 gl.get_parameter_i32(glow::MAX_UNIFORM_BLOCK_SIZE)
726 } as u64,
727 max_storage_buffer_binding_size: if supports_storage {
728 unsafe { gl.get_parameter_i32(glow::MAX_SHADER_STORAGE_BLOCK_SIZE) }
729 } else {
730 0
731 } as u64,
732 max_vertex_buffers: if private_caps
733 .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
734 {
735 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_BINDINGS) } as u32)
736 } else {
737 16 },
739 max_vertex_attributes: (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIBS) }
740 as u32)
741 .min(super::MAX_VERTEX_ATTRIBUTES as u32),
742 max_vertex_buffer_array_stride: if private_caps
743 .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
744 {
745 if let Some(full_ver) = full_ver {
746 if full_ver >= (4, 4) {
747 let value =
749 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) })
750 as u32;
751
752 if value == 0 {
753 log::debug!("Max vertex attribute stride is 0. Assuming it is the OpenGL minimum spec 2048");
757 2048
758 } else {
759 value
760 }
761 } else {
762 log::debug!("Max vertex attribute stride unknown. Assuming it is the OpenGL minimum spec 2048");
763 2048
764 }
765 } else {
766 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) }) as u32
767 }
768 } else {
769 !0
770 },
771 max_immediate_size: super::MAX_IMMEDIATES as u32 * 4,
772 min_uniform_buffer_offset_alignment,
773 min_storage_buffer_offset_alignment,
774 max_inter_stage_shader_variables: {
775 let max_varying_components =
779 unsafe { gl.get_parameter_i32(glow::MAX_VARYING_COMPONENTS) } as u32;
780 if max_varying_components == 0 {
781 15
783 } else {
784 max_varying_components / 4
785 }
786 },
787 max_color_attachments,
788 max_color_attachment_bytes_per_sample,
789 max_compute_workgroup_storage_size: if supports_work_group_params {
790 (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_SHARED_MEMORY_SIZE) } as u32)
791 } else {
792 0
793 },
794 max_compute_invocations_per_workgroup: if supports_work_group_params {
795 (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_WORK_GROUP_INVOCATIONS) } as u32)
796 } else {
797 0
798 },
799 max_compute_workgroup_size_x: if supports_work_group_params {
800 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 0) }
801 as u32)
802 } else {
803 0
804 },
805 max_compute_workgroup_size_y: if supports_work_group_params {
806 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 1) }
807 as u32)
808 } else {
809 0
810 },
811 max_compute_workgroup_size_z: if supports_work_group_params {
812 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 2) }
813 as u32)
814 } else {
815 0
816 },
817 max_compute_workgroups_per_dimension,
818 max_buffer_size: i32::MAX as u64,
819 max_non_sampler_bindings: u32::MAX,
820
821 max_task_mesh_workgroup_total_count: 0,
822 max_task_mesh_workgroups_per_dimension: 0,
823 max_task_invocations_per_workgroup: 0,
824 max_task_invocations_per_dimension: 0,
825 max_mesh_invocations_per_workgroup: 0,
826 max_mesh_invocations_per_dimension: 0,
827 max_task_payload_size: 0,
828 max_mesh_output_vertices: 0,
829 max_mesh_output_primitives: 0,
830 max_mesh_output_layers: 0,
831 max_mesh_multiview_view_count: 0,
832
833 max_blas_primitive_count: 0,
834 max_blas_geometry_count: 0,
835 max_tlas_instance_count: 0,
836 max_acceleration_structures_per_shader_stage: 0,
837
838 max_multiview_view_count: 0,
839 });
840
841 let mut workarounds = super::Workarounds::empty();
842
843 workarounds.set(
844 super::Workarounds::EMULATE_BUFFER_MAP,
845 cfg!(any(webgl, Emscripten)),
846 );
847
848 let r = renderer.to_lowercase();
849 if context.is_owned()
852 && r.contains("mesa")
853 && r.contains("intel")
854 && r.split(&[' ', '(', ')'][..])
855 .any(|substr| substr.len() == 3 && substr.chars().nth(2) == Some('l'))
856 {
857 log::debug!(
858 "Detected skylake derivative running on mesa i915. Clears to srgb textures will \
859 use manual shader clears."
860 );
861 workarounds.set(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR, true);
862 }
863
864 let downlevel_defaults = wgt::DownlevelLimits {};
865 let max_samples = unsafe { gl.get_parameter_i32(glow::MAX_SAMPLES) };
866
867 #[cfg_attr(target_arch = "wasm32", allow(dropping_references))]
871 drop(gl);
872
873 Some(crate::ExposedAdapter {
874 adapter: super::Adapter {
875 shared: Arc::new(super::AdapterShared {
876 context,
877 private_caps,
878 workarounds,
879 features,
880 limits: limits.clone(),
881 options: backend_options,
882 shading_language_version,
883 next_shader_id: Default::default(),
884 program_cache: Default::default(),
885 es: es_ver.is_some(),
886 max_msaa_samples: max_samples,
887 }),
888 },
889 info: Self::make_info(vendor, renderer, version),
890 features,
891 capabilities: crate::Capabilities {
892 limits,
893 downlevel: wgt::DownlevelCapabilities {
894 flags: downlevel_flags,
895 limits: downlevel_defaults,
896 shader_model: wgt::ShaderModel::Sm5,
897 },
898 alignments: crate::Alignments {
899 buffer_copy_offset: wgt::BufferSize::new(4).unwrap(),
900 buffer_copy_pitch: wgt::BufferSize::new(4).unwrap(),
901 uniform_bounds_check_alignment: wgt::BufferSize::new(1).unwrap(),
911 raw_tlas_instance_size: 0,
912 ray_tracing_scratch_buffer_alignment: 0,
913 },
914 cooperative_matrix_properties: Vec::new(),
915 },
916 })
917 }
918
919 unsafe fn compile_shader(
920 source: &str,
921 gl: &glow::Context,
922 shader_type: u32,
923 es: bool,
924 ) -> Option<glow::Shader> {
925 let source = if es {
926 format!("#version 300 es\nprecision lowp float;\n{source}")
927 } else {
928 let version = gl.version();
929 if version.major == 3 && version.minor == 0 {
930 format!("#version 130\n{source}")
932 } else {
933 format!("#version 140\n{source}")
935 }
936 };
937 let shader = unsafe { gl.create_shader(shader_type) }.expect("Could not create shader");
938 unsafe { gl.shader_source(shader, &source) };
939 unsafe { gl.compile_shader(shader) };
940
941 if !unsafe { gl.get_shader_compile_status(shader) } {
942 let msg = unsafe { gl.get_shader_info_log(shader) };
943 if !msg.is_empty() {
944 log::error!("\tShader compile error: {msg}");
945 }
946 unsafe { gl.delete_shader(shader) };
947 None
948 } else {
949 Some(shader)
950 }
951 }
952
953 unsafe fn create_shader_clear_program(
954 gl: &glow::Context,
955 es: bool,
956 ) -> Option<ShaderClearProgram> {
957 let program = unsafe { gl.create_program() }.expect("Could not create shader program");
958 let vertex = unsafe {
959 Self::compile_shader(
960 include_str!("./shaders/clear.vert"),
961 gl,
962 glow::VERTEX_SHADER,
963 es,
964 )?
965 };
966 let fragment = unsafe {
967 Self::compile_shader(
968 include_str!("./shaders/clear.frag"),
969 gl,
970 glow::FRAGMENT_SHADER,
971 es,
972 )?
973 };
974 unsafe { gl.attach_shader(program, vertex) };
975 unsafe { gl.attach_shader(program, fragment) };
976 unsafe { gl.link_program(program) };
977
978 let linked_ok = unsafe { gl.get_program_link_status(program) };
979 let msg = unsafe { gl.get_program_info_log(program) };
980 if !msg.is_empty() {
981 log::error!("Shader link error: {msg}");
982 }
983 if !linked_ok {
984 return None;
985 }
986
987 let color_uniform_location = unsafe { gl.get_uniform_location(program, "color") }
988 .expect("Could not find color uniform in shader clear shader");
989 unsafe { gl.delete_shader(vertex) };
990 unsafe { gl.delete_shader(fragment) };
991
992 Some(ShaderClearProgram {
993 program,
994 color_uniform_location,
995 })
996 }
997}
998
999impl crate::Adapter for super::Adapter {
1000 type A = super::Api;
1001
1002 unsafe fn open(
1003 &self,
1004 features: wgt::Features,
1005 _limits: &wgt::Limits,
1006 _memory_hints: &wgt::MemoryHints,
1007 ) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
1008 let gl = &self.shared.context.lock();
1009 unsafe { gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1) };
1010 unsafe { gl.pixel_store_i32(glow::PACK_ALIGNMENT, 1) };
1011 let main_vao =
1012 unsafe { gl.create_vertex_array() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
1013 unsafe { gl.bind_vertex_array(Some(main_vao)) };
1014
1015 let zero_buffer =
1016 unsafe { gl.create_buffer() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
1017 unsafe { gl.bind_buffer(glow::COPY_READ_BUFFER, Some(zero_buffer)) };
1018 let zeroes = vec![0u8; super::ZERO_BUFFER_SIZE];
1019 unsafe { gl.buffer_data_u8_slice(glow::COPY_READ_BUFFER, &zeroes, glow::STATIC_DRAW) };
1020
1021 let shader_clear_program = if self
1025 .shared
1026 .workarounds
1027 .contains(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR)
1028 {
1029 Some(unsafe {
1030 Self::create_shader_clear_program(gl, self.shared.es)
1031 .ok_or(crate::DeviceError::Lost)?
1032 })
1033 } else {
1034 None
1036 };
1037
1038 Ok(crate::OpenDevice {
1039 device: super::Device {
1040 shared: Arc::clone(&self.shared),
1041 main_vao,
1042 #[cfg(all(native, feature = "renderdoc"))]
1043 render_doc: Default::default(),
1044 counters: Default::default(),
1045 },
1046 queue: super::Queue {
1047 shared: Arc::clone(&self.shared),
1048 features,
1049 draw_fbo: unsafe { gl.create_framebuffer() }
1050 .map_err(|_| crate::DeviceError::OutOfMemory)?,
1051 copy_fbo: unsafe { gl.create_framebuffer() }
1052 .map_err(|_| crate::DeviceError::OutOfMemory)?,
1053 shader_clear_program,
1054 zero_buffer,
1055 temp_query_results: Mutex::new(Vec::new()),
1056 draw_buffer_count: AtomicU8::new(1),
1057 current_index_buffer: Mutex::new(None),
1058 },
1059 })
1060 }
1061
1062 unsafe fn texture_format_capabilities(
1063 &self,
1064 format: wgt::TextureFormat,
1065 ) -> crate::TextureFormatCapabilities {
1066 use crate::TextureFormatCapabilities as Tfc;
1067 use wgt::TextureFormat as Tf;
1068
1069 let sample_count = {
1070 let max_samples = self.shared.max_msaa_samples;
1071 if max_samples >= 16 {
1072 Tfc::MULTISAMPLE_X2
1073 | Tfc::MULTISAMPLE_X4
1074 | Tfc::MULTISAMPLE_X8
1075 | Tfc::MULTISAMPLE_X16
1076 } else if max_samples >= 8 {
1077 Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4 | Tfc::MULTISAMPLE_X8
1078 } else {
1079 Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4
1084 }
1085 };
1086
1087 let empty = Tfc::empty();
1092 let base = Tfc::COPY_SRC | Tfc::COPY_DST;
1093 let unfilterable = base | Tfc::SAMPLED;
1094 let depth = base | Tfc::SAMPLED | sample_count | Tfc::DEPTH_STENCIL_ATTACHMENT;
1095 let filterable = unfilterable | Tfc::SAMPLED_LINEAR;
1096 let renderable =
1097 unfilterable | Tfc::COLOR_ATTACHMENT | sample_count | Tfc::MULTISAMPLE_RESOLVE;
1098 let filterable_renderable = filterable | renderable | Tfc::COLOR_ATTACHMENT_BLEND;
1099 let storage =
1100 base | Tfc::STORAGE_READ_WRITE | Tfc::STORAGE_READ_ONLY | Tfc::STORAGE_WRITE_ONLY;
1101
1102 let feature_fn = |f, caps| {
1103 if self.shared.features.contains(f) {
1104 caps
1105 } else {
1106 empty
1107 }
1108 };
1109
1110 let bcn_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_BC, filterable);
1111 let etc2_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ETC2, filterable);
1112 let astc_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC, filterable);
1113 let astc_hdr_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR, filterable);
1114
1115 let private_caps_fn = |f, caps| {
1116 if self.shared.private_caps.contains(f) {
1117 caps
1118 } else {
1119 empty
1120 }
1121 };
1122
1123 let half_float_renderable = private_caps_fn(
1124 super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT,
1125 Tfc::COLOR_ATTACHMENT
1126 | Tfc::COLOR_ATTACHMENT_BLEND
1127 | sample_count
1128 | Tfc::MULTISAMPLE_RESOLVE,
1129 );
1130
1131 let float_renderable = private_caps_fn(
1132 super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
1133 Tfc::COLOR_ATTACHMENT
1134 | Tfc::COLOR_ATTACHMENT_BLEND
1135 | sample_count
1136 | Tfc::MULTISAMPLE_RESOLVE,
1137 );
1138
1139 let texture_float_linear = feature_fn(wgt::Features::FLOAT32_FILTERABLE, filterable);
1140
1141 let image_atomic = feature_fn(wgt::Features::TEXTURE_ATOMIC, Tfc::STORAGE_ATOMIC);
1142 let image_64_atomic = feature_fn(wgt::Features::TEXTURE_INT64_ATOMIC, Tfc::STORAGE_ATOMIC);
1143
1144 match format {
1145 Tf::R8Unorm => filterable_renderable,
1146 Tf::R8Snorm => filterable,
1147 Tf::R8Uint => renderable,
1148 Tf::R8Sint => renderable,
1149 Tf::R16Uint => renderable,
1150 Tf::R16Sint => renderable,
1151 Tf::R16Unorm => empty,
1152 Tf::R16Snorm => empty,
1153 Tf::R16Float => filterable | half_float_renderable,
1154 Tf::Rg8Unorm => filterable_renderable,
1155 Tf::Rg8Snorm => filterable,
1156 Tf::Rg8Uint => renderable,
1157 Tf::Rg8Sint => renderable,
1158 Tf::R32Uint => renderable | storage | image_atomic,
1159 Tf::R32Sint => renderable | storage | image_atomic,
1160 Tf::R32Float => unfilterable | storage | float_renderable | texture_float_linear,
1161 Tf::Rg16Uint => renderable,
1162 Tf::Rg16Sint => renderable,
1163 Tf::Rg16Unorm => empty,
1164 Tf::Rg16Snorm => empty,
1165 Tf::Rg16Float => filterable | half_float_renderable,
1166 Tf::Rgba8Unorm => filterable_renderable | storage,
1167 Tf::Rgba8UnormSrgb => filterable_renderable,
1168 Tf::Bgra8Unorm | Tf::Bgra8UnormSrgb => filterable_renderable,
1169 Tf::Rgba8Snorm => filterable | storage,
1170 Tf::Rgba8Uint => renderable | storage,
1171 Tf::Rgba8Sint => renderable | storage,
1172 Tf::Rgb10a2Uint => renderable,
1173 Tf::Rgb10a2Unorm => filterable_renderable,
1174 Tf::Rg11b10Ufloat => filterable | float_renderable,
1175 Tf::R64Uint => image_64_atomic,
1176 Tf::Rg32Uint => renderable,
1177 Tf::Rg32Sint => renderable,
1178 Tf::Rg32Float => unfilterable | float_renderable | texture_float_linear,
1179 Tf::Rgba16Uint => renderable | storage,
1180 Tf::Rgba16Sint => renderable | storage,
1181 Tf::Rgba16Unorm => empty,
1182 Tf::Rgba16Snorm => empty,
1183 Tf::Rgba16Float => filterable | storage | half_float_renderable,
1184 Tf::Rgba32Uint => renderable | storage,
1185 Tf::Rgba32Sint => renderable | storage,
1186 Tf::Rgba32Float => unfilterable | storage | float_renderable | texture_float_linear,
1187 Tf::Stencil8
1188 | Tf::Depth16Unorm
1189 | Tf::Depth32Float
1190 | Tf::Depth32FloatStencil8
1191 | Tf::Depth24Plus
1192 | Tf::Depth24PlusStencil8 => depth,
1193 Tf::NV12 => empty,
1194 Tf::P010 => empty,
1195 Tf::Rgb9e5Ufloat => filterable,
1196 Tf::Bc1RgbaUnorm
1197 | Tf::Bc1RgbaUnormSrgb
1198 | Tf::Bc2RgbaUnorm
1199 | Tf::Bc2RgbaUnormSrgb
1200 | Tf::Bc3RgbaUnorm
1201 | Tf::Bc3RgbaUnormSrgb
1202 | Tf::Bc4RUnorm
1203 | Tf::Bc4RSnorm
1204 | Tf::Bc5RgUnorm
1205 | Tf::Bc5RgSnorm
1206 | Tf::Bc6hRgbFloat
1207 | Tf::Bc6hRgbUfloat
1208 | Tf::Bc7RgbaUnorm
1209 | Tf::Bc7RgbaUnormSrgb => bcn_features,
1210 Tf::Etc2Rgb8Unorm
1211 | Tf::Etc2Rgb8UnormSrgb
1212 | Tf::Etc2Rgb8A1Unorm
1213 | Tf::Etc2Rgb8A1UnormSrgb
1214 | Tf::Etc2Rgba8Unorm
1215 | Tf::Etc2Rgba8UnormSrgb
1216 | Tf::EacR11Unorm
1217 | Tf::EacR11Snorm
1218 | Tf::EacRg11Unorm
1219 | Tf::EacRg11Snorm => etc2_features,
1220 Tf::Astc {
1221 block: _,
1222 channel: AstcChannel::Unorm | AstcChannel::UnormSrgb,
1223 } => astc_features,
1224 Tf::Astc {
1225 block: _,
1226 channel: AstcChannel::Hdr,
1227 } => astc_hdr_features,
1228 }
1229 }
1230
1231 unsafe fn surface_capabilities(
1232 &self,
1233 surface: &super::Surface,
1234 ) -> Option<crate::SurfaceCapabilities> {
1235 #[cfg(webgl)]
1236 if self.shared.context.webgl2_context != surface.webgl2_context {
1237 return None;
1238 }
1239
1240 if surface.presentable {
1241 let mut formats = vec![
1242 wgt::TextureFormat::Rgba8Unorm,
1243 #[cfg(native)]
1244 wgt::TextureFormat::Bgra8Unorm,
1245 ];
1246 if surface.supports_srgb() {
1247 formats.extend([
1248 wgt::TextureFormat::Rgba8UnormSrgb,
1249 #[cfg(native)]
1250 wgt::TextureFormat::Bgra8UnormSrgb,
1251 ])
1252 }
1253 if self
1254 .shared
1255 .private_caps
1256 .contains(super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT)
1257 {
1258 formats.push(wgt::TextureFormat::Rgba16Float)
1259 }
1260
1261 Some(crate::SurfaceCapabilities {
1262 formats,
1263 present_modes: if cfg!(windows) {
1264 vec![wgt::PresentMode::Fifo, wgt::PresentMode::Immediate]
1265 } else {
1266 vec![wgt::PresentMode::Fifo] },
1268 composite_alpha_modes: vec![wgt::CompositeAlphaMode::Opaque], maximum_frame_latency: 2..=2, current_extent: None,
1271 usage: wgt::TextureUses::COLOR_TARGET,
1272 })
1273 } else {
1274 None
1275 }
1276 }
1277
1278 unsafe fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
1279 wgt::PresentationTimestamp::INVALID_TIMESTAMP
1280 }
1281
1282 fn get_ordered_buffer_usages(&self) -> wgt::BufferUses {
1283 wgt::BufferUses::INCLUSIVE | wgt::BufferUses::MAP_WRITE
1284 }
1285
1286 fn get_ordered_texture_usages(&self) -> wgt::TextureUses {
1288 wgt::TextureUses::INCLUSIVE
1289 | wgt::TextureUses::COLOR_TARGET
1290 | wgt::TextureUses::DEPTH_STENCIL_WRITE
1291 }
1292}
1293
1294impl super::AdapterShared {
1295 pub(super) unsafe fn get_buffer_sub_data(
1296 &self,
1297 gl: &glow::Context,
1298 target: u32,
1299 offset: i32,
1300 dst_data: &mut [u8],
1301 ) {
1302 if self
1303 .private_caps
1304 .contains(super::PrivateCapabilities::GET_BUFFER_SUB_DATA)
1305 {
1306 unsafe { gl.get_buffer_sub_data(target, offset, dst_data) };
1307 } else {
1308 log::error!("Fake map");
1309 let length = dst_data.len();
1310 let buffer_mapping =
1311 unsafe { gl.map_buffer_range(target, offset, length as _, glow::MAP_READ_BIT) };
1312
1313 unsafe {
1314 core::ptr::copy_nonoverlapping(buffer_mapping, dst_data.as_mut_ptr(), length)
1315 };
1316
1317 unsafe { gl.unmap_buffer(target) };
1318 }
1319 }
1320}
1321
1322#[cfg(send_sync)]
1323unsafe impl Sync for super::Adapter {}
1324#[cfg(send_sync)]
1325unsafe impl Send for super::Adapter {}
1326
1327#[cfg(test)]
1328mod tests {
1329 use super::super::Adapter;
1330
1331 #[test]
1332 fn test_version_parse() {
1333 Adapter::parse_version("1").unwrap_err();
1334 Adapter::parse_version("1.").unwrap_err();
1335 Adapter::parse_version("1 h3l1o. W0rld").unwrap_err();
1336 Adapter::parse_version("1. h3l1o. W0rld").unwrap_err();
1337 Adapter::parse_version("1.2.3").unwrap_err();
1338
1339 assert_eq!(Adapter::parse_version("OpenGL ES 3.1").unwrap(), (3, 1));
1340 assert_eq!(
1341 Adapter::parse_version("OpenGL ES 2.0 Google Nexus").unwrap(),
1342 (2, 0)
1343 );
1344 assert_eq!(Adapter::parse_version("GLSL ES 1.1").unwrap(), (1, 1));
1345 assert_eq!(
1346 Adapter::parse_version("OpenGL ES GLSL ES 3.20").unwrap(),
1347 (3, 2)
1348 );
1349 assert_eq!(
1350 Adapter::parse_version("WebGL 2.0 (OpenGL ES 3.0 Chromium)").unwrap(),
1352 (3, 0)
1353 );
1354 assert_eq!(
1355 Adapter::parse_version("WebGL GLSL ES 3.00 (OpenGL ES GLSL ES 3.0 Chromium)").unwrap(),
1356 (3, 0)
1357 );
1358 }
1359}