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_buffers_in_vertex_stage: 0,
794 max_storage_buffers_in_fragment_stage: 0,
795 max_storage_textures_per_shader_stage,
796 max_storage_textures_in_vertex_stage: 0,
797 max_storage_textures_in_fragment_stage: 0,
798 max_uniform_buffers_per_shader_stage,
799 max_binding_array_elements_per_shader_stage: 0,
800 max_binding_array_sampler_elements_per_shader_stage: 0,
801 max_binding_array_acceleration_structure_elements_per_shader_stage: 0,
802 max_uniform_buffer_binding_size: unsafe {
803 gl.get_parameter_i32(glow::MAX_UNIFORM_BLOCK_SIZE)
804 } as u64,
805 max_storage_buffer_binding_size: if supports_storage {
806 unsafe { gl.get_parameter_i32(glow::MAX_SHADER_STORAGE_BLOCK_SIZE) }
807 } else {
808 0
809 } as u64,
810 max_vertex_buffers: if private_caps
811 .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
812 {
813 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_BINDINGS) } as u32)
814 } else {
815 16 },
817 max_vertex_attributes: (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIBS) }
818 as u32)
819 .min(super::MAX_VERTEX_ATTRIBUTES as u32),
820 max_vertex_buffer_array_stride: if private_caps
821 .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
822 {
823 if let Some(full_ver) = full_ver {
824 if full_ver >= (4, 4) {
825 let value =
827 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) })
828 as u32;
829
830 if value == 0 {
831 log::debug!("Max vertex attribute stride is 0. Assuming it is the OpenGL minimum spec 2048");
835 2048
836 } else {
837 value
838 }
839 } else {
840 log::debug!("Max vertex attribute stride unknown. Assuming it is the OpenGL minimum spec 2048");
841 2048
842 }
843 } else {
844 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) }) as u32
845 }
846 } else {
847 !0
848 },
849 max_immediate_size: super::MAX_IMMEDIATES as u32 * 4,
850 min_uniform_buffer_offset_alignment,
851 min_storage_buffer_offset_alignment,
852 max_inter_stage_shader_variables: {
853 let max_varying_components =
857 unsafe { gl.get_parameter_i32(glow::MAX_VARYING_COMPONENTS) } as u32;
858 if max_varying_components == 0 {
859 15
861 } else {
862 max_varying_components / 4
863 }
864 },
865 max_color_attachments,
866 max_color_attachment_bytes_per_sample,
867 max_compute_workgroup_storage_size: if supports_work_group_params {
868 (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_SHARED_MEMORY_SIZE) } as u32)
869 } else {
870 0
871 },
872 max_compute_invocations_per_workgroup: if supports_work_group_params {
873 (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_WORK_GROUP_INVOCATIONS) } as u32)
874 } else {
875 0
876 },
877 max_compute_workgroup_size_x: if supports_work_group_params {
878 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 0) }
879 as u32)
880 } else {
881 0
882 },
883 max_compute_workgroup_size_y: if supports_work_group_params {
884 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 1) }
885 as u32)
886 } else {
887 0
888 },
889 max_compute_workgroup_size_z: if supports_work_group_params {
890 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 2) }
891 as u32)
892 } else {
893 0
894 },
895 max_compute_workgroups_per_dimension,
896 max_buffer_size: i32::MAX as u64,
897 max_non_sampler_bindings: u32::MAX,
898
899 max_task_workgroup_total_count: 0,
900 max_task_workgroups_per_dimension: 0,
901 max_mesh_workgroup_total_count: 0,
902 max_mesh_workgroups_per_dimension: 0,
903 max_task_invocations_per_workgroup: 0,
904 max_task_invocations_per_dimension: 0,
905 max_mesh_invocations_per_workgroup: 0,
906 max_mesh_invocations_per_dimension: 0,
907 max_task_payload_size: 0,
908 max_mesh_output_vertices: 0,
909 max_mesh_output_primitives: 0,
910 max_mesh_output_layers: 0,
911 max_mesh_multiview_view_count: 0,
912
913 max_blas_primitive_count: 0,
914 max_blas_geometry_count: 0,
915 max_tlas_instance_count: 0,
916 max_acceleration_structures_per_shader_stage: 0,
917 max_buffers_and_acceleration_structures_per_shader_stage: u32::MAX,
918
919 max_multiview_view_count: 0,
920
921 max_ray_dispatch_count: 0,
922 max_ray_recursion_depth: 0,
923 });
924
925 let mut workarounds = super::Workarounds::empty();
926
927 workarounds.set(
928 super::Workarounds::EMULATE_BUFFER_MAP,
929 cfg!(any(webgl, Emscripten)),
930 );
931
932 let r = renderer.to_lowercase();
933 if context.is_owned()
936 && r.contains("mesa")
937 && r.contains("intel")
938 && r.split(&[' ', '(', ')'][..])
939 .any(|substr| substr.len() == 3 && substr.chars().nth(2) == Some('l'))
940 {
941 log::debug!(
942 "Detected skylake derivative running on mesa i915. Clears to srgb textures will \
943 use manual shader clears."
944 );
945 workarounds.set(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR, true);
946 }
947
948 let downlevel_defaults = wgt::DownlevelLimits {};
949 let max_samples = unsafe { gl.get_parameter_i32(glow::MAX_SAMPLES) };
950
951 #[cfg_attr(target_family = "wasm", allow(dropping_references))]
955 drop(gl);
956
957 Some(crate::ExposedAdapter {
958 adapter: super::Adapter {
959 shared: Arc::new(super::AdapterShared {
960 context,
961 private_caps,
962 workarounds,
963 features,
964 limits: limits.clone(),
965 options: backend_options,
966 shading_language_version,
967 next_shader_id: Default::default(),
968 program_cache: Default::default(),
969 es: es_ver.is_some(),
970 max_msaa_samples: max_samples,
971 }),
972 },
973 info: Self::make_info(vendor, renderer, version),
974 features,
975 capabilities: crate::Capabilities {
976 limits,
977 downlevel: wgt::DownlevelCapabilities {
978 flags: downlevel_flags,
979 limits: downlevel_defaults,
980 shader_model: wgt::ShaderModel::Sm5,
981 },
982 alignments: crate::Alignments {
983 buffer_copy_offset: wgt::BufferSize::new(4).unwrap(),
984 buffer_copy_pitch: wgt::BufferSize::new(4).unwrap(),
985 uniform_bounds_check_alignment: wgt::BufferSize::new(1).unwrap(),
995 raw_tlas_instance_size: 0,
996 ray_tracing_scratch_buffer_alignment: 0,
997 ray_tracing_pipeline_group_data_size: 0,
998 ray_tracing_pipeline_group_data_alignment: 0,
999 ray_tracing_pipeline_data_offset_alignment: 0,
1000 },
1001 cooperative_matrix_properties: Vec::new(),
1002 },
1003 })
1004 }
1005
1006 unsafe fn compile_shader(
1007 source: &str,
1008 gl: &glow::Context,
1009 shader_type: u32,
1010 es: bool,
1011 ) -> Option<glow::Shader> {
1012 let source = if es {
1013 format!("#version 300 es\nprecision lowp float;\n{source}")
1014 } else {
1015 let version = gl.version();
1016 if version.major == 3 && version.minor == 0 {
1017 format!("#version 130\n{source}")
1019 } else {
1020 format!("#version 140\n{source}")
1022 }
1023 };
1024 let shader = unsafe { gl.create_shader(shader_type) }.expect("Could not create shader");
1025 unsafe { gl.shader_source(shader, &source) };
1026 unsafe { gl.compile_shader(shader) };
1027
1028 if !unsafe { gl.get_shader_compile_status(shader) } {
1029 let msg = unsafe { gl.get_shader_info_log(shader) };
1030 if !msg.is_empty() {
1031 log::error!("\tShader compile error: {msg}");
1032 }
1033 unsafe { gl.delete_shader(shader) };
1034 None
1035 } else {
1036 Some(shader)
1037 }
1038 }
1039
1040 unsafe fn create_shader_clear_program(
1041 gl: &glow::Context,
1042 es: bool,
1043 ) -> Option<ShaderClearProgram> {
1044 let program = unsafe { gl.create_program() }.expect("Could not create shader program");
1045 let vertex = unsafe {
1046 Self::compile_shader(
1047 include_str!("./shaders/clear.vert"),
1048 gl,
1049 glow::VERTEX_SHADER,
1050 es,
1051 )?
1052 };
1053 let fragment = unsafe {
1054 Self::compile_shader(
1055 include_str!("./shaders/clear.frag"),
1056 gl,
1057 glow::FRAGMENT_SHADER,
1058 es,
1059 )?
1060 };
1061 unsafe { gl.attach_shader(program, vertex) };
1062 unsafe { gl.attach_shader(program, fragment) };
1063 unsafe { gl.link_program(program) };
1064
1065 let linked_ok = unsafe { gl.get_program_link_status(program) };
1066 let msg = unsafe { gl.get_program_info_log(program) };
1067 if !msg.is_empty() {
1068 log::error!("Shader link error: {msg}");
1069 }
1070 if !linked_ok {
1071 return None;
1072 }
1073
1074 let color_uniform_location = unsafe { gl.get_uniform_location(program, "color") }
1075 .expect("Could not find color uniform in shader clear shader");
1076 unsafe { gl.delete_shader(vertex) };
1077 unsafe { gl.delete_shader(fragment) };
1078
1079 Some(ShaderClearProgram {
1080 program,
1081 color_uniform_location,
1082 })
1083 }
1084}
1085
1086impl crate::Adapter for super::Adapter {
1087 type A = super::Api;
1088
1089 unsafe fn open(
1090 &self,
1091 features: wgt::Features,
1092 _limits: &wgt::Limits,
1093 _memory_hints: &wgt::MemoryHints,
1094 ) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
1095 let gl = &self.shared.context.lock();
1096 unsafe { gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1) };
1097 unsafe { gl.pixel_store_i32(glow::PACK_ALIGNMENT, 1) };
1098 let main_vao =
1099 unsafe { gl.create_vertex_array() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
1100 unsafe { gl.bind_vertex_array(Some(main_vao)) };
1101
1102 let zero_buffer =
1103 unsafe { gl.create_buffer() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
1104 unsafe { gl.bind_buffer(glow::COPY_READ_BUFFER, Some(zero_buffer)) };
1105 let zeroes = vec![0u8; super::ZERO_BUFFER_SIZE];
1106 unsafe { gl.buffer_data_u8_slice(glow::COPY_READ_BUFFER, &zeroes, glow::STATIC_DRAW) };
1107
1108 let shader_clear_program = if self
1112 .shared
1113 .workarounds
1114 .contains(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR)
1115 {
1116 Some(unsafe {
1117 Self::create_shader_clear_program(gl, self.shared.es)
1118 .ok_or(crate::DeviceError::Lost)?
1119 })
1120 } else {
1121 None
1123 };
1124
1125 Ok(crate::OpenDevice {
1126 device: super::Device {
1127 shared: Arc::clone(&self.shared),
1128 main_vao,
1129 #[cfg(all(native, feature = "renderdoc"))]
1130 render_doc: Default::default(),
1131 counters: Default::default(),
1132 },
1133 queue: super::Queue {
1134 shared: Arc::clone(&self.shared),
1135 features,
1136 draw_fbo: unsafe { gl.create_framebuffer() }
1137 .map_err(|_| crate::DeviceError::OutOfMemory)?,
1138 copy_fbo: unsafe { gl.create_framebuffer() }
1139 .map_err(|_| crate::DeviceError::OutOfMemory)?,
1140 shader_clear_program,
1141 zero_buffer,
1142 temp_query_results: Mutex::new(Vec::new()),
1143 draw_buffer_count: AtomicU8::new(1),
1144 current_index_buffer: Mutex::new(None),
1145 },
1146 })
1147 }
1148
1149 unsafe fn texture_format_capabilities(
1150 &self,
1151 format: wgt::TextureFormat,
1152 ) -> crate::TextureFormatCapabilities {
1153 use crate::TextureFormatCapabilities as Tfc;
1154 use wgt::TextureFormat as Tf;
1155
1156 let sample_count = {
1157 let max_samples = self.shared.max_msaa_samples;
1158 if max_samples >= 16 {
1159 Tfc::MULTISAMPLE_X2
1160 | Tfc::MULTISAMPLE_X4
1161 | Tfc::MULTISAMPLE_X8
1162 | Tfc::MULTISAMPLE_X16
1163 } else if max_samples >= 8 {
1164 Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4 | Tfc::MULTISAMPLE_X8
1165 } else {
1166 Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4
1171 }
1172 };
1173
1174 let empty = Tfc::empty();
1179 let base = Tfc::COPY_SRC | Tfc::COPY_DST;
1180 let unfilterable = base | Tfc::SAMPLED;
1181 let depth = base | Tfc::SAMPLED | sample_count | Tfc::DEPTH_STENCIL_ATTACHMENT;
1182 let filterable = unfilterable | Tfc::SAMPLED_LINEAR;
1183 let renderable =
1184 unfilterable | Tfc::COLOR_ATTACHMENT | sample_count | Tfc::MULTISAMPLE_RESOLVE;
1185 let filterable_renderable = filterable | renderable | Tfc::COLOR_ATTACHMENT_BLEND;
1186 let storage =
1187 base | Tfc::STORAGE_READ_WRITE | Tfc::STORAGE_READ_ONLY | Tfc::STORAGE_WRITE_ONLY;
1188
1189 let feature_fn = |f, caps| {
1190 if self.shared.features.contains(f) {
1191 caps
1192 } else {
1193 empty
1194 }
1195 };
1196
1197 let bcn_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_BC, filterable);
1198 let etc2_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ETC2, filterable);
1199 let astc_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC, filterable);
1200 let astc_hdr_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR, filterable);
1201
1202 let private_caps_fn = |f, caps| {
1203 if self.shared.private_caps.contains(f) {
1204 caps
1205 } else {
1206 empty
1207 }
1208 };
1209
1210 let half_float_renderable = private_caps_fn(
1211 super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT,
1212 Tfc::COLOR_ATTACHMENT
1213 | Tfc::COLOR_ATTACHMENT_BLEND
1214 | sample_count
1215 | Tfc::MULTISAMPLE_RESOLVE,
1216 );
1217
1218 let float_renderable = private_caps_fn(
1219 super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
1220 Tfc::COLOR_ATTACHMENT
1221 | Tfc::COLOR_ATTACHMENT_BLEND
1222 | sample_count
1223 | Tfc::MULTISAMPLE_RESOLVE,
1224 );
1225
1226 let texture_float_linear = feature_fn(wgt::Features::FLOAT32_FILTERABLE, filterable);
1227
1228 let image_atomic = feature_fn(wgt::Features::TEXTURE_ATOMIC, Tfc::STORAGE_ATOMIC);
1229 let image_64_atomic = feature_fn(wgt::Features::TEXTURE_INT64_ATOMIC, Tfc::STORAGE_ATOMIC);
1230
1231 let norm16_unorm = private_caps_fn(
1236 super::PrivateCapabilities::TEXTURE_FORMAT_NORM16,
1237 filterable_renderable,
1238 );
1239 let norm16_snorm = if self
1240 .shared
1241 .private_caps
1242 .contains(super::PrivateCapabilities::TEXTURE_FORMAT_SNORM16_RENDERABLE)
1243 {
1244 norm16_unorm
1245 } else {
1246 private_caps_fn(
1247 super::PrivateCapabilities::TEXTURE_FORMAT_NORM16,
1248 filterable,
1249 )
1250 };
1251 let norm16_storage = private_caps_fn(
1252 super::PrivateCapabilities::TEXTURE_FORMAT_NORM16_STORAGE,
1253 storage,
1254 );
1255
1256 match format {
1257 Tf::R8Unorm => filterable_renderable,
1258 Tf::R8Snorm => filterable,
1259 Tf::R8Uint => renderable,
1260 Tf::R8Sint => renderable,
1261 Tf::R16Uint => renderable,
1262 Tf::R16Sint => renderable,
1263 Tf::R16Unorm => norm16_unorm | norm16_storage,
1264 Tf::R16Snorm => norm16_snorm | norm16_storage,
1265 Tf::R16Float => filterable | half_float_renderable,
1266 Tf::Rg8Unorm => filterable_renderable,
1267 Tf::Rg8Snorm => filterable,
1268 Tf::Rg8Uint => renderable,
1269 Tf::Rg8Sint => renderable,
1270 Tf::R32Uint => renderable | storage | image_atomic,
1271 Tf::R32Sint => renderable | storage | image_atomic,
1272 Tf::R32Float => unfilterable | storage | float_renderable | texture_float_linear,
1273 Tf::Rg16Uint => renderable,
1274 Tf::Rg16Sint => renderable,
1275 Tf::Rg16Unorm => norm16_unorm | norm16_storage,
1276 Tf::Rg16Snorm => norm16_snorm | norm16_storage,
1277 Tf::Rg16Float => filterable | half_float_renderable,
1278 Tf::Rgba8Unorm => filterable_renderable | storage,
1279 Tf::Rgba8UnormSrgb => filterable_renderable,
1280 Tf::Bgra8Unorm | Tf::Bgra8UnormSrgb => filterable_renderable,
1281 Tf::Rgba8Snorm => filterable | storage,
1282 Tf::Rgba8Uint => renderable | storage,
1283 Tf::Rgba8Sint => renderable | storage,
1284 Tf::Rgb10a2Uint => renderable,
1285 Tf::Rgb10a2Unorm => filterable_renderable,
1286 Tf::Rg11b10Ufloat => filterable | float_renderable,
1287 Tf::R64Uint => image_64_atomic,
1288 Tf::Rg32Uint => renderable,
1289 Tf::Rg32Sint => renderable,
1290 Tf::Rg32Float => unfilterable | float_renderable | texture_float_linear,
1291 Tf::Rgba16Uint => renderable | storage,
1292 Tf::Rgba16Sint => renderable | storage,
1293 Tf::Rgba16Unorm => norm16_unorm | norm16_storage,
1294 Tf::Rgba16Snorm => norm16_snorm | norm16_storage,
1295 Tf::Rgba16Float => filterable | storage | half_float_renderable,
1296 Tf::Rgba32Uint => renderable | storage,
1297 Tf::Rgba32Sint => renderable | storage,
1298 Tf::Rgba32Float => unfilterable | storage | float_renderable | texture_float_linear,
1299 Tf::Stencil8
1300 | Tf::Depth16Unorm
1301 | Tf::Depth32Float
1302 | Tf::Depth32FloatStencil8
1303 | Tf::Depth24Plus
1304 | Tf::Depth24PlusStencil8 => depth,
1305 Tf::NV12 => empty,
1306 Tf::P010 => empty,
1307 Tf::Rgb9e5Ufloat => filterable,
1308 Tf::Bc1RgbaUnorm
1309 | Tf::Bc1RgbaUnormSrgb
1310 | Tf::Bc2RgbaUnorm
1311 | Tf::Bc2RgbaUnormSrgb
1312 | Tf::Bc3RgbaUnorm
1313 | Tf::Bc3RgbaUnormSrgb
1314 | Tf::Bc4RUnorm
1315 | Tf::Bc4RSnorm
1316 | Tf::Bc5RgUnorm
1317 | Tf::Bc5RgSnorm
1318 | Tf::Bc6hRgbFloat
1319 | Tf::Bc6hRgbUfloat
1320 | Tf::Bc7RgbaUnorm
1321 | Tf::Bc7RgbaUnormSrgb => bcn_features,
1322 Tf::Etc2Rgb8Unorm
1323 | Tf::Etc2Rgb8UnormSrgb
1324 | Tf::Etc2Rgb8A1Unorm
1325 | Tf::Etc2Rgb8A1UnormSrgb
1326 | Tf::Etc2Rgba8Unorm
1327 | Tf::Etc2Rgba8UnormSrgb
1328 | Tf::EacR11Unorm
1329 | Tf::EacR11Snorm
1330 | Tf::EacRg11Unorm
1331 | Tf::EacRg11Snorm => etc2_features,
1332 Tf::Astc {
1333 block: _,
1334 channel: AstcChannel::Unorm | AstcChannel::UnormSrgb,
1335 } => astc_features,
1336 Tf::Astc {
1337 block: _,
1338 channel: AstcChannel::Hdr,
1339 } => astc_hdr_features,
1340 }
1341 }
1342
1343 unsafe fn surface_capabilities(
1344 &self,
1345 surface: &super::Surface,
1346 ) -> Option<crate::SurfaceCapabilities> {
1347 #[cfg(webgl)]
1348 if self.shared.context.webgl2_context != surface.webgl2_context {
1349 return None;
1350 }
1351
1352 if surface.presentable {
1353 let format_caps = |format: wgt::TextureFormat| wgt::SurfaceFormatCapabilities {
1356 format,
1357 color_spaces: wgt::SurfaceColorSpaces::SRGB,
1358 };
1359 let mut formats = vec![
1360 format_caps(wgt::TextureFormat::Rgba8Unorm),
1361 #[cfg(native)]
1362 format_caps(wgt::TextureFormat::Bgra8Unorm),
1363 ];
1364 if surface.supports_srgb() {
1365 formats.extend([
1366 format_caps(wgt::TextureFormat::Rgba8UnormSrgb),
1367 #[cfg(native)]
1368 format_caps(wgt::TextureFormat::Bgra8UnormSrgb),
1369 ])
1370 }
1371 if self
1372 .shared
1373 .private_caps
1374 .contains(super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT)
1375 {
1376 formats.push(format_caps(wgt::TextureFormat::Rgba16Float))
1377 }
1378
1379 Some(crate::SurfaceCapabilities {
1380 formats,
1381 present_modes: if cfg!(windows) {
1382 vec![wgt::PresentMode::Fifo, wgt::PresentMode::Immediate]
1383 } else {
1384 vec![wgt::PresentMode::Fifo] },
1386 composite_alpha_modes: vec![wgt::CompositeAlphaMode::Opaque], maximum_frame_latency: 2..=2, current_extent: None,
1389 usage: wgt::TextureUses::COLOR_TARGET,
1390 })
1391 } else {
1392 None
1393 }
1394 }
1395
1396 unsafe fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
1397 wgt::PresentationTimestamp::INVALID_TIMESTAMP
1398 }
1399
1400 fn get_ordered_buffer_usages(&self) -> wgt::BufferUses {
1401 wgt::BufferUses::INCLUSIVE | wgt::BufferUses::MAP_WRITE
1402 }
1403
1404 fn get_ordered_texture_usages(&self) -> wgt::TextureUses {
1406 wgt::TextureUses::INCLUSIVE
1407 | wgt::TextureUses::COLOR_TARGET
1408 | wgt::TextureUses::DEPTH_WRITE
1409 | wgt::TextureUses::STENCIL_WRITE
1410 }
1411}
1412
1413impl super::AdapterShared {
1414 pub(super) unsafe fn get_buffer_sub_data(
1415 &self,
1416 gl: &glow::Context,
1417 target: u32,
1418 offset: i32,
1419 dst_data: &mut [u8],
1420 ) {
1421 if self
1422 .private_caps
1423 .contains(super::PrivateCapabilities::GET_BUFFER_SUB_DATA)
1424 {
1425 unsafe { gl.get_buffer_sub_data(target, offset, dst_data) };
1426 } else {
1427 log::error!("Fake map");
1428 let length = dst_data.len();
1429 if length != 0 {
1431 let buffer_mapping =
1432 unsafe { gl.map_buffer_range(target, offset, length as _, glow::MAP_READ_BIT) };
1433
1434 unsafe {
1435 core::ptr::copy_nonoverlapping(buffer_mapping, dst_data.as_mut_ptr(), length)
1436 };
1437
1438 unsafe { gl.unmap_buffer(target) };
1439 }
1440 }
1441 }
1442}
1443
1444#[cfg(send_sync)]
1445unsafe impl Sync for super::Adapter {}
1446#[cfg(send_sync)]
1447unsafe impl Send for super::Adapter {}
1448
1449#[cfg(test)]
1450mod tests {
1451 use super::super::Adapter;
1452
1453 #[test]
1454 fn test_version_parse() {
1455 Adapter::parse_version("1").unwrap_err();
1456 Adapter::parse_version("1.").unwrap_err();
1457 Adapter::parse_version("1 h3l1o. W0rld").unwrap_err();
1458 Adapter::parse_version("1. h3l1o. W0rld").unwrap_err();
1459 Adapter::parse_version("1.2.3").unwrap_err();
1460
1461 assert_eq!(Adapter::parse_version("OpenGL ES 3.1").unwrap(), (3, 1));
1462 assert_eq!(
1463 Adapter::parse_version("OpenGL ES 2.0 Google Nexus").unwrap(),
1464 (2, 0)
1465 );
1466 assert_eq!(Adapter::parse_version("GLSL ES 1.1").unwrap(), (1, 1));
1467 assert_eq!(
1468 Adapter::parse_version("OpenGL ES GLSL ES 3.20").unwrap(),
1469 (3, 2)
1470 );
1471 assert_eq!(
1472 Adapter::parse_version("WebGL 2.0 (OpenGL ES 3.0 Chromium)").unwrap(),
1474 (3, 0)
1475 );
1476 assert_eq!(
1477 Adapter::parse_version("WebGL GLSL ES 3.00 (OpenGL ES GLSL ES 3.0 Chromium)").unwrap(),
1478 (3, 0)
1479 );
1480 }
1481}