Skip to content

Commit

Permalink
Texture format MSAA capabilities
Browse files Browse the repository at this point in the history
  • Loading branch information
kvark committed Jan 11, 2022
1 parent f8a63c4 commit 911ca09
Show file tree
Hide file tree
Showing 14 changed files with 460 additions and 186 deletions.
2 changes: 2 additions & 0 deletions wgpu-core/src/command/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ impl RenderBundleEncoder {
parent_id: id::DeviceId,
base: Option<BasePass<RenderCommand>>,
) -> Result<Self, CreateRenderBundleError> {
//TODO: validate that attachment formats are renderable,
// have expected aspects, support multisampling.
Ok(Self {
base: base.unwrap_or_else(|| BasePass::new(&desc.label)),
parent_id,
Expand Down
42 changes: 33 additions & 9 deletions wgpu-core/src/command/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,8 @@ pub enum RenderPassErrorInner {
InvalidColorAttachmentFormat(wgt::TextureFormat),
#[error("attachment format {0:?} is not a depth-stencil format")]
InvalidDepthStencilAttachmentFormat(wgt::TextureFormat),
#[error("attachment format {0:?} can not be resolved")]
UnsupportedResolveTargetFormat(wgt::TextureFormat),
#[error("necessary attachments are missing")]
MissingAttachments,
#[error("attachments have differing sizes: {previous:?} is followed by {mismatch:?}")]
Expand All @@ -418,10 +420,15 @@ pub enum RenderPassErrorInner {
},
#[error("attachment's sample count {0} is invalid")]
InvalidSampleCount(u32),
#[error("attachment with resolve target must be multi-sampled")]
InvalidResolveSourceSampleCount,
#[error("resolve target must have a sample count of 1")]
InvalidResolveTargetSampleCount,
#[error("resolve source must be multi-sampled (has {src} samples) while the resolve destination must not be multisampled (has {dst} samples)")]
InvalidResolveSampleCounts { src: u32, dst: u32 },
#[error(
"resource source format ({src:?}) must match the resolve destination format ({dst:?})"
)]
MismatchedResolveTextureFormat {
src: wgt::TextureFormat,
dst: wgt::TextureFormat,
},
#[error("surface texture is dropped before the render pass is finished")]
SurfaceTextureDropped,
#[error("not enough memory left")]
Expand All @@ -438,7 +445,8 @@ pub enum RenderPassErrorInner {
MissingFeatures(#[from] MissingFeatures),
#[error(transparent)]
MissingDownlevelFlags(#[from] MissingDownlevelFlags),
#[error("indirect draw uses bytes {offset}..{end_offset} {} which overruns indirect buffer of size {buffer_size}", count.map_or_else(String::new, |v| format!("(using count {})", v)))]
#[error("indirect draw uses bytes {offset}..{end_offset} {} which overruns indirect buffer of size {buffer_size}",
count.map_or_else(String::new, |v| format!("(using count {})", v)))]
IndirectBufferOverrun {
count: Option<NonZeroU32>,
offset: u64,
Expand Down Expand Up @@ -823,18 +831,34 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
.views
.use_extend(&*view_guard, resolve_target, (), ())
.map_err(|_| RenderPassErrorInner::InvalidAttachment(resolve_target))?;

check_multiview(resolve_view)?;
if color_view.extent != resolve_view.extent {
return Err(RenderPassErrorInner::AttachmentsDimensionMismatch {
previous: (attachment_type_name, extent.unwrap_or_default()),
mismatch: ("resolve", resolve_view.extent),
});
}
if color_view.samples == 1 {
return Err(RenderPassErrorInner::InvalidResolveSourceSampleCount);
if color_view.samples == 1 || resolve_view.samples != 1 {
return Err(RenderPassErrorInner::InvalidResolveSampleCounts {
src: color_view.samples,
dst: resolve_view.samples,
});
}
if color_view.desc.format != resolve_view.desc.format {
return Err(RenderPassErrorInner::MismatchedResolveTextureFormat {
src: color_view.desc.format,
dst: resolve_view.desc.format,
});
}
if resolve_view.samples != 1 {
return Err(RenderPassErrorInner::InvalidResolveTargetSampleCount);
if !resolve_view
.format_features
.flags
.contains(wgt::TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE)
{
return Err(RenderPassErrorInner::UnsupportedResolveTargetFormat(
resolve_view.desc.format,
));
}

cmd_buf.texture_memory_actions.register_implicit_init(
Expand Down
38 changes: 26 additions & 12 deletions wgpu-core/src/device/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1886,19 +1886,19 @@ impl<A: HalApi> Device<A> {
view_samples: view.samples,
});
}
match (sample_type, format_info.sample_type, view.format_features.filterable) {
(Tst::Uint, Tst::Uint, ..) |
(Tst::Sint, Tst::Sint, ..) |
(Tst::Depth, Tst::Depth, ..) |
match (sample_type, format_info.sample_type) {
(Tst::Uint, Tst::Uint) |
(Tst::Sint, Tst::Sint) |
(Tst::Depth, Tst::Depth) |
// if we expect non-filterable, accept anything float
(Tst::Float { filterable: false }, Tst::Float { .. }, ..) |
(Tst::Float { filterable: false }, Tst::Float { .. }) |
// if we expect filterable, require it
(Tst::Float { filterable: true }, Tst::Float { filterable: true }, ..) |
// if we expect filterable, also accept Float that is defined as unfilterable if filterable feature is explicitly enabled
// (only hit if wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES is enabled)
(Tst::Float { filterable: true }, Tst::Float { .. }, true) |
(Tst::Float { filterable: true }, Tst::Float { filterable: true }) |
// if we expect float, also accept depth
(Tst::Float { .. }, Tst::Depth, ..) => {}
// if we expect filterable, also accept Float that is defined as unfilterable if filterable feature is explicitly enabled
// (only hit if wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES is enabled)
(Tst::Float { filterable: true }, Tst::Float { .. }) if view.format_features.flags.contains(wgt::TextureFormatFeatureFlags::FILTERABLE) => {}
_ => {
return Err(Error::InvalidTextureSampleType {
binding,
Expand Down Expand Up @@ -2264,6 +2264,8 @@ impl<A: HalApi> Device<A> {
hub: &Hub<A, G>,
token: &mut Token<Self>,
) -> Result<pipeline::RenderPipeline<A>, pipeline::CreateRenderPipelineError> {
use wgt::TextureFormatFeatureFlags as Tfff;

//TODO: only lock mutable if the layout is derived
let (mut pipeline_layout_guard, mut token) = hub.pipeline_layouts.write(token);
let (mut bgl_guard, mut token) = hub.bind_group_layouts.write(&mut token);
Expand Down Expand Up @@ -2409,12 +2411,16 @@ impl<A: HalApi> Device<A> {
{
break Some(pipeline::ColorStateError::FormatNotRenderable(cs.format));
}
if cs.blend.is_some() && !format_features.filterable {
if cs.blend.is_some() && !format_features.flags.contains(Tfff::FILTERABLE) {
break Some(pipeline::ColorStateError::FormatNotBlendable(cs.format));
}
if !hal::FormatAspects::from(cs.format).contains(hal::FormatAspects::COLOR) {
break Some(pipeline::ColorStateError::FormatNotColor(cs.format));
}
if desc.multisample.count > 1 && !format_features.flags.contains(Tfff::MULTISAMPLE)
{
break Some(pipeline::ColorStateError::FormatNotMultisampled(cs.format));
}

break None;
};
Expand All @@ -2425,15 +2431,16 @@ impl<A: HalApi> Device<A> {

if let Some(ds) = depth_stencil_state {
let error = loop {
if !self
.describe_format_features(adapter, ds.format)?
let format_features = self.describe_format_features(adapter, ds.format)?;
if !format_features
.allowed_usages
.contains(wgt::TextureUsages::RENDER_ATTACHMENT)
{
break Some(pipeline::DepthStencilStateError::FormatNotRenderable(
ds.format,
));
}

let aspect = hal::FormatAspects::from(ds.format);
if ds.is_depth_enabled() && !aspect.contains(hal::FormatAspects::DEPTH) {
break Some(pipeline::DepthStencilStateError::FormatNotDepth(ds.format));
Expand All @@ -2443,6 +2450,13 @@ impl<A: HalApi> Device<A> {
ds.format,
));
}
if desc.multisample.count > 1 && !format_features.flags.contains(Tfff::MULTISAMPLE)
{
break Some(pipeline::DepthStencilStateError::FormatNotMultisampled(
ds.format,
));
}

break None;
};
if let Some(e) = error {
Expand Down
19 changes: 15 additions & 4 deletions wgpu-core/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,14 +270,25 @@ impl<A: HalApi> Adapter<A> {
// We are currently taking the filtering and blending together,
// but we may reconsider this in the future if there are formats
// in the wild for which these two capabilities do not match.
let filterable = caps.contains(Tfc::SAMPLED_LINEAR)
&& (!caps.contains(Tfc::COLOR_ATTACHMENT)
|| caps.contains(Tfc::COLOR_ATTACHMENT_BLEND));
flags.set(
wgt::TextureFormatFeatureFlags::FILTERABLE,
caps.contains(Tfc::SAMPLED_LINEAR)
&& (!caps.contains(Tfc::COLOR_ATTACHMENT)
|| caps.contains(Tfc::COLOR_ATTACHMENT_BLEND)),
);

flags.set(
wgt::TextureFormatFeatureFlags::MULTISAMPLE,
caps.contains(Tfc::MULTISAMPLE),
);
flags.set(
wgt::TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE,
caps.contains(Tfc::MULTISAMPLE_RESOLVE),
);

wgt::TextureFormatFeatures {
allowed_usages,
flags,
filterable,
}
}

Expand Down
4 changes: 4 additions & 0 deletions wgpu-core/src/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ pub enum ColorStateError {
FormatNotBlendable(wgt::TextureFormat),
#[error("format {0:?} does not have a color aspect")]
FormatNotColor(wgt::TextureFormat),
#[error("format {0:?} can't be multisampled")]
FormatNotMultisampled(wgt::TextureFormat),
#[error("output format {pipeline} is incompatible with the shader {shader}")]
IncompatibleFormat {
pipeline: validation::NumericType,
Expand All @@ -287,6 +289,8 @@ pub enum DepthStencilStateError {
FormatNotDepth(wgt::TextureFormat),
#[error("format {0:?} does not have a stencil aspect, but stencil test/write is enabled")]
FormatNotStencil(wgt::TextureFormat),
#[error("format {0:?} can't be multisampled")]
FormatNotMultisampled(wgt::TextureFormat),
}

#[derive(Clone, Debug, Error)]
Expand Down
4 changes: 2 additions & 2 deletions wgpu-core/src/present.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
hal_usage: conv::map_texture_usage(config.usage, config.format.into()),
format_features: wgt::TextureFormatFeatures {
allowed_usages: wgt::TextureUsages::RENDER_ATTACHMENT,
flags: wgt::TextureFormatFeatureFlags::empty(),
filterable: false,
flags: wgt::TextureFormatFeatureFlags::MULTISAMPLE
| wgt::TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE,
},
initialization_status: TextureInitTracker::new(1, 1),
full_range: TextureSelector {
Expand Down
30 changes: 23 additions & 7 deletions wgpu-hal/src/dx12/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,13 +332,16 @@ impl crate::Adapter<super::Api> for super::Adapter {
);

let mut caps = Tfc::COPY_SRC | Tfc::COPY_DST;
let can_image = 0
!= data.Support1
& (d3d12::D3D12_FORMAT_SUPPORT1_TEXTURE1D
| d3d12::D3D12_FORMAT_SUPPORT1_TEXTURE2D
| d3d12::D3D12_FORMAT_SUPPORT1_TEXTURE3D
| d3d12::D3D12_FORMAT_SUPPORT1_TEXTURECUBE);
caps.set(Tfc::SAMPLED, can_image);
let is_texture = data.Support1
& (d3d12::D3D12_FORMAT_SUPPORT1_TEXTURE1D
| d3d12::D3D12_FORMAT_SUPPORT1_TEXTURE2D
| d3d12::D3D12_FORMAT_SUPPORT1_TEXTURE3D
| d3d12::D3D12_FORMAT_SUPPORT1_TEXTURECUBE)
!= 0;
caps.set(
Tfc::SAMPLED,
is_texture && data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_SHADER_LOAD != 0,
);
caps.set(
Tfc::SAMPLED_LINEAR,
data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE != 0,
Expand All @@ -364,6 +367,19 @@ impl crate::Adapter<super::Api> for super::Adapter {
data.Support2 & d3d12::D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD != 0,
);

let no_msaa_load = caps.contains(Tfc::SAMPLED)
&& data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_MULTISAMPLE_LOAD == 0;
let no_msaa_target = data.Support1
& (d3d12::D3D12_FORMAT_SUPPORT1_RENDER_TARGET
| 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,
);

caps
}

Expand Down
4 changes: 3 additions & 1 deletion wgpu-hal/src/gles/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -576,9 +576,11 @@ impl crate::Adapter<super::Api> for super::Adapter {
) -> crate::TextureFormatCapabilities {
use crate::TextureFormatCapabilities as Tfc;
use wgt::TextureFormat as Tf;

// The storage types are sprinkled based on section
// "TEXTURE IMAGE LOADS AND STORES" of GLES-3.2 spec.
let unfiltered_color = Tfc::SAMPLED | Tfc::COLOR_ATTACHMENT;
let unfiltered_color =
Tfc::SAMPLED | Tfc::COLOR_ATTACHMENT | Tfc::MULTISAMPLE | Tfc::MULTISAMPLE_RESOLVE;
let filtered_color = unfiltered_color | Tfc::SAMPLED_LINEAR | Tfc::COLOR_ATTACHMENT_BLEND;
match format {
Tf::R8Unorm | Tf::R8Snorm => filtered_color,
Expand Down
4 changes: 2 additions & 2 deletions wgpu-hal/src/gles/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ impl super::Queue {
gl.pixel_store_i32(glow::UNPACK_ROW_LENGTH, row_texels as i32);
gl.pixel_store_i32(glow::UNPACK_IMAGE_HEIGHT, column_texels as i32);
let mut unbind_unpack_buffer = false;
if format_info.block_dimensions == (1, 1) {
if !format_info.is_compressed() {
let buffer_data;
let unpack_data = match src.raw {
Some(buffer) => {
Expand Down Expand Up @@ -540,7 +540,7 @@ impl super::Queue {
ref copy,
} => {
let format_info = src_format.describe();
if format_info.block_dimensions != (1, 1) {
if format_info.is_compressed() {
log::error!("Not implemented yet: compressed texture copy to buffer");
return;
}
Expand Down
9 changes: 7 additions & 2 deletions wgpu-hal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -562,10 +562,15 @@ 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 used for render pass resolve targets.
const MULTISAMPLE_RESOLVE = 1 << 10;

/// Format can be copied from.
const COPY_SRC = 1 << 9;
const COPY_SRC = 1 << 11;
/// Format can be copied to.
const COPY_DST = 1 << 10;
const COPY_DST = 1 << 12;
}
);

Expand Down
Loading

0 comments on commit 911ca09

Please sign in to comment.