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