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 driver_info: version,
191 backend: 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::warn!("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::warn!("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
384 let mut downlevel_flags = wgt::DownlevelFlags::empty()
385 | wgt::DownlevelFlags::NON_POWER_OF_TWO_MIPMAPPED_TEXTURES
386 | wgt::DownlevelFlags::CUBE_ARRAY_TEXTURES
387 | wgt::DownlevelFlags::COMPARISON_SAMPLERS
388 | wgt::DownlevelFlags::SHADER_F16_IN_F32;
389 downlevel_flags.set(wgt::DownlevelFlags::COMPUTE_SHADERS, supports_compute);
390 downlevel_flags.set(
391 wgt::DownlevelFlags::FRAGMENT_WRITABLE_STORAGE,
392 max_storage_block_size != 0,
393 );
394 downlevel_flags.set(wgt::DownlevelFlags::INDIRECT_EXECUTION, indirect_execution);
395 downlevel_flags.set(wgt::DownlevelFlags::BASE_VERTEX, supported((3, 2), (3, 2)));
396 downlevel_flags.set(
397 wgt::DownlevelFlags::INDEPENDENT_BLEND,
398 supported((3, 2), (4, 0)) || extensions.contains("GL_EXT_draw_buffers_indexed"),
399 );
400 downlevel_flags.set(
401 wgt::DownlevelFlags::VERTEX_STORAGE,
402 max_storage_block_size != 0
403 && max_storage_buffers_per_shader_stage != 0
404 && (vertex_shader_storage_blocks != 0 || vertex_ssbo_false_zero),
405 );
406 downlevel_flags.set(wgt::DownlevelFlags::FRAGMENT_STORAGE, supports_storage);
407 if extensions.contains("EXT_texture_filter_anisotropic")
408 || extensions.contains("GL_EXT_texture_filter_anisotropic")
409 {
410 let max_aniso =
411 unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_MAX_ANISOTROPY_EXT) } as u32;
412 downlevel_flags.set(wgt::DownlevelFlags::ANISOTROPIC_FILTERING, max_aniso >= 16);
413 }
414 downlevel_flags.set(
415 wgt::DownlevelFlags::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED,
416 !(cfg!(any(webgl, Emscripten)) || is_angle),
417 );
418 downlevel_flags.set(
420 wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER,
421 !cfg!(any(webgl, Emscripten)),
422 );
423 downlevel_flags.set(
424 wgt::DownlevelFlags::UNRESTRICTED_EXTERNAL_TEXTURE_COPIES,
425 !cfg!(any(webgl, Emscripten)),
426 );
427 downlevel_flags.set(
428 wgt::DownlevelFlags::FULL_DRAW_INDEX_UINT32,
429 max_element_index == u32::MAX,
430 );
431 downlevel_flags.set(
432 wgt::DownlevelFlags::MULTISAMPLED_SHADING,
433 supported((3, 2), (4, 0)) || extensions.contains("OES_sample_variables"),
434 );
435 let query_buffers = extensions.contains("GL_ARB_query_buffer_object")
436 || extensions.contains("GL_AMD_query_buffer_object");
437 if query_buffers {
438 downlevel_flags.set(wgt::DownlevelFlags::NONBLOCKING_QUERY_RESOLVE, true);
439 }
440
441 let mut features = wgt::Features::empty()
442 | wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES
443 | wgt::Features::CLEAR_TEXTURE
444 | wgt::Features::PUSH_CONSTANTS
445 | wgt::Features::DEPTH32FLOAT_STENCIL8;
446 features.set(
447 wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER | wgt::Features::ADDRESS_MODE_CLAMP_TO_ZERO,
448 extensions.contains("GL_EXT_texture_border_clamp")
449 || extensions.contains("GL_ARB_texture_border_clamp"),
450 );
451 features.set(
452 wgt::Features::DEPTH_CLIP_CONTROL,
453 extensions.contains("GL_EXT_depth_clamp") || extensions.contains("GL_ARB_depth_clamp"),
454 );
455 features.set(
456 wgt::Features::VERTEX_WRITABLE_STORAGE,
457 downlevel_flags.contains(wgt::DownlevelFlags::VERTEX_STORAGE)
458 && vertex_shader_storage_textures != 0,
459 );
460 features.set(
461 wgt::Features::MULTIVIEW,
462 extensions.contains("OVR_multiview2") || extensions.contains("GL_OVR_multiview2"),
463 );
464 features.set(
465 wgt::Features::DUAL_SOURCE_BLENDING,
466 extensions.contains("GL_EXT_blend_func_extended")
467 || extensions.contains("GL_ARB_blend_func_extended"),
468 );
469 features.set(
470 wgt::Features::CLIP_DISTANCES,
471 full_ver.is_some() || extensions.contains("GL_EXT_clip_cull_distance"),
472 );
473 features.set(
474 wgt::Features::SHADER_PRIMITIVE_INDEX,
475 supported((3, 2), (3, 2))
476 || extensions.contains("OES_geometry_shader")
477 || extensions.contains("GL_ARB_geometry_shader4"),
478 );
479 features.set(
480 wgt::Features::SHADER_EARLY_DEPTH_TEST,
481 supported((3, 1), (4, 2)) || extensions.contains("GL_ARB_shader_image_load_store"),
482 );
483 if extensions.contains("GL_ARB_timer_query") {
484 features.set(wgt::Features::TIMESTAMP_QUERY, true);
485 features.set(wgt::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS, true);
486 features.set(wgt::Features::TIMESTAMP_QUERY_INSIDE_PASSES, true);
487 }
488 let gl_bcn_exts = [
489 "GL_EXT_texture_compression_s3tc",
490 "GL_EXT_texture_compression_rgtc",
491 "GL_ARB_texture_compression_bptc",
492 ];
493 let gles_bcn_exts = [
494 "GL_EXT_texture_compression_s3tc_srgb",
495 "GL_EXT_texture_compression_rgtc",
496 "GL_EXT_texture_compression_bptc",
497 ];
498 let webgl_bcn_exts = [
499 "WEBGL_compressed_texture_s3tc",
500 "WEBGL_compressed_texture_s3tc_srgb",
501 "EXT_texture_compression_rgtc",
502 "EXT_texture_compression_bptc",
503 ];
504 let bcn_exts = if cfg!(any(webgl, Emscripten)) {
505 &webgl_bcn_exts[..]
506 } else if es_ver.is_some() {
507 &gles_bcn_exts[..]
508 } else {
509 &gl_bcn_exts[..]
510 };
511 features.set(
512 wgt::Features::TEXTURE_COMPRESSION_BC,
513 bcn_exts.iter().all(|&ext| extensions.contains(ext)),
514 );
515 features.set(
516 wgt::Features::TEXTURE_COMPRESSION_BC_SLICED_3D,
517 bcn_exts.iter().all(|&ext| extensions.contains(ext)), );
519 let has_etc = if cfg!(any(webgl, Emscripten)) {
520 extensions.contains("WEBGL_compressed_texture_etc")
521 } else {
522 es_ver.is_some() || extensions.contains("GL_ARB_ES3_compatibility")
523 };
524 features.set(wgt::Features::TEXTURE_COMPRESSION_ETC2, has_etc);
525
526 if extensions.contains("WEBGL_compressed_texture_astc")
528 || extensions.contains("GL_OES_texture_compression_astc")
529 {
530 #[cfg(webgl)]
531 {
532 if context
533 .glow_context
534 .compressed_texture_astc_supports_ldr_profile()
535 {
536 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC);
537 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D);
538 }
539 if context
540 .glow_context
541 .compressed_texture_astc_supports_hdr_profile()
542 {
543 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR);
544 }
545 }
546
547 #[cfg(any(native, Emscripten))]
548 {
549 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC);
550 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D);
551 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR);
552 }
553 } else {
554 features.set(
555 wgt::Features::TEXTURE_COMPRESSION_ASTC,
556 extensions.contains("GL_KHR_texture_compression_astc_ldr"),
557 );
558 features.set(
559 wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D,
560 extensions.contains("GL_KHR_texture_compression_astc_ldr")
561 && extensions.contains("GL_KHR_texture_compression_astc_sliced_3d"),
562 );
563 features.set(
564 wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR,
565 extensions.contains("GL_KHR_texture_compression_astc_hdr"),
566 );
567 }
568
569 features.set(
570 wgt::Features::FLOAT32_FILTERABLE,
571 extensions.contains("GL_ARB_color_buffer_float")
572 || extensions.contains("GL_EXT_color_buffer_float")
573 || extensions.contains("OES_texture_float_linear"),
574 );
575
576 if es_ver.is_none() {
577 features |= wgt::Features::POLYGON_MODE_LINE | wgt::Features::POLYGON_MODE_POINT;
578 }
579
580 let mut private_caps = super::PrivateCapabilities::empty();
583 private_caps.set(
584 super::PrivateCapabilities::BUFFER_ALLOCATION,
585 extensions.contains("GL_EXT_buffer_storage")
586 || extensions.contains("GL_ARB_buffer_storage"),
587 );
588 private_caps.set(
589 super::PrivateCapabilities::SHADER_BINDING_LAYOUT,
590 supports_compute,
591 );
592 private_caps.set(
593 super::PrivateCapabilities::SHADER_TEXTURE_SHADOW_LOD,
594 extensions.contains("GL_EXT_texture_shadow_lod"),
595 );
596 private_caps.set(
597 super::PrivateCapabilities::MEMORY_BARRIERS,
598 supported((3, 1), (4, 2)),
599 );
600 private_caps.set(
601 super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT,
602 supported((3, 1), (4, 3)) || extensions.contains("GL_ARB_vertex_attrib_binding"),
603 );
604 private_caps.set(
605 super::PrivateCapabilities::INDEX_BUFFER_ROLE_CHANGE,
606 !cfg!(any(webgl, Emscripten)),
607 );
608 private_caps.set(
609 super::PrivateCapabilities::GET_BUFFER_SUB_DATA,
610 cfg!(any(webgl, Emscripten)) || full_ver.is_some(),
611 );
612 let color_buffer_float = extensions.contains("GL_EXT_color_buffer_float")
613 || extensions.contains("GL_ARB_color_buffer_float")
614 || extensions.contains("EXT_color_buffer_float");
615 let color_buffer_half_float = extensions.contains("GL_EXT_color_buffer_half_float")
616 || extensions.contains("GL_ARB_half_float_pixel");
617 private_caps.set(
618 super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT,
619 color_buffer_half_float || color_buffer_float,
620 );
621 private_caps.set(
622 super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
623 color_buffer_float,
624 );
625 private_caps.set(super::PrivateCapabilities::QUERY_BUFFERS, query_buffers);
626 private_caps.set(super::PrivateCapabilities::QUERY_64BIT, full_ver.is_some());
627 private_caps.set(
628 super::PrivateCapabilities::TEXTURE_STORAGE,
629 supported((3, 0), (4, 2)),
630 );
631 private_caps.set(super::PrivateCapabilities::DEBUG_FNS, gl.supports_debug());
632 private_caps.set(
633 super::PrivateCapabilities::INVALIDATE_FRAMEBUFFER,
634 supported((3, 0), (4, 3)),
635 );
636 if let Some(full_ver) = full_ver {
637 let supported =
638 full_ver >= (4, 2) && extensions.contains("GL_ARB_shader_draw_parameters");
639 private_caps.set(
640 super::PrivateCapabilities::FULLY_FEATURED_INSTANCING,
641 supported,
642 );
643 features.set(wgt::Features::INDIRECT_FIRST_INSTANCE, supported);
650 }
651
652 let max_texture_size = unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_SIZE) } as u32;
653 let max_texture_3d_size = unsafe { gl.get_parameter_i32(glow::MAX_3D_TEXTURE_SIZE) } as u32;
654
655 let min_uniform_buffer_offset_alignment =
656 (unsafe { gl.get_parameter_i32(glow::UNIFORM_BUFFER_OFFSET_ALIGNMENT) } as u32);
657 let min_storage_buffer_offset_alignment = if supports_storage {
658 (unsafe { gl.get_parameter_i32(glow::SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT) } as u32)
659 } else {
660 256
661 };
662 let max_uniform_buffers_per_shader_stage =
663 unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_UNIFORM_BLOCKS) }
664 .min(unsafe { gl.get_parameter_i32(glow::MAX_FRAGMENT_UNIFORM_BLOCKS) })
665 as u32;
666
667 let max_compute_workgroups_per_dimension = if supports_work_group_params {
668 unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 0) }
669 .min(unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 1) })
670 .min(unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 2) })
671 as u32
672 } else {
673 0
674 };
675
676 let max_color_attachments = unsafe {
677 gl.get_parameter_i32(glow::MAX_COLOR_ATTACHMENTS)
678 .min(gl.get_parameter_i32(glow::MAX_DRAW_BUFFERS))
679 .min(crate::MAX_COLOR_ATTACHMENTS as i32) as u32
680 };
681
682 let max_color_attachment_bytes_per_sample =
684 max_color_attachments * wgt::TextureFormat::MAX_TARGET_PIXEL_BYTE_COST;
685
686 let limits = wgt::Limits {
687 max_texture_dimension_1d: max_texture_size,
688 max_texture_dimension_2d: max_texture_size,
689 max_texture_dimension_3d: max_texture_3d_size,
690 max_texture_array_layers: unsafe {
691 gl.get_parameter_i32(glow::MAX_ARRAY_TEXTURE_LAYERS)
692 } as u32,
693 max_bind_groups: crate::MAX_BIND_GROUPS as u32,
694 max_bindings_per_bind_group: 65535,
695 max_dynamic_uniform_buffers_per_pipeline_layout: max_uniform_buffers_per_shader_stage,
696 max_dynamic_storage_buffers_per_pipeline_layout: max_storage_buffers_per_shader_stage,
697 max_sampled_textures_per_shader_stage: super::MAX_TEXTURE_SLOTS as u32,
698 max_samplers_per_shader_stage: super::MAX_SAMPLERS as u32,
699 max_storage_buffers_per_shader_stage,
700 max_storage_textures_per_shader_stage,
701 max_uniform_buffers_per_shader_stage,
702 max_binding_array_elements_per_shader_stage: 0,
703 max_binding_array_sampler_elements_per_shader_stage: 0,
704 max_uniform_buffer_binding_size: unsafe {
705 gl.get_parameter_i32(glow::MAX_UNIFORM_BLOCK_SIZE)
706 } as u32,
707 max_storage_buffer_binding_size: if supports_storage {
708 unsafe { gl.get_parameter_i32(glow::MAX_SHADER_STORAGE_BLOCK_SIZE) }
709 } else {
710 0
711 } as u32,
712 max_vertex_buffers: if private_caps
713 .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
714 {
715 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_BINDINGS) } as u32)
716 } else {
717 16 }
719 .min(crate::MAX_VERTEX_BUFFERS as u32),
720 max_vertex_attributes: (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIBS) }
721 as u32)
722 .min(super::MAX_VERTEX_ATTRIBUTES as u32),
723 max_vertex_buffer_array_stride: if private_caps
724 .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
725 {
726 if let Some(full_ver) = full_ver {
727 if full_ver >= (4, 4) {
728 let value =
730 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) })
731 as u32;
732
733 if value == 0 {
734 log::warn!("Max vertex attribute stride is 0. Assuming it is 2048");
738 2048
739 } else {
740 value
741 }
742 } else {
743 log::warn!("Max vertex attribute stride unknown. Assuming it is 2048");
744 2048
745 }
746 } else {
747 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) }) as u32
748 }
749 } else {
750 !0
751 },
752 min_subgroup_size: 0,
753 max_subgroup_size: 0,
754 max_push_constant_size: super::MAX_PUSH_CONSTANTS as u32 * 4,
755 min_uniform_buffer_offset_alignment,
756 min_storage_buffer_offset_alignment,
757 max_inter_stage_shader_components: {
758 let max_varying_components =
762 unsafe { gl.get_parameter_i32(glow::MAX_VARYING_COMPONENTS) } as u32;
763 if max_varying_components == 0 {
764 60
766 } else {
767 max_varying_components
768 }
769 },
770 max_color_attachments,
771 max_color_attachment_bytes_per_sample,
772 max_compute_workgroup_storage_size: if supports_work_group_params {
773 (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_SHARED_MEMORY_SIZE) } as u32)
774 } else {
775 0
776 },
777 max_compute_invocations_per_workgroup: if supports_work_group_params {
778 (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_WORK_GROUP_INVOCATIONS) } as u32)
779 } else {
780 0
781 },
782 max_compute_workgroup_size_x: if supports_work_group_params {
783 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 0) }
784 as u32)
785 } else {
786 0
787 },
788 max_compute_workgroup_size_y: if supports_work_group_params {
789 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 1) }
790 as u32)
791 } else {
792 0
793 },
794 max_compute_workgroup_size_z: if supports_work_group_params {
795 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 2) }
796 as u32)
797 } else {
798 0
799 },
800 max_compute_workgroups_per_dimension,
801 max_buffer_size: i32::MAX as u64,
802 max_non_sampler_bindings: u32::MAX,
803
804 max_task_workgroup_total_count: 0,
805 max_task_workgroups_per_dimension: 0,
806 max_mesh_multiview_count: 0,
807 max_mesh_output_layers: 0,
808
809 max_blas_primitive_count: 0,
810 max_blas_geometry_count: 0,
811 max_tlas_instance_count: 0,
812 max_acceleration_structures_per_shader_stage: 0,
813 };
814
815 let mut workarounds = super::Workarounds::empty();
816
817 workarounds.set(
818 super::Workarounds::EMULATE_BUFFER_MAP,
819 cfg!(any(webgl, Emscripten)),
820 );
821
822 let r = renderer.to_lowercase();
823 if context.is_owned()
826 && r.contains("mesa")
827 && r.contains("intel")
828 && r.split(&[' ', '(', ')'][..])
829 .any(|substr| substr.len() == 3 && substr.chars().nth(2) == Some('l'))
830 {
831 log::warn!(
832 "Detected skylake derivative running on mesa i915. Clears to srgb textures will \
833 use manual shader clears."
834 );
835 workarounds.set(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR, true);
836 }
837
838 let downlevel_defaults = wgt::DownlevelLimits {};
839 let max_samples = unsafe { gl.get_parameter_i32(glow::MAX_SAMPLES) };
840
841 #[cfg_attr(target_arch = "wasm32", allow(dropping_references))]
845 drop(gl);
846
847 Some(crate::ExposedAdapter {
848 adapter: super::Adapter {
849 shared: Arc::new(super::AdapterShared {
850 context,
851 private_caps,
852 workarounds,
853 features,
854 limits: limits.clone(),
855 options: backend_options,
856 shading_language_version,
857 next_shader_id: Default::default(),
858 program_cache: Default::default(),
859 es: es_ver.is_some(),
860 max_msaa_samples: max_samples,
861 }),
862 },
863 info: Self::make_info(vendor, renderer, version),
864 features,
865 capabilities: crate::Capabilities {
866 limits,
867 downlevel: wgt::DownlevelCapabilities {
868 flags: downlevel_flags,
869 limits: downlevel_defaults,
870 shader_model: wgt::ShaderModel::Sm5,
871 },
872 alignments: crate::Alignments {
873 buffer_copy_offset: wgt::BufferSize::new(4).unwrap(),
874 buffer_copy_pitch: wgt::BufferSize::new(4).unwrap(),
875 uniform_bounds_check_alignment: wgt::BufferSize::new(1).unwrap(),
885 raw_tlas_instance_size: 0,
886 ray_tracing_scratch_buffer_alignment: 0,
887 },
888 },
889 })
890 }
891
892 unsafe fn compile_shader(
893 source: &str,
894 gl: &glow::Context,
895 shader_type: u32,
896 es: bool,
897 ) -> Option<glow::Shader> {
898 let source = if es {
899 format!("#version 300 es\nprecision lowp float;\n{source}")
900 } else {
901 let version = gl.version();
902 if version.major == 3 && version.minor == 0 {
903 format!("#version 130\n{source}")
905 } else {
906 format!("#version 140\n{source}")
908 }
909 };
910 let shader = unsafe { gl.create_shader(shader_type) }.expect("Could not create shader");
911 unsafe { gl.shader_source(shader, &source) };
912 unsafe { gl.compile_shader(shader) };
913
914 if !unsafe { gl.get_shader_compile_status(shader) } {
915 let msg = unsafe { gl.get_shader_info_log(shader) };
916 if !msg.is_empty() {
917 log::error!("\tShader compile error: {msg}");
918 }
919 unsafe { gl.delete_shader(shader) };
920 None
921 } else {
922 Some(shader)
923 }
924 }
925
926 unsafe fn create_shader_clear_program(
927 gl: &glow::Context,
928 es: bool,
929 ) -> Option<ShaderClearProgram> {
930 let program = unsafe { gl.create_program() }.expect("Could not create shader program");
931 let vertex = unsafe {
932 Self::compile_shader(
933 include_str!("./shaders/clear.vert"),
934 gl,
935 glow::VERTEX_SHADER,
936 es,
937 )?
938 };
939 let fragment = unsafe {
940 Self::compile_shader(
941 include_str!("./shaders/clear.frag"),
942 gl,
943 glow::FRAGMENT_SHADER,
944 es,
945 )?
946 };
947 unsafe { gl.attach_shader(program, vertex) };
948 unsafe { gl.attach_shader(program, fragment) };
949 unsafe { gl.link_program(program) };
950
951 let linked_ok = unsafe { gl.get_program_link_status(program) };
952 let msg = unsafe { gl.get_program_info_log(program) };
953 if !msg.is_empty() {
954 log::warn!("Shader link error: {msg}");
955 }
956 if !linked_ok {
957 return None;
958 }
959
960 let color_uniform_location = unsafe { gl.get_uniform_location(program, "color") }
961 .expect("Could not find color uniform in shader clear shader");
962 unsafe { gl.delete_shader(vertex) };
963 unsafe { gl.delete_shader(fragment) };
964
965 Some(ShaderClearProgram {
966 program,
967 color_uniform_location,
968 })
969 }
970}
971
972impl crate::Adapter for super::Adapter {
973 type A = super::Api;
974
975 unsafe fn open(
976 &self,
977 features: wgt::Features,
978 _limits: &wgt::Limits,
979 _memory_hints: &wgt::MemoryHints,
980 ) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
981 let gl = &self.shared.context.lock();
982 unsafe { gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1) };
983 unsafe { gl.pixel_store_i32(glow::PACK_ALIGNMENT, 1) };
984 let main_vao =
985 unsafe { gl.create_vertex_array() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
986 unsafe { gl.bind_vertex_array(Some(main_vao)) };
987
988 let zero_buffer =
989 unsafe { gl.create_buffer() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
990 unsafe { gl.bind_buffer(glow::COPY_READ_BUFFER, Some(zero_buffer)) };
991 let zeroes = vec![0u8; super::ZERO_BUFFER_SIZE];
992 unsafe { gl.buffer_data_u8_slice(glow::COPY_READ_BUFFER, &zeroes, glow::STATIC_DRAW) };
993
994 let shader_clear_program = if self
998 .shared
999 .workarounds
1000 .contains(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR)
1001 {
1002 Some(unsafe {
1003 Self::create_shader_clear_program(gl, self.shared.es)
1004 .ok_or(crate::DeviceError::Lost)?
1005 })
1006 } else {
1007 None
1009 };
1010
1011 Ok(crate::OpenDevice {
1012 device: super::Device {
1013 shared: Arc::clone(&self.shared),
1014 main_vao,
1015 #[cfg(all(native, feature = "renderdoc"))]
1016 render_doc: Default::default(),
1017 counters: Default::default(),
1018 },
1019 queue: super::Queue {
1020 shared: Arc::clone(&self.shared),
1021 features,
1022 draw_fbo: unsafe { gl.create_framebuffer() }
1023 .map_err(|_| crate::DeviceError::OutOfMemory)?,
1024 copy_fbo: unsafe { gl.create_framebuffer() }
1025 .map_err(|_| crate::DeviceError::OutOfMemory)?,
1026 shader_clear_program,
1027 zero_buffer,
1028 temp_query_results: Mutex::new(Vec::new()),
1029 draw_buffer_count: AtomicU8::new(1),
1030 current_index_buffer: Mutex::new(None),
1031 },
1032 })
1033 }
1034
1035 unsafe fn texture_format_capabilities(
1036 &self,
1037 format: wgt::TextureFormat,
1038 ) -> crate::TextureFormatCapabilities {
1039 use crate::TextureFormatCapabilities as Tfc;
1040 use wgt::TextureFormat as Tf;
1041
1042 let sample_count = {
1043 let max_samples = self.shared.max_msaa_samples;
1044 if max_samples >= 16 {
1045 Tfc::MULTISAMPLE_X2
1046 | Tfc::MULTISAMPLE_X4
1047 | Tfc::MULTISAMPLE_X8
1048 | Tfc::MULTISAMPLE_X16
1049 } else if max_samples >= 8 {
1050 Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4 | Tfc::MULTISAMPLE_X8
1051 } else {
1052 Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4
1057 }
1058 };
1059
1060 let empty = Tfc::empty();
1065 let base = Tfc::COPY_SRC | Tfc::COPY_DST;
1066 let unfilterable = base | Tfc::SAMPLED;
1067 let depth = base | Tfc::SAMPLED | sample_count | Tfc::DEPTH_STENCIL_ATTACHMENT;
1068 let filterable = unfilterable | Tfc::SAMPLED_LINEAR;
1069 let renderable =
1070 unfilterable | Tfc::COLOR_ATTACHMENT | sample_count | Tfc::MULTISAMPLE_RESOLVE;
1071 let filterable_renderable = filterable | renderable | Tfc::COLOR_ATTACHMENT_BLEND;
1072 let storage =
1073 base | Tfc::STORAGE_READ_WRITE | Tfc::STORAGE_READ_ONLY | Tfc::STORAGE_WRITE_ONLY;
1074
1075 let feature_fn = |f, caps| {
1076 if self.shared.features.contains(f) {
1077 caps
1078 } else {
1079 empty
1080 }
1081 };
1082
1083 let bcn_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_BC, filterable);
1084 let etc2_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ETC2, filterable);
1085 let astc_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC, filterable);
1086 let astc_hdr_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR, filterable);
1087
1088 let private_caps_fn = |f, caps| {
1089 if self.shared.private_caps.contains(f) {
1090 caps
1091 } else {
1092 empty
1093 }
1094 };
1095
1096 let half_float_renderable = private_caps_fn(
1097 super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT,
1098 Tfc::COLOR_ATTACHMENT
1099 | Tfc::COLOR_ATTACHMENT_BLEND
1100 | sample_count
1101 | Tfc::MULTISAMPLE_RESOLVE,
1102 );
1103
1104 let float_renderable = private_caps_fn(
1105 super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
1106 Tfc::COLOR_ATTACHMENT
1107 | Tfc::COLOR_ATTACHMENT_BLEND
1108 | sample_count
1109 | Tfc::MULTISAMPLE_RESOLVE,
1110 );
1111
1112 let texture_float_linear = feature_fn(wgt::Features::FLOAT32_FILTERABLE, filterable);
1113
1114 let image_atomic = feature_fn(wgt::Features::TEXTURE_ATOMIC, Tfc::STORAGE_ATOMIC);
1115 let image_64_atomic = feature_fn(wgt::Features::TEXTURE_INT64_ATOMIC, Tfc::STORAGE_ATOMIC);
1116
1117 match format {
1118 Tf::R8Unorm => filterable_renderable,
1119 Tf::R8Snorm => filterable,
1120 Tf::R8Uint => renderable,
1121 Tf::R8Sint => renderable,
1122 Tf::R16Uint => renderable,
1123 Tf::R16Sint => renderable,
1124 Tf::R16Unorm => empty,
1125 Tf::R16Snorm => empty,
1126 Tf::R16Float => filterable | half_float_renderable,
1127 Tf::Rg8Unorm => filterable_renderable,
1128 Tf::Rg8Snorm => filterable,
1129 Tf::Rg8Uint => renderable,
1130 Tf::Rg8Sint => renderable,
1131 Tf::R32Uint => renderable | storage | image_atomic,
1132 Tf::R32Sint => renderable | storage | image_atomic,
1133 Tf::R32Float => unfilterable | storage | float_renderable | texture_float_linear,
1134 Tf::Rg16Uint => renderable,
1135 Tf::Rg16Sint => renderable,
1136 Tf::Rg16Unorm => empty,
1137 Tf::Rg16Snorm => empty,
1138 Tf::Rg16Float => filterable | half_float_renderable,
1139 Tf::Rgba8Unorm => filterable_renderable | storage,
1140 Tf::Rgba8UnormSrgb => filterable_renderable,
1141 Tf::Bgra8Unorm | Tf::Bgra8UnormSrgb => filterable_renderable,
1142 Tf::Rgba8Snorm => filterable | storage,
1143 Tf::Rgba8Uint => renderable | storage,
1144 Tf::Rgba8Sint => renderable | storage,
1145 Tf::Rgb10a2Uint => renderable,
1146 Tf::Rgb10a2Unorm => filterable_renderable,
1147 Tf::Rg11b10Ufloat => filterable | float_renderable,
1148 Tf::R64Uint => image_64_atomic,
1149 Tf::Rg32Uint => renderable,
1150 Tf::Rg32Sint => renderable,
1151 Tf::Rg32Float => unfilterable | float_renderable | texture_float_linear,
1152 Tf::Rgba16Uint => renderable | storage,
1153 Tf::Rgba16Sint => renderable | storage,
1154 Tf::Rgba16Unorm => empty,
1155 Tf::Rgba16Snorm => empty,
1156 Tf::Rgba16Float => filterable | storage | half_float_renderable,
1157 Tf::Rgba32Uint => renderable | storage,
1158 Tf::Rgba32Sint => renderable | storage,
1159 Tf::Rgba32Float => unfilterable | storage | float_renderable | texture_float_linear,
1160 Tf::Stencil8
1161 | Tf::Depth16Unorm
1162 | Tf::Depth32Float
1163 | Tf::Depth32FloatStencil8
1164 | Tf::Depth24Plus
1165 | Tf::Depth24PlusStencil8 => depth,
1166 Tf::NV12 => empty,
1167 Tf::P010 => empty,
1168 Tf::Rgb9e5Ufloat => filterable,
1169 Tf::Bc1RgbaUnorm
1170 | Tf::Bc1RgbaUnormSrgb
1171 | Tf::Bc2RgbaUnorm
1172 | Tf::Bc2RgbaUnormSrgb
1173 | Tf::Bc3RgbaUnorm
1174 | Tf::Bc3RgbaUnormSrgb
1175 | Tf::Bc4RUnorm
1176 | Tf::Bc4RSnorm
1177 | Tf::Bc5RgUnorm
1178 | Tf::Bc5RgSnorm
1179 | Tf::Bc6hRgbFloat
1180 | Tf::Bc6hRgbUfloat
1181 | Tf::Bc7RgbaUnorm
1182 | Tf::Bc7RgbaUnormSrgb => bcn_features,
1183 Tf::Etc2Rgb8Unorm
1184 | Tf::Etc2Rgb8UnormSrgb
1185 | Tf::Etc2Rgb8A1Unorm
1186 | Tf::Etc2Rgb8A1UnormSrgb
1187 | Tf::Etc2Rgba8Unorm
1188 | Tf::Etc2Rgba8UnormSrgb
1189 | Tf::EacR11Unorm
1190 | Tf::EacR11Snorm
1191 | Tf::EacRg11Unorm
1192 | Tf::EacRg11Snorm => etc2_features,
1193 Tf::Astc {
1194 block: _,
1195 channel: AstcChannel::Unorm | AstcChannel::UnormSrgb,
1196 } => astc_features,
1197 Tf::Astc {
1198 block: _,
1199 channel: AstcChannel::Hdr,
1200 } => astc_hdr_features,
1201 }
1202 }
1203
1204 unsafe fn surface_capabilities(
1205 &self,
1206 surface: &super::Surface,
1207 ) -> Option<crate::SurfaceCapabilities> {
1208 #[cfg(webgl)]
1209 if self.shared.context.webgl2_context != surface.webgl2_context {
1210 return None;
1211 }
1212
1213 if surface.presentable {
1214 let mut formats = vec![
1215 wgt::TextureFormat::Rgba8Unorm,
1216 #[cfg(native)]
1217 wgt::TextureFormat::Bgra8Unorm,
1218 ];
1219 if surface.supports_srgb() {
1220 formats.extend([
1221 wgt::TextureFormat::Rgba8UnormSrgb,
1222 #[cfg(native)]
1223 wgt::TextureFormat::Bgra8UnormSrgb,
1224 ])
1225 }
1226 if self
1227 .shared
1228 .private_caps
1229 .contains(super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT)
1230 {
1231 formats.push(wgt::TextureFormat::Rgba16Float)
1232 }
1233
1234 Some(crate::SurfaceCapabilities {
1235 formats,
1236 present_modes: if cfg!(windows) {
1237 vec![wgt::PresentMode::Fifo, wgt::PresentMode::Immediate]
1238 } else {
1239 vec![wgt::PresentMode::Fifo] },
1241 composite_alpha_modes: vec![wgt::CompositeAlphaMode::Opaque], maximum_frame_latency: 2..=2, current_extent: None,
1244 usage: wgt::TextureUses::COLOR_TARGET,
1245 })
1246 } else {
1247 None
1248 }
1249 }
1250
1251 unsafe fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
1252 wgt::PresentationTimestamp::INVALID_TIMESTAMP
1253 }
1254}
1255
1256impl super::AdapterShared {
1257 pub(super) unsafe fn get_buffer_sub_data(
1258 &self,
1259 gl: &glow::Context,
1260 target: u32,
1261 offset: i32,
1262 dst_data: &mut [u8],
1263 ) {
1264 if self
1265 .private_caps
1266 .contains(super::PrivateCapabilities::GET_BUFFER_SUB_DATA)
1267 {
1268 unsafe { gl.get_buffer_sub_data(target, offset, dst_data) };
1269 } else {
1270 log::error!("Fake map");
1271 let length = dst_data.len();
1272 let buffer_mapping =
1273 unsafe { gl.map_buffer_range(target, offset, length as _, glow::MAP_READ_BIT) };
1274
1275 unsafe {
1276 core::ptr::copy_nonoverlapping(buffer_mapping, dst_data.as_mut_ptr(), length)
1277 };
1278
1279 unsafe { gl.unmap_buffer(target) };
1280 }
1281 }
1282}
1283
1284#[cfg(send_sync)]
1285unsafe impl Sync for super::Adapter {}
1286#[cfg(send_sync)]
1287unsafe impl Send for super::Adapter {}
1288
1289#[cfg(test)]
1290mod tests {
1291 use super::super::Adapter;
1292
1293 #[test]
1294 fn test_version_parse() {
1295 Adapter::parse_version("1").unwrap_err();
1296 Adapter::parse_version("1.").unwrap_err();
1297 Adapter::parse_version("1 h3l1o. W0rld").unwrap_err();
1298 Adapter::parse_version("1. h3l1o. W0rld").unwrap_err();
1299 Adapter::parse_version("1.2.3").unwrap_err();
1300
1301 assert_eq!(Adapter::parse_version("OpenGL ES 3.1").unwrap(), (3, 1));
1302 assert_eq!(
1303 Adapter::parse_version("OpenGL ES 2.0 Google Nexus").unwrap(),
1304 (2, 0)
1305 );
1306 assert_eq!(Adapter::parse_version("GLSL ES 1.1").unwrap(), (1, 1));
1307 assert_eq!(
1308 Adapter::parse_version("OpenGL ES GLSL ES 3.20").unwrap(),
1309 (3, 2)
1310 );
1311 assert_eq!(
1312 Adapter::parse_version("WebGL 2.0 (OpenGL ES 3.0 Chromium)").unwrap(),
1314 (3, 0)
1315 );
1316 assert_eq!(
1317 Adapter::parse_version("WebGL GLSL ES 3.00 (OpenGL ES GLSL ES 3.0 Chromium)").unwrap(),
1318 (3, 0)
1319 );
1320 }
1321}