diff --git a/Cargo.lock b/Cargo.lock index 5fd73b0f806..36143d0b54e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -147,9 +147,9 @@ checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" [[package]] name = "bytemuck" -version = "1.7.0" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9966d2ab714d0f785dbac0a0396251a35280aeb42413281617d0209ab4898435" +checksum = "72957246c41db82b8ef88a5486143830adeb8227ef9837740bdec67724cf2c5b" dependencies = [ "bytemuck_derive", ] @@ -189,9 +189,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.68" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787" +checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2" [[package]] name = "cfg-if" @@ -527,9 +527,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77b705829d1e87f762c2df6da140b26af5839e1033aa84aa5f56bb688e4e1bdb" +checksum = "b394ed3d285a429378d3b384b9eb1285267e7df4b166df24b7a6939a04dc392e" dependencies = [ "instant", ] @@ -664,7 +664,7 @@ checksum = "e8a70f1e87a3840ed6a3e99e02c2b861e4dbdf26f0d07e38f42ea5aff46cfce2" dependencies = [ "bitflags", "gpu-descriptor-types", - "hashbrown", + "hashbrown 0.9.1", ] [[package]] @@ -685,11 +685,17 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + [[package]] name = "hermit-abi" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] @@ -708,12 +714,12 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "indexmap" -version = "1.6.2" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" +checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.11.2", ] [[package]] @@ -724,9 +730,9 @@ checksum = "90953f308a79fe6d62a4643e51f848fbfddcd05975a38e69fdf4ab86a7baf7ca" [[package]] name = "instant" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -792,9 +798,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.97" +version = "0.2.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6" +checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" [[package]] name = "libloading" @@ -1176,9 +1182,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" +checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" [[package]] name = "pkg-config" @@ -1235,18 +1241,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" +checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" dependencies = [ "unicode-xid", ] [[package]] name = "profiling" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a7c000c0ce9d9bb94c0fbacdf20e5087fbe652c556ffb2c9387d980e17d51fb" +checksum = "87dfd5592a8eed7e74f56ad7b125f8234763b805c30f0c7c95c486920026a6ec" [[package]] name = "quote" @@ -1433,9 +1439,9 @@ checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" [[package]] name = "slotmap" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585cd5dffe4e9e06f6dfdf66708b70aca3f781bed561f4f667b2d9c0d4559e36" +checksum = "a952280edbecfb1d4bd3cf2dbc309dc6ab523e53487c438ae21a6df09fe84bc4" dependencies = [ "version_check", ] @@ -1483,9 +1489,9 @@ checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" [[package]] name = "syn" -version = "1.0.73" +version = "1.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" +checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c" dependencies = [ "proc-macro2", "quote", @@ -1503,18 +1509,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa6f76457f59514c7eeb4e59d891395fab0b2fd1d40723ae737d64153392e9c6" +checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d" +checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745" dependencies = [ "proc-macro2", "quote", @@ -1645,9 +1651,9 @@ checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f" [[package]] name = "wayland-client" -version = "0.28.5" +version = "0.28.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ca44d86554b85cf449f1557edc6cc7da935cc748c8e4bf1c507cbd43bae02c" +checksum = "e3ab332350e502f159382201394a78e3cc12d0f04db863429260164ea40e0355" dependencies = [ "bitflags", "downcast-rs", @@ -1661,9 +1667,9 @@ dependencies = [ [[package]] name = "wayland-commons" -version = "0.28.5" +version = "0.28.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bd75ae380325dbcff2707f0cd9869827ea1d2d6d534cff076858d3f0460fd5a" +checksum = "a21817947c7011bbd0a27e11b17b337bfd022e8544b071a2641232047966fbda" dependencies = [ "nix 0.20.0", "once_cell", @@ -1673,9 +1679,9 @@ dependencies = [ [[package]] name = "wayland-cursor" -version = "0.28.5" +version = "0.28.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b37e5455ec72f5de555ec39b5c3704036ac07c2ecd50d0bffe02d5fe2d4e65ab" +checksum = "be610084edd1586d45e7bdd275fe345c7c1873598caa464c4fb835dee70fa65a" dependencies = [ "nix 0.20.0", "wayland-client", @@ -1684,9 +1690,9 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.28.5" +version = "0.28.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95df3317872bcf9eec096c864b69aa4769a1d5d6291a5b513f8ba0af0efbd52c" +checksum = "286620ea4d803bacf61fa087a4242ee316693099ee5a140796aaba02b29f861f" dependencies = [ "bitflags", "wayland-client", @@ -1696,9 +1702,9 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.28.5" +version = "0.28.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389d680d7bd67512dc9c37f39560224327038deb0f0e8d33f870900441b68720" +checksum = "ce923eb2deb61de332d1f356ec7b6bf37094dc5573952e1c8936db03b54c03f1" dependencies = [ "proc-macro2", "quote", @@ -1707,9 +1713,9 @@ dependencies = [ [[package]] name = "wayland-sys" -version = "0.28.5" +version = "0.28.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2907bd297eef464a95ba9349ea771611771aa285b932526c633dc94d5400a8e2" +checksum = "d841fca9aed7febf9bed2e9796c49bf58d4152ceda8ac949ebe00868d8f0feb8" dependencies = [ "dlib 0.5.0", "lazy_static", diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index c0d810f65bc..02ba1809974 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -507,28 +507,31 @@ impl Features { /// Represents the sets of limits an adapter/device supports. /// -/// We provide two different defaults. -/// - [`Limits::downlevel_defaults()]. This is a set of limits that is guaranteed to -/// work on all backends, including "downlevel" backends such -/// as OpenGL and D3D11. For most applications we recommend using these -/// limits, assuming they are high enough for your application. -/// - [`Limits::default()`]. This is the set of limits that is guaranteed to -/// work on all modern backends and is guaranteed to be supported by WebGPU. -/// Applications needing more modern features can use this as a reasonable set of -/// limits if they are targeting only desktop and modern mobile devices. +/// We provide three different defaults. +/// - [`Limits::downlevel_defaults()`]. This is a set of limits that is guarenteed to work on almost +/// all backends, including "downlevel" backends such as OpenGL and D3D11, other than WebGL. For +/// most applications we recommend using these limits, assuming they are high enough for your +/// application, and you do not intent to support WebGL. +/// - [`Limits::downlevel_webgl2_defaults()`] This is a set of limits that is lower even than the +/// [`downlevel_defaults()`], configured to be low enough to support running in the browser using +/// WebGL2. +/// - [`Limits::default()`]. This is the set of limits that is guarenteed to work on all modern +/// backends and is guarenteed to be supported by WebGPU. Applications needing more modern +/// features can use this as a reasonable set of limits if they are targetting only desktop and +/// modern mobile devices. /// -/// We recommend starting with the most restrictive limits you can and manually -/// increasing the limits you need boosted. This will let you stay running on -/// all hardware that supports the limits you need. +/// We recommend starting with the most restrictive limits you can and manually increasing the +/// limits you need boosted. This will let you stay running on all hardware that supports the limits +/// you need. /// /// Limits "better" than the default must be supported by the adapter and requested when requesting -/// a device. If limits "better" than the adapter supports are requested, requesting a device will panic. -/// Once a device is requested, you may only use resources up to the limits requested _even_ if the -/// adapter supports "better" limits. +/// a device. If limits "better" than the adapter supports are requested, requesting a device will +/// panic. Once a device is requested, you may only use resources up to the limits requested _even_ +/// if the adapter supports "better" limits. /// /// Requesting limits that are "better" than you need may cause performance to decrease because the -/// implementation needs to support more than is needed. You should ideally only request exactly what -/// you need. +/// implementation needs to support more than is needed. You should ideally only request exactly +/// what you need. /// /// See also: #[repr(C)] @@ -618,7 +621,7 @@ impl Default for Limits { } impl Limits { - /// These default limits are guaranteed to be compatible with GLES3, WebGL, and D3D11 + /// These default limits are guarenteed to be compatible with GLES3, and D3D11 pub fn downlevel_defaults() -> Self { Self { max_texture_dimension_1d: 2096, @@ -630,18 +633,31 @@ impl Limits { max_dynamic_storage_buffers_per_pipeline_layout: 4, max_sampled_textures_per_shader_stage: 16, max_samplers_per_shader_stage: 16, - max_storage_buffers_per_shader_stage: 0, - max_storage_textures_per_shader_stage: 0, + max_storage_buffers_per_shader_stage: 4, + max_storage_textures_per_shader_stage: 4, max_uniform_buffers_per_shader_stage: 12, max_uniform_buffer_binding_size: 16384, - max_storage_buffer_binding_size: 0, + max_storage_buffer_binding_size: 128 << 20, max_vertex_buffers: 8, max_vertex_attributes: 16, - max_vertex_buffer_array_stride: 255, + max_vertex_buffer_array_stride: 2048, max_push_constant_size: 0, } } + /// These default limits are guarenteed to be compatible with GLES3, and D3D11, and WebGL2 + pub fn downlevel_webgl2_defaults() -> Self { + Self { + max_storage_buffers_per_shader_stage: 0, + max_storage_textures_per_shader_stage: 0, + max_storage_buffer_binding_size: 0, + max_vertex_buffer_array_stride: 255, + + // Most of the values should be the same as the downlevel defaults + ..Self::downlevel_defaults() + } + } + /// Modify the current limits to use the resolution limits of the other. /// /// This is useful because the swapchain might need to be larger than any other image in the application. @@ -702,37 +718,51 @@ impl DownlevelCapabilities { } bitflags::bitflags! { - /// Binary flags listing various ways the underlying platform does not conform to the WebGPU standard. + /// Binary flags listing features that may or may not be present on downlevel adapters. + /// + /// Flags that are **not** present for a downlevel adapter or device usually indicates + /// non-compliance with the WebGPU specification, but not always. + /// + /// You can check whether a set of flags is compliant by by comparing to + /// [`DownlevelFlags::compliant()`]: + /// + /// ``` + /// # let my_flags = DownlevelFlags::all(); + /// # use wgpu_types::DownlevelFlags; + /// assert_eq!(my_flags & DownlevelFlags::compliant(), DownlevelFlags::compliant()); + /// ``` pub struct DownlevelFlags: u32 { /// The device supports compiling and using compute shaders. - const COMPUTE_SHADERS = 0x0000_0001; + const COMPUTE_SHADERS = 1 << 0; /// Supports binding storage buffers and textures to fragment shaders. - const FRAGMENT_WRITABLE_STORAGE = 0x0000_0002; + const FRAGMENT_WRITABLE_STORAGE = 1 << 1; /// Supports indirect drawing and dispatching. - const INDIRECT_EXECUTION = 0x0000_0004; + const INDIRECT_EXECUTION = 1 << 2; /// Supports non-zero `base_vertex` parameter to indexed draw calls. - const BASE_VERTEX = 0x0000_0008; - /// Supports reading from a depth/stencil buffer while using as a read-only depth/stencil attachment. - const READ_ONLY_DEPTH_STENCIL = 0x0000_0010; + const BASE_VERTEX = 1 << 3; + /// Supports reading from a depth/stencil buffer while using as a read-only depth/stencil + /// attachment. + const READ_ONLY_DEPTH_STENCIL = 1 << 4; /// Supports: /// - copy_image_to_image /// - copy_buffer_to_image and copy_image_to_buffer with a buffer without a MAP_* usage - const DEVICE_LOCAL_IMAGE_COPIES = 0x0000_0020; + const DEVICE_LOCAL_IMAGE_COPIES = 1 << 5; /// Supports textures with mipmaps which have a non power of two size. - const NON_POWER_OF_TWO_MIPMAPPED_TEXTURES = 0x0000_0040; + const NON_POWER_OF_TWO_MIPMAPPED_TEXTURES = 1 << 6; /// Supports textures that are cube arrays. - const CUBE_ARRAY_TEXTURES = 0x0000_0080; + const CUBE_ARRAY_TEXTURES = 1 << 7; /// Supports comparison samplers. - const COMPARISON_SAMPLERS = 0x0000_0100; + const COMPARISON_SAMPLERS = 1 << 8; /// Supports different blending modes per color target. - const INDEPENDENT_BLENDING = 0x0000_0200; - + const INDEPENDENT_BLENDING = 1 << 9; - /// Supports samplers with anisotropic filtering. Note this isn't actually required by WebGPU, - /// the implementation is allowed to completely ignore aniso clamp. This flag is here for native backends - /// so they can comunicate to the user of aniso is enabled. - const ANISOTROPIC_FILTERING = 0x8000_0000; + /// Supports samplers with anisotropic filtering. Note this isn't actually required by + /// WebGPU, the implementation is allowed to completely ignore aniso clamp. This flag is + /// here for native backends so they can comunicate to the user of aniso is enabled. + const ANISOTROPIC_FILTERING = 1 << 10; + /// Allows creating and using buffers with STORAGE_* usage + const STORAGE_RESOURCES = 1 << 11; } } diff --git a/wgpu/examples/boids/main.rs b/wgpu/examples/boids/main.rs index 2231531b753..4ccd8e6c36e 100644 --- a/wgpu/examples/boids/main.rs +++ b/wgpu/examples/boids/main.rs @@ -31,6 +31,17 @@ struct Example { } impl framework::Example for Example { + fn required_limits(_: &wgpu::Adapter) -> wgpu::Limits { + wgpu::Limits::downlevel_defaults() + } + + fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities { + wgpu::DownlevelCapabilities { + flags: wgpu::DownlevelFlags::COMPUTE_SHADERS, + ..Default::default() + } + } + /// constructs initial instance of Example struct fn init( sc_desc: &wgpu::SwapChainDescriptor, diff --git a/wgpu/examples/framework.rs b/wgpu/examples/framework.rs index c876e7ee25a..ef11c536d47 100644 --- a/wgpu/examples/framework.rs +++ b/wgpu/examples/framework.rs @@ -1,6 +1,7 @@ use std::future::Future; #[cfg(not(target_arch = "wasm32"))] use std::time::{Duration, Instant}; +use wgpu::DownlevelFlags; use winit::{ event::{self, WindowEvent}, event_loop::{ControlFlow, EventLoop}, @@ -40,8 +41,16 @@ pub trait Example: 'static + Sized { fn required_features() -> wgpu::Features { wgpu::Features::empty() } - fn required_limits() -> wgpu::Limits { - wgpu::Limits::downlevel_defaults() // These downlevel limits will allow the code to run on all possible hardware + fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities { + wgpu::DownlevelCapabilities { + flags: DownlevelFlags::empty(), + shader_model: wgpu::ShaderModel::Sm5, + ..wgpu::DownlevelCapabilities::default() + } + } + fn required_limits(adapter: &wgpu::Adapter) -> wgpu::Limits { + let _ = adapter; + wgpu::Limits::downlevel_webgl2_defaults() // These downlevel limits will allow the code to run on all possible hardware } fn init( sc_desc: &wgpu::SwapChainDescriptor, @@ -142,8 +151,23 @@ async fn setup(title: &str) -> Setup { required_features - adapter_features ); + let required_downlevel_capabilities = E::required_downlevel_capabilities(); + let downlevel_capabilities = adapter.get_downlevel_properties(); + assert!( + downlevel_capabilities.shader_model >= required_downlevel_capabilities.shader_model, + "Adapter does not support the minimum shader model required to run this example: {:?}", + required_downlevel_capabilities.shader_model + ); + assert!( + downlevel_capabilities + .flags + .contains(required_downlevel_capabilities.flags), + "Adapter does not support the downlevel capabilities required to run this example: {:?}", + required_downlevel_capabilities.flags - downlevel_capabilities.flags + ); + // Make sure we use the texture resolution limits from the adapter, so we can support images the size of the swapchain. - let needed_limits = E::required_limits().using_resolution(adapter.limits()); + let needed_limits = E::required_limits(&adapter).using_resolution(adapter.limits()); let trace_dir = std::env::var("WGPU_TRACE"); let (device, queue) = adapter @@ -421,12 +445,10 @@ pub fn test(mut params: FrameworkRefTest) { assert_eq!(params.width % 64, 0, "width needs to be aligned 64"); let features = E::required_features() | params.optional_features; - let limits = E::required_limits(); test_common::initialize_test( - mem::take(&mut params.base_test_parameters) - .features(features) - .limits(limits), + mem::take(&mut params.base_test_parameters).features(features), + |adapter| E::required_limits(&adapter), |ctx| { let spawner = Spawner::new(); diff --git a/wgpu/examples/hello-compute/tests.rs b/wgpu/examples/hello-compute/tests.rs index 3dd6dbad4e8..fd929a5c2f8 100644 --- a/wgpu/examples/hello-compute/tests.rs +++ b/wgpu/examples/hello-compute/tests.rs @@ -10,6 +10,7 @@ use common::{initialize_test, TestParameters}; fn test_compute_1() { initialize_test( TestParameters::default().specific_failure(None, None, Some("V3D"), true), + |_| wgpu::Limits::downlevel_defaults(), |ctx| { let input = &[1, 2, 3, 4]; @@ -27,6 +28,7 @@ fn test_compute_1() { fn test_compute_2() { initialize_test( TestParameters::default().specific_failure(None, None, Some("V3D"), true), + |_| wgpu::Limits::downlevel_defaults(), |ctx| { let input = &[5, 23, 10, 9]; @@ -44,6 +46,7 @@ fn test_compute_2() { fn test_compute_overflow() { initialize_test( TestParameters::default().specific_failure(None, None, Some("V3D"), true), + |_| wgpu::Limits::downlevel_defaults(), |ctx| { let input = &[77031, 837799, 8400511, 63728127]; pollster::block_on(assert_execute_gpu( @@ -62,6 +65,7 @@ fn test_multithreaded_compute() { TestParameters::default() .backend_failure(wgpu::Backends::GL) .specific_failure(None, None, Some("V3D"), true), + |_| wgpu::Limits::downlevel_defaults(), |ctx| { use std::{sync::mpsc, thread, time::Duration}; diff --git a/wgpu/examples/shadow/main.rs b/wgpu/examples/shadow/main.rs index 07ca92d7159..d93f16403e9 100644 --- a/wgpu/examples/shadow/main.rs +++ b/wgpu/examples/shadow/main.rs @@ -213,12 +213,36 @@ impl framework::Example for Example { wgpu::Features::DEPTH_CLAMPING } + fn required_limits(adapter: &wgpu::Adapter) -> wgpu::Limits { + let downlevel_limits = wgpu::Limits::downlevel_defaults(); + let webgl_limits = wgpu::Limits::downlevel_webgl2_defaults(); + if adapter + .get_downlevel_properties() + .flags + .contains(wgpu::DownlevelFlags::STORAGE_RESOURCES) + { + wgpu::Limits { + max_storage_buffers_per_shader_stage: downlevel_limits + .max_storage_buffers_per_shader_stage, + max_storage_buffer_binding_size: downlevel_limits.max_storage_buffer_binding_size, + ..webgl_limits + } + } else { + webgl_limits + } + } + fn init( sc_desc: &wgpu::SwapChainDescriptor, - _adapter: &wgpu::Adapter, + adapter: &wgpu::Adapter, device: &wgpu::Device, _queue: &wgpu::Queue, ) -> Self { + let supports_storage_resources = adapter + .get_downlevel_properties() + .flags + .contains(wgpu::DownlevelFlags::STORAGE_RESOURCES); + // Create the vertex and index buffers let vertex_size = mem::size_of::(); let (cube_vertex_data, cube_index_data) = create_cube(); @@ -427,8 +451,11 @@ impl framework::Example for Example { let light_storage_buf = device.create_buffer(&wgpu::BufferDescriptor { label: None, size: light_uniform_size, - usage: wgpu::BufferUsages::STORAGE - | wgpu::BufferUsages::COPY_SRC + usage: if supports_storage_resources { + wgpu::BufferUsages::STORAGE + } else { + wgpu::BufferUsages::UNIFORM + } | wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::COPY_DST, mapped_at_creation: false, }); @@ -540,18 +567,31 @@ impl framework::Example for Example { }, count: None, }, - wgpu::BindGroupLayoutEntry { - binding: 1, // lights - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Storage { read_only: true }, - has_dynamic_offset: false, - min_binding_size: wgpu::BufferSize::new(light_uniform_size), - }, - count: None, + if supports_storage_resources { + wgpu::BindGroupLayoutEntry { + binding: 1, // lights + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Storage { read_only: true }, + has_dynamic_offset: false, + min_binding_size: wgpu::BufferSize::new(light_uniform_size), + }, + count: None, + } + } else { + wgpu::BindGroupLayoutEntry { + binding: 2, // lights + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: wgpu::BufferSize::new(light_uniform_size), + }, + count: None, + } }, wgpu::BindGroupLayoutEntry { - binding: 2, + binding: 3, visibility: wgpu::ShaderStages::FRAGMENT, ty: wgpu::BindingType::Texture { multisampled: false, @@ -561,7 +601,7 @@ impl framework::Example for Example { count: None, }, wgpu::BindGroupLayoutEntry { - binding: 3, + binding: 4, visibility: wgpu::ShaderStages::FRAGMENT, ty: wgpu::BindingType::Sampler { comparison: true, @@ -597,16 +637,23 @@ impl framework::Example for Example { binding: 0, resource: uniform_buf.as_entire_binding(), }, - wgpu::BindGroupEntry { - binding: 1, - resource: light_storage_buf.as_entire_binding(), + if supports_storage_resources { + wgpu::BindGroupEntry { + binding: 1, + resource: light_storage_buf.as_entire_binding(), + } + } else { + wgpu::BindGroupEntry { + binding: 2, + resource: light_storage_buf.as_entire_binding(), + } }, wgpu::BindGroupEntry { - binding: 2, + binding: 3, resource: wgpu::BindingResource::TextureView(&shadow_view), }, wgpu::BindGroupEntry { - binding: 3, + binding: 4, resource: wgpu::BindingResource::Sampler(&shadow_sampler), }, ], @@ -624,7 +671,11 @@ impl framework::Example for Example { }, fragment: Some(wgpu::FragmentState { module: &shader, - entry_point: "fs_main", + entry_point: if supports_storage_resources { + "fs_main" + } else { + "fs_main_without_storage" + }, targets: &[sc_desc.format.into()], }), primitive: wgpu::PrimitiveState { diff --git a/wgpu/examples/shadow/shader.wgsl b/wgpu/examples/shadow/shader.wgsl index 49b4bb911b2..f4cb12efc4c 100644 --- a/wgpu/examples/shadow/shader.wgsl +++ b/wgpu/examples/shadow/shader.wgsl @@ -54,11 +54,19 @@ struct Lights { data: [[stride(96)]] array; }; +// Used when storage types are nnot supported +[[block]] +struct LightsWithoutStorage { + data: array; +}; + [[group(0), binding(1)]] var s_lights: [[access(read)]] Lights; [[group(0), binding(2)]] -var t_shadow: texture_depth_2d_array; +var u_lights: LightsWithoutStorage; [[group(0), binding(3)]] +var t_shadow: texture_depth_2d_array; +[[group(0), binding(4)]] var sampler_shadow: sampler_comparison; fn fetch_shadow(light_id: u32, homogeneous_coords: vec4) -> f32 { @@ -102,3 +110,27 @@ fn fs_main(in: VertexOutput) -> [[location(0)]] vec4 { // multiply the light by material color return vec4(color, 1.0) * u_entity.color; } + +// The fragment entrypoint used when storage buffers are not available for the lights +[[stage(fragment)]] +fn fs_main_without_storage(in: VertexOutput) -> [[location(0)]] vec4 { + let normal = normalize(in.world_normal); + var color: vec3 = c_ambient; + var i: u32 = 0u; + loop { + if (i >= min(u_globals.num_lights.x, c_max_lights)) { + break; + } + // This line is the only difference from the entrypoint above. It uses the lights + // uniform instead of the lights storage buffer + let light = u_lights.data[i]; + let shadow = fetch_shadow(i, light.proj * in.world_position); + let light_dir = normalize(light.pos.xyz - in.world_position.xyz); + let diffuse = max(0.0, dot(normal, light_dir)); + color = color + shadow * diffuse * light.color.xyz; + continuing { + i = i + 1u; + } + } + return vec4(color, 1.0) * u_entity.color; +} diff --git a/wgpu/examples/texture-arrays/main.rs b/wgpu/examples/texture-arrays/main.rs index 3bdab8632b6..e88f14277c0 100644 --- a/wgpu/examples/texture-arrays/main.rs +++ b/wgpu/examples/texture-arrays/main.rs @@ -78,7 +78,7 @@ impl framework::Example for Example { fn required_features() -> wgpu::Features { wgpu::Features::TEXTURE_BINDING_ARRAY | wgpu::Features::SPIRV_SHADER_PASSTHROUGH } - fn required_limits() -> wgpu::Limits { + fn required_limits(_: &wgpu::Adapter) -> wgpu::Limits { wgpu::Limits { max_push_constant_size: 4, ..wgpu::Limits::default() diff --git a/wgpu/tests/common/mod.rs b/wgpu/tests/common/mod.rs index 90288d5a886..83d87b60905 100644 --- a/wgpu/tests/common/mod.rs +++ b/wgpu/tests/common/mod.rs @@ -81,7 +81,6 @@ pub struct FailureCase { // This information determines if a test should run. pub struct TestParameters { pub required_features: Features, - pub required_limits: Limits, pub required_downlevel_properties: DownlevelCapabilities, // Backends where test should fail. pub failures: Vec, @@ -91,7 +90,6 @@ impl Default for TestParameters { fn default() -> Self { Self { required_features: Features::empty(), - required_limits: Limits::downlevel_defaults(), required_downlevel_properties: lowest_downlevel_properties(), failures: Vec::new(), } @@ -120,12 +118,6 @@ impl TestParameters { self } - /// Set the list - pub fn limits(mut self, limits: Limits) -> Self { - self.required_limits = limits; - self - } - pub fn downlevel_flags(mut self, downlevel_flags: DownlevelFlags) -> Self { self.required_downlevel_properties.flags |= downlevel_flags; self @@ -177,7 +169,11 @@ impl TestParameters { } } -pub fn initialize_test(parameters: TestParameters, test_function: impl FnOnce(TestingContext)) { +pub fn initialize_test( + parameters: TestParameters, + limits_function: impl FnOnce(&Adapter) -> wgpu::Limits, + test_function: impl FnOnce(TestingContext), +) { // We don't actually care if it fails let _ = env_logger::try_init(); @@ -189,6 +185,7 @@ pub fn initialize_test(parameters: TestParameters, test_function: impl FnOnce(Te )) .expect("could not find sutable adapter on the system"); + let required_limits = limits_function(&adapter); let adapter_info = adapter.get_info(); let adapter_lowercase_name = adapter_info.name.to_lowercase(); let adapter_features = adapter.features(); @@ -201,7 +198,7 @@ pub fn initialize_test(parameters: TestParameters, test_function: impl FnOnce(Te return; } - if adapter_limits < parameters.required_limits { + if adapter_limits < required_limits { println!("TEST SKIPPED: LIMIT TOO LOW"); return; } @@ -229,7 +226,7 @@ pub fn initialize_test(parameters: TestParameters, test_function: impl FnOnce(Te let (device, queue) = pollster::block_on(initialize_device( &adapter, parameters.required_features, - parameters.required_limits, + required_limits, )); let context = TestingContext {