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