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> {
31 let webgl_sig = "WebGL ";
32 let is_webgl = src.starts_with(webgl_sig);
36 if is_webgl {
37 let pos = src.rfind(webgl_sig).unwrap_or(0);
38 src = &src[pos + webgl_sig.len()..];
39 } else {
40 let es_sig = " ES ";
41 match src.rfind(es_sig) {
42 Some(pos) => {
43 src = &src[pos + es_sig.len()..];
44 }
45 None => {
46 log::warn!("ES not found in '{}'", src);
47 return Err(crate::InstanceError);
48 }
49 }
50 };
51
52 let glsl_es_sig = "GLSL ES ";
53 let is_glsl = match src.find(glsl_es_sig) {
54 Some(pos) => {
55 src = &src[pos + glsl_es_sig.len()..];
56 true
57 }
58 None => false,
59 };
60
61 let (version, _vendor_info) = match src.find(' ') {
62 Some(i) => (&src[..i], src[i + 1..].to_string()),
63 None => (src, String::new()),
64 };
65
66 let mut it = version.split('.');
69 let major = it.next().and_then(|s| s.parse().ok());
70 let minor = it.next().and_then(|s| {
71 let trimmed = if s.starts_with('0') {
72 "0"
73 } else {
74 s.trim_end_matches('0')
75 };
76 trimmed.parse().ok()
77 });
78
79 match (major, minor) {
80 (Some(major), Some(minor)) => Ok((
81 if is_webgl && !is_glsl {
83 major + 1
84 } else {
85 major
86 },
87 minor,
88 )),
89 _ => {
90 log::warn!("Unable to extract the version from '{}'", version);
91 Err(crate::InstanceError)
92 }
93 }
94 }
95
96 fn make_info(vendor_orig: String, renderer_orig: String) -> wgt::AdapterInfo {
97 let vendor = vendor_orig.to_lowercase();
98 let renderer = renderer_orig.to_lowercase();
99
100 let strings_that_imply_integrated = [
102 " xpress", "amd renoir",
104 "radeon hd 4200",
105 "radeon hd 4250",
106 "radeon hd 4290",
107 "radeon hd 4270",
108 "radeon hd 4225",
109 "radeon hd 3100",
110 "radeon hd 3200",
111 "radeon hd 3000",
112 "radeon hd 3300",
113 "radeon(tm) r4 graphics",
114 "radeon(tm) r5 graphics",
115 "radeon(tm) r6 graphics",
116 "radeon(tm) r7 graphics",
117 "radeon r7 graphics",
118 "nforce", "tegra", "shield", "igp",
122 "mali",
123 "intel",
124 "v3d",
125 "apple m", ];
127 let strings_that_imply_cpu = ["mesa offscreen", "swiftshader", "llvmpipe"];
128
129 let inferred_device_type = if vendor.contains("qualcomm")
131 || vendor.contains("intel")
132 || strings_that_imply_integrated
133 .iter()
134 .any(|&s| renderer.contains(s))
135 {
136 wgt::DeviceType::IntegratedGpu
137 } else if strings_that_imply_cpu.iter().any(|&s| renderer.contains(s)) {
138 wgt::DeviceType::Cpu
139 } else {
140 wgt::DeviceType::Other
146 };
147
148 let vendor_id = if vendor.contains("amd") {
150 db::amd::VENDOR
151 } else if vendor.contains("imgtec") {
152 db::imgtec::VENDOR
153 } else if vendor.contains("nvidia") {
154 db::nvidia::VENDOR
155 } else if vendor.contains("arm") {
156 db::arm::VENDOR
157 } else if vendor.contains("qualcomm") {
158 db::qualcomm::VENDOR
159 } else if vendor.contains("intel") {
160 db::intel::VENDOR
161 } else if vendor.contains("broadcom") {
162 db::broadcom::VENDOR
163 } else if vendor.contains("mesa") {
164 db::mesa::VENDOR
165 } else if vendor.contains("apple") {
166 db::apple::VENDOR
167 } else {
168 0
169 };
170
171 wgt::AdapterInfo {
172 name: renderer_orig,
173 vendor: vendor_id,
174 device: 0,
175 device_type: inferred_device_type,
176 driver: String::new(),
177 driver_info: String::new(),
178 backend: wgt::Backend::Gl,
179 }
180 }
181
182 pub(super) unsafe fn expose(
183 context: super::AdapterContext,
184 ) -> Option<crate::ExposedAdapter<super::Api>> {
185 let gl = context.lock();
186 let extensions = gl.supported_extensions();
187
188 let (vendor_const, renderer_const) = if extensions.contains("WEBGL_debug_renderer_info") {
189 #[cfg(target_os = "emscripten")]
192 if unsafe { super::emscripten::enable_extension("WEBGL_debug_renderer_info\0") } {
193 (GL_UNMASKED_VENDOR_WEBGL, GL_UNMASKED_RENDERER_WEBGL)
194 } else {
195 (glow::VENDOR, glow::RENDERER)
196 }
197 #[cfg(not(target_os = "emscripten"))]
199 (GL_UNMASKED_VENDOR_WEBGL, GL_UNMASKED_RENDERER_WEBGL)
200 } else {
201 (glow::VENDOR, glow::RENDERER)
202 };
203
204 let (vendor, renderer) = {
205 let vendor = unsafe { gl.get_parameter_string(vendor_const) };
206 let renderer = unsafe { gl.get_parameter_string(renderer_const) };
207
208 (vendor, renderer)
209 };
210 let version = unsafe { gl.get_parameter_string(glow::VERSION) };
211 log::info!("Vendor: {}", vendor);
212 log::info!("Renderer: {}", renderer);
213 log::info!("Version: {}", version);
214
215 log::debug!("Extensions: {:#?}", extensions);
216
217 let ver = Self::parse_version(&version).ok()?;
218 if ver < (3, 0) {
219 log::warn!(
220 "Returned GLES context is {}.{}, when 3.0+ was requested",
221 ver.0,
222 ver.1
223 );
224 return None;
225 }
226
227 let supports_storage = ver >= (3, 1);
228 let supports_work_group_params = ver >= (3, 1);
229
230 let shading_language_version = {
231 let sl_version = unsafe { gl.get_parameter_string(glow::SHADING_LANGUAGE_VERSION) };
232 log::info!("SL version: {}", &sl_version);
233 let (sl_major, sl_minor) = Self::parse_version(&sl_version).ok()?;
234 let value = sl_major as u16 * 100 + sl_minor as u16 * 10;
235 naga::back::glsl::Version::Embedded {
236 version: value,
237 is_webgl: cfg!(target_arch = "wasm32"),
238 }
239 };
240
241 let is_angle = renderer.contains("ANGLE");
243
244 let vertex_shader_storage_blocks = if supports_storage {
245 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_SHADER_STORAGE_BLOCKS) } as u32)
246 } else {
247 0
248 };
249 let fragment_shader_storage_blocks = if supports_storage {
250 (unsafe { gl.get_parameter_i32(glow::MAX_FRAGMENT_SHADER_STORAGE_BLOCKS) } as u32)
251 } else {
252 0
253 };
254 let vertex_shader_storage_textures = if supports_storage {
255 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_IMAGE_UNIFORMS) } as u32)
256 } else {
257 0
258 };
259 let fragment_shader_storage_textures = if supports_storage {
260 (unsafe { gl.get_parameter_i32(glow::MAX_FRAGMENT_IMAGE_UNIFORMS) } as u32)
261 } else {
262 0
263 };
264 let max_storage_block_size = if supports_storage {
265 (unsafe { gl.get_parameter_i32(glow::MAX_SHADER_STORAGE_BLOCK_SIZE) } as u32)
266 } else {
267 0
268 };
269 let max_element_index = unsafe { gl.get_parameter_i32(glow::MAX_ELEMENT_INDEX) } as u32;
270
271 let vertex_ssbo_false_zero =
277 vertex_shader_storage_blocks == 0 && vertex_shader_storage_textures != 0;
278 if vertex_ssbo_false_zero {
279 log::warn!("Max vertex shader SSBO == 0 and SSTO != 0. Interpreting as false zero.");
281 }
282
283 let max_storage_buffers_per_shader_stage = if vertex_shader_storage_blocks == 0 {
284 fragment_shader_storage_blocks
285 } else {
286 vertex_shader_storage_blocks.min(fragment_shader_storage_blocks)
287 };
288 let max_storage_textures_per_shader_stage = if vertex_shader_storage_textures == 0 {
289 fragment_shader_storage_textures
290 } else {
291 vertex_shader_storage_textures.min(fragment_shader_storage_textures)
292 };
293
294 let mut downlevel_flags = wgt::DownlevelFlags::empty()
295 | wgt::DownlevelFlags::NON_POWER_OF_TWO_MIPMAPPED_TEXTURES
296 | wgt::DownlevelFlags::CUBE_ARRAY_TEXTURES
297 | wgt::DownlevelFlags::COMPARISON_SAMPLERS;
298 downlevel_flags.set(wgt::DownlevelFlags::COMPUTE_SHADERS, ver >= (3, 1));
299 downlevel_flags.set(
300 wgt::DownlevelFlags::FRAGMENT_WRITABLE_STORAGE,
301 max_storage_block_size != 0,
302 );
303 downlevel_flags.set(wgt::DownlevelFlags::INDIRECT_EXECUTION, ver >= (3, 1));
304 downlevel_flags.set(wgt::DownlevelFlags::BASE_VERTEX, ver >= (3, 2));
307 downlevel_flags.set(
308 wgt::DownlevelFlags::INDEPENDENT_BLEND,
309 ver >= (3, 2) || extensions.contains("GL_EXT_draw_buffers_indexed"),
310 );
311 downlevel_flags.set(
312 wgt::DownlevelFlags::VERTEX_STORAGE,
313 max_storage_block_size != 0
314 && max_storage_buffers_per_shader_stage != 0
315 && (vertex_shader_storage_blocks != 0 || vertex_ssbo_false_zero),
316 );
317 downlevel_flags.set(wgt::DownlevelFlags::FRAGMENT_STORAGE, supports_storage);
318 if extensions.contains("EXT_texture_filter_anisotropic") {
319 let max_aniso =
320 unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_MAX_ANISOTROPY_EXT) } as u32;
321 downlevel_flags.set(wgt::DownlevelFlags::ANISOTROPIC_FILTERING, max_aniso >= 16);
322 }
323 downlevel_flags.set(
324 wgt::DownlevelFlags::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED,
325 !(cfg!(target_arch = "wasm32") || is_angle),
326 );
327 downlevel_flags.set(
329 wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER,
330 !cfg!(target_arch = "wasm32"),
331 );
332 downlevel_flags.set(
333 wgt::DownlevelFlags::UNRESTRICTED_EXTERNAL_TEXTURE_COPIES,
334 !cfg!(target_arch = "wasm32"),
335 );
336 downlevel_flags.set(
337 wgt::DownlevelFlags::FULL_DRAW_INDEX_UINT32,
338 max_element_index == u32::MAX,
339 );
340 downlevel_flags.set(
341 wgt::DownlevelFlags::MULTISAMPLED_SHADING,
342 ver >= (3, 2) || extensions.contains("OES_sample_variables"),
343 );
344
345 let mut features = wgt::Features::empty()
346 | wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES
347 | wgt::Features::CLEAR_TEXTURE
348 | wgt::Features::PUSH_CONSTANTS;
349 features.set(
350 wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER | wgt::Features::ADDRESS_MODE_CLAMP_TO_ZERO,
351 extensions.contains("GL_EXT_texture_border_clamp"),
352 );
353 features.set(
354 wgt::Features::DEPTH_CLIP_CONTROL,
355 extensions.contains("GL_EXT_depth_clamp"),
356 );
357 features.set(
358 wgt::Features::VERTEX_WRITABLE_STORAGE,
359 downlevel_flags.contains(wgt::DownlevelFlags::VERTEX_STORAGE)
360 && vertex_shader_storage_textures != 0,
361 );
362 features.set(
363 wgt::Features::MULTIVIEW,
364 extensions.contains("OVR_multiview2"),
365 );
366 features.set(
367 wgt::Features::SHADER_PRIMITIVE_INDEX,
368 ver >= (3, 2) || extensions.contains("OES_geometry_shader"),
369 );
370 features.set(wgt::Features::SHADER_EARLY_DEPTH_TEST, ver >= (3, 1));
371 let gles_bcn_exts = [
372 "GL_EXT_texture_compression_s3tc_srgb",
373 "GL_EXT_texture_compression_rgtc",
374 "GL_EXT_texture_compression_bptc",
375 ];
376 let webgl_bcn_exts = [
377 "WEBGL_compressed_texture_s3tc",
378 "WEBGL_compressed_texture_s3tc_srgb",
379 "EXT_texture_compression_rgtc",
380 "EXT_texture_compression_bptc",
381 ];
382 let bcn_exts = if cfg!(target_arch = "wasm32") {
383 &webgl_bcn_exts[..]
384 } else {
385 &gles_bcn_exts[..]
386 };
387 features.set(
388 wgt::Features::TEXTURE_COMPRESSION_BC,
389 bcn_exts.iter().all(|&ext| extensions.contains(ext)),
390 );
391 features.set(
392 wgt::Features::TEXTURE_COMPRESSION_ETC2,
393 !cfg!(target_arch = "wasm32") || extensions.contains("WEBGL_compressed_texture_etc"),
395 );
396 if extensions.contains("WEBGL_compressed_texture_astc")
398 || extensions.contains("GL_OES_texture_compression_astc")
399 {
400 #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
401 {
402 if context
403 .glow_context
404 .compressed_texture_astc_supports_ldr_profile()
405 {
406 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC);
407 }
408 if context
409 .glow_context
410 .compressed_texture_astc_supports_hdr_profile()
411 {
412 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR);
413 }
414 }
415
416 #[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))]
417 {
418 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC);
419 features.insert(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR);
420 }
421 } else {
422 features.set(
423 wgt::Features::TEXTURE_COMPRESSION_ASTC,
424 extensions.contains("GL_KHR_texture_compression_astc_ldr"),
425 );
426 features.set(
427 wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR,
428 extensions.contains("GL_KHR_texture_compression_astc_hdr"),
429 );
430 }
431
432 let mut private_caps = super::PrivateCapabilities::empty();
433 private_caps.set(
434 super::PrivateCapabilities::BUFFER_ALLOCATION,
435 extensions.contains("GL_EXT_buffer_storage"),
436 );
437 private_caps.set(
438 super::PrivateCapabilities::SHADER_BINDING_LAYOUT,
439 ver >= (3, 1),
440 );
441 private_caps.set(
442 super::PrivateCapabilities::SHADER_TEXTURE_SHADOW_LOD,
443 extensions.contains("GL_EXT_texture_shadow_lod"),
444 );
445 private_caps.set(super::PrivateCapabilities::MEMORY_BARRIERS, ver >= (3, 1));
446 private_caps.set(
447 super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT,
448 ver >= (3, 1),
449 );
450 private_caps.set(
451 super::PrivateCapabilities::INDEX_BUFFER_ROLE_CHANGE,
452 !cfg!(target_arch = "wasm32"),
453 );
454 private_caps.set(
455 super::PrivateCapabilities::CAN_DISABLE_DRAW_BUFFER,
456 !cfg!(target_arch = "wasm32"),
457 );
458 private_caps.set(
459 super::PrivateCapabilities::GET_BUFFER_SUB_DATA,
460 cfg!(target_arch = "wasm32"),
461 );
462 let color_buffer_float = extensions.contains("GL_EXT_color_buffer_float")
463 || extensions.contains("EXT_color_buffer_float");
464 let color_buffer_half_float = extensions.contains("GL_EXT_color_buffer_half_float");
465 private_caps.set(
466 super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT,
467 color_buffer_half_float || color_buffer_float,
468 );
469 private_caps.set(
470 super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
471 color_buffer_float,
472 );
473 private_caps.set(
474 super::PrivateCapabilities::TEXTURE_FLOAT_LINEAR,
475 extensions.contains("OES_texture_float_linear"),
476 );
477
478 let max_texture_size = unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_SIZE) } as u32;
479 let max_texture_3d_size = unsafe { gl.get_parameter_i32(glow::MAX_3D_TEXTURE_SIZE) } as u32;
480
481 let min_uniform_buffer_offset_alignment =
482 (unsafe { gl.get_parameter_i32(glow::UNIFORM_BUFFER_OFFSET_ALIGNMENT) } as u32);
483 let min_storage_buffer_offset_alignment = if ver >= (3, 1) {
484 (unsafe { gl.get_parameter_i32(glow::SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT) } as u32)
485 } else {
486 256
487 };
488 let max_uniform_buffers_per_shader_stage =
489 unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_UNIFORM_BLOCKS) }
490 .min(unsafe { gl.get_parameter_i32(glow::MAX_FRAGMENT_UNIFORM_BLOCKS) })
491 as u32;
492
493 let max_compute_workgroups_per_dimension = if supports_work_group_params {
494 unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 0) }
495 .min(unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 1) })
496 .min(unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, 2) })
497 as u32
498 } else {
499 0
500 };
501
502 let limits = wgt::Limits {
503 max_texture_dimension_1d: max_texture_size,
504 max_texture_dimension_2d: max_texture_size,
505 max_texture_dimension_3d: max_texture_3d_size,
506 max_texture_array_layers: unsafe {
507 gl.get_parameter_i32(glow::MAX_ARRAY_TEXTURE_LAYERS)
508 } as u32,
509 max_bind_groups: crate::MAX_BIND_GROUPS as u32,
510 max_bindings_per_bind_group: 65535,
511 max_dynamic_uniform_buffers_per_pipeline_layout: max_uniform_buffers_per_shader_stage,
512 max_dynamic_storage_buffers_per_pipeline_layout: max_storage_buffers_per_shader_stage,
513 max_sampled_textures_per_shader_stage: super::MAX_TEXTURE_SLOTS as u32,
514 max_samplers_per_shader_stage: super::MAX_SAMPLERS as u32,
515 max_storage_buffers_per_shader_stage,
516 max_storage_textures_per_shader_stage,
517 max_uniform_buffers_per_shader_stage,
518 max_uniform_buffer_binding_size: unsafe {
519 gl.get_parameter_i32(glow::MAX_UNIFORM_BLOCK_SIZE)
520 } as u32,
521 max_storage_buffer_binding_size: if ver >= (3, 1) {
522 unsafe { gl.get_parameter_i32(glow::MAX_SHADER_STORAGE_BLOCK_SIZE) }
523 } else {
524 0
525 } as u32,
526 max_vertex_buffers: if private_caps
527 .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
528 {
529 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_BINDINGS) } as u32)
530 } else {
531 16 },
533 max_vertex_attributes: (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIBS) }
534 as u32)
535 .min(super::MAX_VERTEX_ATTRIBUTES as u32),
536 max_vertex_buffer_array_stride: if private_caps
537 .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
538 {
539 (unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) } as u32)
540 } else {
541 !0
542 },
543 max_push_constant_size: super::MAX_PUSH_CONSTANTS as u32 * 4,
544 min_uniform_buffer_offset_alignment,
545 min_storage_buffer_offset_alignment,
546 max_inter_stage_shader_components: unsafe {
547 gl.get_parameter_i32(glow::MAX_VARYING_COMPONENTS)
548 } as u32,
549 max_compute_workgroup_storage_size: if supports_work_group_params {
550 (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_SHARED_MEMORY_SIZE) } as u32)
551 } else {
552 0
553 },
554 max_compute_invocations_per_workgroup: if supports_work_group_params {
555 (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_WORK_GROUP_INVOCATIONS) } as u32)
556 } else {
557 0
558 },
559 max_compute_workgroup_size_x: if supports_work_group_params {
560 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 0) }
561 as u32)
562 } else {
563 0
564 },
565 max_compute_workgroup_size_y: if supports_work_group_params {
566 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 1) }
567 as u32)
568 } else {
569 0
570 },
571 max_compute_workgroup_size_z: if supports_work_group_params {
572 (unsafe { gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, 2) }
573 as u32)
574 } else {
575 0
576 },
577 max_compute_workgroups_per_dimension,
578 max_buffer_size: i32::MAX as u64,
579 };
580
581 let mut workarounds = super::Workarounds::empty();
582
583 workarounds.set(
584 super::Workarounds::EMULATE_BUFFER_MAP,
585 cfg!(target_arch = "wasm32"),
586 );
587
588 let r = renderer.to_lowercase();
589 if context.is_owned()
592 && r.contains("mesa")
593 && r.contains("intel")
594 && r.split(&[' ', '(', ')'][..])
595 .any(|substr| substr.len() == 3 && substr.chars().nth(2) == Some('l'))
596 {
597 log::warn!(
598 "Detected skylake derivative running on mesa i915. Clears to srgb textures will \
599 use manual shader clears."
600 );
601 workarounds.set(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR, true);
602 }
603
604 let downlevel_defaults = wgt::DownlevelLimits {};
605
606 #[allow(clippy::drop_ref)]
610 drop(gl);
611
612 Some(crate::ExposedAdapter {
613 adapter: super::Adapter {
614 shared: Arc::new(super::AdapterShared {
615 context,
616 private_caps,
617 workarounds,
618 features,
619 shading_language_version,
620 max_texture_size,
621 next_shader_id: Default::default(),
622 program_cache: Default::default(),
623 }),
624 },
625 info: Self::make_info(vendor, renderer),
626 features,
627 capabilities: crate::Capabilities {
628 limits,
629 downlevel: wgt::DownlevelCapabilities {
630 flags: downlevel_flags,
631 limits: downlevel_defaults,
632 shader_model: wgt::ShaderModel::Sm5,
633 },
634 alignments: crate::Alignments {
635 buffer_copy_offset: wgt::BufferSize::new(4).unwrap(),
636 buffer_copy_pitch: wgt::BufferSize::new(4).unwrap(),
637 },
638 },
639 })
640 }
641
642 unsafe fn create_shader_clear_program(
643 gl: &glow::Context,
644 ) -> (glow::Program, glow::UniformLocation) {
645 let program = unsafe { gl.create_program() }.expect("Could not create shader program");
646 let vertex =
647 unsafe { gl.create_shader(glow::VERTEX_SHADER) }.expect("Could not create shader");
648 unsafe { gl.shader_source(vertex, include_str!("./shaders/clear.vert")) };
649 unsafe { gl.compile_shader(vertex) };
650 let fragment =
651 unsafe { gl.create_shader(glow::FRAGMENT_SHADER) }.expect("Could not create shader");
652 unsafe { gl.shader_source(fragment, include_str!("./shaders/clear.frag")) };
653 unsafe { gl.compile_shader(fragment) };
654 unsafe { gl.attach_shader(program, vertex) };
655 unsafe { gl.attach_shader(program, fragment) };
656 unsafe { gl.link_program(program) };
657 let color_uniform_location = unsafe { gl.get_uniform_location(program, "color") }
658 .expect("Could not find color uniform in shader clear shader");
659 unsafe { gl.delete_shader(vertex) };
660 unsafe { gl.delete_shader(fragment) };
661
662 (program, color_uniform_location)
663 }
664}
665
666impl crate::Adapter<super::Api> for super::Adapter {
667 unsafe fn open(
668 &self,
669 features: wgt::Features,
670 _limits: &wgt::Limits,
671 ) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
672 let gl = &self.shared.context.lock();
673 unsafe { gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1) };
674 unsafe { gl.pixel_store_i32(glow::PACK_ALIGNMENT, 1) };
675 let main_vao =
676 unsafe { gl.create_vertex_array() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
677 unsafe { gl.bind_vertex_array(Some(main_vao)) };
678
679 let zero_buffer =
680 unsafe { gl.create_buffer() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
681 unsafe { gl.bind_buffer(glow::COPY_READ_BUFFER, Some(zero_buffer)) };
682 let zeroes = vec![0u8; super::ZERO_BUFFER_SIZE];
683 unsafe { gl.buffer_data_u8_slice(glow::COPY_READ_BUFFER, &zeroes, glow::STATIC_DRAW) };
684
685 let (shader_clear_program, shader_clear_program_color_uniform_location) =
688 unsafe { Self::create_shader_clear_program(gl) };
689
690 Ok(crate::OpenDevice {
691 device: super::Device {
692 shared: Arc::clone(&self.shared),
693 main_vao,
694 #[cfg(all(not(target_arch = "wasm32"), feature = "renderdoc"))]
695 render_doc: Default::default(),
696 },
697 queue: super::Queue {
698 shared: Arc::clone(&self.shared),
699 features,
700 draw_fbo: unsafe { gl.create_framebuffer() }
701 .map_err(|_| crate::DeviceError::OutOfMemory)?,
702 copy_fbo: unsafe { gl.create_framebuffer() }
703 .map_err(|_| crate::DeviceError::OutOfMemory)?,
704 shader_clear_program,
705 shader_clear_program_color_uniform_location,
706 zero_buffer,
707 temp_query_results: Vec::new(),
708 draw_buffer_count: 1,
709 current_index_buffer: None,
710 },
711 })
712 }
713
714 unsafe fn texture_format_capabilities(
715 &self,
716 format: wgt::TextureFormat,
717 ) -> crate::TextureFormatCapabilities {
718 use crate::TextureFormatCapabilities as Tfc;
719 use wgt::TextureFormat as Tf;
720
721 let sample_count = {
722 let max_samples = unsafe {
723 self.shared
724 .context
725 .lock()
726 .get_parameter_i32(glow::MAX_SAMPLES)
727 };
728 if max_samples >= 16 {
729 Tfc::MULTISAMPLE_X2
730 | Tfc::MULTISAMPLE_X4
731 | Tfc::MULTISAMPLE_X8
732 | Tfc::MULTISAMPLE_X16
733 } else if max_samples >= 8 {
734 Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4 | Tfc::MULTISAMPLE_X8
735 } else {
736 Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4
741 }
742 };
743
744 let empty = Tfc::empty();
749 let base = Tfc::COPY_SRC | Tfc::COPY_DST;
750 let unfilterable = base | Tfc::SAMPLED;
751 let depth = base | Tfc::SAMPLED | sample_count | Tfc::DEPTH_STENCIL_ATTACHMENT;
752 let filterable = unfilterable | Tfc::SAMPLED_LINEAR;
753 let renderable =
754 unfilterable | Tfc::COLOR_ATTACHMENT | sample_count | Tfc::MULTISAMPLE_RESOLVE;
755 let filterable_renderable = filterable | renderable | Tfc::COLOR_ATTACHMENT_BLEND;
756 let storage = base | Tfc::STORAGE | Tfc::STORAGE_READ_WRITE;
757
758 let feature_fn = |f, caps| {
759 if self.shared.features.contains(f) {
760 caps
761 } else {
762 empty
763 }
764 };
765
766 let bcn_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_BC, filterable);
767 let etc2_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ETC2, filterable);
768 let astc_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC, filterable);
769 let astc_hdr_features = feature_fn(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR, filterable);
770
771 let private_caps_fn = |f, caps| {
772 if self.shared.private_caps.contains(f) {
773 caps
774 } else {
775 empty
776 }
777 };
778
779 let half_float_renderable = private_caps_fn(
780 super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT,
781 Tfc::COLOR_ATTACHMENT
782 | Tfc::COLOR_ATTACHMENT_BLEND
783 | sample_count
784 | Tfc::MULTISAMPLE_RESOLVE,
785 );
786
787 let float_renderable = private_caps_fn(
788 super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
789 Tfc::COLOR_ATTACHMENT
790 | Tfc::COLOR_ATTACHMENT_BLEND
791 | sample_count
792 | Tfc::MULTISAMPLE_RESOLVE,
793 );
794
795 let texture_float_linear =
796 private_caps_fn(super::PrivateCapabilities::TEXTURE_FLOAT_LINEAR, filterable);
797
798 match format {
799 Tf::R8Unorm => filterable_renderable,
800 Tf::R8Snorm => filterable,
801 Tf::R8Uint => renderable,
802 Tf::R8Sint => renderable,
803 Tf::R16Uint => renderable,
804 Tf::R16Sint => renderable,
805 Tf::R16Unorm => empty,
806 Tf::R16Snorm => empty,
807 Tf::R16Float => filterable | half_float_renderable,
808 Tf::Rg8Unorm => filterable_renderable,
809 Tf::Rg8Snorm => filterable,
810 Tf::Rg8Uint => renderable,
811 Tf::Rg8Sint => renderable,
812 Tf::R32Uint => renderable | storage,
813 Tf::R32Sint => renderable | storage,
814 Tf::R32Float => unfilterable | storage | float_renderable | texture_float_linear,
815 Tf::Rg16Uint => renderable,
816 Tf::Rg16Sint => renderable,
817 Tf::Rg16Unorm => empty,
818 Tf::Rg16Snorm => empty,
819 Tf::Rg16Float => filterable | half_float_renderable,
820 Tf::Rgba8Unorm | Tf::Rgba8UnormSrgb => filterable_renderable | storage,
821 Tf::Bgra8Unorm | Tf::Bgra8UnormSrgb => filterable_renderable,
822 Tf::Rgba8Snorm => filterable,
823 Tf::Rgba8Uint => renderable | storage,
824 Tf::Rgba8Sint => renderable | storage,
825 Tf::Rgb10a2Unorm => filterable_renderable,
826 Tf::Rg11b10Float => filterable | float_renderable,
827 Tf::Rg32Uint => renderable,
828 Tf::Rg32Sint => renderable,
829 Tf::Rg32Float => unfilterable | float_renderable | texture_float_linear,
830 Tf::Rgba16Uint => renderable | storage,
831 Tf::Rgba16Sint => renderable | storage,
832 Tf::Rgba16Unorm => empty,
833 Tf::Rgba16Snorm => empty,
834 Tf::Rgba16Float => filterable | storage | half_float_renderable,
835 Tf::Rgba32Uint => renderable | storage,
836 Tf::Rgba32Sint => renderable | storage,
837 Tf::Rgba32Float => unfilterable | storage | float_renderable | texture_float_linear,
838 Tf::Stencil8
839 | Tf::Depth16Unorm
840 | Tf::Depth32Float
841 | Tf::Depth32FloatStencil8
842 | Tf::Depth24Plus
843 | Tf::Depth24PlusStencil8 => depth,
844 Tf::Rgb9e5Ufloat => filterable,
845 Tf::Bc1RgbaUnorm
846 | Tf::Bc1RgbaUnormSrgb
847 | Tf::Bc2RgbaUnorm
848 | Tf::Bc2RgbaUnormSrgb
849 | Tf::Bc3RgbaUnorm
850 | Tf::Bc3RgbaUnormSrgb
851 | Tf::Bc4RUnorm
852 | Tf::Bc4RSnorm
853 | Tf::Bc5RgUnorm
854 | Tf::Bc5RgSnorm
855 | Tf::Bc6hRgbFloat
856 | Tf::Bc6hRgbUfloat
857 | Tf::Bc7RgbaUnorm
858 | Tf::Bc7RgbaUnormSrgb => bcn_features,
859 Tf::Etc2Rgb8Unorm
860 | Tf::Etc2Rgb8UnormSrgb
861 | Tf::Etc2Rgb8A1Unorm
862 | Tf::Etc2Rgb8A1UnormSrgb
863 | Tf::Etc2Rgba8Unorm
864 | Tf::Etc2Rgba8UnormSrgb
865 | Tf::EacR11Unorm
866 | Tf::EacR11Snorm
867 | Tf::EacRg11Unorm
868 | Tf::EacRg11Snorm => etc2_features,
869 Tf::Astc {
870 block: _,
871 channel: AstcChannel::Unorm | AstcChannel::UnormSrgb,
872 } => astc_features,
873 Tf::Astc {
874 block: _,
875 channel: AstcChannel::Hdr,
876 } => astc_hdr_features,
877 }
878 }
879
880 unsafe fn surface_capabilities(
881 &self,
882 surface: &super::Surface,
883 ) -> Option<crate::SurfaceCapabilities> {
884 if surface.presentable {
885 let mut formats = vec![
886 wgt::TextureFormat::Rgba8Unorm,
887 #[cfg(not(target_arch = "wasm32"))]
888 wgt::TextureFormat::Bgra8Unorm,
889 ];
890 if surface.supports_srgb() {
891 formats.extend([
892 wgt::TextureFormat::Rgba8UnormSrgb,
893 #[cfg(not(target_arch = "wasm32"))]
894 wgt::TextureFormat::Bgra8UnormSrgb,
895 ])
896 }
897 if self
898 .shared
899 .private_caps
900 .contains(super::PrivateCapabilities::COLOR_BUFFER_HALF_FLOAT)
901 {
902 formats.push(wgt::TextureFormat::Rgba16Float)
903 }
904
905 Some(crate::SurfaceCapabilities {
906 formats,
907 present_modes: vec![wgt::PresentMode::Fifo], composite_alpha_modes: vec![wgt::CompositeAlphaMode::Opaque], swap_chain_sizes: 2..=2,
910 current_extent: None,
911 extents: wgt::Extent3d {
912 width: 4,
913 height: 4,
914 depth_or_array_layers: 1,
915 }..=wgt::Extent3d {
916 width: self.shared.max_texture_size,
917 height: self.shared.max_texture_size,
918 depth_or_array_layers: 1,
919 },
920 usage: crate::TextureUses::COLOR_TARGET,
921 })
922 } else {
923 None
924 }
925 }
926
927 unsafe fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
928 wgt::PresentationTimestamp::INVALID_TIMESTAMP
929 }
930}
931
932impl super::AdapterShared {
933 pub(super) unsafe fn get_buffer_sub_data(
934 &self,
935 gl: &glow::Context,
936 target: u32,
937 offset: i32,
938 dst_data: &mut [u8],
939 ) {
940 if self
941 .private_caps
942 .contains(super::PrivateCapabilities::GET_BUFFER_SUB_DATA)
943 {
944 unsafe { gl.get_buffer_sub_data(target, offset, dst_data) };
945 } else {
946 log::error!("Fake map");
947 let length = dst_data.len();
948 let buffer_mapping =
949 unsafe { gl.map_buffer_range(target, offset, length as _, glow::MAP_READ_BIT) };
950
951 unsafe { std::ptr::copy_nonoverlapping(buffer_mapping, dst_data.as_mut_ptr(), length) };
952
953 unsafe { gl.unmap_buffer(target) };
954 }
955 }
956}
957
958#[cfg(all(
959 target_arch = "wasm32",
960 feature = "fragile-send-sync-non-atomic-wasm",
961 not(target_feature = "atomics")
962))]
963unsafe impl Sync for super::Adapter {}
964#[cfg(all(
965 target_arch = "wasm32",
966 feature = "fragile-send-sync-non-atomic-wasm",
967 not(target_feature = "atomics")
968))]
969unsafe impl Send for super::Adapter {}
970
971#[cfg(test)]
972mod tests {
973 use super::super::Adapter;
974
975 #[test]
976 fn test_version_parse() {
977 let error = Err(crate::InstanceError);
978 assert_eq!(Adapter::parse_version("1"), error);
979 assert_eq!(Adapter::parse_version("1."), error);
980 assert_eq!(Adapter::parse_version("1 h3l1o. W0rld"), error);
981 assert_eq!(Adapter::parse_version("1. h3l1o. W0rld"), error);
982 assert_eq!(Adapter::parse_version("1.2.3"), error);
983 assert_eq!(Adapter::parse_version("OpenGL ES 3.1"), Ok((3, 1)));
984 assert_eq!(
985 Adapter::parse_version("OpenGL ES 2.0 Google Nexus"),
986 Ok((2, 0))
987 );
988 assert_eq!(Adapter::parse_version("GLSL ES 1.1"), Ok((1, 1)));
989 assert_eq!(Adapter::parse_version("OpenGL ES GLSL ES 3.20"), Ok((3, 2)));
990 assert_eq!(
991 Adapter::parse_version("WebGL 2.0 (OpenGL ES 3.0 Chromium)"),
993 Ok((3, 0))
994 );
995 assert_eq!(
996 Adapter::parse_version("WebGL GLSL ES 3.00 (OpenGL ES GLSL ES 3.0 Chromium)"),
997 Ok((3, 0))
998 );
999 }
1000}