1use alloc::{borrow::ToOwned as _, format, string::String, sync::Arc, vec, vec::Vec};
2
3use glow::HasContext;
4use wgpu_sync::{atomic::AtomicU8, Mutex};
5use wgt::AstcChannel;
6
7use crate::auxil::db;
8use crate::gles::ShaderClearProgram;
9
10const GL_UNMASKED_VENDOR_WEBGL: u32 = 0x9245;
13const GL_UNMASKED_RENDERER_WEBGL: u32 = 0x9246;
14
15impl super::Adapter {
16 pub fn get_glsl_version(&self) -> naga::back::glsl::Version {
17 self.shared.shading_language_version
18 }
19
20 fn parse_version(mut src: &str) -> Result<(u8, u8), crate::InstanceError> {
26 let webgl_sig = "WebGL ";
27 let is_webgl = src.starts_with(webgl_sig);
31 if is_webgl {
32 let pos = src.rfind(webgl_sig).unwrap_or(0);
33 src = &src[pos + webgl_sig.len()..];
34 } else {
35 let es_sig = " ES ";
36 match src.rfind(es_sig) {
37 Some(pos) => {
38 src = &src[pos + es_sig.len()..];
39 }
40 None => {
41 return Err(crate::InstanceError::new(format!(
42 "OpenGL version {src:?} does not contain 'ES'"
43 )));
44 }
45 }
46 };
47
48 let glsl_es_sig = "GLSL ES ";
49 let is_glsl = match src.find(glsl_es_sig) {
50 Some(pos) => {
51 src = &src[pos + glsl_es_sig.len()..];
52 true
53 }
54 None => false,
55 };
56
57 Self::parse_full_version(src).map(|(major, minor)| {
58 (
59 if is_webgl && !is_glsl {
61 major + 1
62 } else {
63 major
64 },
65 minor,
66 )
67 })
68 }
69
70 pub(super) fn parse_full_version(src: &str) -> Result<(u8, u8), crate::InstanceError> {
86 let (version, _vendor_info) = match src.find(' ') {
87 Some(i) => (&src[..i], src[i + 1..].to_owned()),
88 None => (src, String::new()),
89 };
90
91 let mut it = version.split('.');
94 let major = it.next().and_then(|s| s.parse().ok());
95 let minor = it.next().and_then(|s| {
96 let trimmed = if s.starts_with('0') {
97 "0"
98 } else {
99 s.trim_end_matches('0')
100 };
101 trimmed.parse().ok()
102 });
103
104 match (major, minor) {
105 (Some(major), Some(minor)) => Ok((major, minor)),
106 _ => Err(crate::InstanceError::new(format!(
107 "unable to extract OpenGL version from {version:?}"
108 ))),
109 }
110 }
111
112 fn make_info(vendor_orig: String, renderer_orig: String, version: String) -> wgt::AdapterInfo {
113 let vendor = vendor_orig.to_lowercase();
114 let renderer = renderer_orig.to_lowercase();
115
116 let strings_that_imply_integrated = [
118 " xpress", "amd renoir",
120 "radeon hd 4200",
121 "radeon hd 4250",
122 "radeon hd 4290",
123 "radeon hd 4270",
124 "radeon hd 4225",
125 "radeon hd 3100",
126 "radeon hd 3200",
127 "radeon hd 3000",
128 "radeon hd 3300",
129 "radeon(tm) r4 graphics",
130 "radeon(tm) r5 graphics",
131 "radeon(tm) r6 graphics",
132 "radeon(tm) r7 graphics",
133 "radeon r7 graphics",
134 "nforce", "tegra", "shield", "igp",
138 "mali",
139 "intel",
140 "v3d",
141 "apple m", ];
143 let strings_that_imply_cpu = ["mesa offscreen", "swiftshader", "llvmpipe"];
144
145 let inferred_device_type = if vendor.contains("qualcomm")
147 || vendor.contains("intel")
148 || strings_that_imply_integrated
149 .iter()
150 .any(|&s| renderer.contains(s))
151 {
152 wgt::DeviceType::IntegratedGpu
153 } else if strings_that_imply_cpu.iter().any(|&s| renderer.contains(s)) {
154 wgt::DeviceType::Cpu
155 } else {
156 wgt::DeviceType::Other
162 };
163
164 let vendor_id = if vendor.contains("amd") {
166 db::amd::VENDOR
167 } else if vendor.contains("imgtec") {
168 db::imgtec::VENDOR
169 } else if vendor.contains("nvidia") {
170 db::nvidia::VENDOR
171 } else if vendor.contains("arm") {
172 db::arm::VENDOR
173 } else if vendor.contains("qualcomm") {
174 db::qualcomm::VENDOR
175 } else if vendor.contains("intel") {
176 db::intel::VENDOR
177 } else if vendor.contains("broadcom") {
178 db::broadcom::VENDOR
179 } else if vendor.contains("mesa") {
180 db::mesa::VENDOR
181 } else if vendor.contains("apple") {
182 db::apple::VENDOR
183 } else {
184 0
185 };
186
187 wgt::AdapterInfo {
188 name: renderer_orig,
189 vendor: vendor_id,
190 driver_info: version,
191 ..wgt::AdapterInfo::new(inferred_device_type, wgt::Backend::Gl)
192 }
193 }
194
195 pub(super) unsafe fn expose(
196 context: super::AdapterContext,
197 backend_options: wgt::GlBackendOptions,
198 ) -> Option<crate::ExposedAdapter<super::Api>> {
199 let gl = context.lock();
200 let extensions = gl.supported_extensions();
201
202 let (vendor_const, renderer_const) = if extensions.contains("WEBGL_debug_renderer_info") {
203 #[cfg(Emscripten)]
206 if unsafe {
207 super::emscripten::enable_extension(c"WEBGL_debug_renderer_info".to_str().unwrap())
208 } {
209 (GL_UNMASKED_VENDOR_WEBGL, GL_UNMASKED_RENDERER_WEBGL)
210 } else {
211 (glow::VENDOR, glow::RENDERER)
212 }
213 #[cfg(not(Emscripten))]
215 (GL_UNMASKED_VENDOR_WEBGL, GL_UNMASKED_RENDERER_WEBGL)
216 } else {
217 (glow::VENDOR, glow::RENDERER)
218 };
219
220 let vendor = unsafe { gl.get_parameter_string(vendor_const) };
221 let renderer = unsafe { gl.get_parameter_string(renderer_const) };
222 let version = unsafe { gl.get_parameter_string(glow::VERSION) };
223 log::debug!("Vendor: {vendor}");
224 log::debug!("Renderer: {renderer}");
225 log::debug!("Version: {version}");
226
227 let full_ver = Self::parse_full_version(&version).ok();
228 let es_ver = full_ver.map_or_else(|| Self::parse_version(&version).ok(), |_| None);
229
230 if let Some(full_ver) = full_ver {
231 let core_profile = (full_ver >= (3, 2)).then(|| unsafe {
232 gl.get_parameter_i32(glow::CONTEXT_PROFILE_MASK)
233 & glow::CONTEXT_CORE_PROFILE_BIT as i32
234 != 0
235 });
236 log::trace!(
237 "Profile: {}",
238 core_profile
239 .map(|core_profile| if core_profile {
240 "Core"
241 } else {
242 "Compatibility"
243 })
244 .unwrap_or("Legacy")
245 );
246 }
247
248 if es_ver.is_none() && full_ver.is_none() {
249 log::warn!("Unable to parse OpenGL version");
250 return None;
251 }
252
253 if let Some(es_ver) = es_ver {
254 if es_ver < (3, 0) {
255 log::warn!(
256 "Returned GLES context is {}.{}, when 3.0+ was requested",
257 es_ver.0,
258 es_ver.1
259 );
260 return None;
261 }
262 }
263
264 if let Some(full_ver) = full_ver {
265 if full_ver < (3, 3) {
266 log::warn!(
267 "Returned GL context is {}.{}, when 3.3+ is needed",
268 full_ver.0,
269 full_ver.1
270 );
271 return None;
272 }
273 }
274
275 let shading_language_version = {
276 let sl_version = unsafe { gl.get_parameter_string(glow::SHADING_LANGUAGE_VERSION) };
277 log::debug!("SL version: {}", &sl_version);
278 if full_ver.is_some() {
279 let (sl_major, sl_minor) = Self::parse_full_version(&sl_version).ok()?;
280 let mut value = sl_major as u16 * 100 + sl_minor as u16 * 10;
281 if value > 450 {
283 value = 450;
284 }
285 naga::back::glsl::Version::Desktop(value)
286 } else {
287 let (sl_major, sl_minor) = Self::parse_version(&sl_version).ok()?;
288 let value = sl_major as u16 * 100 + sl_minor as u16 * 10;
289 naga::back::glsl::Version::Embedded {
290 version: value,
291 is_webgl: cfg!(any(webgl, Emscripten)),
292 }
293 }
294 };
295
296 log::debug!("Supported GL Extensions: {extensions:#?}");
297
298 let supported = |(req_es_major, req_es_minor), (req_full_major, req_full_minor)| {
299 let es_supported = es_ver
300 .map(|es_ver| es_ver >= (req_es_major, req_es_minor))
301 .unwrap_or_default();
302
303 let full_supported = full_ver
304 .map(|full_ver| full_ver >= (req_full_major, req_full_minor))
305 .unwrap_or_default();
306
307 es_supported || full_supported
308 };
309
310 let supports_storage =
311 supported((3, 1), (4, 3)) || extensions.contains("GL_ARB_shader_storage_buffer_object");
312 let supports_compute =
313 supported((3, 1), (4, 3)) || extensions.contains("GL_ARB_compute_shader");
314 let supports_work_group_params = supports_compute;
315
316 let is_angle = renderer.contains("ANGLE");
318
319 let vertex_shader_storage_blocks = if supports_storage {
320 let value =
321 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_SHADER_STORAGE_BLOCKS) } as u32);
322
323 if value == 0 && extensions.contains("GL_ARB_shader_storage_buffer_object") {
324 let new = (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_SHADER_STORAGE_BLOCKS) }
327 as u32);
328 log::debug!("Max vertex shader storage blocks is zero, but GL_ARB_shader_storage_buffer_object is specified. Assuming the compute value {new}");
329 new
330 } else {
331 value
332 }
333 } else {
334 0
335 };
336 let fragment_shader_storage_blocks = if supports_storage {
337 (unsafe { gl.get_parameter_i32(glow::MAX_FRAGMENT_SHADER_STORAGE_BLOCKS) } as u32)
338 } else {
339 0
340 };
341 let vertex_shader_storage_textures = if supports_storage {
342 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_IMAGE_UNIFORMS) } as u32)
343 } else {
344 0
345 };
346 let fragment_shader_storage_textures = if supports_storage {
347 (unsafe { gl.get_parameter_i32(glow::MAX_FRAGMENT_IMAGE_UNIFORMS) } as u32)
348 } else {
349 0
350 };
351 let max_storage_block_size = if supports_storage {
352 (unsafe { gl.get_parameter_i32(glow::MAX_SHADER_STORAGE_BLOCK_SIZE) } as u32)
353 } else {
354 0
355 };
356 let max_element_index = unsafe { gl.get_parameter_i32(glow::MAX_ELEMENT_INDEX) } as u32;
357
358 let vertex_ssbo_false_zero =
364 vertex_shader_storage_blocks == 0 && vertex_shader_storage_textures != 0;
365 if vertex_ssbo_false_zero {
366 log::debug!("Max vertex shader SSBO == 0 and SSTO != 0. Interpreting as false zero.");
368 }
369
370 let max_storage_buffers_per_shader_stage = if vertex_shader_storage_blocks == 0 {
371 fragment_shader_storage_blocks
372 } else {
373 vertex_shader_storage_blocks.min(fragment_shader_storage_blocks)
374 };
375 let max_storage_textures_per_shader_stage = if vertex_shader_storage_textures == 0 {
376 fragment_shader_storage_textures
377 } else {
378 vertex_shader_storage_textures.min(fragment_shader_storage_textures)
379 };
380 let indirect_execution = supported((3, 1), (4, 3))
382 || (extensions.contains("GL_ARB_draw_indirect") && supports_compute);
383 let supports_cube_array = supported((3, 2), (4, 0))
384 || (supported((3, 1), (4, 0)) && extensions.contains("GL_EXT_texture_cube_map_array"));
385
386 let mut downlevel_flags = wgt::DownlevelFlags::empty()
387 | wgt::DownlevelFlags::NON_POWER_OF_TWO_MIPMAPPED_TEXTURES
388 | wgt::DownlevelFlags::COMPARISON_SAMPLERS
389 | wgt::DownlevelFlags::SHADER_F16_IN_F32
390 | wgt::DownlevelFlags::MSL2_1;
391 downlevel_flags.set(
392 wgt::DownlevelFlags::CUBE_ARRAY_TEXTURES,
393 supports_cube_array,
394 );
395 downlevel_flags.set(wgt::DownlevelFlags::COMPUTE_SHADERS, supports_compute);
396 downlevel_flags.set(
397 wgt::DownlevelFlags::FRAGMENT_WRITABLE_STORAGE,
398 max_storage_block_size != 0,
399 );
400 downlevel_flags.set(wgt::DownlevelFlags::INDIRECT_EXECUTION, indirect_execution);
401 downlevel_flags.set(wgt::DownlevelFlags::BASE_VERTEX, supported((3, 2), (3, 2)));
402 downlevel_flags.set(
403 wgt::DownlevelFlags::INDEPENDENT_BLEND,
404 supported((3, 2), (4, 0)) || extensions.contains("GL_EXT_draw_buffers_indexed"),
405 );
406 downlevel_flags.set(
407 wgt::DownlevelFlags::VERTEX_STORAGE,
408 max_storage_block_size != 0
409 && max_storage_buffers_per_shader_stage != 0
410 && (vertex_shader_storage_blocks != 0 || vertex_ssbo_false_zero),
411 );
412 downlevel_flags.set(wgt::DownlevelFlags::FRAGMENT_STORAGE, supports_storage);
413 if extensions.contains("EXT_texture_filter_anisotropic")
414 || extensions.contains("GL_EXT_texture_filter_anisotropic")
415 {
416 let max_aniso =
417 unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_MAX_ANISOTROPY_EXT) } as u32;
418 downlevel_flags.set(wgt::DownlevelFlags::ANISOTROPIC_FILTERING, max_aniso >= 16);
419 }
420 downlevel_flags.set(
421 wgt::DownlevelFlags::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED,
422 !(cfg!(any(webgl, Emscripten)) || is_angle),
423 );
424 downlevel_flags.set(
426 wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER,
427 !cfg!(any(webgl, Emscripten)),
428 );
429 downlevel_flags.set(
430 wgt::DownlevelFlags::UNRESTRICTED_EXTERNAL_TEXTURE_COPIES,
431 !cfg!(any(webgl, Emscripten)),
432 );
433 downlevel_flags.set(
434 wgt::DownlevelFlags::FULL_DRAW_INDEX_UINT32,
435 max_element_index == u32::MAX,
436 );
437 downlevel_flags.set(
438 wgt::DownlevelFlags::MULTISAMPLED_SHADING,
439 supported((3, 2), (4, 0)) || extensions.contains("OES_sample_variables"),
440 );
441 downlevel_flags.set(
445 wgt::DownlevelFlags::LINEAR_INTERPOLATION,
446 !shading_language_version.is_es(),
447 );
448 let query_buffers = extensions.contains("GL_ARB_query_buffer_object")
449 || extensions.contains("GL_AMD_query_buffer_object");
450 if query_buffers {
451 downlevel_flags.set(wgt::DownlevelFlags::NONBLOCKING_QUERY_RESOLVE, true);
452 }
453
454 let supports_16bit_norm = if es_ver.is_some() {
457 extensions.contains("GL_EXT_texture_norm16")
458 || extensions.contains("EXT_texture_norm16")
459 } else {
460 true
461 };
462 let supports_16bit_snorm_renderable = supports_16bit_norm
468 && (extensions.contains("GL_EXT_render_snorm")
469 || extensions.contains("EXT_render_snorm"));
470 let supports_16bit_norm_storage = supports_16bit_norm
482 && if es_ver.is_some() {
483 extensions.contains("GL_NV_image_formats")
484 } else {
485 full_ver.is_some_and(|v| v >= (4, 2))
486 || extensions.contains("GL_ARB_shader_image_load_store")
487 };
488
489 let mut features = wgt::Features::empty()
490 | wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES
491 | wgt::Features::CLEAR_TEXTURE
492 | wgt::Features::IMMEDIATES
493 | wgt::Features::DEPTH32FLOAT_STENCIL8
494 | wgt::Features::PASSTHROUGH_SHADERS;
495 features.set(
496 wgt::Features::TEXTURE_FORMAT_16BIT_NORM,
497 supports_16bit_norm,
498 );
499 features.set(
500 wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER | wgt::Features::ADDRESS_MODE_CLAMP_TO_ZERO,
501 extensions.contains("GL_EXT_texture_border_clamp")
502 || extensions.contains("GL_ARB_texture_border_clamp"),
503 );
504 features.set(
505 wgt::Features::DEPTH_CLIP_CONTROL,
506 extensions.contains("GL_EXT_depth_clamp") || extensions.contains("GL_ARB_depth_clamp"),
507 );
508 features.set(
509 wgt::Features::VERTEX_WRITABLE_STORAGE,
510 downlevel_flags.contains(wgt::DownlevelFlags::VERTEX_STORAGE)
511 && vertex_shader_storage_textures != 0,
512 );
513 features.set(
514 wgt::Features::MULTIVIEW,
515 extensions.contains("OVR_multiview2") || extensions.contains("GL_OVR_multiview2"),
516 );
517 features.set(
518 wgt::Features::DUAL_SOURCE_BLENDING,
519 extensions.contains("GL_EXT_blend_func_extended")
520 || extensions.contains("GL_ARB_blend_func_extended"),
521 );
522 features.set(
523 wgt::Features::CLIP_DISTANCES,
524 full_ver.is_some() || extensions.contains("GL_EXT_clip_cull_distance"),
525 );
526 features.set(
527 wgt::Features::PRIMITIVE_INDEX,
528 supported((3, 2), (3, 2))
529 || extensions.contains("OES_geometry_shader")
530 || extensions.contains("GL_ARB_geometry_shader4"),
531 );
532 features.set(
533 wgt::Features::SHADER_EARLY_DEPTH_TEST,
534 supported((3, 1), (4, 2)) || extensions.contains("GL_ARB_shader_image_load_store"),
535 );
536 if extensions.contains("GL_ARB_timer_query") {
537 features.set(wgt::Features::TIMESTAMP_QUERY, true);
538 features.set(wgt::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS, true);
539 features.set(wgt::Features::TIMESTAMP_QUERY_INSIDE_PASSES, true);
540 }
541 let gl_bcn_exts = [
542 "GL_EXT_texture_compression_s3tc",
543 "GL_EXT_texture_compression_rgtc",
544 "GL_ARB_texture_compression_bptc",
545 ];
546 let gles_bcn_exts = [
547 "GL_EXT_texture_compression_s3tc_srgb",
548 "GL_EXT_texture_compression_rgtc",
549 "GL_EXT_texture_compression_bptc",
550 ];
551 let webgl_bcn_exts = [
552 "WEBGL_compressed_texture_s3tc",
553 "WEBGL_compressed_texture_s3tc_srgb",
554 "EXT_texture_compression_rgtc",
555 "EXT_texture_compression_bptc",
556 ];
557 let bcn_exts = if cfg!(any(webgl, Emscripten)) {
558 &webgl_bcn_exts[..]
559 } else if es_ver.is_some() {
560 &gles_bcn_exts[..]
561 } else {
562 &gl_bcn_exts[..]
563 };
564 features.set(
565 wgt::Features::TEXTURE_COMPRESSION_BC,
566 bcn_exts.iter().all(|&ext| extensions.contains(ext)),
567 );
568 features.set(
569 wgt::Features::TEXTURE_COMPRESSION_BC_SLICED_3D,
570 bcn_exts.iter().all(|&ext| extensions.contains(ext)), );
572 let has_etc = if cfg!(any(webgl, Emscripten)) {
573 extensions.contains("WEBGL_compressed_texture_etc")
574 } else {
575 es_ver.is_some() || extensions.contains("GL_ARB_ES3_compatibility")
576 };
577 features.set(wgt::Features::TEXTURE_COMPRESSION_ETC2, has_etc);
578
579 if extensions.contains("WEBGL_compressed_texture_astc")
581 || extensions.contains("GL_OES_texture_compression_astc")
582 {
583 #[cfg(webgl)]
584 {
585 if context
586 .glow_context
587 .compressed_texture_astc_supports_ldr_profile()
588 {
589 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC);
590 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D);
591 }
592 if context
593 .glow_context
594 .compressed_texture_astc_supports_hdr_profile()
595 {
596 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR);
597 }
598 }
599
600 #[cfg(any(native, Emscripten))]
601 {
602 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC);
603 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D);
604 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR);
605 }
606 } else {
607 features.set(
608 wgt::Features::TEXTURE_COMPRESSION_ASTC,
609 extensions.contains("GL_KHR_texture_compression_astc_ldr"),
610 );
611 features.set(
612 wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D,
613 extensions.contains("GL_KHR_texture_compression_astc_ldr")
614 && extensions.contains("GL_KHR_texture_compression_astc_sliced_3d"),
615 );
616 features.set(
617 wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR,
618 extensions.contains("GL_KHR_texture_compression_astc_hdr"),
619 );
620 }
621
622 downlevel_flags.set(
623 wgt::DownlevelFlags::TEXTURE_COMPRESSION,
624 features.contains(wgt::Features::TEXTURE_COMPRESSION_BC)
625 || features.contains(
626 wgt::Features::TEXTURE_COMPRESSION_ETC2
627 | wgt::Features::TEXTURE_COMPRESSION_ASTC,
628 ),
629 );
630
631 features.set(
632 wgt::Features::FLOAT32_FILTERABLE,
633 extensions.contains("GL_ARB_color_buffer_float")
634 || extensions.contains("GL_EXT_color_buffer_float")
635 || extensions.contains("OES_texture_float_linear"),
636 );
637
638 if es_ver.is_none() {
639 features |= wgt::Features::POLYGON_MODE_LINE | wgt::Features::POLYGON_MODE_POINT;
640 }
641
642 let mut private_caps = super::PrivateCapabilities::empty();
645 private_caps.set(
646 super::PrivateCapabilities::BUFFER_ALLOCATION,
647 extensions.contains("GL_EXT_buffer_storage")
648 || extensions.contains("GL_ARB_buffer_storage"),
649 );
650 private_caps.set(
651 super::PrivateCapabilities::SHADER_BINDING_LAYOUT,
652 supports_compute,
653 );
654 private_caps.set(
655 super::PrivateCapabilities::SHADER_TEXTURE_SHADOW_LOD,
656 extensions.contains("GL_EXT_texture_shadow_lod"),
657 );
658 private_caps.set(
659 super::PrivateCapabilities::MEMORY_BARRIERS,
660 supported((3, 1), (4, 2)),
661 );
662 private_caps.set(
663 super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT,
664 supported((3, 1), (4, 3)) || extensions.contains("GL_ARB_vertex_attrib_binding"),
665 );
666 private_caps.set(
667 super::PrivateCapabilities::INDEX_BUFFER_ROLE_CHANGE,
668 !cfg!(any(webgl, Emscripten)),
669 );
670 private_caps.set(
671 super::PrivateCapabilities::GET_BUFFER_SUB_DATA,
672 cfg!(any(webgl, Emscripten)) || full_ver.is_some(),
673 );
674 let color_buffer_float = extensions.contains("GL_EXT_color_buffer_float")
675 || extensions.contains("GL_ARB_color_buffer_float")
676 || extensions.contains("EXT_color_buffer_float");
677 let color_buffer_half_float = extensions.contains("GL_EXT_color_buffer_half_float")
678 || extensions.contains("GL_ARB_half_float_pixel");
679 private_caps.set(
680 super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT,
681 color_buffer_half_float || color_buffer_float,
682 );
683 private_caps.set(
684 super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
685 color_buffer_float,
686 );
687 private_caps.set(super::PrivateCapabilities::QUERY_BUFFERS, query_buffers);
688 private_caps.set(super::PrivateCapabilities::QUERY_64BIT, full_ver.is_some());
689 private_caps.set(
690 super::PrivateCapabilities::TEXTURE_STORAGE,
691 supported((3, 0), (4, 2)),
692 );
693 let is_mali = renderer.to_lowercase().contains("mali");
694 let debug_fns_enabled = match backend_options.debug_fns {
695 wgt::GlDebugFns::Auto => gl.supports_debug() && !is_mali,
696 wgt::GlDebugFns::ForceEnabled => gl.supports_debug(),
697 wgt::GlDebugFns::Disabled => false,
698 };
699 private_caps.set(super::PrivateCapabilities::DEBUG_FNS, debug_fns_enabled);
700 private_caps.set(
701 super::PrivateCapabilities::INVALIDATE_FRAMEBUFFER,
702 supported((3, 0), (4, 3)),
703 );
704 if let Some(full_ver) = full_ver {
705 let supported =
706 full_ver >= (4, 2) && extensions.contains("GL_ARB_shader_draw_parameters");
707 private_caps.set(
708 super::PrivateCapabilities::FULLY_FEATURED_INSTANCING,
709 supported,
710 );
711 features.set(wgt::Features::INDIRECT_FIRST_INSTANCE, supported);
718 }
719 private_caps.set(
720 super::PrivateCapabilities::MULTISAMPLED_RENDER_TO_TEXTURE,
721 extensions.contains("GL_EXT_multisampled_render_to_texture"),
722 );
723 private_caps.set(
724 super::PrivateCapabilities::TEXTURE_FORMAT_NORM16,
725 supports_16bit_norm,
726 );
727 private_caps.set(
728 super::PrivateCapabilities::TEXTURE_FORMAT_SNORM16_RENDERABLE,
729 supports_16bit_snorm_renderable,
730 );
731 private_caps.set(
732 super::PrivateCapabilities::TEXTURE_FORMAT_NORM16_STORAGE,
733 supports_16bit_norm_storage,
734 );
735
736 if supports_storage {
739 features |= wgt::Features::MEMORY_DECORATION_COHERENT
740 | wgt::Features::MEMORY_DECORATION_VOLATILE;
741 }
742
743 let max_texture_size = unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_SIZE) } as u32;
744 let max_texture_3d_size = unsafe { gl.get_parameter_i32(glow::MAX_3D_TEXTURE_SIZE) } as u32;
745
746 let min_uniform_buffer_offset_alignment =
747 (unsafe { gl.get_parameter_i32(glow::UNIFORM_BUFFER_OFFSET_ALIGNMENT) } as u32);
748 let min_storage_buffer_offset_alignment = if supports_storage {
749 (unsafe { gl.get_parameter_i32(glow::SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT) } as u32)
750 } else {
751 256
752 };
753 let max_uniform_buffers_per_shader_stage =
754 unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_UNIFORM_BLOCKS) }
755 .min(unsafe { gl.get_parameter_i32(glow::MAX_FRAGMENT_UNIFORM_BLOCKS) })
756 as u32;
757
758 let max_compute_workgroups_per_dimension = if supports_work_group_params {
759 unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 0) }
760 .min(unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 1) })
761 .min(unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 2) })
762 as u32
763 } else {
764 0
765 };
766
767 let max_color_attachments = unsafe {
768 gl.get_parameter_i32(glow::MAX_COLOR_ATTACHMENTS)
769 .min(gl.get_parameter_i32(glow::MAX_DRAW_BUFFERS)) as u32
770 };
771
772 let max_color_attachment_bytes_per_sample =
774 max_color_attachments * wgt::TextureFormat::MAX_TARGET_PIXEL_BYTE_COST;
775
776 let limits = crate::auxil::adjust_raw_limits(wgt::Limits {
777 max_texture_dimension_1d: max_texture_size,
778 max_texture_dimension_2d: max_texture_size,
779 max_texture_dimension_3d: max_texture_3d_size,
780 max_texture_array_layers: unsafe {
781 gl.get_parameter_i32(glow::MAX_ARRAY_TEXTURE_LAYERS)
782 } as u32,
783 max_bind_groups: u32::MAX,
784 max_bind_groups_plus_vertex_buffers: u32::MAX,
786 max_bindings_per_bind_group: u32::MAX,
788 max_dynamic_uniform_buffers_per_pipeline_layout: max_uniform_buffers_per_shader_stage,
789 max_dynamic_storage_buffers_per_pipeline_layout: max_storage_buffers_per_shader_stage,
790 max_sampled_textures_per_shader_stage: super::MAX_TEXTURE_SLOTS as u32,
791 max_samplers_per_shader_stage: super::MAX_SAMPLERS as u32,
792 max_storage_buffers_per_shader_stage,
793 max_storage_textures_per_shader_stage,
794 max_uniform_buffers_per_shader_stage,
795 max_binding_array_elements_per_shader_stage: 0,
796 max_binding_array_sampler_elements_per_shader_stage: 0,
797 max_binding_array_acceleration_structure_elements_per_shader_stage: 0,
798 max_uniform_buffer_binding_size: unsafe {
799 gl.get_parameter_i32(glow::MAX_UNIFORM_BLOCK_SIZE)
800 } as u64,
801 max_storage_buffer_binding_size: if supports_storage {
802 unsafe { gl.get_parameter_i32(glow::MAX_SHADER_STORAGE_BLOCK_SIZE) }
803 } else {
804 0
805 } as u64,
806 max_vertex_buffers: if private_caps
807 .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
808 {
809 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_BINDINGS) } as u32)
810 } else {
811 16 },
813 max_vertex_attributes: (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIBS) }
814 as u32)
815 .min(super::MAX_VERTEX_ATTRIBUTES as u32),
816 max_vertex_buffer_array_stride: if private_caps
817 .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
818 {
819 if let Some(full_ver) = full_ver {
820 if full_ver >= (4, 4) {
821 let value =
823 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) })
824 as u32;
825
826 if value == 0 {
827 log::debug!("Max vertex attribute stride is 0. Assuming it is the OpenGL minimum spec 2048");
831 2048
832 } else {
833 value
834 }
835 } else {
836 log::debug!("Max vertex attribute stride unknown. Assuming it is the OpenGL minimum spec 2048");
837 2048
838 }
839 } else {
840 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) }) as u32
841 }
842 } else {
843 !0
844 },
845 max_immediate_size: super::MAX_IMMEDIATES as u32 * 4,
846 min_uniform_buffer_offset_alignment,
847 min_storage_buffer_offset_alignment,
848 max_inter_stage_shader_variables: {
849 let max_varying_components =
853 unsafe { gl.get_parameter_i32(glow::MAX_VARYING_COMPONENTS) } as u32;
854 if max_varying_components == 0 {
855 15
857 } else {
858 max_varying_components / 4
859 }
860 },
861 max_color_attachments,
862 max_color_attachment_bytes_per_sample,
863 max_compute_workgroup_storage_size: if supports_work_group_params {
864 (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_SHARED_MEMORY_SIZE) } as u32)
865 } else {
866 0
867 },
868 max_compute_invocations_per_workgroup: if supports_work_group_params {
869 (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_WORK_GROUP_INVOCATIONS) } as u32)
870 } else {
871 0
872 },
873 max_compute_workgroup_size_x: if supports_work_group_params {
874 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 0) }
875 as u32)
876 } else {
877 0
878 },
879 max_compute_workgroup_size_y: if supports_work_group_params {
880 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 1) }
881 as u32)
882 } else {
883 0
884 },
885 max_compute_workgroup_size_z: if supports_work_group_params {
886 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 2) }
887 as u32)
888 } else {
889 0
890 },
891 max_compute_workgroups_per_dimension,
892 max_buffer_size: i32::MAX as u64,
893 max_non_sampler_bindings: u32::MAX,
894
895 max_task_workgroup_total_count: 0,
896 max_task_workgroups_per_dimension: 0,
897 max_mesh_workgroup_total_count: 0,
898 max_mesh_workgroups_per_dimension: 0,
899 max_task_invocations_per_workgroup: 0,
900 max_task_invocations_per_dimension: 0,
901 max_mesh_invocations_per_workgroup: 0,
902 max_mesh_invocations_per_dimension: 0,
903 max_task_payload_size: 0,
904 max_mesh_output_vertices: 0,
905 max_mesh_output_primitives: 0,
906 max_mesh_output_layers: 0,
907 max_mesh_multiview_view_count: 0,
908
909 max_blas_primitive_count: 0,
910 max_blas_geometry_count: 0,
911 max_tlas_instance_count: 0,
912 max_acceleration_structures_per_shader_stage: 0,
913 max_buffers_and_acceleration_structures_per_shader_stage: u32::MAX,
914
915 max_multiview_view_count: 0,
916
917 max_ray_dispatch_count: 0,
918 max_ray_recursion_depth: 0,
919 });
920
921 let mut workarounds = super::Workarounds::empty();
922
923 workarounds.set(
924 super::Workarounds::EMULATE_BUFFER_MAP,
925 cfg!(any(webgl, Emscripten)),
926 );
927
928 let r = renderer.to_lowercase();
929 if context.is_owned()
932 && r.contains("mesa")
933 && r.contains("intel")
934 && r.split(&[' ', '(', ')'][..])
935 .any(|substr| substr.len() == 3 && substr.chars().nth(2) == Some('l'))
936 {
937 log::debug!(
938 "Detected skylake derivative running on mesa i915. Clears to srgb textures will \
939 use manual shader clears."
940 );
941 workarounds.set(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR, true);
942 }
943
944 let downlevel_defaults = wgt::DownlevelLimits {};
945 let max_samples = unsafe { gl.get_parameter_i32(glow::MAX_SAMPLES) };
946
947 #[cfg_attr(target_family = "wasm", allow(dropping_references))]
951 drop(gl);
952
953 Some(crate::ExposedAdapter {
954 adapter: super::Adapter {
955 shared: Arc::new(super::AdapterShared {
956 context,
957 private_caps,
958 workarounds,
959 features,
960 limits: limits.clone(),
961 options: backend_options,
962 shading_language_version,
963 next_shader_id: Default::default(),
964 program_cache: Default::default(),
965 es: es_ver.is_some(),
966 max_msaa_samples: max_samples,
967 }),
968 },
969 info: Self::make_info(vendor, renderer, version),
970 features,
971 capabilities: crate::Capabilities {
972 limits,
973 downlevel: wgt::DownlevelCapabilities {
974 flags: downlevel_flags,
975 limits: downlevel_defaults,
976 shader_model: wgt::ShaderModel::Sm5,
977 },
978 alignments: crate::Alignments {
979 buffer_copy_offset: wgt::BufferSize::new(4).unwrap(),
980 buffer_copy_pitch: wgt::BufferSize::new(4).unwrap(),
981 uniform_bounds_check_alignment: wgt::BufferSize::new(1).unwrap(),
991 raw_tlas_instance_size: 0,
992 ray_tracing_scratch_buffer_alignment: 0,
993 ray_tracing_pipeline_group_data_size: 0,
994 ray_tracing_pipeline_group_data_alignment: 0,
995 ray_tracing_pipeline_data_offset_alignment: 0,
996 },
997 cooperative_matrix_properties: Vec::new(),
998 },
999 })
1000 }
1001
1002 unsafe fn compile_shader(
1003 source: &str,
1004 gl: &glow::Context,
1005 shader_type: u32,
1006 es: bool,
1007 ) -> Option<glow::Shader> {
1008 let source = if es {
1009 format!("#version 300 es\nprecision lowp float;\n{source}")
1010 } else {
1011 let version = gl.version();
1012 if version.major == 3 && version.minor == 0 {
1013 format!("#version 130\n{source}")
1015 } else {
1016 format!("#version 140\n{source}")
1018 }
1019 };
1020 let shader = unsafe { gl.create_shader(shader_type) }.expect("Could not create shader");
1021 unsafe { gl.shader_source(shader, &source) };
1022 unsafe { gl.compile_shader(shader) };
1023
1024 if !unsafe { gl.get_shader_compile_status(shader) } {
1025 let msg = unsafe { gl.get_shader_info_log(shader) };
1026 if !msg.is_empty() {
1027 log::error!("\tShader compile error: {msg}");
1028 }
1029 unsafe { gl.delete_shader(shader) };
1030 None
1031 } else {
1032 Some(shader)
1033 }
1034 }
1035
1036 unsafe fn create_shader_clear_program(
1037 gl: &glow::Context,
1038 es: bool,
1039 ) -> Option<ShaderClearProgram> {
1040 let program = unsafe { gl.create_program() }.expect("Could not create shader program");
1041 let vertex = unsafe {
1042 Self::compile_shader(
1043 include_str!("./shaders/clear.vert"),
1044 gl,
1045 glow::VERTEX_SHADER,
1046 es,
1047 )?
1048 };
1049 let fragment = unsafe {
1050 Self::compile_shader(
1051 include_str!("./shaders/clear.frag"),
1052 gl,
1053 glow::FRAGMENT_SHADER,
1054 es,
1055 )?
1056 };
1057 unsafe { gl.attach_shader(program, vertex) };
1058 unsafe { gl.attach_shader(program, fragment) };
1059 unsafe { gl.link_program(program) };
1060
1061 let linked_ok = unsafe { gl.get_program_link_status(program) };
1062 let msg = unsafe { gl.get_program_info_log(program) };
1063 if !msg.is_empty() {
1064 log::error!("Shader link error: {msg}");
1065 }
1066 if !linked_ok {
1067 return None;
1068 }
1069
1070 let color_uniform_location = unsafe { gl.get_uniform_location(program, "color") }
1071 .expect("Could not find color uniform in shader clear shader");
1072 unsafe { gl.delete_shader(vertex) };
1073 unsafe { gl.delete_shader(fragment) };
1074
1075 Some(ShaderClearProgram {
1076 program,
1077 color_uniform_location,
1078 })
1079 }
1080}
1081
1082impl crate::Adapter for super::Adapter {
1083 type A = super::Api;
1084
1085 unsafe fn open(
1086 &self,
1087 features: wgt::Features,
1088 _limits: &wgt::Limits,
1089 _memory_hints: &wgt::MemoryHints,
1090 ) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
1091 let gl = &self.shared.context.lock();
1092 unsafe { gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1) };
1093 unsafe { gl.pixel_store_i32(glow::PACK_ALIGNMENT, 1) };
1094 let main_vao =
1095 unsafe { gl.create_vertex_array() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
1096 unsafe { gl.bind_vertex_array(Some(main_vao)) };
1097
1098 let zero_buffer =
1099 unsafe { gl.create_buffer() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
1100 unsafe { gl.bind_buffer(glow::COPY_READ_BUFFER, Some(zero_buffer)) };
1101 let zeroes = vec![0u8; super::ZERO_BUFFER_SIZE];
1102 unsafe { gl.buffer_data_u8_slice(glow::COPY_READ_BUFFER, &zeroes, glow::STATIC_DRAW) };
1103
1104 let shader_clear_program = if self
1108 .shared
1109 .workarounds
1110 .contains(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR)
1111 {
1112 Some(unsafe {
1113 Self::create_shader_clear_program(gl, self.shared.es)
1114 .ok_or(crate::DeviceError::Lost)?
1115 })
1116 } else {
1117 None
1119 };
1120
1121 Ok(crate::OpenDevice {
1122 device: super::Device {
1123 shared: Arc::clone(&self.shared),
1124 main_vao,
1125 #[cfg(all(native, feature = "renderdoc"))]
1126 render_doc: Default::default(),
1127 counters: Default::default(),
1128 },
1129 queue: super::Queue {
1130 shared: Arc::clone(&self.shared),
1131 features,
1132 draw_fbo: unsafe { gl.create_framebuffer() }
1133 .map_err(|_| crate::DeviceError::OutOfMemory)?,
1134 copy_fbo: unsafe { gl.create_framebuffer() }
1135 .map_err(|_| crate::DeviceError::OutOfMemory)?,
1136 shader_clear_program,
1137 zero_buffer,
1138 temp_query_results: Mutex::new(Vec::new()),
1139 draw_buffer_count: AtomicU8::new(1),
1140 current_index_buffer: Mutex::new(None),
1141 },
1142 })
1143 }
1144
1145 unsafe fn texture_format_capabilities(
1146 &self,
1147 format: wgt::TextureFormat,
1148 ) -> crate::TextureFormatCapabilities {
1149 use crate::TextureFormatCapabilities as Tfc;
1150 use wgt::TextureFormat as Tf;
1151
1152 let sample_count = {
1153 let max_samples = self.shared.max_msaa_samples;
1154 if max_samples >= 16 {
1155 Tfc::MULTISAMPLE_X2
1156 | Tfc::MULTISAMPLE_X4
1157 | Tfc::MULTISAMPLE_X8
1158 | Tfc::MULTISAMPLE_X16
1159 } else if max_samples >= 8 {
1160 Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4 | Tfc::MULTISAMPLE_X8
1161 } else {
1162 Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4
1167 }
1168 };
1169
1170 let empty = Tfc::empty();
1175 let base = Tfc::COPY_SRC | Tfc::COPY_DST;
1176 let unfilterable = base | Tfc::SAMPLED;
1177 let depth = base | Tfc::SAMPLED | sample_count | Tfc::DEPTH_STENCIL_ATTACHMENT;
1178 let filterable = unfilterable | Tfc::SAMPLED_LINEAR;
1179 let renderable =
1180 unfilterable | Tfc::COLOR_ATTACHMENT | sample_count | Tfc::MULTISAMPLE_RESOLVE;
1181 let filterable_renderable = filterable | renderable | Tfc::COLOR_ATTACHMENT_BLEND;
1182 let storage =
1183 base | Tfc::STORAGE_READ_WRITE | Tfc::STORAGE_READ_ONLY | Tfc::STORAGE_WRITE_ONLY;
1184
1185 let feature_fn = |f, caps| {
1186 if self.shared.features.contains(f) {
1187 caps
1188 } else {
1189 empty
1190 }
1191 };
1192
1193 let bcn_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_BC, filterable);
1194 let etc2_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ETC2, filterable);
1195 let astc_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC, filterable);
1196 let astc_hdr_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR, filterable);
1197
1198 let private_caps_fn = |f, caps| {
1199 if self.shared.private_caps.contains(f) {
1200 caps
1201 } else {
1202 empty
1203 }
1204 };
1205
1206 let half_float_renderable = private_caps_fn(
1207 super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT,
1208 Tfc::COLOR_ATTACHMENT
1209 | Tfc::COLOR_ATTACHMENT_BLEND
1210 | sample_count
1211 | Tfc::MULTISAMPLE_RESOLVE,
1212 );
1213
1214 let float_renderable = private_caps_fn(
1215 super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
1216 Tfc::COLOR_ATTACHMENT
1217 | Tfc::COLOR_ATTACHMENT_BLEND
1218 | sample_count
1219 | Tfc::MULTISAMPLE_RESOLVE,
1220 );
1221
1222 let texture_float_linear = feature_fn(wgt::Features::FLOAT32_FILTERABLE, filterable);
1223
1224 let image_atomic = feature_fn(wgt::Features::TEXTURE_ATOMIC, Tfc::STORAGE_ATOMIC);
1225 let image_64_atomic = feature_fn(wgt::Features::TEXTURE_INT64_ATOMIC, Tfc::STORAGE_ATOMIC);
1226
1227 let norm16_unorm = private_caps_fn(
1232 super::PrivateCapabilities::TEXTURE_FORMAT_NORM16,
1233 filterable_renderable,
1234 );
1235 let norm16_snorm = if self
1236 .shared
1237 .private_caps
1238 .contains(super::PrivateCapabilities::TEXTURE_FORMAT_SNORM16_RENDERABLE)
1239 {
1240 norm16_unorm
1241 } else {
1242 private_caps_fn(
1243 super::PrivateCapabilities::TEXTURE_FORMAT_NORM16,
1244 filterable,
1245 )
1246 };
1247 let norm16_storage = private_caps_fn(
1248 super::PrivateCapabilities::TEXTURE_FORMAT_NORM16_STORAGE,
1249 storage,
1250 );
1251
1252 match format {
1253 Tf::R8Unorm => filterable_renderable,
1254 Tf::R8Snorm => filterable,
1255 Tf::R8Uint => renderable,
1256 Tf::R8Sint => renderable,
1257 Tf::R16Uint => renderable,
1258 Tf::R16Sint => renderable,
1259 Tf::R16Unorm => norm16_unorm | norm16_storage,
1260 Tf::R16Snorm => norm16_snorm | norm16_storage,
1261 Tf::R16Float => filterable | half_float_renderable,
1262 Tf::Rg8Unorm => filterable_renderable,
1263 Tf::Rg8Snorm => filterable,
1264 Tf::Rg8Uint => renderable,
1265 Tf::Rg8Sint => renderable,
1266 Tf::R32Uint => renderable | storage | image_atomic,
1267 Tf::R32Sint => renderable | storage | image_atomic,
1268 Tf::R32Float => unfilterable | storage | float_renderable | texture_float_linear,
1269 Tf::Rg16Uint => renderable,
1270 Tf::Rg16Sint => renderable,
1271 Tf::Rg16Unorm => norm16_unorm | norm16_storage,
1272 Tf::Rg16Snorm => norm16_snorm | norm16_storage,
1273 Tf::Rg16Float => filterable | half_float_renderable,
1274 Tf::Rgba8Unorm => filterable_renderable | storage,
1275 Tf::Rgba8UnormSrgb => filterable_renderable,
1276 Tf::Bgra8Unorm | Tf::Bgra8UnormSrgb => filterable_renderable,
1277 Tf::Rgba8Snorm => filterable | storage,
1278 Tf::Rgba8Uint => renderable | storage,
1279 Tf::Rgba8Sint => renderable | storage,
1280 Tf::Rgb10a2Uint => renderable,
1281 Tf::Rgb10a2Unorm => filterable_renderable,
1282 Tf::Rg11b10Ufloat => filterable | float_renderable,
1283 Tf::R64Uint => image_64_atomic,
1284 Tf::Rg32Uint => renderable,
1285 Tf::Rg32Sint => renderable,
1286 Tf::Rg32Float => unfilterable | float_renderable | texture_float_linear,
1287 Tf::Rgba16Uint => renderable | storage,
1288 Tf::Rgba16Sint => renderable | storage,
1289 Tf::Rgba16Unorm => norm16_unorm | norm16_storage,
1290 Tf::Rgba16Snorm => norm16_snorm | norm16_storage,
1291 Tf::Rgba16Float => filterable | storage | half_float_renderable,
1292 Tf::Rgba32Uint => renderable | storage,
1293 Tf::Rgba32Sint => renderable | storage,
1294 Tf::Rgba32Float => unfilterable | storage | float_renderable | texture_float_linear,
1295 Tf::Stencil8
1296 | Tf::Depth16Unorm
1297 | Tf::Depth32Float
1298 | Tf::Depth32FloatStencil8
1299 | Tf::Depth24Plus
1300 | Tf::Depth24PlusStencil8 => depth,
1301 Tf::NV12 => empty,
1302 Tf::P010 => empty,
1303 Tf::Rgb9e5Ufloat => filterable,
1304 Tf::Bc1RgbaUnorm
1305 | Tf::Bc1RgbaUnormSrgb
1306 | Tf::Bc2RgbaUnorm
1307 | Tf::Bc2RgbaUnormSrgb
1308 | Tf::Bc3RgbaUnorm
1309 | Tf::Bc3RgbaUnormSrgb
1310 | Tf::Bc4RUnorm
1311 | Tf::Bc4RSnorm
1312 | Tf::Bc5RgUnorm
1313 | Tf::Bc5RgSnorm
1314 | Tf::Bc6hRgbFloat
1315 | Tf::Bc6hRgbUfloat
1316 | Tf::Bc7RgbaUnorm
1317 | Tf::Bc7RgbaUnormSrgb => bcn_features,
1318 Tf::Etc2Rgb8Unorm
1319 | Tf::Etc2Rgb8UnormSrgb
1320 | Tf::Etc2Rgb8A1Unorm
1321 | Tf::Etc2Rgb8A1UnormSrgb
1322 | Tf::Etc2Rgba8Unorm
1323 | Tf::Etc2Rgba8UnormSrgb
1324 | Tf::EacR11Unorm
1325 | Tf::EacR11Snorm
1326 | Tf::EacRg11Unorm
1327 | Tf::EacRg11Snorm => etc2_features,
1328 Tf::Astc {
1329 block: _,
1330 channel: AstcChannel::Unorm | AstcChannel::UnormSrgb,
1331 } => astc_features,
1332 Tf::Astc {
1333 block: _,
1334 channel: AstcChannel::Hdr,
1335 } => astc_hdr_features,
1336 }
1337 }
1338
1339 unsafe fn surface_capabilities(
1340 &self,
1341 surface: &super::Surface,
1342 ) -> Option<crate::SurfaceCapabilities> {
1343 #[cfg(webgl)]
1344 if self.shared.context.webgl2_context != surface.webgl2_context {
1345 return None;
1346 }
1347
1348 if surface.presentable {
1349 let format_caps = |format: wgt::TextureFormat| wgt::SurfaceFormatCapabilities {
1352 format,
1353 color_spaces: wgt::SurfaceColorSpaces::SRGB,
1354 };
1355 let mut formats = vec![
1356 format_caps(wgt::TextureFormat::Rgba8Unorm),
1357 #[cfg(native)]
1358 format_caps(wgt::TextureFormat::Bgra8Unorm),
1359 ];
1360 if surface.supports_srgb() {
1361 formats.extend([
1362 format_caps(wgt::TextureFormat::Rgba8UnormSrgb),
1363 #[cfg(native)]
1364 format_caps(wgt::TextureFormat::Bgra8UnormSrgb),
1365 ])
1366 }
1367 if self
1368 .shared
1369 .private_caps
1370 .contains(super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT)
1371 {
1372 formats.push(format_caps(wgt::TextureFormat::Rgba16Float))
1373 }
1374
1375 Some(crate::SurfaceCapabilities {
1376 formats,
1377 present_modes: if cfg!(windows) {
1378 vec![wgt::PresentMode::Fifo, wgt::PresentMode::Immediate]
1379 } else {
1380 vec![wgt::PresentMode::Fifo] },
1382 composite_alpha_modes: vec![wgt::CompositeAlphaMode::Opaque], maximum_frame_latency: 2..=2, current_extent: None,
1385 usage: wgt::TextureUses::COLOR_TARGET,
1386 })
1387 } else {
1388 None
1389 }
1390 }
1391
1392 unsafe fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
1393 wgt::PresentationTimestamp::INVALID_TIMESTAMP
1394 }
1395
1396 fn get_ordered_buffer_usages(&self) -> wgt::BufferUses {
1397 wgt::BufferUses::INCLUSIVE | wgt::BufferUses::MAP_WRITE
1398 }
1399
1400 fn get_ordered_texture_usages(&self) -> wgt::TextureUses {
1402 wgt::TextureUses::INCLUSIVE
1403 | wgt::TextureUses::COLOR_TARGET
1404 | wgt::TextureUses::DEPTH_WRITE
1405 | wgt::TextureUses::STENCIL_WRITE
1406 }
1407}
1408
1409impl super::AdapterShared {
1410 pub(super) unsafe fn get_buffer_sub_data(
1411 &self,
1412 gl: &glow::Context,
1413 target: u32,
1414 offset: i32,
1415 dst_data: &mut [u8],
1416 ) {
1417 if self
1418 .private_caps
1419 .contains(super::PrivateCapabilities::GET_BUFFER_SUB_DATA)
1420 {
1421 unsafe { gl.get_buffer_sub_data(target, offset, dst_data) };
1422 } else {
1423 log::error!("Fake map");
1424 let length = dst_data.len();
1425 if length != 0 {
1427 let buffer_mapping =
1428 unsafe { gl.map_buffer_range(target, offset, length as _, glow::MAP_READ_BIT) };
1429
1430 unsafe {
1431 core::ptr::copy_nonoverlapping(buffer_mapping, dst_data.as_mut_ptr(), length)
1432 };
1433
1434 unsafe { gl.unmap_buffer(target) };
1435 }
1436 }
1437 }
1438}
1439
1440#[cfg(send_sync)]
1441unsafe impl Sync for super::Adapter {}
1442#[cfg(send_sync)]
1443unsafe impl Send for super::Adapter {}
1444
1445#[cfg(test)]
1446mod tests {
1447 use super::super::Adapter;
1448
1449 #[test]
1450 fn test_version_parse() {
1451 Adapter::parse_version("1").unwrap_err();
1452 Adapter::parse_version("1.").unwrap_err();
1453 Adapter::parse_version("1 h3l1o. W0rld").unwrap_err();
1454 Adapter::parse_version("1. h3l1o. W0rld").unwrap_err();
1455 Adapter::parse_version("1.2.3").unwrap_err();
1456
1457 assert_eq!(Adapter::parse_version("OpenGL ES 3.1").unwrap(), (3, 1));
1458 assert_eq!(
1459 Adapter::parse_version("OpenGL ES 2.0 Google Nexus").unwrap(),
1460 (2, 0)
1461 );
1462 assert_eq!(Adapter::parse_version("GLSL ES 1.1").unwrap(), (1, 1));
1463 assert_eq!(
1464 Adapter::parse_version("OpenGL ES GLSL ES 3.20").unwrap(),
1465 (3, 2)
1466 );
1467 assert_eq!(
1468 Adapter::parse_version("WebGL 2.0 (OpenGL ES 3.0 Chromium)").unwrap(),
1470 (3, 0)
1471 );
1472 assert_eq!(
1473 Adapter::parse_version("WebGL GLSL ES 3.00 (OpenGL ES GLSL ES 3.0 Chromium)").unwrap(),
1474 (3, 0)
1475 );
1476 }
1477}