diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f58d1ace07..2a9bc5f0fd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,7 @@ Bottom level categories: - New downlevel feature `UNRESTRICTED_INDEX_BUFFER` to indicate support for using `INDEX` together with other non-copy/map usages (unsupported on WebGL). By @Wumpf in [#3157](https://github.com/gfx-rs/wgpu/pull/3157) #### WebGPU + - Implement `queue_validate_write_buffer` by @jinleili in [#3098](https://github.com/gfx-rs/wgpu/pull/3098) #### GLES @@ -64,12 +65,17 @@ Bottom level categories: - Add the `"wgsl"` feature, to enable WGSL shaders in `wgpu-core` and `wgpu`. Enabled by default in `wgpu`. By @daxpedda in [#2890](https://github.com/gfx-rs/wgpu/pull/2890). - Implement `Clone` for `ShaderSource` and `ShaderModuleDescriptor` in `wgpu`. By @daxpedda in [#3086](https://github.com/gfx-rs/wgpu/pull/3086). - Add `get_default_config` for `Surface` to simplify user creation of `SurfaceConfiguration`. By @jinleili in [#3034](https://github.com/gfx-rs/wgpu/pull/3034) +- Native adapters can now use MSAA x2 and x8 if it's supported , previously only x1 and x4 were supported . By @39ali in [3140](https://github.com/gfx-rs/wgpu/pull/3140) #### GLES - Surfaces support now `TextureFormat::Rgba8Unorm` and (non-web only) `TextureFormat::Bgra8Unorm`. By @Wumpf in [#3070](https://github.com/gfx-rs/wgpu/pull/3070) - Support alpha to coverage. By @Wumpf in [#3156](https://github.com/gfx-rs/wgpu/pull/3156) +#### WebGPU + +- Add `MULTISAMPLE_X2`, `MULTISAMPLE_X4` and `MULTISAMPLE_X8` to `TextureFormatFeatureFlags`. By @39ali in [3140](https://github.com/gfx-rs/wgpu/pull/3140) + ### Bug Fixes #### General @@ -81,6 +87,7 @@ Bottom level categories: - Fix an integer overflow in `queue_write_texture` by @nical in (#3146)[https://github.com/gfx-rs/wgpu/pull/3146] #### WebGPU + - Use `log` instead of `println` in hello example by @JolifantoBambla in [#2858](https://github.com/gfx-rs/wgpu/pull/2858) #### GLES @@ -95,6 +102,7 @@ Bottom level categories: By @jimblandy in [#3171](https://github.com/gfx-rs/wgpu/pull/3171) ### Examples + - Log adapter info in hello example on wasm target by @JolifantoBambla in [#2858](https://github.com/gfx-rs/wgpu/pull/2858) ### Testing/Internal @@ -204,6 +212,7 @@ both `raw_window_handle::HasRawWindowHandle` and `raw_window_handle::HasRawDispl - Report Apple M2 gpu as integrated. By @i509VCB [#3036](https://github.com/gfx-rs/wgpu/pull/3036) #### WebGPU + - When called in a web worker, `Context::init()` now uses `web_sys::WorkerGlobalContext` to create a `wgpu::Instance` instead of trying to access the unavailable `web_sys::Window` by @JolifantoBambla in [#2858](https://github.com/gfx-rs/wgpu/pull/2858) ### Changes @@ -360,6 +369,7 @@ Added items to the public API - Update present_mode docs as most of them don't automatically fall back to Fifo anymore. by @Elabajaba in [#2855](https://github.com/gfx-rs/wgpu/pull/2855) #### Hal + - Document safety requirements for `Adapter::from_external` in gles hal by @i509VCB in [#2863](https://github.com/gfx-rs/wgpu/pull/2863) - Make `AdapterContext` a publicly accessible type in the gles hal by @i509VCB in [#2870](https://github.com/gfx-rs/wgpu/pull/2870) diff --git a/wgpu-core/src/command/render.rs b/wgpu-core/src/command/render.rs index d86c8884e2b..09af0bbe6ab 100644 --- a/wgpu-core/src/command/render.rs +++ b/wgpu-core/src/command/render.rs @@ -732,9 +732,6 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> { expected: sample_count, }); } - if sample_count != 1 && sample_count != 4 { - return Err(RenderPassErrorInner::InvalidSampleCount(sample_count)); - } attachment_type_name = type_name; Ok(()) }; diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index 42447565f85..7a7e0be4c99 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -811,12 +811,23 @@ impl Device { return Err(CreateTextureError::MultisampledNotRenderAttachment); } + if !format_features.flags.intersects( + wgt::TextureFormatFeatureFlags::MULTISAMPLE_X4 + | wgt::TextureFormatFeatureFlags::MULTISAMPLE_X2 + | wgt::TextureFormatFeatureFlags::MULTISAMPLE_X8, + ) { + return Err(CreateTextureError::InvalidMultisampledFormat(desc.format)); + } + if !format_features .flags - .contains(wgt::TextureFormatFeatureFlags::MULTISAMPLE) + .sample_count_supported(desc.sample_count) { - return Err(CreateTextureError::InvalidMultisampledFormat(desc.format)); - } + return Err(CreateTextureError::InvalidSampleCount( + desc.sample_count, + desc.format, + )); + }; } let mips = desc.mip_level_count; @@ -2665,7 +2676,9 @@ impl Device { break Some(pipeline::ColorStateError::FormatNotColor(cs.format)); } if desc.multisample.count > 1 - && !format_features.flags.contains(Tfff::MULTISAMPLE) + && !format_features + .flags + .sample_count_supported(desc.multisample.count) { break Some(pipeline::ColorStateError::FormatNotMultisampled(cs.format)); } @@ -2699,7 +2712,10 @@ impl Device { ds.format, )); } - if desc.multisample.count > 1 && !format_features.flags.contains(Tfff::MULTISAMPLE) + if desc.multisample.count > 1 + && !format_features + .flags + .sample_count_supported(desc.multisample.count) { break Some(pipeline::DepthStencilStateError::FormatNotMultisampled( ds.format, diff --git a/wgpu-core/src/instance.rs b/wgpu-core/src/instance.rs index 22800f7abc0..e237c6bcaf7 100644 --- a/wgpu-core/src/instance.rs +++ b/wgpu-core/src/instance.rs @@ -268,9 +268,18 @@ impl Adapter { ); flags.set( - wgt::TextureFormatFeatureFlags::MULTISAMPLE, - caps.contains(Tfc::MULTISAMPLE), + wgt::TextureFormatFeatureFlags::MULTISAMPLE_X2, + caps.contains(Tfc::MULTISAMPLE_X2), ); + flags.set( + wgt::TextureFormatFeatureFlags::MULTISAMPLE_X4, + caps.contains(Tfc::MULTISAMPLE_X4), + ); + flags.set( + wgt::TextureFormatFeatureFlags::MULTISAMPLE_X8, + caps.contains(Tfc::MULTISAMPLE_X8), + ); + flags.set( wgt::TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE, caps.contains(Tfc::MULTISAMPLE_RESOLVE), diff --git a/wgpu-core/src/present.rs b/wgpu-core/src/present.rs index 21047199ada..41da2fa585d 100644 --- a/wgpu-core/src/present.rs +++ b/wgpu-core/src/present.rs @@ -184,7 +184,7 @@ impl Global { hal_usage: conv::map_texture_usage(config.usage, config.format.into()), format_features: wgt::TextureFormatFeatures { allowed_usages: wgt::TextureUsages::RENDER_ATTACHMENT, - flags: wgt::TextureFormatFeatureFlags::MULTISAMPLE + flags: wgt::TextureFormatFeatureFlags::MULTISAMPLE_X4 | wgt::TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE, }, initialization_status: TextureInitTracker::new(1, 1), diff --git a/wgpu-core/src/resource.rs b/wgpu-core/src/resource.rs index a008a1d422d..6339d77b6b9 100644 --- a/wgpu-core/src/resource.rs +++ b/wgpu-core/src/resource.rs @@ -517,6 +517,8 @@ pub enum CreateTextureError { MultisampledNotRenderAttachment, #[error("Texture format {0:?} can't be used due to missing features.")] MissingFeatures(wgt::TextureFormat, #[source] MissingFeatures), + #[error("Sample count {0} is not supported by format {1:?} on this device. It may be supported by your adapter through the TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES feature.")] + InvalidSampleCount(u32, wgt::TextureFormat), } impl Resource for Texture { diff --git a/wgpu-hal/src/dx12/adapter.rs b/wgpu-hal/src/dx12/adapter.rs index 496e52e6686..5bf92069a86 100644 --- a/wgpu-hal/src/dx12/adapter.rs +++ b/wgpu-hal/src/dx12/adapter.rs @@ -446,12 +446,37 @@ impl crate::Adapter for super::Adapter { | d3d12::D3D12_FORMAT_SUPPORT1_DEPTH_STENCIL) != 0 && data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_MULTISAMPLE_RENDERTARGET == 0; - caps.set(Tfc::MULTISAMPLE, !no_msaa_load && !no_msaa_target); + caps.set( Tfc::MULTISAMPLE_RESOLVE, data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_MULTISAMPLE_RESOLVE != 0, ); + let mut ms_levels = d3d12::D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS { + Format: raw_format, + SampleCount: 0, + Flags: d3d12::D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG_NONE, + NumQualityLevels: 0, + }; + + let mut set_sample_count = |sc: u32, tfc: Tfc| { + ms_levels.SampleCount = sc; + + if self.device.CheckFeatureSupport( + d3d12::D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, + <*mut _>::cast(&mut ms_levels), + mem::size_of::() as _, + ) == winerror::S_OK + && ms_levels.NumQualityLevels != 0 + { + caps.set(tfc, !no_msaa_load && !no_msaa_target); + } + }; + + set_sample_count(2, Tfc::MULTISAMPLE_X2); + set_sample_count(4, Tfc::MULTISAMPLE_X4); + set_sample_count(8, Tfc::MULTISAMPLE_X8); + caps } diff --git a/wgpu-hal/src/empty.rs b/wgpu-hal/src/empty.rs index b13ceb94895..a761ef7fb14 100644 --- a/wgpu-hal/src/empty.rs +++ b/wgpu-hal/src/empty.rs @@ -89,6 +89,7 @@ impl crate::Adapter for Context { ) -> crate::TextureFormatCapabilities { crate::TextureFormatCapabilities::empty() } + unsafe fn surface_capabilities(&self, surface: &Context) -> Option { None } diff --git a/wgpu-hal/src/gles/adapter.rs b/wgpu-hal/src/gles/adapter.rs index 38b0d76f44b..83355649da0 100644 --- a/wgpu-hal/src/gles/adapter.rs +++ b/wgpu-hal/src/gles/adapter.rs @@ -196,6 +196,7 @@ impl super::Adapter { (vendor, renderer) }; + let version = unsafe { gl.get_parameter_string(glow::VERSION) }; log::info!("Vendor: {}", vendor); @@ -664,6 +665,21 @@ impl crate::Adapter for super::Adapter { use crate::TextureFormatCapabilities as Tfc; use wgt::TextureFormat as Tf; + let sample_count = { + let max_samples = self + .shared + .context + .lock() + .get_parameter_i32(glow::MAX_SAMPLES); + if max_samples >= 8 { + Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4 | Tfc::MULTISAMPLE_X8 + } else if max_samples >= 4 { + Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4 + } else { + Tfc::MULTISAMPLE_X2 + } + }; + // Base types are pulled from the table in the OpenGLES 3.0 spec in section 3.8. // // The storage types are based on table 8.26, in section @@ -671,10 +687,10 @@ impl crate::Adapter for super::Adapter { let empty = Tfc::empty(); let base = Tfc::COPY_SRC | Tfc::COPY_DST; let unfilterable = base | Tfc::SAMPLED; - let depth = base | Tfc::SAMPLED | Tfc::MULTISAMPLE | Tfc::DEPTH_STENCIL_ATTACHMENT; + let depth = base | Tfc::SAMPLED | sample_count | Tfc::DEPTH_STENCIL_ATTACHMENT; let filterable = unfilterable | Tfc::SAMPLED_LINEAR; let renderable = - unfilterable | Tfc::COLOR_ATTACHMENT | Tfc::MULTISAMPLE | Tfc::MULTISAMPLE_RESOLVE; + unfilterable | Tfc::COLOR_ATTACHMENT | sample_count | Tfc::MULTISAMPLE_RESOLVE; let filterable_renderable = filterable | renderable | Tfc::COLOR_ATTACHMENT_BLEND; let storage = base | Tfc::STORAGE | Tfc::STORAGE_READ_WRITE; diff --git a/wgpu-hal/src/lib.rs b/wgpu-hal/src/lib.rs index 980cf64b1bb..33aa9306c84 100644 --- a/wgpu-hal/src/lib.rs +++ b/wgpu-hal/src/lib.rs @@ -583,15 +583,20 @@ bitflags!( /// Format can be used as depth-stencil and input attachment. const DEPTH_STENCIL_ATTACHMENT = 1 << 8; - /// Format can be multisampled. - const MULTISAMPLE = 1 << 9; + /// Format can be multisampled by x2. + const MULTISAMPLE_X2 = 1 << 9; + /// Format can be multisampled by x4. + const MULTISAMPLE_X4 = 1 << 10; + /// Format can be multisampled by x8. + const MULTISAMPLE_X8 = 1 << 11; + /// Format can be used for render pass resolve targets. - const MULTISAMPLE_RESOLVE = 1 << 10; + const MULTISAMPLE_RESOLVE = 1 << 12; /// Format can be copied from. - const COPY_SRC = 1 << 11; + const COPY_SRC = 1 << 13; /// Format can be copied to. - const COPY_DST = 1 << 12; + const COPY_DST = 1 << 14; } ); diff --git a/wgpu-hal/src/metal/adapter.rs b/wgpu-hal/src/metal/adapter.rs index 465517e25ee..201b6960b9f 100644 --- a/wgpu-hal/src/metal/adapter.rs +++ b/wgpu-hal/src/metal/adapter.rs @@ -56,16 +56,8 @@ impl crate::Adapter for super::Adapter { (Tfc::STORAGE_READ_WRITE, Tfc::STORAGE_READ_WRITE) } }; - let msaa_desktop_if = if pc.msaa_desktop { - Tfc::MULTISAMPLE - } else { - Tfc::empty() - }; - let msaa_apple7x_if = if pc.msaa_desktop | pc.msaa_apple7 { - Tfc::MULTISAMPLE - } else { - Tfc::empty() - }; + let msaa_count = pc.sample_count_mask; + let msaa_resolve_desktop_if = if pc.msaa_desktop { Tfc::MULTISAMPLE_RESOLVE } else { @@ -90,7 +82,7 @@ impl crate::Adapter for super::Adapter { | Tfc::STORAGE | Tfc::COLOR_ATTACHMENT | Tfc::COLOR_ATTACHMENT_BLEND - | Tfc::MULTISAMPLE + | msaa_count | Tfc::MULTISAMPLE_RESOLVE; let extra = match format { @@ -110,7 +102,7 @@ impl crate::Adapter for super::Adapter { | Tf::Rgba8Sint | Tf::Rgba16Uint | Tf::Rgba16Sint => { - read_write_tier2_if | Tfc::STORAGE | Tfc::COLOR_ATTACHMENT | Tfc::MULTISAMPLE + read_write_tier2_if | Tfc::STORAGE | Tfc::COLOR_ATTACHMENT | msaa_count } Tf::R16Unorm | Tf::R16Snorm @@ -122,26 +114,23 @@ impl crate::Adapter for super::Adapter { | Tfc::STORAGE | Tfc::COLOR_ATTACHMENT | Tfc::COLOR_ATTACHMENT_BLEND - | Tfc::MULTISAMPLE + | msaa_count | msaa_resolve_desktop_if } Tf::Rg8Unorm | Tf::Rg16Float | Tf::Bgra8Unorm => all_caps, - Tf::Rg8Uint | Tf::Rg8Sint => Tfc::STORAGE | Tfc::COLOR_ATTACHMENT | Tfc::MULTISAMPLE, + Tf::Rg8Uint | Tf::Rg8Sint => Tfc::STORAGE | Tfc::COLOR_ATTACHMENT | msaa_count, Tf::R32Uint | Tf::R32Sint => { - read_write_tier1_if | Tfc::STORAGE | Tfc::COLOR_ATTACHMENT | msaa_desktop_if + read_write_tier1_if | Tfc::STORAGE | Tfc::COLOR_ATTACHMENT | msaa_count } Tf::R32Float => { let flags = if pc.format_r32float_all { all_caps } else { - Tfc::STORAGE - | Tfc::COLOR_ATTACHMENT - | Tfc::COLOR_ATTACHMENT_BLEND - | Tfc::MULTISAMPLE + Tfc::STORAGE | Tfc::COLOR_ATTACHMENT | Tfc::COLOR_ATTACHMENT_BLEND | msaa_count }; read_write_tier1_if | flags } - Tf::Rg16Uint | Tf::Rg16Sint => Tfc::STORAGE | Tfc::COLOR_ATTACHMENT | Tfc::MULTISAMPLE, + Tf::Rg16Uint | Tf::Rg16Sint => Tfc::STORAGE | Tfc::COLOR_ATTACHMENT | msaa_count, Tf::Rgba8UnormSrgb | Tf::Bgra8UnormSrgb => { let mut flags = all_caps; flags.set(Tfc::STORAGE, pc.format_rgba8_srgb_all); @@ -157,26 +146,23 @@ impl crate::Adapter for super::Adapter { flags.set(Tfc::STORAGE, pc.format_rg11b10_all); flags } - Tf::Rg32Uint | Tf::Rg32Sint => Tfc::COLOR_ATTACHMENT | Tfc::STORAGE | msaa_apple7x_if, + Tf::Rg32Uint | Tf::Rg32Sint => Tfc::COLOR_ATTACHMENT | Tfc::STORAGE | msaa_count, Tf::Rg32Float => { if pc.format_rg32float_all { all_caps } else { - Tfc::STORAGE - | Tfc::COLOR_ATTACHMENT - | Tfc::COLOR_ATTACHMENT_BLEND - | msaa_apple7x_if + Tfc::STORAGE | Tfc::COLOR_ATTACHMENT | Tfc::COLOR_ATTACHMENT_BLEND | msaa_count } } Tf::Rgba32Uint | Tf::Rgba32Sint => { - read_write_tier2_if | Tfc::STORAGE | Tfc::COLOR_ATTACHMENT | msaa_desktop_if + read_write_tier2_if | Tfc::STORAGE | Tfc::COLOR_ATTACHMENT | msaa_count } Tf::Rgba32Float => { let mut flags = read_write_tier2_if | Tfc::STORAGE | Tfc::COLOR_ATTACHMENT; if pc.format_rgba32float_all { flags |= all_caps } else if pc.msaa_apple7 { - flags |= Tfc::MULTISAMPLE + flags |= msaa_count }; flags } @@ -189,7 +175,7 @@ impl crate::Adapter for super::Adapter { }*/ Tf::Depth16Unorm => { let mut flags = - Tfc::DEPTH_STENCIL_ATTACHMENT | Tfc::MULTISAMPLE | msaa_resolve_apple3x_if; + Tfc::DEPTH_STENCIL_ATTACHMENT | msaa_count | msaa_resolve_apple3x_if; if pc.format_depth16unorm { flags |= Tfc::SAMPLED_LINEAR } @@ -197,14 +183,14 @@ impl crate::Adapter for super::Adapter { } Tf::Depth32Float | Tf::Depth32FloatStencil8 => { let mut flags = - Tfc::DEPTH_STENCIL_ATTACHMENT | Tfc::MULTISAMPLE | msaa_resolve_apple3x_if; + Tfc::DEPTH_STENCIL_ATTACHMENT | msaa_count | msaa_resolve_apple3x_if; if pc.format_depth32float_filter { flags |= Tfc::SAMPLED_LINEAR } flags } Tf::Depth24Plus | Tf::Depth24PlusStencil8 => { - let mut flags = Tfc::DEPTH_STENCIL_ATTACHMENT | Tfc::MULTISAMPLE; + let mut flags = Tfc::DEPTH_STENCIL_ATTACHMENT | msaa_count; if pc.format_depth24_stencil8 { flags |= Tfc::SAMPLED_LINEAR | Tfc::MULTISAMPLE_RESOLVE } else { @@ -224,7 +210,7 @@ impl crate::Adapter for super::Adapter { Tfc::SAMPLED_LINEAR | Tfc::COLOR_ATTACHMENT | Tfc::COLOR_ATTACHMENT_BLEND - | Tfc::MULTISAMPLE + | msaa_count | Tfc::MULTISAMPLE_RESOLVE } } @@ -489,12 +475,12 @@ impl super::PrivateCapabilities { version.is_mac = os_is_mac; let family_check = version.at_least((10, 15), (13, 0)); - let mut sample_count_mask: u8 = 1 | 4; // 1 and 4 samples are supported on all devices + let mut sample_count_mask = crate::TextureFormatCapabilities::MULTISAMPLE_X4; // 1 and 4 samples are supported on all devices if device.supports_texture_sample_count(2) { - sample_count_mask |= 2; + sample_count_mask |= crate::TextureFormatCapabilities::MULTISAMPLE_X2; } if device.supports_texture_sample_count(8) { - sample_count_mask |= 8; + sample_count_mask |= crate::TextureFormatCapabilities::MULTISAMPLE_X8; } let rw_texture_tier = if version.at_least((10, 13), (11, 0)) { diff --git a/wgpu-hal/src/metal/mod.rs b/wgpu-hal/src/metal/mod.rs index 5fb23d0a313..b49e9da3dc4 100644 --- a/wgpu-hal/src/metal/mod.rs +++ b/wgpu-hal/src/metal/mod.rs @@ -218,7 +218,7 @@ struct PrivateCapabilities { max_varying_components: u32, max_threads_per_group: u32, max_total_threadgroup_memory: u32, - sample_count_mask: u8, + sample_count_mask: crate::TextureFormatCapabilities, supports_debug_markers: bool, supports_binary_archives: bool, supports_capture_manager: bool, diff --git a/wgpu-hal/src/metal/surface.rs b/wgpu-hal/src/metal/surface.rs index ce9d0a210a4..fffad30f033 100644 --- a/wgpu-hal/src/metal/surface.rs +++ b/wgpu-hal/src/metal/surface.rs @@ -100,7 +100,7 @@ impl super::Surface { } /// If not called on the main thread, this will panic. - pub unsafe fn get_metal_layer( + pub(crate) unsafe fn get_metal_layer( view: *mut Object, delegate: Option<&HalManagedMetalLayerDelegate>, ) -> *mut Object { diff --git a/wgpu-hal/src/vulkan/adapter.rs b/wgpu-hal/src/vulkan/adapter.rs index 2c30dcd7e1f..72ae9ce90ea 100644 --- a/wgpu-hal/src/vulkan/adapter.rs +++ b/wgpu-hal/src/vulkan/adapter.rs @@ -1424,10 +1424,42 @@ impl crate::Adapter for super::Adapter { ), ); // Vulkan is very permissive about MSAA + flags.set(Tfc::MULTISAMPLE_RESOLVE, !format.describe().is_compressed()); + + // get the supported sample counts + let format_aspect = crate::FormatAspects::from(format); + let limits = self.phd_capabilities.properties.limits; + + let sample_flags = if format_aspect.contains(crate::FormatAspects::DEPTH) { + limits + .framebuffer_depth_sample_counts + .min(limits.sampled_image_depth_sample_counts) + } else if format_aspect.contains(crate::FormatAspects::STENCIL) { + limits + .framebuffer_stencil_sample_counts + .min(limits.sampled_image_stencil_sample_counts) + } else { + limits + .framebuffer_color_sample_counts + .min(limits.sampled_image_color_sample_counts) + .min(limits.sampled_image_integer_sample_counts) + .min(limits.storage_image_sample_counts) + }; + + flags.set( + Tfc::MULTISAMPLE_X2, + sample_flags.contains(vk::SampleCountFlags::TYPE_2), + ); flags.set( - Tfc::MULTISAMPLE | Tfc::MULTISAMPLE_RESOLVE, - !format.describe().is_compressed(), + Tfc::MULTISAMPLE_X4, + sample_flags.contains(vk::SampleCountFlags::TYPE_4), ); + + flags.set( + Tfc::MULTISAMPLE_X8, + sample_flags.contains(vk::SampleCountFlags::TYPE_8), + ); + flags } diff --git a/wgpu-info/src/main.rs b/wgpu-info/src/main.rs index df49e7ad514..9d477e7cd52 100644 --- a/wgpu-info/src/main.rs +++ b/wgpu-info/src/main.rs @@ -230,7 +230,7 @@ mod inner { } } - println!("\tTexture Format Features: ┌──────────┬──────────┬──────────Allowed┬Usages───────────┬───────────────────┐ ┌────────────┬─────────────┬──────────────Feature┬Flags───────────────┬─────────────────┐"); + println!("\tTexture Format Features: ┌──────────┬──────────┬──────────Allowed┬Usages───────────┬───────────────────┐ ┌────────────┬────────────────┬──────────────Feature┬Flags──────┬─────────────────────┬────────────────────┬─"); for format in TEXTURE_FORMAT_LIST { let features = adapter.get_texture_format_features(format); let format_name = match format { @@ -271,9 +271,10 @@ mod inner { } } } + println!(" │"); } - println!("\t └──────────┴──────────┴─────────────────┴─────────────────┴───────────────────┘ └────────────┴─────────────┴─────────────────────┴────────────────────┴─────────────────┘"); + println!("\t └──────────┴──────────┴─────────────────┴─────────────────┴───────────────────┘ └────────────┴────────────────┴────────────────┴────────────────┴─────────────────────┘"); } pub fn main() { diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index 068346cc10c..59710afeca6 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -1629,19 +1629,40 @@ bitflags::bitflags! { /// If not present, the texture can't be sampled with a filtering sampler. /// This may overwrite TextureSampleType::Float.filterable const FILTERABLE = 1 << 0; - /// Allows [`TextureDescriptor::sample_count`] greater than `1`. - const MULTISAMPLE = 1 << 1; + /// Allows [`TextureDescriptor::sample_count`] to be `2`. + const MULTISAMPLE_X2 = 1 << 1; + /// Allows [`TextureDescriptor::sample_count`] to be `4`. + const MULTISAMPLE_X4 = 1 << 2 ; + /// Allows [`TextureDescriptor::sample_count`] to be `8`. + const MULTISAMPLE_X8 = 1 << 3 ; /// Allows a texture of this format to back a view passed as `resolve_target` /// to a render pass for an automatic driver-implemented resolve. - const MULTISAMPLE_RESOLVE = 1 << 2; + const MULTISAMPLE_RESOLVE = 1 << 4; /// When used as a STORAGE texture, then a texture with this format can be bound with /// [`StorageTextureAccess::ReadOnly`] or [`StorageTextureAccess::ReadWrite`]. - const STORAGE_READ_WRITE = 1 << 3; + const STORAGE_READ_WRITE = 1 << 5; /// When used as a STORAGE texture, then a texture with this format can be written to with atomics. // TODO: No access flag exposed as of writing - const STORAGE_ATOMICS = 1 << 4; + const STORAGE_ATOMICS = 1 << 6; /// If not present, the texture can't be blended into the render target. - const BLENDABLE = 1 << 5; + const BLENDABLE = 1 << 7; + } +} + +impl TextureFormatFeatureFlags { + /// Sample count supported by a given texture format. + /// + /// returns `true` if `count` is a supported sample count. + pub fn sample_count_supported(&self, count: u32) -> bool { + use TextureFormatFeatureFlags as tfsc; + + match count { + 1 => true, + 2 => self.contains(tfsc::MULTISAMPLE_X2), + 4 => self.contains(tfsc::MULTISAMPLE_X4), + 8 => self.contains(tfsc::MULTISAMPLE_X8), + _ => false, + } } } @@ -2308,9 +2329,9 @@ impl TextureFormat { // Multisampling let noaa = TextureFormatFeatureFlags::empty(); - let msaa = TextureFormatFeatureFlags::MULTISAMPLE; - let msaa_resolve = - TextureFormatFeatureFlags::MULTISAMPLE | TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE; + let msaa = TextureFormatFeatureFlags::MULTISAMPLE_X4; + let msaa_resolve = TextureFormatFeatureFlags::MULTISAMPLE_X4 + | TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE; // Flags let basic = diff --git a/wgpu/examples/msaa-line/main.rs b/wgpu/examples/msaa-line/main.rs index 27d1e807988..b39c144877f 100644 --- a/wgpu/examples/msaa-line/main.rs +++ b/wgpu/examples/msaa-line/main.rs @@ -32,6 +32,7 @@ struct Example { sample_count: u32, rebuild_bundle: bool, config: wgpu::SurfaceConfiguration, + max_sample_count: u32, } impl Example { @@ -117,6 +118,10 @@ impl Example { } impl framework::Example for Example { + fn optional_features() -> wgt::Features { + wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES + } + fn init( config: &wgpu::SurfaceConfiguration, _adapter: &wgpu::Adapter, @@ -124,7 +129,22 @@ impl framework::Example for Example { _queue: &wgpu::Queue, ) -> Self { log::info!("Press left/right arrow keys to change sample_count."); - let sample_count = 4; + + let sample_flags = _adapter.get_texture_format_features(config.format).flags; + + let max_sample_count = { + if sample_flags.contains(wgpu::TextureFormatFeatureFlags::MULTISAMPLE_X8) { + 8 + } else if sample_flags.contains(wgpu::TextureFormatFeatureFlags::MULTISAMPLE_X4) { + 4 + } else if sample_flags.contains(wgpu::TextureFormatFeatureFlags::MULTISAMPLE_X2) { + 2 + } else { + 1 + } + }; + + let sample_count = max_sample_count; let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { label: None, @@ -181,6 +201,7 @@ impl framework::Example for Example { vertex_buffer, vertex_count, sample_count, + max_sample_count, rebuild_bundle: false, config: config.clone(), } @@ -195,14 +216,14 @@ impl framework::Example for Example { // TODO: Switch back to full scans of possible options when we expose // supported sample counts to the user. Some(winit::event::VirtualKeyCode::Left) => { - if self.sample_count == 4 { + if self.sample_count == self.max_sample_count { self.sample_count = 1; self.rebuild_bundle = true; } } Some(winit::event::VirtualKeyCode::Right) => { if self.sample_count == 1 { - self.sample_count = 4; + self.sample_count = self.max_sample_count; self.rebuild_bundle = true; } } @@ -295,7 +316,8 @@ fn msaa_line() { image_path: "/examples/msaa-line/screenshot.png", width: 1024, height: 768, - optional_features: wgpu::Features::default(), + optional_features: wgpu::Features::default() + | wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES, base_test_parameters: framework::test_common::TestParameters::default(), tolerance: 64, max_outliers: 1 << 16, // MSAA is comically different between vendors, 32k is a decent limit