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::COMPARISON_SAMPLERS
388 | wgt::DownlevelFlags::MSL2_1
389 | wgt::DownlevelFlags::NON_POWER_OF_TWO_MIPMAPPED_TEXTURES
390 | wgt::DownlevelFlags::SHADER_F16_IN_F32;
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::CLEAR_TEXTURE
491 | wgt::Features::DEPTH32FLOAT_STENCIL8
492 | wgt::Features::IMMEDIATES
493 | wgt::Features::PASSTHROUGH_SHADERS
494 | wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES
495 | wgt::Features::UNRESTRICTED_BYTES_PER_ROW;
496 features.set(
497 wgt::Features::TEXTURE_FORMAT_16BIT_NORM,
498 supports_16bit_norm,
499 );
500 features.set(
501 wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER | wgt::Features::ADDRESS_MODE_CLAMP_TO_ZERO,
502 extensions.contains("GL_EXT_texture_border_clamp")
503 || extensions.contains("GL_ARB_texture_border_clamp"),
504 );
505 features.set(
506 wgt::Features::DEPTH_CLIP_CONTROL,
507 extensions.contains("GL_EXT_depth_clamp") || extensions.contains("GL_ARB_depth_clamp"),
508 );
509 features.set(
510 wgt::Features::VERTEX_WRITABLE_STORAGE,
511 downlevel_flags.contains(wgt::DownlevelFlags::VERTEX_STORAGE)
512 && vertex_shader_storage_textures != 0,
513 );
514 features.set(
515 wgt::Features::MULTIVIEW,
516 extensions.contains("OVR_multiview2") || extensions.contains("GL_OVR_multiview2"),
517 );
518 features.set(
519 wgt::Features::DUAL_SOURCE_BLENDING,
520 extensions.contains("GL_EXT_blend_func_extended")
521 || extensions.contains("GL_ARB_blend_func_extended"),
522 );
523 features.set(
524 wgt::Features::CLIP_DISTANCES,
525 full_ver.is_some() || extensions.contains("GL_EXT_clip_cull_distance"),
526 );
527 features.set(
528 wgt::Features::PRIMITIVE_INDEX,
529 supported((3, 2), (3, 2))
530 || extensions.contains("OES_geometry_shader")
531 || extensions.contains("GL_ARB_geometry_shader4"),
532 );
533 features.set(
534 wgt::Features::SHADER_EARLY_DEPTH_TEST,
535 supported((3, 1), (4, 2)) || extensions.contains("GL_ARB_shader_image_load_store"),
536 );
537 if extensions.contains("GL_ARB_timer_query") {
538 features.set(wgt::Features::TIMESTAMP_QUERY, true);
539 features.set(wgt::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS, true);
540 features.set(wgt::Features::TIMESTAMP_QUERY_INSIDE_PASSES, true);
541 }
542 let gl_bcn_exts = [
543 "GL_EXT_texture_compression_s3tc",
544 "GL_EXT_texture_compression_rgtc",
545 "GL_ARB_texture_compression_bptc",
546 ];
547 let gles_bcn_exts = [
548 "GL_EXT_texture_compression_s3tc_srgb",
549 "GL_EXT_texture_compression_rgtc",
550 "GL_EXT_texture_compression_bptc",
551 ];
552 let webgl_bcn_exts = [
553 "WEBGL_compressed_texture_s3tc",
554 "WEBGL_compressed_texture_s3tc_srgb",
555 "EXT_texture_compression_rgtc",
556 "EXT_texture_compression_bptc",
557 ];
558 let bcn_exts = if cfg!(any(webgl, Emscripten)) {
559 &webgl_bcn_exts[..]
560 } else if es_ver.is_some() {
561 &gles_bcn_exts[..]
562 } else {
563 &gl_bcn_exts[..]
564 };
565 features.set(
566 wgt::Features::TEXTURE_COMPRESSION_BC,
567 bcn_exts.iter().all(|&ext| extensions.contains(ext)),
568 );
569 features.set(
570 wgt::Features::TEXTURE_COMPRESSION_BC_SLICED_3D,
571 bcn_exts.iter().all(|&ext| extensions.contains(ext)), );
573 let has_etc = if cfg!(any(webgl, Emscripten)) {
574 extensions.contains("WEBGL_compressed_texture_etc")
575 } else {
576 es_ver.is_some() || extensions.contains("GL_ARB_ES3_compatibility")
577 };
578 features.set(wgt::Features::TEXTURE_COMPRESSION_ETC2, has_etc);
579
580 if extensions.contains("WEBGL_compressed_texture_astc")
582 || extensions.contains("GL_OES_texture_compression_astc")
583 {
584 #[cfg(webgl)]
585 {
586 if context
587 .glow_context
588 .compressed_texture_astc_supports_ldr_profile()
589 {
590 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC);
591 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D);
592 }
593 if context
594 .glow_context
595 .compressed_texture_astc_supports_hdr_profile()
596 {
597 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR);
598 }
599 }
600
601 #[cfg(any(native, Emscripten))]
602 {
603 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC);
604 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D);
605 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR);
606 }
607 } else {
608 features.set(
609 wgt::Features::TEXTURE_COMPRESSION_ASTC,
610 extensions.contains("GL_KHR_texture_compression_astc_ldr"),
611 );
612 features.set(
613 wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D,
614 extensions.contains("GL_KHR_texture_compression_astc_ldr")
615 && extensions.contains("GL_KHR_texture_compression_astc_sliced_3d"),
616 );
617 features.set(
618 wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR,
619 extensions.contains("GL_KHR_texture_compression_astc_hdr"),
620 );
621 }
622
623 downlevel_flags.set(
624 wgt::DownlevelFlags::TEXTURE_COMPRESSION,
625 features.contains(wgt::Features::TEXTURE_COMPRESSION_BC)
626 || features.contains(
627 wgt::Features::TEXTURE_COMPRESSION_ETC2
628 | wgt::Features::TEXTURE_COMPRESSION_ASTC,
629 ),
630 );
631
632 features.set(
633 wgt::Features::FLOAT32_FILTERABLE,
634 extensions.contains("GL_ARB_color_buffer_float")
635 || extensions.contains("GL_EXT_color_buffer_float")
636 || extensions.contains("OES_texture_float_linear"),
637 );
638
639 if es_ver.is_none() {
640 features |= wgt::Features::POLYGON_MODE_LINE | wgt::Features::POLYGON_MODE_POINT;
641 }
642
643 let mut private_caps = super::PrivateCapabilities::empty();
646 private_caps.set(
647 super::PrivateCapabilities::BUFFER_ALLOCATION,
648 extensions.contains("GL_EXT_buffer_storage")
649 || extensions.contains("GL_ARB_buffer_storage"),
650 );
651 private_caps.set(
652 super::PrivateCapabilities::SHADER_BINDING_LAYOUT,
653 supports_compute,
654 );
655 private_caps.set(
656 super::PrivateCapabilities::SHADER_TEXTURE_SHADOW_LOD,
657 extensions.contains("GL_EXT_texture_shadow_lod"),
658 );
659 private_caps.set(
660 super::PrivateCapabilities::MEMORY_BARRIERS,
661 supported((3, 1), (4, 2)),
662 );
663 private_caps.set(
664 super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT,
665 supported((3, 1), (4, 3)) || extensions.contains("GL_ARB_vertex_attrib_binding"),
666 );
667 private_caps.set(
668 super::PrivateCapabilities::INDEX_BUFFER_ROLE_CHANGE,
669 !cfg!(any(webgl, Emscripten)),
670 );
671 private_caps.set(
672 super::PrivateCapabilities::GET_BUFFER_SUB_DATA,
673 cfg!(any(webgl, Emscripten)) || full_ver.is_some(),
674 );
675 let color_buffer_float = extensions.contains("GL_EXT_color_buffer_float")
676 || extensions.contains("GL_ARB_color_buffer_float")
677 || extensions.contains("EXT_color_buffer_float");
678 let color_buffer_half_float = extensions.contains("GL_EXT_color_buffer_half_float")
679 || extensions.contains("GL_ARB_half_float_pixel");
680 private_caps.set(
681 super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT,
682 color_buffer_half_float || color_buffer_float,
683 );
684 private_caps.set(
685 super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
686 color_buffer_float,
687 );
688 private_caps.set(super::PrivateCapabilities::QUERY_BUFFERS, query_buffers);
689 private_caps.set(super::PrivateCapabilities::QUERY_64BIT, full_ver.is_some());
690 private_caps.set(
691 super::PrivateCapabilities::TEXTURE_STORAGE,
692 supported((3, 0), (4, 2)),
693 );
694 let is_mali = renderer.to_lowercase().contains("mali");
695 let debug_fns_enabled = match backend_options.debug_fns {
696 wgt::GlDebugFns::Auto => gl.supports_debug() && !is_mali,
697 wgt::GlDebugFns::ForceEnabled => gl.supports_debug(),
698 wgt::GlDebugFns::Disabled => false,
699 };
700 private_caps.set(super::PrivateCapabilities::DEBUG_FNS, debug_fns_enabled);
701 private_caps.set(
702 super::PrivateCapabilities::INVALIDATE_FRAMEBUFFER,
703 supported((3, 0), (4, 3)),
704 );
705 if let Some(full_ver) = full_ver {
706 let supported =
707 full_ver >= (4, 2) && extensions.contains("GL_ARB_shader_draw_parameters");
708 private_caps.set(
709 super::PrivateCapabilities::FULLY_FEATURED_INSTANCING,
710 supported,
711 );
712 features.set(wgt::Features::INDIRECT_FIRST_INSTANCE, supported);
719 }
720 private_caps.set(
721 super::PrivateCapabilities::MULTISAMPLED_RENDER_TO_TEXTURE,
722 extensions.contains("GL_EXT_multisampled_render_to_texture"),
723 );
724 private_caps.set(
725 super::PrivateCapabilities::TEXTURE_FORMAT_NORM16,
726 supports_16bit_norm,
727 );
728 private_caps.set(
729 super::PrivateCapabilities::TEXTURE_FORMAT_SNORM16_RENDERABLE,
730 supports_16bit_snorm_renderable,
731 );
732 private_caps.set(
733 super::PrivateCapabilities::TEXTURE_FORMAT_NORM16_STORAGE,
734 supports_16bit_norm_storage,
735 );
736
737 if supports_storage {
740 features |= wgt::Features::MEMORY_DECORATION_COHERENT
741 | wgt::Features::MEMORY_DECORATION_VOLATILE;
742 }
743
744 let max_texture_size = unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_SIZE) } as u32;
745 let max_texture_3d_size = unsafe { gl.get_parameter_i32(glow::MAX_3D_TEXTURE_SIZE) } as u32;
746
747 let min_uniform_buffer_offset_alignment =
748 (unsafe { gl.get_parameter_i32(glow::UNIFORM_BUFFER_OFFSET_ALIGNMENT) } as u32);
749 let min_storage_buffer_offset_alignment = if supports_storage {
750 (unsafe { gl.get_parameter_i32(glow::SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT) } as u32)
751 } else {
752 256
753 };
754 let max_uniform_buffers_per_shader_stage =
755 unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_UNIFORM_BLOCKS) }
756 .min(unsafe { gl.get_parameter_i32(glow::MAX_FRAGMENT_UNIFORM_BLOCKS) })
757 as u32;
758
759 let max_compute_workgroups_per_dimension = if supports_work_group_params {
760 unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 0) }
761 .min(unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 1) })
762 .min(unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 2) })
763 as u32
764 } else {
765 0
766 };
767
768 let max_color_attachments = unsafe {
769 gl.get_parameter_i32(glow::MAX_COLOR_ATTACHMENTS)
770 .min(gl.get_parameter_i32(glow::MAX_DRAW_BUFFERS)) as u32
771 };
772
773 let max_color_attachment_bytes_per_sample =
775 max_color_attachments * wgt::TextureFormat::MAX_TARGET_PIXEL_BYTE_COST;
776
777 let limits = crate::auxil::adjust_raw_limits(wgt::Limits {
778 max_texture_dimension_1d: max_texture_size,
779 max_texture_dimension_2d: max_texture_size,
780 max_texture_dimension_3d: max_texture_3d_size,
781 max_texture_array_layers: unsafe {
782 gl.get_parameter_i32(glow::MAX_ARRAY_TEXTURE_LAYERS)
783 } as u32,
784 max_bind_groups: u32::MAX,
785 max_bind_groups_plus_vertex_buffers: u32::MAX,
787 max_bindings_per_bind_group: u32::MAX,
789 max_dynamic_uniform_buffers_per_pipeline_layout: max_uniform_buffers_per_shader_stage,
790 max_dynamic_storage_buffers_per_pipeline_layout: max_storage_buffers_per_shader_stage,
791 max_sampled_textures_per_shader_stage: super::MAX_TEXTURE_SLOTS as u32,
792 max_samplers_per_shader_stage: super::MAX_SAMPLERS as u32,
793 max_storage_buffers_per_shader_stage,
794 max_storage_buffers_in_vertex_stage: 0,
795 max_storage_buffers_in_fragment_stage: 0,
796 max_storage_textures_per_shader_stage,
797 max_storage_textures_in_vertex_stage: 0,
798 max_storage_textures_in_fragment_stage: 0,
799 max_uniform_buffers_per_shader_stage,
800 max_binding_array_elements_per_shader_stage: 0,
801 max_binding_array_sampler_elements_per_shader_stage: 0,
802 max_binding_array_acceleration_structure_elements_per_shader_stage: 0,
803 max_uniform_buffer_binding_size: unsafe {
804 gl.get_parameter_i32(glow::MAX_UNIFORM_BLOCK_SIZE)
805 } as u64,
806 max_storage_buffer_binding_size: if supports_storage {
807 unsafe { gl.get_parameter_i32(glow::MAX_SHADER_STORAGE_BLOCK_SIZE) }
808 } else {
809 0
810 } as u64,
811 max_vertex_buffers: if private_caps
812 .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
813 {
814 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_BINDINGS) } as u32)
815 } else {
816 16 },
818 max_vertex_attributes: (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIBS) }
819 as u32)
820 .min(super::MAX_VERTEX_ATTRIBUTES as u32),
821 max_vertex_buffer_array_stride: if private_caps
822 .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
823 {
824 if let Some(full_ver) = full_ver {
825 if full_ver >= (4, 4) {
826 let value =
828 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) })
829 as u32;
830
831 if value == 0 {
832 log::debug!("Max vertex attribute stride is 0. Assuming it is the OpenGL minimum spec 2048");
836 2048
837 } else {
838 value
839 }
840 } else {
841 log::debug!("Max vertex attribute stride unknown. Assuming it is the OpenGL minimum spec 2048");
842 2048
843 }
844 } else {
845 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) }) as u32
846 }
847 } else {
848 !0
849 },
850 max_immediate_size: super::MAX_IMMEDIATES as u32 * 4,
851 min_uniform_buffer_offset_alignment,
852 min_storage_buffer_offset_alignment,
853 max_inter_stage_shader_variables: {
854 let max_varying_components =
858 unsafe { gl.get_parameter_i32(glow::MAX_VARYING_COMPONENTS) } as u32;
859 if max_varying_components == 0 {
860 15
862 } else {
863 max_varying_components / 4
864 }
865 },
866 max_color_attachments,
867 max_color_attachment_bytes_per_sample,
868 max_compute_workgroup_storage_size: if supports_work_group_params {
869 (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_SHARED_MEMORY_SIZE) } as u32)
870 } else {
871 0
872 },
873 max_compute_invocations_per_workgroup: if supports_work_group_params {
874 (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_WORK_GROUP_INVOCATIONS) } as u32)
875 } else {
876 0
877 },
878 max_compute_workgroup_size_x: if supports_work_group_params {
879 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 0) }
880 as u32)
881 } else {
882 0
883 },
884 max_compute_workgroup_size_y: if supports_work_group_params {
885 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 1) }
886 as u32)
887 } else {
888 0
889 },
890 max_compute_workgroup_size_z: if supports_work_group_params {
891 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 2) }
892 as u32)
893 } else {
894 0
895 },
896 max_compute_workgroups_per_dimension,
897 max_buffer_size: i32::MAX as u64,
898 max_non_sampler_bindings: u32::MAX,
899
900 max_task_workgroup_total_count: 0,
901 max_task_workgroups_per_dimension: 0,
902 max_mesh_workgroup_total_count: 0,
903 max_mesh_workgroups_per_dimension: 0,
904 max_task_invocations_per_workgroup: 0,
905 max_task_invocations_per_dimension: 0,
906 max_mesh_invocations_per_workgroup: 0,
907 max_mesh_invocations_per_dimension: 0,
908 max_task_payload_size: 0,
909 max_mesh_output_vertices: 0,
910 max_mesh_output_primitives: 0,
911 max_mesh_output_layers: 0,
912 max_mesh_multiview_view_count: 0,
913
914 max_blas_primitive_count: 0,
915 max_blas_geometry_count: 0,
916 max_tlas_instance_count: 0,
917 max_acceleration_structures_per_shader_stage: 0,
918 max_buffers_and_acceleration_structures_per_shader_stage: u32::MAX,
919
920 max_multiview_view_count: 0,
921
922 max_ray_dispatch_count: 0,
923 max_ray_recursion_depth: 0,
924 });
925
926 let mut workarounds = super::Workarounds::empty();
927
928 workarounds.set(
929 super::Workarounds::EMULATE_BUFFER_MAP,
930 cfg!(any(webgl, Emscripten)),
931 );
932
933 let r = renderer.to_lowercase();
934 if context.is_owned()
937 && r.contains("mesa")
938 && r.contains("intel")
939 && r.split(&[' ', '(', ')'][..])
940 .any(|substr| substr.len() == 3 && substr.chars().nth(2) == Some('l'))
941 {
942 log::debug!(
943 "Detected skylake derivative running on mesa i915. Clears to srgb textures will \
944 use manual shader clears."
945 );
946 workarounds.set(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR, true);
947 }
948
949 let downlevel_defaults = wgt::DownlevelLimits {};
950 let max_samples = unsafe { gl.get_parameter_i32(glow::MAX_SAMPLES) };
951
952 #[cfg_attr(target_family = "wasm", allow(dropping_references))]
956 drop(gl);
957
958 Some(crate::ExposedAdapter {
959 adapter: super::Adapter {
960 shared: Arc::new(super::AdapterShared {
961 context,
962 private_caps,
963 workarounds,
964 features,
965 limits: limits.clone(),
966 options: backend_options,
967 shading_language_version,
968 next_shader_id: Default::default(),
969 program_cache: Default::default(),
970 es: es_ver.is_some(),
971 max_msaa_samples: max_samples,
972 }),
973 },
974 info: Self::make_info(vendor, renderer, version),
975 features,
976 capabilities: crate::Capabilities {
977 limits,
978 downlevel: wgt::DownlevelCapabilities {
979 flags: downlevel_flags,
980 limits: downlevel_defaults,
981 shader_model: wgt::ShaderModel::Sm5,
982 },
983 alignments: crate::Alignments {
984 buffer_copy_offset: wgt::BufferSize::new(4).unwrap(),
985 buffer_copy_pitch: wgt::BufferSize::new(4).unwrap(),
986 uniform_bounds_check_alignment: wgt::BufferSize::new(1).unwrap(),
996 raw_tlas_instance_size: 0,
997 ray_tracing_scratch_buffer_alignment: 0,
998 ray_tracing_pipeline_group_data_size: 0,
999 ray_tracing_pipeline_group_data_alignment: 0,
1000 ray_tracing_pipeline_data_offset_alignment: 0,
1001 },
1002 cooperative_matrix_properties: Vec::new(),
1003 },
1004 })
1005 }
1006
1007 unsafe fn compile_shader(
1008 source: &str,
1009 gl: &glow::Context,
1010 shader_type: u32,
1011 es: bool,
1012 ) -> Option<glow::Shader> {
1013 let source = if es {
1014 format!("#version 300 es\nprecision lowp float;\n{source}")
1015 } else {
1016 let version = gl.version();
1017 if version.major == 3 && version.minor == 0 {
1018 format!("#version 130\n{source}")
1020 } else {
1021 format!("#version 140\n{source}")
1023 }
1024 };
1025 let shader = unsafe { gl.create_shader(shader_type) }.expect("Could not create shader");
1026 unsafe { gl.shader_source(shader, &source) };
1027 unsafe { gl.compile_shader(shader) };
1028
1029 if !unsafe { gl.get_shader_compile_status(shader) } {
1030 let msg = unsafe { gl.get_shader_info_log(shader) };
1031 if !msg.is_empty() {
1032 log::error!("\tShader compile error: {msg}");
1033 }
1034 unsafe { gl.delete_shader(shader) };
1035 None
1036 } else {
1037 Some(shader)
1038 }
1039 }
1040
1041 unsafe fn create_shader_clear_program(
1042 gl: &glow::Context,
1043 es: bool,
1044 ) -> Option<ShaderClearProgram> {
1045 let program = unsafe { gl.create_program() }.expect("Could not create shader program");
1046 let vertex = unsafe {
1047 Self::compile_shader(
1048 include_str!("./shaders/clear.vert"),
1049 gl,
1050 glow::VERTEX_SHADER,
1051 es,
1052 )?
1053 };
1054 let fragment = unsafe {
1055 Self::compile_shader(
1056 include_str!("./shaders/clear.frag"),
1057 gl,
1058 glow::FRAGMENT_SHADER,
1059 es,
1060 )?
1061 };
1062 unsafe { gl.attach_shader(program, vertex) };
1063 unsafe { gl.attach_shader(program, fragment) };
1064 unsafe { gl.link_program(program) };
1065
1066 let linked_ok = unsafe { gl.get_program_link_status(program) };
1067 let msg = unsafe { gl.get_program_info_log(program) };
1068 if !msg.is_empty() {
1069 log::error!("Shader link error: {msg}");
1070 }
1071 if !linked_ok {
1072 return None;
1073 }
1074
1075 let color_uniform_location = unsafe { gl.get_uniform_location(program, "color") }
1076 .expect("Could not find color uniform in shader clear shader");
1077 unsafe { gl.delete_shader(vertex) };
1078 unsafe { gl.delete_shader(fragment) };
1079
1080 Some(ShaderClearProgram {
1081 program,
1082 color_uniform_location,
1083 })
1084 }
1085}
1086
1087impl crate::Adapter for super::Adapter {
1088 type A = super::Api;
1089
1090 unsafe fn open(
1091 &self,
1092 features: wgt::Features,
1093 _limits: &wgt::Limits,
1094 _memory_hints: &wgt::MemoryHints,
1095 ) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
1096 let gl = &self.shared.context.lock();
1097 unsafe { gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1) };
1098 unsafe { gl.pixel_store_i32(glow::PACK_ALIGNMENT, 1) };
1099 let main_vao =
1100 unsafe { gl.create_vertex_array() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
1101 unsafe { gl.bind_vertex_array(Some(main_vao)) };
1102
1103 if !self.shared.es {
1105 unsafe { gl.enable(glow::TEXTURE_CUBE_MAP_SEAMLESS) };
1106 }
1107
1108 let zero_buffer =
1109 unsafe { gl.create_buffer() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
1110 unsafe { gl.bind_buffer(glow::COPY_READ_BUFFER, Some(zero_buffer)) };
1111 let zeroes = vec![0u8; super::ZERO_BUFFER_SIZE];
1112 unsafe { gl.buffer_data_u8_slice(glow::COPY_READ_BUFFER, &zeroes, glow::STATIC_DRAW) };
1113
1114 let shader_clear_program = if self
1118 .shared
1119 .workarounds
1120 .contains(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR)
1121 {
1122 Some(unsafe {
1123 Self::create_shader_clear_program(gl, self.shared.es)
1124 .ok_or(crate::DeviceError::Lost)?
1125 })
1126 } else {
1127 None
1129 };
1130
1131 Ok(crate::OpenDevice {
1132 device: super::Device {
1133 shared: Arc::clone(&self.shared),
1134 main_vao,
1135 #[cfg(all(native, feature = "renderdoc"))]
1136 render_doc: Default::default(),
1137 counters: Default::default(),
1138 },
1139 queue: super::Queue {
1140 shared: Arc::clone(&self.shared),
1141 features,
1142 draw_fbo: unsafe { gl.create_framebuffer() }
1143 .map_err(|_| crate::DeviceError::OutOfMemory)?,
1144 copy_fbo: unsafe { gl.create_framebuffer() }
1145 .map_err(|_| crate::DeviceError::OutOfMemory)?,
1146 shader_clear_program,
1147 zero_buffer,
1148 temp_query_results: Mutex::new(Vec::new()),
1149 draw_buffer_count: AtomicU8::new(1),
1150 current_index_buffer: Mutex::new(None),
1151 },
1152 })
1153 }
1154
1155 unsafe fn texture_format_capabilities(
1156 &self,
1157 format: wgt::TextureFormat,
1158 ) -> crate::TextureFormatCapabilities {
1159 use crate::TextureFormatCapabilities as Tfc;
1160 use wgt::TextureFormat as Tf;
1161
1162 let sample_count = {
1163 let max_samples = self.shared.max_msaa_samples;
1164 if max_samples >= 16 {
1165 Tfc::MULTISAMPLE_X2
1166 | Tfc::MULTISAMPLE_X4
1167 | Tfc::MULTISAMPLE_X8
1168 | Tfc::MULTISAMPLE_X16
1169 } else if max_samples >= 8 {
1170 Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4 | Tfc::MULTISAMPLE_X8
1171 } else {
1172 Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4
1177 }
1178 };
1179
1180 let empty = Tfc::empty();
1185 let base = Tfc::COPY_SRC | Tfc::COPY_DST;
1186 let unfilterable = base | Tfc::SAMPLED;
1187 let depth = base | Tfc::SAMPLED | sample_count | Tfc::DEPTH_STENCIL_ATTACHMENT;
1188 let filterable = unfilterable | Tfc::SAMPLED_LINEAR;
1189 let renderable =
1190 unfilterable | Tfc::COLOR_ATTACHMENT | sample_count | Tfc::MULTISAMPLE_RESOLVE;
1191 let filterable_renderable = filterable | renderable | Tfc::COLOR_ATTACHMENT_BLEND;
1192 let storage =
1193 base | Tfc::STORAGE_READ_WRITE | Tfc::STORAGE_READ_ONLY | Tfc::STORAGE_WRITE_ONLY;
1194
1195 let feature_fn = |f, caps| {
1196 if self.shared.features.contains(f) {
1197 caps
1198 } else {
1199 empty
1200 }
1201 };
1202
1203 let bcn_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_BC, filterable);
1204 let etc2_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ETC2, filterable);
1205 let astc_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC, filterable);
1206 let astc_hdr_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR, filterable);
1207
1208 let private_caps_fn = |f, caps| {
1209 if self.shared.private_caps.contains(f) {
1210 caps
1211 } else {
1212 empty
1213 }
1214 };
1215
1216 let half_float_renderable = private_caps_fn(
1217 super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT,
1218 Tfc::COLOR_ATTACHMENT
1219 | Tfc::COLOR_ATTACHMENT_BLEND
1220 | sample_count
1221 | Tfc::MULTISAMPLE_RESOLVE,
1222 );
1223
1224 let float_renderable = private_caps_fn(
1225 super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
1226 Tfc::COLOR_ATTACHMENT
1227 | Tfc::COLOR_ATTACHMENT_BLEND
1228 | sample_count
1229 | Tfc::MULTISAMPLE_RESOLVE,
1230 );
1231
1232 let texture_float_linear = feature_fn(wgt::Features::FLOAT32_FILTERABLE, filterable);
1233
1234 let image_atomic = feature_fn(wgt::Features::TEXTURE_ATOMIC, Tfc::STORAGE_ATOMIC);
1235 let image_64_atomic = feature_fn(wgt::Features::TEXTURE_INT64_ATOMIC, Tfc::STORAGE_ATOMIC);
1236
1237 let norm16_unorm = private_caps_fn(
1242 super::PrivateCapabilities::TEXTURE_FORMAT_NORM16,
1243 filterable_renderable,
1244 );
1245 let norm16_snorm = if self
1246 .shared
1247 .private_caps
1248 .contains(super::PrivateCapabilities::TEXTURE_FORMAT_SNORM16_RENDERABLE)
1249 {
1250 norm16_unorm
1251 } else {
1252 private_caps_fn(
1253 super::PrivateCapabilities::TEXTURE_FORMAT_NORM16,
1254 filterable,
1255 )
1256 };
1257 let norm16_storage = private_caps_fn(
1258 super::PrivateCapabilities::TEXTURE_FORMAT_NORM16_STORAGE,
1259 storage,
1260 );
1261
1262 match format {
1263 Tf::R8Unorm => filterable_renderable,
1264 Tf::R8Snorm => filterable,
1265 Tf::R8Uint => renderable,
1266 Tf::R8Sint => renderable,
1267 Tf::R16Uint => renderable,
1268 Tf::R16Sint => renderable,
1269 Tf::R16Unorm => norm16_unorm | norm16_storage,
1270 Tf::R16Snorm => norm16_snorm | norm16_storage,
1271 Tf::R16Float => filterable | half_float_renderable,
1272 Tf::Rg8Unorm => filterable_renderable,
1273 Tf::Rg8Snorm => filterable,
1274 Tf::Rg8Uint => renderable,
1275 Tf::Rg8Sint => renderable,
1276 Tf::R32Uint => renderable | storage | image_atomic,
1277 Tf::R32Sint => renderable | storage | image_atomic,
1278 Tf::R32Float => unfilterable | storage | float_renderable | texture_float_linear,
1279 Tf::Rg16Uint => renderable,
1280 Tf::Rg16Sint => renderable,
1281 Tf::Rg16Unorm => norm16_unorm | norm16_storage,
1282 Tf::Rg16Snorm => norm16_snorm | norm16_storage,
1283 Tf::Rg16Float => filterable | half_float_renderable,
1284 Tf::Rgba8Unorm => filterable_renderable | storage,
1285 Tf::Rgba8UnormSrgb => filterable_renderable,
1286 Tf::Bgra8Unorm | Tf::Bgra8UnormSrgb => filterable_renderable,
1287 Tf::Rgba8Snorm => filterable | storage,
1288 Tf::Rgba8Uint => renderable | storage,
1289 Tf::Rgba8Sint => renderable | storage,
1290 Tf::Rgb10a2Uint => renderable,
1291 Tf::Rgb10a2Unorm => filterable_renderable,
1292 Tf::Rg11b10Ufloat => filterable | float_renderable,
1293 Tf::R64Uint => image_64_atomic,
1294 Tf::Rg32Uint => renderable,
1295 Tf::Rg32Sint => renderable,
1296 Tf::Rg32Float => unfilterable | float_renderable | texture_float_linear,
1297 Tf::Rgba16Uint => renderable | storage,
1298 Tf::Rgba16Sint => renderable | storage,
1299 Tf::Rgba16Unorm => norm16_unorm | norm16_storage,
1300 Tf::Rgba16Snorm => norm16_snorm | norm16_storage,
1301 Tf::Rgba16Float => filterable | storage | half_float_renderable,
1302 Tf::Rgba32Uint => renderable | storage,
1303 Tf::Rgba32Sint => renderable | storage,
1304 Tf::Rgba32Float => unfilterable | storage | float_renderable | texture_float_linear,
1305 Tf::Stencil8
1306 | Tf::Depth16Unorm
1307 | Tf::Depth32Float
1308 | Tf::Depth32FloatStencil8
1309 | Tf::Depth24Plus
1310 | Tf::Depth24PlusStencil8 => depth,
1311 Tf::NV12 => empty,
1312 Tf::P010 => empty,
1313 Tf::Rgb9e5Ufloat => filterable,
1314 Tf::Bc1RgbaUnorm
1315 | Tf::Bc1RgbaUnormSrgb
1316 | Tf::Bc2RgbaUnorm
1317 | Tf::Bc2RgbaUnormSrgb
1318 | Tf::Bc3RgbaUnorm
1319 | Tf::Bc3RgbaUnormSrgb
1320 | Tf::Bc4RUnorm
1321 | Tf::Bc4RSnorm
1322 | Tf::Bc5RgUnorm
1323 | Tf::Bc5RgSnorm
1324 | Tf::Bc6hRgbFloat
1325 | Tf::Bc6hRgbUfloat
1326 | Tf::Bc7RgbaUnorm
1327 | Tf::Bc7RgbaUnormSrgb => bcn_features,
1328 Tf::Etc2Rgb8Unorm
1329 | Tf::Etc2Rgb8UnormSrgb
1330 | Tf::Etc2Rgb8A1Unorm
1331 | Tf::Etc2Rgb8A1UnormSrgb
1332 | Tf::Etc2Rgba8Unorm
1333 | Tf::Etc2Rgba8UnormSrgb
1334 | Tf::EacR11Unorm
1335 | Tf::EacR11Snorm
1336 | Tf::EacRg11Unorm
1337 | Tf::EacRg11Snorm => etc2_features,
1338 Tf::Astc {
1339 block: _,
1340 channel: AstcChannel::Unorm | AstcChannel::UnormSrgb,
1341 } => astc_features,
1342 Tf::Astc {
1343 block: _,
1344 channel: AstcChannel::Hdr,
1345 } => astc_hdr_features,
1346 }
1347 }
1348
1349 unsafe fn surface_capabilities(
1350 &self,
1351 surface: &super::Surface,
1352 ) -> Option<crate::SurfaceCapabilities> {
1353 #[cfg(webgl)]
1354 if self.shared.context.webgl2_context != surface.webgl2_context {
1355 return None;
1356 }
1357
1358 if surface.presentable {
1359 let format_caps = |format: wgt::TextureFormat| wgt::SurfaceFormatCapabilities {
1362 format,
1363 color_spaces: wgt::SurfaceColorSpaces::SRGB,
1364 };
1365 let mut formats = vec![
1366 format_caps(wgt::TextureFormat::Rgba8Unorm),
1367 #[cfg(native)]
1368 format_caps(wgt::TextureFormat::Bgra8Unorm),
1369 ];
1370 if surface.supports_srgb() {
1371 formats.extend([
1372 format_caps(wgt::TextureFormat::Rgba8UnormSrgb),
1373 #[cfg(native)]
1374 format_caps(wgt::TextureFormat::Bgra8UnormSrgb),
1375 ])
1376 }
1377 if self
1378 .shared
1379 .private_caps
1380 .contains(super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT)
1381 {
1382 formats.push(format_caps(wgt::TextureFormat::Rgba16Float))
1383 }
1384
1385 Some(crate::SurfaceCapabilities {
1386 formats,
1387 present_modes: if cfg!(windows) {
1388 vec![wgt::PresentMode::Fifo, wgt::PresentMode::Immediate]
1389 } else {
1390 vec![wgt::PresentMode::Fifo] },
1392 composite_alpha_modes: vec![wgt::CompositeAlphaMode::Opaque], maximum_frame_latency: 2..=2, current_extent: None,
1395 usage: wgt::TextureUses::COLOR_TARGET,
1396 })
1397 } else {
1398 None
1399 }
1400 }
1401
1402 unsafe fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
1403 wgt::PresentationTimestamp::INVALID_TIMESTAMP
1404 }
1405
1406 fn get_ordered_buffer_usages(&self) -> wgt::BufferUses {
1407 wgt::BufferUses::INCLUSIVE | wgt::BufferUses::MAP_WRITE
1408 }
1409
1410 fn get_ordered_texture_usages(&self) -> wgt::TextureUses {
1412 wgt::TextureUses::INCLUSIVE
1413 | wgt::TextureUses::COLOR_TARGET
1414 | wgt::TextureUses::DEPTH_WRITE
1415 | wgt::TextureUses::STENCIL_WRITE
1416 }
1417}
1418
1419impl super::AdapterShared {
1420 pub(super) unsafe fn get_buffer_sub_data(
1421 &self,
1422 gl: &glow::Context,
1423 target: u32,
1424 offset: i32,
1425 dst_data: &mut [u8],
1426 ) {
1427 if self
1428 .private_caps
1429 .contains(super::PrivateCapabilities::GET_BUFFER_SUB_DATA)
1430 {
1431 unsafe { gl.get_buffer_sub_data(target, offset, dst_data) };
1432 } else {
1433 log::error!("Fake map");
1434 let length = dst_data.len();
1435 if length != 0 {
1437 let buffer_mapping =
1438 unsafe { gl.map_buffer_range(target, offset, length as _, glow::MAP_READ_BIT) };
1439
1440 unsafe {
1441 core::ptr::copy_nonoverlapping(buffer_mapping, dst_data.as_mut_ptr(), length)
1442 };
1443
1444 unsafe { gl.unmap_buffer(target) };
1445 }
1446 }
1447 }
1448}
1449
1450#[cfg(send_sync)]
1451unsafe impl Sync for super::Adapter {}
1452#[cfg(send_sync)]
1453unsafe impl Send for super::Adapter {}
1454
1455#[cfg(test)]
1456mod tests {
1457 use super::super::Adapter;
1458
1459 #[test]
1460 fn test_version_parse() {
1461 Adapter::parse_version("1").unwrap_err();
1462 Adapter::parse_version("1.").unwrap_err();
1463 Adapter::parse_version("1 h3l1o. W0rld").unwrap_err();
1464 Adapter::parse_version("1. h3l1o. W0rld").unwrap_err();
1465 Adapter::parse_version("1.2.3").unwrap_err();
1466
1467 assert_eq!(Adapter::parse_version("OpenGL ES 3.1").unwrap(), (3, 1));
1468 assert_eq!(
1469 Adapter::parse_version("OpenGL ES 2.0 Google Nexus").unwrap(),
1470 (2, 0)
1471 );
1472 assert_eq!(Adapter::parse_version("GLSL ES 1.1").unwrap(), (1, 1));
1473 assert_eq!(
1474 Adapter::parse_version("OpenGL ES GLSL ES 3.20").unwrap(),
1475 (3, 2)
1476 );
1477 assert_eq!(
1478 Adapter::parse_version("WebGL 2.0 (OpenGL ES 3.0 Chromium)").unwrap(),
1480 (3, 0)
1481 );
1482 assert_eq!(
1483 Adapter::parse_version("WebGL GLSL ES 3.00 (OpenGL ES GLSL ES 3.0 Chromium)").unwrap(),
1484 (3, 0)
1485 );
1486 }
1487}