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