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