Better validation of image view creation, descriptor set updates, draw-time resources and others (#1806)

This commit is contained in:
Rua 2022-01-29 21:53:33 +01:00 committed by GitHub
parent b25598ee70
commit 0c06394e6c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 1906 additions and 601 deletions

View File

@ -18,21 +18,21 @@ use winit::window::{Window, WindowBuilder};
use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess};
use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, SubpassContents};
use vulkano::descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet};
use vulkano::device::{Device, DeviceExtensions, Features};
use vulkano::device::physical::{PhysicalDevice, PhysicalDeviceType};
use vulkano::device::{Device, DeviceExtensions, Features};
use vulkano::format::Format;
use vulkano::image::{
ImageDimensions, ImageUsage, ImmutableImage, MipmapsCount, SwapchainImage, view::ImageView,
};
use vulkano::image::ImageAccess;
use vulkano::image::{
view::ImageView, ImageDimensions, ImageUsage, ImmutableImage, MipmapsCount, SwapchainImage,
};
use vulkano::instance::Instance;
use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint};
use vulkano::pipeline::graphics::color_blend::ColorBlendState;
use vulkano::pipeline::graphics::input_assembly::{InputAssemblyState, PrimitiveTopology};
use vulkano::pipeline::graphics::vertex_input::BuffersDefinition;
use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState};
use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint};
use vulkano::render_pass::{Framebuffer, RenderPass, Subpass};
use vulkano::sampler::{Filter, Sampler, SamplerAddressMode};
use vulkano::sampler::{Sampler};
use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError};
use vulkano::sync::{self, FlushError, GpuFuture};
use vulkano::Version;
@ -86,7 +86,7 @@ fn main() {
.union(&device_extensions),
[(queue_family, 0.5)].iter().cloned(),
)
.unwrap();
.unwrap();
let queue = queues.next().unwrap();
let (mut swapchain, images) = {
@ -131,10 +131,10 @@ fn main() {
position: [0.5, 0.2],
},
]
.iter()
.cloned(),
.iter()
.cloned(),
)
.unwrap();
.unwrap();
let vs = vs::load(device.clone()).unwrap();
let fs = fs::load(device.clone()).unwrap();
@ -153,11 +153,16 @@ fn main() {
depth_stencil: {}
}
)
.unwrap();
.unwrap();
let (texture, tex_future) = {
let image_array_data: Vec<_> = vec![include_bytes!("square.png").to_vec(), include_bytes!("star.png").to_vec(), include_bytes!("asterisk.png").to_vec()]
.into_iter().flat_map(|png_bytes| {
let image_array_data: Vec<_> = vec![
include_bytes!("square.png").to_vec(),
include_bytes!("star.png").to_vec(),
include_bytes!("asterisk.png").to_vec(),
]
.into_iter()
.flat_map(|png_bytes| {
let cursor = Cursor::new(png_bytes);
let decoder = png::Decoder::new(cursor);
let mut reader = decoder.read_info().unwrap();
@ -166,7 +171,8 @@ fn main() {
image_data.resize((info.width * info.height * 4) as usize, 0);
reader.next_frame(&mut image_data).unwrap();
image_data
}).collect();
})
.collect();
let dimensions = ImageDimensions::Dim2d {
width: 128,
height: 128,
@ -179,7 +185,7 @@ fn main() {
Format::R8G8B8A8_SRGB,
queue.clone(),
)
.unwrap();
.unwrap();
(ImageView::new(image).unwrap(), future)
};
@ -206,7 +212,7 @@ fn main() {
sampler.clone(),
)],
)
.unwrap();
.unwrap();
let mut viewport = Viewport {
origin: [0.0, 0.0],
@ -269,7 +275,7 @@ fn main() {
queue.family(),
CommandBufferUsage::OneTimeSubmit,
)
.unwrap();
.unwrap();
builder
.begin_render_pass(
framebuffers[image_num].clone(),

View File

@ -231,7 +231,7 @@ where
let minor = spirv.version().minor;
let patch = spirv.version().patch;
quote! {
Version {
::vulkano::Version {
major: #major,
minor: #minor,
patch: #patch,
@ -240,7 +240,7 @@ where
};
let spirv_capabilities = reflect::spirv_capabilities(&spirv).map(|capability| {
let name = format_ident!("{}", format!("{:?}", capability));
quote! { &Capability::#name }
quote! { &::vulkano::shader::spirv::Capability::#name }
});
let spirv_extensions = reflect::spirv_extensions(&spirv);
let entry_points = reflect::entry_points(&spirv)
@ -267,21 +267,12 @@ where
pub fn #load_name(device: ::std::sync::Arc<::vulkano::device::Device>)
-> Result<::std::sync::Arc<::vulkano::shader::ShaderModule>, ::vulkano::shader::ShaderCreationError>
{
use vulkano::shader::EntryPointInfo;
use vulkano::shader::GeometryShaderExecution;
use vulkano::shader::ShaderExecution;
use vulkano::shader::ShaderModule;
use vulkano::shader::ShaderStage;
use vulkano::shader::SpecializationConstantRequirements;
use vulkano::shader::spirv::Capability;
use vulkano::Version;
let _bytes = ( #( #include_bytes),* );
static WORDS: &[u32] = &[ #( #words ),* ];
unsafe {
Ok(ShaderModule::from_words_with_data(
Ok(::vulkano::shader::ShaderModule::from_words_with_data(
device,
WORDS,
#spirv_version,

View File

@ -12,8 +12,8 @@ use proc_macro2::TokenStream;
use vulkano::pipeline::layout::PipelineLayoutPcRange;
use vulkano::shader::spirv::ExecutionModel;
use vulkano::shader::{
DescriptorIdentifier, DescriptorRequirements, GeometryShaderExecution, ShaderExecution,
ShaderInterfaceEntry, ShaderInterfaceEntryType, SpecializationConstantRequirements,
DescriptorIdentifier, DescriptorRequirements, ShaderExecution, ShaderInterfaceEntry,
ShaderInterfaceEntryType, SpecializationConstantRequirements,
};
use vulkano::shader::{EntryPointInfo, ShaderInterface, ShaderStages};
@ -24,7 +24,7 @@ pub(super) fn write_entry_point(
) -> TokenStream {
let execution = write_shader_execution(&info.execution);
let model = syn::parse_str::<syn::Path>(&format!(
"vulkano::shader::spirv::ExecutionModel::{:?}",
"::vulkano::shader::spirv::ExecutionModel::{:?}",
model
))
.unwrap();
@ -40,7 +40,7 @@ pub(super) fn write_entry_point(
(
#name.to_owned(),
#model,
EntryPointInfo {
::vulkano::shader::EntryPointInfo {
execution: #execution,
descriptor_requirements: #descriptor_requirements.into_iter().collect(),
push_constant_requirements: #push_constant_requirements,
@ -54,21 +54,23 @@ pub(super) fn write_entry_point(
fn write_shader_execution(execution: &ShaderExecution) -> TokenStream {
match execution {
ShaderExecution::Vertex => quote! { ShaderExecution::Vertex },
ShaderExecution::TessellationControl => quote! { ShaderExecution::TessellationControl },
ShaderExecution::TessellationEvaluation => {
quote! { ShaderExecution::TessellationEvaluation }
ShaderExecution::Vertex => quote! { ::vulkano::shader::ShaderExecution::Vertex },
ShaderExecution::TessellationControl => {
quote! { ::vulkano::shader::ShaderExecution::TessellationControl }
}
ShaderExecution::Geometry(GeometryShaderExecution { input }) => {
ShaderExecution::TessellationEvaluation => {
quote! { ::vulkano::shader::ShaderExecution::TessellationEvaluation }
}
ShaderExecution::Geometry(::vulkano::shader::GeometryShaderExecution { input }) => {
let input = format_ident!("{}", format!("{:?}", input));
quote! {
ShaderExecution::Geometry {
::vulkano::shader::ShaderExecution::Geometry {
input: GeometryShaderInput::#input,
}
}
}
ShaderExecution::Fragment => quote! { ShaderExecution::Fragment },
ShaderExecution::Compute => quote! { ShaderExecution::Compute },
ShaderExecution::Fragment => quote! { ::vulkano::shader::ShaderExecution::Fragment },
ShaderExecution::Compute => quote! { ::vulkano::shader::ShaderExecution::Compute },
}
}
@ -80,36 +82,46 @@ fn write_descriptor_requirements(
let DescriptorRequirements {
descriptor_types,
descriptor_count,
format,
image_format,
image_multisampled,
image_scalar_type,
image_view_type,
multisampled,
mutable,
sampler_no_unnormalized,
sampler_compare,
sampler_no_unnormalized_coordinates,
sampler_with_images,
stages,
storage_image_atomic,
storage_read,
storage_write,
} = reqs;
let descriptor_types = descriptor_types.into_iter().map(|ty| {
let ident = format_ident!("{}", format!("{:?}", ty));
quote! { DescriptorType::#ident }
quote! { ::vulkano::descriptor_set::layout::DescriptorType::#ident }
});
let format = match format {
Some(format) => {
let ident = format_ident!("{}", format!("{:?}", format));
quote! { Some(Format::#ident) }
let image_format = match image_format {
Some(image_format) => {
let ident = format_ident!("{}", format!("{:?}", image_format));
quote! { Some(::vulkano::format::Format::#ident) }
}
None => quote! { None },
};
let image_scalar_type = match image_scalar_type {
Some(image_scalar_type) => {
let ident = format_ident!("{}", format!("{:?}", image_scalar_type));
quote! { Some(::vulkano::shader::ShaderScalarType::#ident) }
}
None => quote! { None },
};
let image_view_type = match image_view_type {
Some(image_view_type) => {
let ident = format_ident!("{}", format!("{:?}", image_view_type));
quote! { Some(ImageViewType::#ident) }
quote! { Some(::vulkano::image::view::ImageViewType::#ident) }
}
None => quote! { None },
};
let mutable = mutable.iter();
let sampler_no_unnormalized = sampler_no_unnormalized.iter();
let sampler_compare = sampler_compare.iter();
let sampler_no_unnormalized_coordinates = sampler_no_unnormalized_coordinates.iter();
let sampler_with_images = {
sampler_with_images.iter().map(|(&index, identifiers)| {
let identifiers = identifiers.iter().map(
@ -119,7 +131,7 @@ fn write_descriptor_requirements(
index,
}| {
quote! {
vulkano::shader::DescriptorIdentifier {
::vulkano::shader::DescriptorIdentifier {
set: #set,
binding: #binding,
index: #index,
@ -152,7 +164,7 @@ fn write_descriptor_requirements(
} = stages;
quote! {
ShaderStages {
::vulkano::shader::ShaderStages {
vertex: #vertex,
tessellation_control: #tessellation_control,
tessellation_evaluation: #tessellation_evaluation,
@ -169,21 +181,26 @@ fn write_descriptor_requirements(
}
};
let storage_image_atomic = storage_image_atomic.iter();
let storage_read = storage_read.iter();
let storage_write = storage_write.iter();
quote! {
(
(#set_num, #binding_num),
DescriptorRequirements {
::vulkano::shader::DescriptorRequirements {
descriptor_types: vec![#(#descriptor_types),*],
descriptor_count: #descriptor_count,
format: #format,
image_format: #image_format,
image_multisampled: #image_multisampled,
image_scalar_type: #image_scalar_type,
image_view_type: #image_view_type,
multisampled: #multisampled,
mutable: [#(#mutable),*].into_iter().collect(),
sampler_no_unnormalized: [#(#sampler_no_unnormalized),*].into_iter().collect(),
sampler_compare: [#(#sampler_compare),*].into_iter().collect(),
sampler_no_unnormalized_coordinates: [#(#sampler_no_unnormalized_coordinates),*].into_iter().collect(),
sampler_with_images: [#(#sampler_with_images),*].into_iter().collect(),
stages: #stages,
storage_image_atomic: [#(#storage_image_atomic),*].into_iter().collect(),
storage_read: [#(#storage_read),*].into_iter().collect(),
storage_write: [#(#storage_write),*].into_iter().collect(),
},
),
}
@ -222,7 +239,7 @@ fn write_push_constant_requirements(
} = stages;
quote! {
ShaderStages {
::vulkano::shader::ShaderStages {
vertex: #vertex,
tessellation_control: #tessellation_control,
tessellation_evaluation: #tessellation_evaluation,
@ -240,7 +257,7 @@ fn write_push_constant_requirements(
};
quote! {
Some(PipelineLayoutPcRange {
Some(::vulkano::pipeline::layout::PipelineLayoutPcRange {
offset: #offset,
size: #size,
stages: #stages,
@ -263,7 +280,7 @@ fn write_specialization_constant_requirements(
quote! {
(
#constant_id,
SpecializationConstantRequirements {
::vulkano::shader::SpecializationConstantRequirements {
size: #size,
},
),

View File

@ -895,34 +895,6 @@ pub fn shader(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let uses = &input.types_meta.uses;
let result = quote! {
#[allow(unused_imports)]
use std::sync::Arc;
#[allow(unused_imports)]
use std::vec::IntoIter as VecIntoIter;
#[allow(unused_imports)]
use vulkano::device::Device;
#[allow(unused_imports)]
use vulkano::descriptor_set::layout::DescriptorType;
#[allow(unused_imports)]
use vulkano::format::Format;
#[allow(unused_imports)]
use vulkano::image::view::ImageViewType;
#[allow(unused_imports)]
use vulkano::pipeline::layout::PipelineLayout;
#[allow(unused_imports)]
use vulkano::pipeline::layout::PipelineLayoutPcRange;
#[allow(unused_imports)]
use vulkano::shader::DescriptorRequirements;
#[allow(unused_imports)]
use vulkano::shader::ShaderStages;
#[allow(unused_imports)]
use vulkano::shader::SpecializationConstants as SpecConstsTrait;
#[allow(unused_imports)]
use vulkano::shader::SpecializationMapEntry;
#[allow(unused_imports)]
use vulkano::Version;
#(
#shaders_code
)*

View File

@ -814,7 +814,7 @@ pub(super) fn write_specialization_constants<'a>(
let constant_id = spec_const.constant_id;
let rust_size = spec_const.rust_size;
map_entries.push(quote! {
SpecializationMapEntry {
::vulkano::shader::SpecializationMapEntry {
constant_id: #constant_id,
offset: #curr_offset,
size: #rust_size,
@ -857,9 +857,9 @@ pub(super) fn write_specialization_constants<'a>(
}
}
unsafe impl SpecConstsTrait for #struct_name {
fn descriptors() -> &'static [SpecializationMapEntry] {
static DESCRIPTORS: [SpecializationMapEntry; #num_map_entries] = [
unsafe impl ::vulkano::shader::SpecializationConstants for #struct_name {
fn descriptors() -> &'static [::vulkano::shader::SpecializationMapEntry] {
static DESCRIPTORS: [::vulkano::shader::SpecializationMapEntry; #num_map_entries] = [
#( #map_entries ),*
];
&DESCRIPTORS

View File

@ -2732,7 +2732,7 @@ impl SyncCommandBufferBuilder {
let access = (0..).map(|index| {
let mut access = access;
let mutable = reqs.mutable.contains(&index);
let mutable = reqs.storage_write.contains(&index);
access.access.shader_write = mutable;
access.exclusive = mutable;
access

View File

@ -102,14 +102,14 @@ where
}
}
let source_dimensions = match source.dimensions().mipmap_dimensions(source_mip_level) {
let source_dimensions = match source.dimensions().mip_level_dimensions(source_mip_level) {
Some(d) => d,
None => return Err(CheckBlitImageError::SourceCoordinatesOutOfRange),
};
let destination_dimensions = match destination
.dimensions()
.mipmap_dimensions(destination_mip_level)
.mip_level_dimensions(destination_mip_level)
{
Some(d) => d,
None => return Err(CheckBlitImageError::DestinationCoordinatesOutOfRange),

View File

@ -88,14 +88,14 @@ where
}
}
let source_dimensions = match source.dimensions().mipmap_dimensions(source_mip_level) {
let source_dimensions = match source.dimensions().mip_level_dimensions(source_mip_level) {
Some(d) => d,
None => return Err(CheckCopyImageError::SourceCoordinatesOutOfRange),
};
let destination_dimensions = match destination
.dimensions()
.mipmap_dimensions(destination_mip_level)
.mip_level_dimensions(destination_mip_level)
{
Some(d) => d,
None => return Err(CheckCopyImageError::DestinationCoordinatesOutOfRange),

View File

@ -86,7 +86,7 @@ where
return Err(CheckCopyBufferImageError::UnexpectedMultisampled);
}
let image_dimensions = match image.dimensions().mipmap_dimensions(image_mipmap) {
let image_dimensions = match image.dimensions().mip_level_dimensions(image_mipmap) {
Some(d) => d,
None => return Err(CheckCopyBufferImageError::ImageCoordinatesOutOfRange),
};

View File

@ -7,14 +7,20 @@
// notice may not be copied, modified, or distributed except
// according to those terms.
use crate::buffer::BufferAccess;
use crate::buffer::BufferViewAbstract;
use crate::command_buffer::synced::CommandBufferState;
use crate::descriptor_set::layout::DescriptorType;
use crate::descriptor_set::DescriptorBindingResources;
use crate::format::Format;
use crate::image::view::ImageViewType;
use crate::image::ImageViewAbstract;
use crate::image::SampleCount;
use crate::pipeline::Pipeline;
use crate::sampler::Sampler;
use crate::sampler::SamplerImageViewIncompatibleError;
use crate::shader::DescriptorRequirements;
use crate::shader::ShaderScalarType;
use std::error;
use std::fmt;
use std::sync::Arc;
@ -29,12 +35,14 @@ pub(in super::super) fn check_descriptor_sets_validity<'a, P: Pipeline>(
return Ok(());
}
// VUID-vkCmdDispatch-None-02697
let bindings_pipeline_layout =
match current_state.descriptor_sets_pipeline_layout(pipeline.bind_point()) {
Some(x) => x,
None => return Err(CheckDescriptorSetsValidityError::IncompatiblePipelineLayout),
};
// VUID-vkCmdDispatch-None-02697
if !pipeline.layout().is_compatible_with(
bindings_pipeline_layout,
pipeline.num_used_descriptor_sets(),
@ -43,17 +51,68 @@ pub(in super::super) fn check_descriptor_sets_validity<'a, P: Pipeline>(
}
for ((set_num, binding_num), reqs) in descriptor_requirements {
let check_image_view = |image_view: &Arc<dyn ImageViewAbstract>| {
if let Some(image_view_type) = reqs.image_view_type {
if image_view.ty() != image_view_type {
return Err(InvalidDescriptorResource::ImageViewTypeMismatch {
required: reqs.image_view_type.unwrap(),
obtained: image_view.ty(),
});
let layout_binding = pipeline.layout().descriptor_set_layouts()[set_num as usize]
.desc()
.descriptor(binding_num)
.unwrap();
let check_buffer = |index: u32, buffer: &Arc<dyn BufferAccess>| Ok(());
let check_buffer_view = |index: u32, buffer_view: &Arc<dyn BufferViewAbstract>| {
if layout_binding.ty == DescriptorType::StorageTexelBuffer {
// VUID-vkCmdDispatch-OpTypeImage-06423
if reqs.image_format.is_none()
&& reqs.storage_write.contains(&index)
&& !buffer_view.format_features().storage_write_without_format
{
return Err(InvalidDescriptorResource::StorageWriteWithoutFormatNotSupported);
}
// VUID-vkCmdDispatch-OpTypeImage-06424
if reqs.image_format.is_none()
&& reqs.storage_read.contains(&index)
&& !buffer_view.format_features().storage_read_without_format
{
return Err(InvalidDescriptorResource::StorageReadWithoutFormatNotSupported);
}
}
if let Some(format) = reqs.format {
Ok(())
};
let check_image_view_common = |index: u32, image_view: &Arc<dyn ImageViewAbstract>| {
// VUID-vkCmdDispatch-None-02691
if reqs.storage_image_atomic.contains(&index)
&& !image_view.format_features().storage_image_atomic
{
return Err(InvalidDescriptorResource::StorageImageAtomicNotSupported);
}
if layout_binding.ty == DescriptorType::StorageImage {
// VUID-vkCmdDispatch-OpTypeImage-06423
if reqs.image_format.is_none()
&& reqs.storage_write.contains(&index)
&& !image_view.format_features().storage_write_without_format
{
return Err(InvalidDescriptorResource::StorageWriteWithoutFormatNotSupported);
}
// VUID-vkCmdDispatch-OpTypeImage-06424
if reqs.image_format.is_none()
&& reqs.storage_read.contains(&index)
&& !image_view.format_features().storage_read_without_format
{
return Err(InvalidDescriptorResource::StorageReadWithoutFormatNotSupported);
}
}
/*
Instruction/Sampler/Image View Validation
https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap16.html#textures-input-validation
*/
// The SPIR-V Image Format is not compatible with the image views format.
if let Some(format) = reqs.image_format {
if image_view.format() != format {
return Err(InvalidDescriptorResource::ImageViewFormatMismatch {
required: format,
@ -62,13 +121,145 @@ pub(in super::super) fn check_descriptor_sets_validity<'a, P: Pipeline>(
}
}
if reqs.multisampled != (image_view.image().samples() != SampleCount::Sample1) {
return Err(InvalidDescriptorResource::ImageMultisampledMismatch {
required: reqs.multisampled,
// Rules for viewType
if let Some(image_view_type) = reqs.image_view_type {
if image_view.ty() != image_view_type {
return Err(InvalidDescriptorResource::ImageViewTypeMismatch {
required: image_view_type,
obtained: image_view.ty(),
});
}
}
// - If the image was created with VkImageCreateInfo::samples equal to
// VK_SAMPLE_COUNT_1_BIT, the instruction must have MS = 0.
// - If the image was created with VkImageCreateInfo::samples not equal to
// VK_SAMPLE_COUNT_1_BIT, the instruction must have MS = 1.
if reqs.image_multisampled != (image_view.image().samples() != SampleCount::Sample1) {
return Err(InvalidDescriptorResource::ImageViewMultisampledMismatch {
required: reqs.image_multisampled,
obtained: image_view.image().samples() != SampleCount::Sample1,
});
}
// - If the Sampled Type of the OpTypeImage does not match the numeric format of the
// image, as shown in the SPIR-V Sampled Type column of the
// Interpretation of Numeric Format table.
// - If the signedness of any read or sample operation does not match the signedness of
// the images format.
if let Some(scalar_type) = reqs.image_scalar_type {
let aspects = image_view.aspects();
let view_scalar_type = ShaderScalarType::from(
if aspects.color || aspects.plane0 || aspects.plane1 || aspects.plane2 {
image_view.format().type_color().unwrap()
} else if aspects.depth {
image_view.format().type_depth().unwrap()
} else if aspects.stencil {
image_view.format().type_stencil().unwrap()
} else {
// Per `ImageViewBuilder::aspects` and
// VUID-VkDescriptorImageInfo-imageView-01976
unreachable!()
},
);
if scalar_type != view_scalar_type {
return Err(InvalidDescriptorResource::ImageViewScalarTypeMismatch {
required: scalar_type,
obtained: view_scalar_type,
});
}
}
Ok(())
};
let check_sampler_common = |index: u32, sampler: &Arc<Sampler>| {
// VUID-vkCmdDispatch-None-02703
// VUID-vkCmdDispatch-None-02704
if reqs.sampler_no_unnormalized_coordinates.contains(&index)
&& sampler.unnormalized_coordinates()
{
return Err(InvalidDescriptorResource::SamplerUnnormalizedCoordinatesNotAllowed);
}
/*
Instruction/Sampler/Image View Validation
https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap16.html#textures-input-validation
*/
// - The SPIR-V instruction is one of the OpImage*Dref* instructions and the sampler
// compareEnable is VK_FALSE
// - The SPIR-V instruction is not one of the OpImage*Dref* instructions and the sampler
// compareEnable is VK_TRUE
if reqs.sampler_compare.contains(&index) != sampler.compare().is_some() {
return Err(InvalidDescriptorResource::SamplerCompareMismatch {
required: reqs.sampler_compare.contains(&index),
obtained: sampler.compare().is_some(),
});
}
Ok(())
};
let check_image_view = |index: u32, image_view: &Arc<dyn ImageViewAbstract>| {
check_image_view_common(index, image_view)?;
if let Some(sampler) = layout_binding.immutable_samplers.get(index as usize) {
check_sampler_common(index, sampler)?;
}
Ok(())
};
let check_image_view_sampler =
|index: u32, (image_view, sampler): &(Arc<dyn ImageViewAbstract>, Arc<Sampler>)| {
check_image_view_common(index, image_view)?;
check_sampler_common(index, sampler)?;
Ok(())
};
let check_sampler = |index: u32, sampler: &Arc<Sampler>| {
check_sampler_common(index, sampler)?;
// Check sampler-image compatibility. Only done for separate samplers; combined image
// samplers are checked when updating the descriptor set.
if let Some(with_images) = reqs.sampler_with_images.get(&index) {
// If the image view isn't actually present in the resources, then just skip it.
// It will be caught later by check_resources.
let iter = with_images.iter().filter_map(|id| {
current_state
.descriptor_set(pipeline.bind_point(), id.set)
.and_then(|set| set.resources().binding(id.binding))
.and_then(|res| match res {
DescriptorBindingResources::ImageView(elements) => elements
.get(id.index as usize)
.and_then(|opt| opt.as_ref().map(|opt| (id, opt))),
_ => None,
})
});
for (id, image_view) in iter {
if let Err(error) = sampler.check_can_sample(image_view.as_ref()) {
return Err(InvalidDescriptorResource::SamplerImageViewIncompatible {
image_view_set_num: id.set,
image_view_binding_num: id.binding,
image_view_index: id.index,
error,
});
}
}
}
Ok(())
};
let check_none = |index: u32, _: &()| {
if let Some(sampler) = layout_binding.immutable_samplers.get(index as usize) {
check_sampler(index, sampler)?;
}
Ok(())
};
@ -80,25 +271,29 @@ pub(in super::super) fn check_descriptor_sets_validity<'a, P: Pipeline>(
let binding_resources = set_resources.binding(binding_num).unwrap();
match binding_resources {
DescriptorBindingResources::None(_) => (),
DescriptorBindingResources::None(elements) => {
check_resources(set_num, binding_num, reqs, elements, check_none)?;
}
DescriptorBindingResources::Buffer(elements) => {
check_resources(set_num, binding_num, reqs, elements, |_| Ok(()))?;
check_resources(set_num, binding_num, reqs, elements, check_buffer)?;
}
DescriptorBindingResources::BufferView(elements) => {
check_resources(set_num, binding_num, reqs, elements, |_| Ok(()))?;
check_resources(set_num, binding_num, reqs, elements, check_buffer_view)?;
}
DescriptorBindingResources::ImageView(elements) => {
check_resources(set_num, binding_num, reqs, elements, |i| {
check_image_view(i)
})?;
check_resources(set_num, binding_num, reqs, elements, check_image_view)?;
}
DescriptorBindingResources::ImageViewSampler(elements) => {
check_resources(set_num, binding_num, reqs, elements, |(i, s)| {
check_image_view(i)
})?;
check_resources(
set_num,
binding_num,
reqs,
elements,
check_image_view_sampler,
)?;
}
DescriptorBindingResources::Sampler(elements) => {
check_resources(set_num, binding_num, reqs, elements, |_| Ok(()))?;
check_resources(set_num, binding_num, reqs, elements, check_sampler)?;
}
}
}
@ -162,12 +357,15 @@ fn check_resources<T>(
binding_num: u32,
reqs: &DescriptorRequirements,
elements: &[Option<T>],
mut extra_check: impl FnMut(&T) -> Result<(), InvalidDescriptorResource>,
mut extra_check: impl FnMut(u32, &T) -> Result<(), InvalidDescriptorResource>,
) -> Result<(), CheckDescriptorSetsValidityError> {
for (index, element) in elements[0..reqs.descriptor_count as usize]
.iter()
.enumerate()
{
let index = index as u32;
// VUID-vkCmdDispatch-None-02699
let element = match element {
Some(x) => x,
None => {
@ -175,19 +373,19 @@ fn check_resources<T>(
CheckDescriptorSetsValidityError::InvalidDescriptorResource {
set_num,
binding_num,
index: index as u32,
index,
error: InvalidDescriptorResource::Missing,
},
)
}
};
if let Err(error) = extra_check(element) {
if let Err(error) = extra_check(index, element) {
return Err(
CheckDescriptorSetsValidityError::InvalidDescriptorResource {
set_num,
binding_num,
index: index as u32,
index,
error,
},
);
@ -203,35 +401,95 @@ pub enum InvalidDescriptorResource {
required: Format,
obtained: Format,
},
ImageMultisampledMismatch {
ImageViewMultisampledMismatch {
required: bool,
obtained: bool,
},
ImageViewScalarTypeMismatch {
required: ShaderScalarType,
obtained: ShaderScalarType,
},
ImageViewTypeMismatch {
required: ImageViewType,
obtained: ImageViewType,
},
Missing,
SamplerCompareMismatch {
required: bool,
obtained: bool,
},
SamplerImageViewIncompatible {
image_view_set_num: u32,
image_view_binding_num: u32,
image_view_index: u32,
error: SamplerImageViewIncompatibleError,
},
SamplerUnnormalizedCoordinatesNotAllowed,
StorageImageAtomicNotSupported,
StorageReadWithoutFormatNotSupported,
StorageWriteWithoutFormatNotSupported,
}
impl error::Error for InvalidDescriptorResource {}
impl error::Error for InvalidDescriptorResource {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Self::SamplerImageViewIncompatible { error, .. } => Some(error),
_ => None,
}
}
}
impl fmt::Display for InvalidDescriptorResource {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self {
Self::Missing => {
write!(fmt, "no resource was bound")
}
Self::ImageViewFormatMismatch { required, obtained } => {
write!(fmt, "the bound image view did not have the required format; required {:?}, obtained {:?}", required, obtained)
}
Self::ImageMultisampledMismatch { required, obtained } => {
Self::ImageViewMultisampledMismatch { required, obtained } => {
write!(fmt, "the bound image did not have the required multisampling; required {}, obtained {}", required, obtained)
}
Self::ImageViewScalarTypeMismatch { required, obtained } => {
write!(fmt, "the bound image view did not have a format and aspect with the required scalar type; required {:?}, obtained {:?}", required, obtained)
}
Self::ImageViewTypeMismatch { required, obtained } => {
write!(fmt, "the bound image view did not have the required type; required {:?}, obtained {:?}", required, obtained)
}
Self::Missing => {
write!(fmt, "no resource was bound")
}
Self::SamplerImageViewIncompatible {
image_view_set_num,
image_view_binding_num,
image_view_index,
..
} => {
write!(
fmt,
"the bound sampler samples an image view that is not compatible with it"
)
}
Self::SamplerCompareMismatch { required, obtained } => {
write!(
fmt,
"the bound sampler did not have the required depth comparison state; required {}, obtained {}", required, obtained
)
}
Self::SamplerUnnormalizedCoordinatesNotAllowed => {
write!(
fmt,
"the bound sampler is required to have unnormalized coordinates disabled"
)
}
Self::StorageImageAtomicNotSupported => {
write!(fmt, "the bound image view did not support the `storage_image_atomic` format feature")
}
Self::StorageReadWithoutFormatNotSupported => {
write!(fmt, "the bound image view or buffer view did not support the `storage_read_without_format` format feature")
}
Self::StorageWriteWithoutFormatNotSupported => {
write!(fmt, "the bound image view or buffer view did not support the `storage_write_without_format` format feature")
}
}
}
}

View File

@ -311,14 +311,17 @@ impl DescriptorDesc {
let DescriptorRequirements {
descriptor_types,
descriptor_count,
format,
image_format,
image_multisampled,
image_scalar_type,
image_view_type,
multisampled,
mutable,
sampler_no_unnormalized,
sampler_compare,
sampler_no_unnormalized_coordinates,
sampler_with_images,
stages,
storage_image_atomic,
storage_read,
storage_write,
} = descriptor_requirements;
if !descriptor_types.contains(&self.ty) {

View File

@ -499,7 +499,7 @@ where
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[derive(Clone, Copy, Debug)]
pub enum DescriptorSetCreationError {
DescriptorSetUpdateError(DescriptorSetUpdateError),
OomError(OomError),

View File

@ -11,8 +11,9 @@ use crate::buffer::{BufferAccess, BufferInner, BufferViewAbstract};
use crate::descriptor_set::layout::{DescriptorDesc, DescriptorType};
use crate::descriptor_set::DescriptorSetLayout;
use crate::device::DeviceOwned;
use crate::image::ImageViewAbstract;
use crate::sampler::Sampler;
use crate::image::view::ImageViewType;
use crate::image::{ImageType, ImageViewAbstract};
use crate::sampler::{Sampler, SamplerImageViewIncompatibleError};
use crate::DeviceSize;
use crate::VulkanObject;
use smallvec::SmallVec;
@ -561,7 +562,8 @@ pub(crate) fn check_descriptor_write<'a>(
layout.device().internal_object(),
);
if !image_view.image().inner().image.usage().sampled {
// VUID-VkWriteDescriptorSet-descriptorType-00337
if !image_view.usage().sampled {
return Err(DescriptorSetUpdateError::MissingUsage {
binding: write.binding(),
index: descriptor_range_start + index as u32,
@ -569,10 +571,32 @@ pub(crate) fn check_descriptor_write<'a>(
});
}
if !sampler.can_sample(image_view.as_ref()) {
// VUID-VkDescriptorImageInfo-imageView-00343
if matches!(
image_view.ty(),
ImageViewType::Dim2d | ImageViewType::Dim2dArray
) && image_view.image().inner().image.dimensions().image_type()
== ImageType::Dim3d
{
return Err(DescriptorSetUpdateError::ImageView2dFrom3d {
binding: write.binding(),
index: descriptor_range_start + index as u32,
});
}
// VUID-VkDescriptorImageInfo-imageView-01976
if image_view.aspects().depth && image_view.aspects().stencil {
return Err(DescriptorSetUpdateError::ImageViewDepthAndStencil {
binding: write.binding(),
index: descriptor_range_start + index as u32,
});
}
if let Err(error) = sampler.check_can_sample(image_view.as_ref()) {
return Err(DescriptorSetUpdateError::ImageViewIncompatibleSampler {
binding: write.binding(),
index: descriptor_range_start + index as u32,
error,
});
}
}
@ -584,13 +608,35 @@ pub(crate) fn check_descriptor_write<'a>(
layout.device().internal_object(),
);
if !image_view.image().inner().image.usage().sampled {
// VUID-VkWriteDescriptorSet-descriptorType-00337
if !image_view.usage().sampled {
return Err(DescriptorSetUpdateError::MissingUsage {
binding: write.binding(),
index: descriptor_range_start + index as u32,
usage: "sampled",
});
}
// VUID-VkDescriptorImageInfo-imageView-00343
if matches!(
image_view.ty(),
ImageViewType::Dim2d | ImageViewType::Dim2dArray
) && image_view.image().inner().image.dimensions().image_type()
== ImageType::Dim3d
{
return Err(DescriptorSetUpdateError::ImageView2dFrom3d {
binding: write.binding(),
index: descriptor_range_start + index as u32,
});
}
// VUID-VkDescriptorImageInfo-imageView-01976
if image_view.aspects().depth && image_view.aspects().stencil {
return Err(DescriptorSetUpdateError::ImageViewDepthAndStencil {
binding: write.binding(),
index: descriptor_range_start + index as u32,
});
}
}
}
DescriptorType::StorageImage => {
@ -600,7 +646,8 @@ pub(crate) fn check_descriptor_write<'a>(
layout.device().internal_object(),
);
if !image_view.image().inner().image.usage().storage {
// VUID-VkWriteDescriptorSet-descriptorType-00339
if !image_view.usage().storage {
return Err(DescriptorSetUpdateError::MissingUsage {
binding: write.binding(),
index: descriptor_range_start + index as u32,
@ -608,6 +655,28 @@ pub(crate) fn check_descriptor_write<'a>(
});
}
// VUID-VkDescriptorImageInfo-imageView-00343
if matches!(
image_view.ty(),
ImageViewType::Dim2d | ImageViewType::Dim2dArray
) && image_view.image().inner().image.dimensions().image_type()
== ImageType::Dim3d
{
return Err(DescriptorSetUpdateError::ImageView2dFrom3d {
binding: write.binding(),
index: descriptor_range_start + index as u32,
});
}
// VUID-VkDescriptorImageInfo-imageView-01976
if image_view.aspects().depth && image_view.aspects().stencil {
return Err(DescriptorSetUpdateError::ImageViewDepthAndStencil {
binding: write.binding(),
index: descriptor_range_start + index as u32,
});
}
// VUID-VkWriteDescriptorSet-descriptorType-00336
if !image_view.component_mapping().is_identity() {
return Err(DescriptorSetUpdateError::ImageViewNotIdentitySwizzled {
binding: write.binding(),
@ -623,7 +692,8 @@ pub(crate) fn check_descriptor_write<'a>(
layout.device().internal_object(),
);
if !image_view.image().inner().image.usage().input_attachment {
// VUID-VkWriteDescriptorSet-descriptorType-00338
if !image_view.usage().input_attachment {
return Err(DescriptorSetUpdateError::MissingUsage {
binding: write.binding(),
index: descriptor_range_start + index as u32,
@ -631,6 +701,28 @@ pub(crate) fn check_descriptor_write<'a>(
});
}
// VUID-VkDescriptorImageInfo-imageView-00343
if matches!(
image_view.ty(),
ImageViewType::Dim2d | ImageViewType::Dim2dArray
) && image_view.image().inner().image.dimensions().image_type()
== ImageType::Dim3d
{
return Err(DescriptorSetUpdateError::ImageView2dFrom3d {
binding: write.binding(),
index: descriptor_range_start + index as u32,
});
}
// VUID-VkDescriptorImageInfo-imageView-01976
if image_view.aspects().depth && image_view.aspects().stencil {
return Err(DescriptorSetUpdateError::ImageViewDepthAndStencil {
binding: write.binding(),
index: descriptor_range_start + index as u32,
});
}
// VUID-VkWriteDescriptorSet-descriptorType-00336
if !image_view.component_mapping().is_identity() {
return Err(DescriptorSetUpdateError::ImageViewNotIdentitySwizzled {
binding: write.binding(),
@ -638,9 +730,7 @@ pub(crate) fn check_descriptor_write<'a>(
});
}
let image_layers = image_view.array_layers();
let num_layers = image_layers.end - image_layers.start;
// VUID??
if image_view.ty().is_arrayed() {
return Err(DescriptorSetUpdateError::ImageViewIsArrayed {
binding: write.binding(),
@ -673,7 +763,8 @@ pub(crate) fn check_descriptor_write<'a>(
layout.device().internal_object(),
);
if !image_view.image().inner().image.usage().sampled {
// VUID-VkWriteDescriptorSet-descriptorType-00337
if !image_view.usage().sampled {
return Err(DescriptorSetUpdateError::MissingUsage {
binding: write.binding(),
index: descriptor_range_start + index as u32,
@ -681,10 +772,32 @@ pub(crate) fn check_descriptor_write<'a>(
});
}
if !sampler.can_sample(image_view.as_ref()) {
// VUID-VkDescriptorImageInfo-imageView-00343
if matches!(
image_view.ty(),
ImageViewType::Dim2d | ImageViewType::Dim2dArray
) && image_view.image().inner().image.dimensions().image_type()
== ImageType::Dim3d
{
return Err(DescriptorSetUpdateError::ImageView2dFrom3d {
binding: write.binding(),
index: descriptor_range_start + index as u32,
});
}
// VUID-VkDescriptorImageInfo-imageView-01976
if image_view.aspects().depth && image_view.aspects().stencil {
return Err(DescriptorSetUpdateError::ImageViewDepthAndStencil {
binding: write.binding(),
index: descriptor_range_start + index as u32,
});
}
if let Err(error) = sampler.check_can_sample(image_view.as_ref()) {
return Err(DescriptorSetUpdateError::ImageViewIncompatibleSampler {
binding: write.binding(),
index: descriptor_range_start + index as u32,
error,
});
}
}
@ -721,7 +834,7 @@ pub(crate) fn check_descriptor_write<'a>(
Ok(layout_binding)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[derive(Clone, Copy, Debug)]
pub enum DescriptorSetUpdateError {
/// Tried to write more elements than were available in a binding.
ArrayIndexOutOfBounds {
@ -733,13 +846,23 @@ pub enum DescriptorSetUpdateError {
written_count: u32,
},
/// Tried to write an image view with a 2D type and a 3D underlying image.
ImageView2dFrom3d { binding: u32, index: u32 },
/// Tried to write an image view that has both the `depth` and `stencil` aspects.
ImageViewDepthAndStencil { binding: u32, index: u32 },
/// Tried to write an image view of an arrayed type to a descriptor type that does not support
/// it.
ImageViewIsArrayed { binding: u32, index: u32 },
/// Tried to write an image view that was not compatible with the sampler that was provided as
/// part of the update or immutably in the layout.
ImageViewIncompatibleSampler { binding: u32, index: u32 },
ImageViewIncompatibleSampler {
binding: u32,
index: u32,
error: SamplerImageViewIncompatibleError,
},
/// Tried to write an image view to a descriptor type that requires it to be identity swizzled,
/// but it was not.
@ -763,7 +886,14 @@ pub enum DescriptorSetUpdateError {
SamplerIsImmutable { binding: u32 },
}
impl std::error::Error for DescriptorSetUpdateError {}
impl std::error::Error for DescriptorSetUpdateError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::ImageViewIncompatibleSampler { error, .. } => Some(error),
_ => None,
}
}
}
impl std::fmt::Display for DescriptorSetUpdateError {
#[inline]
@ -778,12 +908,22 @@ impl std::fmt::Display for DescriptorSetUpdateError {
"tried to write up to element {} to binding {}, but only {} descriptors are available",
written_count, binding, available_count,
),
Self::ImageView2dFrom3d { binding, index } => write!(
fmt,
"tried to write an image view to binding {} index {} with a 2D type and a 3D underlying image",
binding, index,
),
Self::ImageViewDepthAndStencil { binding, index } => write!(
fmt,
"tried to write an image view to binding {} index {} that has both the `depth` and `stencil` aspects",
binding, index,
),
Self::ImageViewIsArrayed { binding, index } => write!(
fmt,
"tried to write an image view of an arrayed type to binding {} index {}, but this binding has a descriptor type that does not support arrayed image views",
binding, index,
),
Self::ImageViewIncompatibleSampler { binding, index } => write!(
Self::ImageViewIncompatibleSampler { binding, index, .. } => write!(
fmt,
"tried to write an image view to binding {} index {}, that was not compatible with the sampler that was provided as part of the update or immutably in the layout",
binding, index,

View File

@ -9,6 +9,7 @@
use crate::device::{DeviceExtensions, Features, FeaturesFfi, Properties, PropertiesFfi};
use crate::format::Format;
use crate::image::view::ImageViewType;
use crate::image::{ImageCreateFlags, ImageTiling, ImageType, ImageUsage, SampleCounts};
use crate::instance::{Instance, InstanceCreationError};
use crate::memory::ExternalMemoryHandleType;
@ -494,7 +495,10 @@ impl<'a> PhysicalDevice<'a> {
usage: ImageUsage,
flags: ImageCreateFlags,
external_memory_handle_type: Option<ExternalMemoryHandleType>,
image_view_type: Option<ImageViewType>,
) -> Result<Option<ImageFormatProperties>, OomError> {
/* Input */
let mut format_info2 = ash::vk::PhysicalDeviceImageFormatInfo2::builder()
.format(format.into())
.ty(ty.into())
@ -526,7 +530,40 @@ impl<'a> PhysicalDevice<'a> {
format_info2 = format_info2.push_next(next);
}
let mut image_view_image_format_info = if let Some(image_view_type) = image_view_type {
if !self.supported_extensions().ext_filter_cubic {
// Can't query this, return unsupported
return Ok(None);
}
if !image_view_type.is_compatible_with(ty) {
return Ok(None);
}
Some(
ash::vk::PhysicalDeviceImageViewImageFormatInfoEXT::builder()
.image_view_type(image_view_type.into()),
)
} else {
None
};
if let Some(next) = image_view_image_format_info.as_mut() {
format_info2 = format_info2.push_next(next);
}
/* Output */
let mut image_format_properties2 = ash::vk::ImageFormatProperties2::default();
let mut filter_cubic_image_view_image_format_properties =
ash::vk::FilterCubicImageViewImageFormatPropertiesEXT::default();
if image_view_type.is_some() {
filter_cubic_image_view_image_format_properties.p_next =
image_format_properties2.p_next;
image_format_properties2.p_next =
&mut filter_cubic_image_view_image_format_properties as *mut _ as *mut _;
}
let result = unsafe {
let fns = self.instance.fns();
@ -567,9 +604,14 @@ impl<'a> PhysicalDevice<'a> {
};
match result {
Ok(_) => Ok(Some(
image_format_properties2.image_format_properties.into(),
)),
Ok(_) => Ok(Some(ImageFormatProperties {
filter_cubic: filter_cubic_image_view_image_format_properties.filter_cubic
!= ash::vk::FALSE,
filter_cubic_minmax: filter_cubic_image_view_image_format_properties
.filter_cubic_minmax
!= ash::vk::FALSE,
..image_format_properties2.image_format_properties.into()
})),
Err(Error::FormatNotSupported) => Ok(None),
Err(err) => Err(err.into()),
}
@ -1307,6 +1349,14 @@ pub struct ImageFormatProperties {
/// The maximum total size of an image, in bytes. This is guaranteed to be at least
/// 0x80000000.
pub max_resource_size: DeviceSize,
/// When querying with an image view type, whether such image views support sampling with
/// a [`Cubic`](crate::sampler::Filter::Cubic) `mag_filter` or `min_filter`.
pub filter_cubic: bool,
/// When querying with an image view type, whether such image views support sampling with
/// a [`Cubic`](crate::sampler::Filter::Cubic) `mag_filter` or `min_filter`, and with a
/// [`Min`](crate::sampler::SamplerReductionMode::Min) or
/// [`Max`](crate::sampler::SamplerReductionMode::Max) `reduction_mode`.
pub filter_cubic_minmax: bool,
}
impl From<ash::vk::ImageFormatProperties> for ImageFormatProperties {
@ -1321,6 +1371,8 @@ impl From<ash::vk::ImageFormatProperties> for ImageFormatProperties {
max_array_layers: props.max_array_layers,
sample_counts: props.sample_counts.into(),
max_resource_size: props.max_resource_size,
filter_cubic: false,
filter_cubic_minmax: false,
}
}
}

View File

@ -113,6 +113,58 @@ impl Format {
physical_device.format_properties(*self)
}
/// Returns whether the format can be used with a storage image, without specifying
/// the format in the shader, if the
/// [`shader_storage_image_read_without_format`](crate::device::Features::shader_storage_image_read_without_format)
/// and/or
/// [`shader_storage_image_write_without_format`](crate::device::Features::shader_storage_image_write_without_format)
/// features are enabled on the device.
#[inline]
pub fn shader_storage_image_without_format(&self) -> bool {
matches!(
*self,
Format::R8G8B8A8_UNORM
| Format::R8G8B8A8_SNORM
| Format::R8G8B8A8_UINT
| Format::R8G8B8A8_SINT
| Format::R32_UINT
| Format::R32_SINT
| Format::R32_SFLOAT
| Format::R32G32_UINT
| Format::R32G32_SINT
| Format::R32G32_SFLOAT
| Format::R32G32B32A32_UINT
| Format::R32G32B32A32_SINT
| Format::R32G32B32A32_SFLOAT
| Format::R16G16B16A16_UINT
| Format::R16G16B16A16_SINT
| Format::R16G16B16A16_SFLOAT
| Format::R16G16_SFLOAT
| Format::B10G11R11_UFLOAT_PACK32
| Format::R16_SFLOAT
| Format::R16G16B16A16_UNORM
| Format::A2B10G10R10_UNORM_PACK32
| Format::R16G16_UNORM
| Format::R8G8_UNORM
| Format::R16_UNORM
| Format::R8_UNORM
| Format::R16G16B16A16_SNORM
| Format::R16G16_SNORM
| Format::R8G8_SNORM
| Format::R16_SNORM
| Format::R8_SNORM
| Format::R16G16_SINT
| Format::R8G8_SINT
| Format::R16_SINT
| Format::R8_SINT
| Format::A2B10G10R10_UINT_PACK32
| Format::R16G16_UINT
| Format::R8G8_UINT
| Format::R16_UINT
| Format::R8_UINT
)
}
#[inline]
pub fn decode_clear_value(&self, value: ClearValue) -> ClearValue {
let aspects = self.aspects();
@ -156,6 +208,7 @@ impl From<Format> for ash::vk::Format {
}
}
// https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap46.html#spirvenv-image-formats
impl From<ImageFormat> for Option<Format> {
fn from(val: ImageFormat) -> Self {
match val {

View File

@ -66,6 +66,32 @@ impl ImageAspects {
memory_plane2: false,
}
}
pub const fn contains(&self, other: &Self) -> bool {
let Self {
color,
depth,
stencil,
metadata,
plane0,
plane1,
plane2,
memory_plane0,
memory_plane1,
memory_plane2,
} = *self;
(color || !other.color)
&& (depth || !other.depth)
&& (stencil || !other.stencil)
&& (metadata || !other.metadata)
&& (plane0 || !other.plane0)
&& (plane1 || !other.plane1)
&& (plane2 || !other.plane2)
&& (memory_plane0 || !other.memory_plane0)
&& (memory_plane1 || !other.memory_plane1)
&& (memory_plane2 || !other.memory_plane2)
}
}
impl BitOr for ImageAspects {

View File

@ -128,32 +128,18 @@ fn generate_mipmaps<L>(
for level in 1..image.mip_levels() {
for layer in 0..image.dimensions().array_layers() {
let [xs, ys, ds] = dimensions
.mipmap_dimensions(level - 1)
.mip_level_dimensions(level - 1)
.unwrap()
.width_height_depth();
let [xd, yd, dd] = dimensions
.mipmap_dimensions(level)
.mip_level_dimensions(level)
.unwrap()
.width_height_depth();
let src = SubImage::new(
image.clone(),
level - 1,
1,
layer,
1,
layout,
);
let dst = SubImage::new(
image.clone(),
level,
1,
layer,
1,
layout,
);
let src = SubImage::new(image.clone(), level - 1, 1, layer, 1, layout);
let dst = SubImage::new(image.clone(), level, 1, layer, 1, layout);
cbb.blit_image(
src, //source
[0, 0, 0], //source_top_left

View File

@ -415,9 +415,18 @@ impl ImageDimensions {
self.width() * self.height() * self.depth() * self.array_layers()
}
/// Returns the maximum number of mipmaps for these image dimensions.
#[inline]
pub fn image_type(&self) -> ImageType {
match *self {
ImageDimensions::Dim1d { .. } => ImageType::Dim1d,
ImageDimensions::Dim2d { .. } => ImageType::Dim2d,
ImageDimensions::Dim3d { .. } => ImageType::Dim3d,
}
}
/// Returns the maximum number of mipmap levels for these image dimensions.
///
/// The returned value is always at least superior or equal to 1.
/// The returned value is always at least 1.
///
/// # Example
///
@ -430,17 +439,28 @@ impl ImageDimensions {
/// array_layers: 1,
/// };
///
/// assert_eq!(dims.max_mipmaps(), 6);
/// assert_eq!(dims.max_mip_levels(), 6);
/// ```
///
pub fn max_mipmaps(&self) -> u32 {
32 - (self.width() | self.height() | self.depth()).leading_zeros()
#[inline]
pub fn max_mip_levels(&self) -> u32 {
// This calculates `log2(max(width, height, depth)) + 1` using fast integer operations.
let max = match *self {
ImageDimensions::Dim1d { width, .. } => width,
ImageDimensions::Dim2d { width, height, .. } => width | height,
ImageDimensions::Dim3d {
width,
height,
depth,
} => width | height | depth,
};
32 - max.leading_zeros()
}
/// Returns the dimensions of the `level`th mipmap level. If `level` is 0, then the dimensions
/// are left unchanged.
///
/// Returns `None` if `level` is superior or equal to `max_mipmaps()`.
/// Returns `None` if `level` is superior or equal to `max_mip_levels()`.
///
/// # Example
///
@ -453,23 +473,23 @@ impl ImageDimensions {
/// array_layers: 1,
/// };
///
/// assert_eq!(dims.mipmap_dimensions(0), Some(dims));
/// assert_eq!(dims.mipmap_dimensions(1), Some(ImageDimensions::Dim2d {
/// assert_eq!(dims.mip_level_dimensions(0), Some(dims));
/// assert_eq!(dims.mip_level_dimensions(1), Some(ImageDimensions::Dim2d {
/// width: 481,
/// height: 128,
/// array_layers: 1,
/// }));
/// assert_eq!(dims.mipmap_dimensions(6), Some(ImageDimensions::Dim2d {
/// assert_eq!(dims.mip_level_dimensions(6), Some(ImageDimensions::Dim2d {
/// width: 15,
/// height: 4,
/// array_layers: 1,
/// }));
/// assert_eq!(dims.mipmap_dimensions(9), Some(ImageDimensions::Dim2d {
/// assert_eq!(dims.mip_level_dimensions(9), Some(ImageDimensions::Dim2d {
/// width: 1,
/// height: 1,
/// array_layers: 1,
/// }));
/// assert_eq!(dims.mipmap_dimensions(11), None);
/// assert_eq!(dims.mip_level_dimensions(11), None);
/// ```
///
/// # Panic
@ -477,12 +497,12 @@ impl ImageDimensions {
/// In debug mode, Panics if `width`, `height` or `depth` is equal to 0. In release, returns
/// an unspecified value.
///
pub fn mipmap_dimensions(&self, level: u32) -> Option<ImageDimensions> {
pub fn mip_level_dimensions(&self, level: u32) -> Option<ImageDimensions> {
if level == 0 {
return Some(*self);
}
if level >= self.max_mipmaps() {
if level >= self.max_mip_levels() {
return None;
}
@ -538,39 +558,39 @@ mod tests {
use crate::image::MipmapsCount;
#[test]
fn max_mipmaps() {
fn max_mip_levels() {
let dims = ImageDimensions::Dim2d {
width: 2,
height: 1,
array_layers: 1,
};
assert_eq!(dims.max_mipmaps(), 2);
assert_eq!(dims.max_mip_levels(), 2);
let dims = ImageDimensions::Dim2d {
width: 2,
height: 3,
array_layers: 1,
};
assert_eq!(dims.max_mipmaps(), 2);
assert_eq!(dims.max_mip_levels(), 2);
let dims = ImageDimensions::Dim2d {
width: 512,
height: 512,
array_layers: 1,
};
assert_eq!(dims.max_mipmaps(), 10);
assert_eq!(dims.max_mip_levels(), 10);
}
#[test]
fn mipmap_dimensions() {
fn mip_level_dimensions() {
let dims = ImageDimensions::Dim2d {
width: 283,
height: 175,
array_layers: 1,
};
assert_eq!(dims.mipmap_dimensions(0), Some(dims));
assert_eq!(dims.mip_level_dimensions(0), Some(dims));
assert_eq!(
dims.mipmap_dimensions(1),
dims.mip_level_dimensions(1),
Some(ImageDimensions::Dim2d {
width: 141,
height: 87,
@ -578,7 +598,7 @@ mod tests {
})
);
assert_eq!(
dims.mipmap_dimensions(2),
dims.mip_level_dimensions(2),
Some(ImageDimensions::Dim2d {
width: 70,
height: 43,
@ -586,7 +606,7 @@ mod tests {
})
);
assert_eq!(
dims.mipmap_dimensions(3),
dims.mip_level_dimensions(3),
Some(ImageDimensions::Dim2d {
width: 35,
height: 21,
@ -595,7 +615,7 @@ mod tests {
);
assert_eq!(
dims.mipmap_dimensions(4),
dims.mip_level_dimensions(4),
Some(ImageDimensions::Dim2d {
width: 17,
height: 10,
@ -603,7 +623,7 @@ mod tests {
})
);
assert_eq!(
dims.mipmap_dimensions(5),
dims.mip_level_dimensions(5),
Some(ImageDimensions::Dim2d {
width: 8,
height: 5,
@ -611,7 +631,7 @@ mod tests {
})
);
assert_eq!(
dims.mipmap_dimensions(6),
dims.mip_level_dimensions(6),
Some(ImageDimensions::Dim2d {
width: 4,
height: 2,
@ -619,7 +639,7 @@ mod tests {
})
);
assert_eq!(
dims.mipmap_dimensions(7),
dims.mip_level_dimensions(7),
Some(ImageDimensions::Dim2d {
width: 2,
height: 1,
@ -627,14 +647,14 @@ mod tests {
})
);
assert_eq!(
dims.mipmap_dimensions(8),
dims.mip_level_dimensions(8),
Some(ImageDimensions::Dim2d {
width: 1,
height: 1,
array_layers: 1,
})
);
assert_eq!(dims.mipmap_dimensions(9), None);
assert_eq!(dims.mip_level_dimensions(9), None);
}
#[test]

View File

@ -510,7 +510,7 @@ impl UnsafeImageBuilder {
// Compute the number of mip levels
let mip_levels = match mip_levels.into() {
MipmapsCount::Specific(num) => num,
MipmapsCount::Log2 => dimensions.max_mipmaps(),
MipmapsCount::Log2 => dimensions.max_mip_levels(),
MipmapsCount::One => 1,
};
@ -533,9 +533,10 @@ impl UnsafeImageBuilder {
};
// Check mip levels
let max_mip_levels = dimensions.max_mipmaps();
let max_mip_levels = dimensions.max_mip_levels();
debug_assert!(max_mip_levels >= 1);
// VUID-VkImageCreateInfo-mipLevels-00958
if mip_levels > max_mip_levels {
return Err(ImageCreationError::MaxMipLevelsExceeded {
mip_levels,
@ -543,7 +544,7 @@ impl UnsafeImageBuilder {
});
}
// Check limits for multisampled images
// VUID-VkImageCreateInfo-samples-02257
if samples != SampleCount::Sample1 {
if image_type != ImageType::Dim2d {
return Err(ImageCreationError::MultisampleNot2d);
@ -564,18 +565,22 @@ impl UnsafeImageBuilder {
// Check limits for YCbCr formats
if let Some(chroma_sampling) = format.ycbcr_chroma_sampling() {
// VUID-VkImageCreateInfo-format-06410
if mip_levels != 1 {
return Err(ImageCreationError::YcbcrFormatMultipleMipLevels);
}
// VUID-VkImageCreateInfo-format-06411
if samples != SampleCount::Sample1 {
return Err(ImageCreationError::YcbcrFormatMultisampling);
}
// VUID-VkImageCreateInfo-format-06412
if image_type != ImageType::Dim2d {
return Err(ImageCreationError::YcbcrFormatNot2d);
}
// VUID-VkImageCreateInfo-format-06413
if array_layers > 1 && !device.enabled_features().ycbcr_image_arrays {
return Err(ImageCreationError::FeatureNotEnabled {
feature: "ycbcr_image_arrays",
@ -586,11 +591,14 @@ impl UnsafeImageBuilder {
match chroma_sampling {
ChromaSampling::Mode444 => (),
ChromaSampling::Mode422 => {
// VUID-VkImageCreateInfo-format-04712
if !(extent[0] % 2 == 0) {
return Err(ImageCreationError::YcbcrFormatInvalidDimensions);
}
}
ChromaSampling::Mode420 => {
// VUID-VkImageCreateInfo-format-04712
// VUID-VkImageCreateInfo-format-04713
if !(extent[0] % 2 == 0 && extent[1] % 2 == 0) {
return Err(ImageCreationError::YcbcrFormatInvalidDimensions);
}
@ -624,6 +632,8 @@ impl UnsafeImageBuilder {
});
}
// VUID-VkImageCreateInfo-usage-00964
// VUID-VkImageCreateInfo-usage-00965
if (usage.color_attachment
|| usage.depth_stencil_attachment
|| usage.input_attachment
@ -645,8 +655,7 @@ impl UnsafeImageBuilder {
return Err(ImageCreationError::FormatUsageNotSupported { usage: "storage" });
}
// If the `shaderStorageImageMultisample` feature is not enabled and we have
// `usage_storage` set to true, then the number of samples must be 1.
// VUID-VkImageCreateInfo-usage-00968
if samples != SampleCount::Sample1
&& !device.enabled_features().shader_storage_image_multisample
{
@ -674,30 +683,31 @@ impl UnsafeImageBuilder {
/* Check flags requirements */
if flags.cube_compatible {
// VUID-VkImageCreateInfo-flags-00949
if image_type != ImageType::Dim2d {
return Err(ImageCreationError::CubeCompatibleNot2d);
}
// VUID-VkImageCreateInfo-imageType-00954
if extent[0] != extent[1] {
return Err(ImageCreationError::CubeCompatibleNotSquare);
}
// VUID-VkImageCreateInfo-imageType-00954
if array_layers < 6 {
return Err(ImageCreationError::CubeCompatibleNotEnoughArrayLayers);
}
if samples != SampleCount::Sample1 {
return Err(ImageCreationError::CubeCompatibleMultisampling);
}
}
if flags.array_2d_compatible {
// VUID-VkImageCreateInfo-flags-00950
if image_type != ImageType::Dim3d {
return Err(ImageCreationError::Array2dCompatibleNot3d);
}
}
if flags.block_texel_view_compatible {
// VUID-VkImageCreateInfo-flags-01572
if format.compression().is_none() {
return Err(ImageCreationError::BlockTexelViewCompatibleNotCompressed);
}
@ -716,6 +726,7 @@ impl UnsafeImageBuilder {
debug_assert!(ids.len() >= 2);
for &id in ids {
// VUID-VkImageCreateInfo-sharingMode-01420
if device.physical_device().queue_family_by_id(id).is_none() {
return Err(ImageCreationError::SharingInvalidQueueFamilyId { id });
}
@ -737,6 +748,7 @@ impl UnsafeImageBuilder {
});
}
// VUID-VkImageCreateInfo-pNext-01443
if initial_layout != ImageLayout::Undefined {
return Err(ImageCreationError::ExternalMemoryInvalidInitialLayout);
}
@ -940,6 +952,7 @@ impl UnsafeImageBuilder {
usage,
flags,
handle_type,
None,
)?;
let ImageFormatProperties {
@ -948,11 +961,15 @@ impl UnsafeImageBuilder {
max_array_layers,
sample_counts,
max_resource_size,
..
} = match image_format_properties {
Some(x) => x,
None => return Err(ImageCreationError::ImageFormatPropertiesNotSupported),
};
// VUID-VkImageCreateInfo-extent-02252
// VUID-VkImageCreateInfo-extent-02253
// VUID-VkImageCreateInfo-extent-02254
if extent[0] > max_extent[0]
|| extent[1] > max_extent[1]
|| extent[2] > max_extent[2]
@ -963,6 +980,7 @@ impl UnsafeImageBuilder {
});
}
// VUID-VkImageCreateInfo-mipLevels-02255
if mip_levels > max_mip_levels {
return Err(ImageCreationError::MaxMipLevelsExceeded {
mip_levels,
@ -970,6 +988,7 @@ impl UnsafeImageBuilder {
});
}
// VUID-VkImageCreateInfo-arrayLayers-02256
if array_layers > max_array_layers {
return Err(ImageCreationError::MaxArrayLayersExceeded {
array_layers,
@ -977,6 +996,7 @@ impl UnsafeImageBuilder {
});
}
// VUID-VkImageCreateInfo-samples-02258
if !sample_counts.contains(samples) {
return Err(ImageCreationError::SampleCountNotSupported {
samples,
@ -1056,9 +1076,17 @@ impl UnsafeImageBuilder {
#[inline]
pub fn dimensions(mut self, dimensions: ImageDimensions) -> Self {
let extent = dimensions.width_height_depth();
// VUID-VkImageCreateInfo-extent-00944
assert!(extent[0] != 0);
// VUID-VkImageCreateInfo-extent-00945
assert!(extent[1] != 0);
// VUID-VkImageCreateInfo-extent-00946
assert!(extent[2] != 0);
// VUID-VkImageCreateInfo-arrayLayers-00948
assert!(dimensions.array_layers() != 0);
self.dimensions = Some(dimensions);
@ -1088,7 +1116,9 @@ impl UnsafeImageBuilder {
/// - Panics if `flags` contains `block_texel_view_compatible` but not `mutable_format`.
#[inline]
pub fn flags(mut self, flags: ImageCreateFlags) -> Self {
// VUID-VkImageCreateInfo-flags-01573
assert!(!(flags.block_texel_view_compatible && !flags.mutable_format));
self.flags = flags;
self
}
@ -1113,10 +1143,12 @@ impl UnsafeImageBuilder {
/// [`Preinitialized`](ImageLayout::Preinitialized).
#[inline]
pub fn initial_layout(mut self, layout: ImageLayout) -> Self {
// VUID-VkImageCreateInfo-initialLayout-00993
assert!(matches!(
layout,
ImageLayout::Undefined | ImageLayout::Preinitialized
));
self.initial_layout = layout;
self
}
@ -1134,7 +1166,10 @@ impl UnsafeImageBuilder {
M: Into<MipmapsCount>,
{
let mip_levels = mip_levels.into();
// VUID-VkImageCreateInfo-mipLevels-00947
assert!(!matches!(mip_levels, MipmapsCount::Specific(0)));
self.mip_levels = mip_levels;
self
}
@ -1164,9 +1199,12 @@ impl UnsafeImageBuilder {
Sharing::Exclusive => Sharing::Exclusive,
Sharing::Concurrent(ids) => {
let mut ids: SmallVec<[u32; 4]> = ids.into_iter().collect();
// VUID-VkImageCreateInfo-sharingMode-00942
ids.sort_unstable();
ids.dedup();
assert!(ids.len() >= 2);
Sharing::Concurrent(ids)
}
};
@ -1194,15 +1232,16 @@ impl UnsafeImageBuilder {
/// other than these.
#[inline]
pub fn usage(mut self, usage: ImageUsage) -> Self {
// VUID-VkImageCreateInfo-usage-requiredbitmask
assert!(usage != ImageUsage::none());
if usage.transient_attachment {
// At least one of these must also be set
// VUID-VkImageCreateInfo-usage-00966
assert!(
usage.color_attachment || usage.depth_stencil_attachment || usage.input_attachment
);
// And no others must be set
// VUID-VkImageCreateInfo-usage-00963
assert!(
ImageUsage {
transient_attachment: false,

File diff suppressed because it is too large Load Diff

View File

@ -20,6 +20,7 @@
pub use self::compute::ComputePipeline;
pub use self::graphics::GraphicsPipeline;
pub use self::layout::PipelineLayout;
use crate::device::DeviceOwned;
use std::sync::Arc;
pub mod cache;
@ -28,7 +29,7 @@ pub mod graphics;
pub mod layout;
/// A trait for operations shared between pipeline types.
pub trait Pipeline {
pub trait Pipeline: DeviceOwned {
/// Returns the bind point of this pipeline.
fn bind_point(&self) -> PipelineBindPoint;

View File

@ -64,7 +64,7 @@ where
.any(|&(n, _)| n == attachment_num)
{
debug_assert!(aspects.color); // Was normally checked by the render pass.
if !image_view.image().inner().image.usage().color_attachment {
if !image_view.usage().color_attachment {
return Err(IncompatibleRenderPassAttachmentError::MissingColorAttachmentUsage);
}
}
@ -73,13 +73,7 @@ where
if ds == attachment_num {
// Was normally checked by the render pass.
debug_assert!(aspects.depth || aspects.stencil);
if !image_view
.image()
.inner()
.image
.usage()
.depth_stencil_attachment
{
if !image_view.usage().depth_stencil_attachment {
return Err(
IncompatibleRenderPassAttachmentError::MissingDepthStencilAttachmentUsage,
);
@ -92,7 +86,7 @@ where
.iter()
.any(|&(n, _)| n == attachment_num)
{
if !image_view.image().inner().image.usage().input_attachment {
if !image_view.usage().input_attachment {
return Err(IncompatibleRenderPassAttachmentError::MissingInputAttachmentUsage);
}
}

View File

@ -47,8 +47,10 @@
use crate::check_errors;
use crate::device::Device;
use crate::device::DeviceOwned;
use crate::image::view::ImageViewType;
use crate::image::ImageViewAbstract;
use crate::pipeline::graphics::depth_stencil::CompareOp;
use crate::shader::ShaderScalarType;
use crate::Error;
use crate::OomError;
use crate::VulkanObject;
@ -89,11 +91,14 @@ use std::sync::Arc;
pub struct Sampler {
handle: ash::vk::Sampler,
device: Arc<Device>,
compare_mode: bool,
unnormalized: bool,
usable_with_float_formats: bool,
usable_with_int_formats: bool,
usable_with_swizzling: bool,
border_color: Option<BorderColor>,
compare: Option<CompareOp>,
mag_filter: Filter,
min_filter: Filter,
mipmap_mode: SamplerMipmapMode,
reduction_mode: SamplerReductionMode,
unnormalized_coordinates: bool,
}
impl Sampler {
@ -147,44 +152,195 @@ impl Sampler {
.build()
}
/// Returns whether this sampler is allowed to sample `image_view`.
pub fn can_sample<I>(&self, image_view: &I) -> bool
/// Checks whether this sampler is compatible with `image_view`.
pub fn check_can_sample<I>(
&self,
image_view: &I,
) -> Result<(), SamplerImageViewIncompatibleError>
where
I: ImageViewAbstract + ?Sized,
{
// TODO: many more things need to be tested here
/*
Note: Most of these checks come from the Instruction/Sampler/Image View Validation
section, and are not strictly VUIDs.
https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap16.html#textures-input-validation
*/
true
if self.compare.is_some() {
// VUID-vkCmdDispatch-None-06479
if !image_view.format_features().sampled_image_depth_comparison {
return Err(SamplerImageViewIncompatibleError::DepthComparisonNotSupported);
}
// The SPIR-V instruction is one of the OpImage*Dref* instructions, the image
// view format is one of the depth/stencil formats, and the image view aspect
// is not VK_IMAGE_ASPECT_DEPTH_BIT.
if !image_view.aspects().depth {
return Err(SamplerImageViewIncompatibleError::DepthComparisonWrongAspect);
}
} else {
if !image_view.format_features().sampled_image_filter_linear {
// VUID-vkCmdDispatch-magFilter-04553
if self.mag_filter == Filter::Linear || self.min_filter == Filter::Linear {
return Err(SamplerImageViewIncompatibleError::FilterLinearNotSupported);
}
// VUID-vkCmdDispatch-mipmapMode-04770
if self.mipmap_mode == SamplerMipmapMode::Linear {
return Err(SamplerImageViewIncompatibleError::MipmapModeLinearNotSupported);
}
}
}
if self.mag_filter == Filter::Cubic || self.min_filter == Filter::Cubic {
// VUID-vkCmdDispatch-None-02692
if !image_view.format_features().sampled_image_filter_cubic {
return Err(SamplerImageViewIncompatibleError::FilterCubicNotSupported);
}
// VUID-vkCmdDispatch-filterCubic-02694
if !image_view.filter_cubic() {
return Err(SamplerImageViewIncompatibleError::FilterCubicNotSupported);
}
// VUID-vkCmdDispatch-filterCubicMinmax-02695
if matches!(
self.reduction_mode,
SamplerReductionMode::Min | SamplerReductionMode::Max
) && !image_view.filter_cubic_minmax()
{
return Err(SamplerImageViewIncompatibleError::FilterCubicMinmaxNotSupported);
}
}
if let Some(border_color) = self.border_color {
let aspects = image_view.aspects();
let view_scalar_type = ShaderScalarType::from(
if aspects.color || aspects.plane0 || aspects.plane1 || aspects.plane2 {
image_view.format().type_color().unwrap()
} else if aspects.depth {
image_view.format().type_depth().unwrap()
} else if aspects.stencil {
image_view.format().type_stencil().unwrap()
} else {
// Per `ImageViewBuilder::aspects` and
// VUID-VkDescriptorImageInfo-imageView-01976
unreachable!()
},
);
match border_color {
BorderColor::IntTransparentBlack
| BorderColor::IntOpaqueBlack
| BorderColor::IntOpaqueWhite => {
// The sampler borderColor is an integer type and the image view
// format is not one of the VkFormat integer types or a stencil
// component of a depth/stencil format.
if !matches!(
view_scalar_type,
ShaderScalarType::Sint | ShaderScalarType::Uint
) {
return Err(
SamplerImageViewIncompatibleError::BorderColorFormatNotCompatible,
);
}
}
BorderColor::FloatTransparentBlack
| BorderColor::FloatOpaqueBlack
| BorderColor::FloatOpaqueWhite => {
// The sampler borderColor is a float type and the image view
// format is not one of the VkFormat float types or a depth
// component of a depth/stencil format.
if !matches!(view_scalar_type, ShaderScalarType::Float) {
return Err(
SamplerImageViewIncompatibleError::BorderColorFormatNotCompatible,
);
}
}
}
// The sampler borderColor is one of the opaque black colors
// (VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK or VK_BORDER_COLOR_INT_OPAQUE_BLACK)
// and the image view VkComponentSwizzle for any of the VkComponentMapping
// components is not the identity swizzle, and
// VkPhysicalDeviceBorderColorSwizzleFeaturesEXT::borderColorSwizzleFromImage
// feature is not enabled, and
// VkSamplerBorderColorComponentMappingCreateInfoEXT is not specified.
if matches!(
border_color,
BorderColor::FloatOpaqueBlack | BorderColor::IntOpaqueBlack
) && !image_view.component_mapping().is_identity()
{
return Err(
SamplerImageViewIncompatibleError::BorderColorOpaqueBlackNotIdentitySwizzled,
);
}
}
// The sampler unnormalizedCoordinates is VK_TRUE and any of the limitations of
// unnormalized coordinates are violated.
// https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap13.html#samplers-unnormalizedCoordinates
if self.unnormalized_coordinates {
// The viewType must be either VK_IMAGE_VIEW_TYPE_1D or
// VK_IMAGE_VIEW_TYPE_2D.
// VUID-vkCmdDispatch-None-02702
if !matches!(image_view.ty(), ImageViewType::Dim1d | ImageViewType::Dim2d) {
return Err(
SamplerImageViewIncompatibleError::UnnormalizedCoordinatesViewTypeNotCompatible,
);
}
// The image view must have a single layer and a single mip level.
if image_view.mip_levels().end - image_view.mip_levels().start != 1 {
return Err(
SamplerImageViewIncompatibleError::UnnormalizedCoordinatesMultipleMipLevels,
);
}
}
Ok(())
}
/// Returns true if the sampler is a compare-mode sampler.
/// Returns the border color if one is used by this sampler.
#[inline]
pub fn compare_mode(&self) -> bool {
self.compare_mode
pub fn border_color(&self) -> Option<BorderColor> {
self.border_color
}
/// Returns true if the sampler is unnormalized.
/// Returns the compare operation if the sampler is a compare-mode sampler.
#[inline]
pub fn is_unnormalized(&self) -> bool {
self.unnormalized
pub fn compare(&self) -> Option<CompareOp> {
self.compare
}
/// Returns true if the sampler can be used with floating-point image views.
/// Returns the magnification filter.
#[inline]
pub fn usable_with_float_formats(&self) -> bool {
self.usable_with_float_formats
pub fn mag_filter(&self) -> Filter {
self.mag_filter
}
/// Returns true if the sampler can be used with integer image views.
/// Returns the minification filter.
#[inline]
pub fn usable_with_int_formats(&self) -> bool {
self.usable_with_int_formats
pub fn min_filter(&self) -> Filter {
self.min_filter
}
/// Returns true if the sampler can be used with image views that have non-identity swizzling.
/// Returns the mipmap mode.
#[inline]
pub fn usable_with_swizzling(&self) -> bool {
self.usable_with_swizzling
pub fn mipmap_mode(&self) -> SamplerMipmapMode {
self.mipmap_mode
}
/// Returns the reduction mode.
#[inline]
pub fn reduction_mode(&self) -> SamplerReductionMode {
self.reduction_mode
}
/// Returns true if the sampler uses unnormalized coordinates.
#[inline]
pub fn unnormalized_coordinates(&self) -> bool {
self.unnormalized_coordinates
}
}
@ -254,15 +410,26 @@ pub struct SamplerBuilder {
impl SamplerBuilder {
/// Creates the `Sampler`.
pub fn build(self) -> Result<Arc<Sampler>, SamplerCreationError> {
let device = self.device;
let Self {
device,
mag_filter,
min_filter,
mipmap_mode,
address_mode_u,
address_mode_v,
address_mode_w,
mip_lod_bias,
anisotropy,
compare,
lod,
border_color,
unnormalized_coordinates,
reduction_mode,
} = self;
if [
self.address_mode_u,
self.address_mode_v,
self.address_mode_w,
]
.into_iter()
.any(|mode| mode == SamplerAddressMode::MirrorClampToEdge)
if [address_mode_u, address_mode_v, address_mode_w]
.into_iter()
.any(|mode| mode == SamplerAddressMode::MirrorClampToEdge)
{
if !device.enabled_features().sampler_mirror_clamp_to_edge
&& !device.enabled_extensions().khr_sampler_mirror_clamp_to_edge
@ -287,15 +454,15 @@ impl SamplerBuilder {
{
let limit = device.physical_device().properties().max_sampler_lod_bias;
if self.mip_lod_bias.abs() > limit {
if mip_lod_bias.abs() > limit {
return Err(SamplerCreationError::MaxSamplerLodBiasExceeded {
requested: self.mip_lod_bias,
requested: mip_lod_bias,
maximum: limit,
});
}
}
let (anisotropy_enable, max_anisotropy) = if let Some(max_anisotropy) = self.anisotropy {
let (anisotropy_enable, max_anisotropy) = if let Some(max_anisotropy) = anisotropy {
if !device.enabled_features().sampler_anisotropy {
return Err(SamplerCreationError::FeatureNotEnabled {
feature: "sampler_anisotropy",
@ -311,13 +478,13 @@ impl SamplerBuilder {
});
}
if [self.mag_filter, self.min_filter]
if [mag_filter, min_filter]
.into_iter()
.any(|filter| filter == Filter::Cubic)
{
return Err(SamplerCreationError::AnisotropyInvalidFilter {
mag_filter: self.mag_filter,
min_filter: self.min_filter,
mag_filter: mag_filter,
min_filter: min_filter,
});
}
@ -326,11 +493,9 @@ impl SamplerBuilder {
(ash::vk::FALSE, 1.0)
};
let (compare_enable, compare_op) = if let Some(compare_op) = self.compare {
if self.reduction_mode != SamplerReductionMode::WeightedAverage {
return Err(SamplerCreationError::CompareInvalidReductionMode {
reduction_mode: self.reduction_mode,
});
let (compare_enable, compare_op) = if let Some(compare_op) = compare {
if reduction_mode != SamplerReductionMode::WeightedAverage {
return Err(SamplerCreationError::CompareInvalidReductionMode { reduction_mode });
}
(ash::vk::TRUE, compare_op)
@ -338,60 +503,47 @@ impl SamplerBuilder {
(ash::vk::FALSE, CompareOp::Never)
};
let border_color_used = [
self.address_mode_u,
self.address_mode_v,
self.address_mode_w,
]
.into_iter()
.any(|mode| mode == SamplerAddressMode::ClampToBorder);
if self.unnormalized_coordinates {
if self.min_filter != self.mag_filter {
if unnormalized_coordinates {
if min_filter != mag_filter {
return Err(
SamplerCreationError::UnnormalizedCoordinatesFiltersNotEqual {
mag_filter: self.mag_filter,
min_filter: self.min_filter,
mag_filter,
min_filter,
},
);
}
if self.mipmap_mode != SamplerMipmapMode::Nearest {
if mipmap_mode != SamplerMipmapMode::Nearest {
return Err(
SamplerCreationError::UnnormalizedCoordinatesInvalidMipmapMode {
mipmap_mode: self.mipmap_mode,
},
SamplerCreationError::UnnormalizedCoordinatesInvalidMipmapMode { mipmap_mode },
);
}
if self.lod != (0.0..=0.0) {
if lod != (0.0..=0.0) {
return Err(SamplerCreationError::UnnormalizedCoordinatesNonzeroLod {
lod: self.lod.clone(),
lod: lod.clone(),
});
}
if [self.address_mode_u, self.address_mode_v]
.into_iter()
.any(|mode| {
!matches!(
mode,
SamplerAddressMode::ClampToEdge | SamplerAddressMode::ClampToBorder
)
})
{
if [address_mode_u, address_mode_v].into_iter().any(|mode| {
!matches!(
mode,
SamplerAddressMode::ClampToEdge | SamplerAddressMode::ClampToBorder
)
}) {
return Err(
SamplerCreationError::UnnormalizedCoordinatesInvalidAddressMode {
address_mode_u: self.address_mode_u,
address_mode_v: self.address_mode_v,
address_mode_u,
address_mode_v,
},
);
}
if self.anisotropy.is_some() {
if anisotropy.is_some() {
return Err(SamplerCreationError::UnnormalizedCoordinatesAnisotropyEnabled);
}
if self.compare.is_some() {
if compare.is_some() {
return Err(SamplerCreationError::UnnormalizedCoordinatesCompareEnabled);
}
}
@ -401,11 +553,11 @@ impl SamplerBuilder {
|| device.enabled_extensions().ext_sampler_filter_minmax
{
Some(ash::vk::SamplerReductionModeCreateInfo {
reduction_mode: self.reduction_mode.into(),
reduction_mode: reduction_mode.into(),
..Default::default()
})
} else {
if self.reduction_mode != SamplerReductionMode::WeightedAverage {
if reduction_mode != SamplerReductionMode::WeightedAverage {
if device
.physical_device()
.supported_features()
@ -430,21 +582,21 @@ impl SamplerBuilder {
let handle = unsafe {
let mut create_info = ash::vk::SamplerCreateInfo {
flags: ash::vk::SamplerCreateFlags::empty(),
mag_filter: self.mag_filter.into(),
min_filter: self.min_filter.into(),
mipmap_mode: self.mipmap_mode.into(),
address_mode_u: self.address_mode_u.into(),
address_mode_v: self.address_mode_v.into(),
address_mode_w: self.address_mode_w.into(),
mip_lod_bias: self.mip_lod_bias,
mag_filter: mag_filter.into(),
min_filter: min_filter.into(),
mipmap_mode: mipmap_mode.into(),
address_mode_u: address_mode_u.into(),
address_mode_v: address_mode_v.into(),
address_mode_w: address_mode_w.into(),
mip_lod_bias,
anisotropy_enable,
max_anisotropy,
compare_enable,
compare_op: compare_op.into(),
min_lod: *self.lod.start(),
max_lod: *self.lod.end(),
border_color: self.border_color.into(),
unnormalized_coordinates: self.unnormalized_coordinates as ash::vk::Bool32,
min_lod: *lod.start(),
max_lod: *lod.end(),
border_color: border_color.into(),
unnormalized_coordinates: unnormalized_coordinates as ash::vk::Bool32,
..Default::default()
};
@ -468,28 +620,17 @@ impl SamplerBuilder {
Ok(Arc::new(Sampler {
handle,
device,
compare_mode: self.compare.is_some(),
unnormalized: self.unnormalized_coordinates,
usable_with_float_formats: !border_color_used
|| matches!(
self.border_color,
BorderColor::FloatTransparentBlack
| BorderColor::FloatOpaqueBlack
| BorderColor::FloatOpaqueWhite
),
usable_with_int_formats: (!border_color_used
|| matches!(
self.border_color,
BorderColor::IntTransparentBlack
| BorderColor::IntOpaqueBlack
| BorderColor::IntOpaqueWhite
))
&& self.compare.is_none(),
usable_with_swizzling: !border_color_used
|| !matches!(
self.border_color,
BorderColor::FloatOpaqueBlack | BorderColor::IntOpaqueBlack
),
border_color: [address_mode_u, address_mode_v, address_mode_w]
.into_iter()
.any(|mode| mode == SamplerAddressMode::ClampToBorder)
.then(|| border_color),
compare,
mag_filter,
min_filter,
mipmap_mode,
reduction_mode,
unnormalized_coordinates,
}))
}
@ -1165,6 +1306,64 @@ impl From<SamplerReductionMode> for ash::vk::SamplerReductionMode {
}
}
#[derive(Clone, Copy, Debug)]
pub enum SamplerImageViewIncompatibleError {
/// The sampler has a border color with a numeric type different from the image view.
BorderColorFormatNotCompatible,
/// The sampler has an opaque black border color, but the image view is not identity swizzled.
BorderColorOpaqueBlackNotIdentitySwizzled,
/// The sampler has depth comparison enabled, but this is not supported by the image view.
DepthComparisonNotSupported,
/// The sampler has depth comparison enabled, but the image view does not select the `depth`
/// aspect.
DepthComparisonWrongAspect,
/// The sampler uses a linear filter, but this is not supported by the image view's format
/// features.
FilterLinearNotSupported,
/// The sampler uses a cubic filter, but this is not supported by the image view's format
/// features.
FilterCubicNotSupported,
/// The sampler uses a cubic filter with a `Min` or `Max` reduction mode, but this is not
/// supported by the image view's format features.
FilterCubicMinmaxNotSupported,
/// The sampler uses a linear mipmap mode, but this is not supported by the image view's format
/// features.
MipmapModeLinearNotSupported,
/// The sampler uses unnormalized coordinates, but the image view has multiple mip levels.
UnnormalizedCoordinatesMultipleMipLevels,
/// The sampler uses unnormalized coordinates, but the image view has a type other than `Dim1d`
/// or `Dim2d`.
UnnormalizedCoordinatesViewTypeNotCompatible,
}
impl error::Error for SamplerImageViewIncompatibleError {}
impl fmt::Display for SamplerImageViewIncompatibleError {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match self {
Self::BorderColorFormatNotCompatible => write!(fmt, "the sampler has a border color with a numeric type different from the image view"),
Self::BorderColorOpaqueBlackNotIdentitySwizzled => write!(fmt, "the sampler has an opaque black border color, but the image view is not identity swizzled"),
Self::DepthComparisonNotSupported => write!(fmt, "the sampler has depth comparison enabled, but this is not supported by the image view"),
Self::DepthComparisonWrongAspect => write!(fmt, "the sampler has depth comparison enabled, but the image view does not select the `depth` aspect"),
Self::FilterLinearNotSupported => write!(fmt, "the sampler uses a linear filter, but this is not supported by the image view's format features"),
Self::FilterCubicNotSupported => write!(fmt, "the sampler uses a cubic filter, but this is not supported by the image view's format features"),
Self::FilterCubicMinmaxNotSupported => write!(fmt, "the sampler uses a cubic filter with a `Min` or `Max` reduction mode, but this is not supported by the image view's format features"),
Self::MipmapModeLinearNotSupported => write!(fmt, "the sampler uses a linear mipmap mode, but this is not supported by the image view's format features"),
Self::UnnormalizedCoordinatesMultipleMipLevels => write!(fmt, "the sampler uses unnormalized coordinates, but the image view has multiple mip levels"),
Self::UnnormalizedCoordinatesViewTypeNotCompatible => write!(fmt, "the sampler uses unnormalized coordinates, but the image view has a type other than `Dim1d` or `Dim2d`"),
}
}
}
#[cfg(test)]
mod tests {
use crate::{
@ -1185,8 +1384,8 @@ mod tests {
.lod(0.0..=2.0)
.build()
.unwrap();
assert!(!s.compare_mode());
assert!(!s.is_unnormalized());
assert!(!s.compare().is_some());
assert!(!s.unnormalized_coordinates());
}
#[test]
@ -1201,8 +1400,8 @@ mod tests {
.lod(0.0..=2.0)
.build()
.unwrap();
assert!(s.compare_mode());
assert!(!s.is_unnormalized());
assert!(s.compare().is_some());
assert!(!s.unnormalized_coordinates());
}
#[test]
@ -1214,8 +1413,8 @@ mod tests {
.unnormalized_coordinates(true)
.build()
.unwrap();
assert!(!s.compare_mode());
assert!(s.is_unnormalized());
assert!(!s.compare().is_some());
assert!(s.unnormalized_coordinates());
}
#[test]

View File

@ -20,7 +20,7 @@
use crate::check_errors;
use crate::descriptor_set::layout::DescriptorType;
use crate::device::Device;
use crate::format::Format;
use crate::format::{Format, NumericType};
use crate::image::view::ImageViewType;
use crate::pipeline::graphics::input_assembly::PrimitiveTopology;
use crate::pipeline::layout::PipelineLayoutPcRange;
@ -540,22 +540,26 @@ pub struct DescriptorRequirements {
/// The image format that is required for image views bound to this descriptor. If this is
/// `None`, then any image format is allowed.
pub format: Option<Format>,
/// The view type that is required for image views bound to this descriptor. This is `None` for
/// non-image descriptors.
pub image_view_type: Option<ImageViewType>,
pub image_format: Option<Format>,
/// Whether image views bound to this descriptor must have multisampling enabled or disabled.
pub multisampled: bool,
pub image_multisampled: bool,
/// The descriptor indices that require mutable (exclusive) access to the bound resource.
pub mutable: FnvHashSet<u32>,
/// The base scalar type required for the format of image views bound to this descriptor.
/// This is `None` for non-image descriptors.
pub image_scalar_type: Option<ShaderScalarType>,
/// The view type that is required for image views bound to this descriptor.
/// This is `None` for non-image descriptors.
pub image_view_type: Option<ImageViewType>,
/// For sampler bindings, the descriptor indices that require a depth comparison sampler.
pub sampler_compare: FnvHashSet<u32>,
/// For sampler bindings, the descriptor indices that perform sampling operations that are not
/// permitted with unnormalized coordinates. This includes sampling with `ImplicitLod`,
/// `Dref` or `Proj` SPIR-V instructions or with an LOD bias or offset.
pub sampler_no_unnormalized: FnvHashSet<u32>,
pub sampler_no_unnormalized_coordinates: FnvHashSet<u32>,
/// For sampler bindings, the sampled image descriptors that are used in combination with each
/// sampler descriptor index.
@ -566,6 +570,14 @@ pub struct DescriptorRequirements {
/// For storage image bindings, the descriptor indices that atomic operations are used with.
pub storage_image_atomic: FnvHashSet<u32>,
/// For storage images and storage texel buffers, the descriptor indices that perform read
/// operations on the bound resource.
pub storage_read: FnvHashSet<u32>,
/// For storage buffers, storage images and storage texel buffers, the descriptor indices that
/// perform write operations on the bound resource.
pub storage_write: FnvHashSet<u32>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@ -590,9 +602,15 @@ impl DescriptorRequirements {
return Err(DescriptorRequirementsIncompatible::DescriptorType);
}
if let (Some(first), Some(second)) = (self.format, other.format) {
if let (Some(first), Some(second)) = (self.image_format, other.image_format) {
if first != second {
return Err(DescriptorRequirementsIncompatible::Format);
return Err(DescriptorRequirementsIncompatible::ImageFormat);
}
}
if let (Some(first), Some(second)) = (self.image_scalar_type, other.image_scalar_type) {
if first != second {
return Err(DescriptorRequirementsIncompatible::ImageScalarType);
}
}
@ -602,8 +620,8 @@ impl DescriptorRequirements {
}
}
if self.multisampled != other.multisampled {
return Err(DescriptorRequirementsIncompatible::Multisampled);
if self.image_multisampled != other.image_multisampled {
return Err(DescriptorRequirementsIncompatible::ImageMultisampled);
}
let sampler_with_images = {
@ -619,14 +637,18 @@ impl DescriptorRequirements {
Ok(Self {
descriptor_types,
descriptor_count: self.descriptor_count.max(other.descriptor_count),
format: self.format.or(other.format),
image_format: self.image_format.or(other.image_format),
image_multisampled: self.image_multisampled,
image_scalar_type: self.image_scalar_type.or(other.image_scalar_type),
image_view_type: self.image_view_type.or(other.image_view_type),
multisampled: self.multisampled,
mutable: &self.mutable | &other.mutable,
sampler_no_unnormalized: &self.sampler_no_unnormalized | &other.sampler_no_unnormalized,
sampler_compare: &self.sampler_compare | &other.sampler_compare,
sampler_no_unnormalized_coordinates: &self.sampler_no_unnormalized_coordinates
| &other.sampler_no_unnormalized_coordinates,
sampler_with_images,
stages: self.stages | other.stages,
storage_image_atomic: &self.storage_image_atomic | &other.storage_image_atomic,
storage_read: &self.storage_read | &other.storage_read,
storage_write: &self.storage_write | &other.storage_write,
})
}
}
@ -638,35 +660,36 @@ pub enum DescriptorRequirementsIncompatible {
/// The allowed descriptor types of the descriptors do not overlap.
DescriptorType,
/// The descriptors require different formats.
Format,
ImageFormat,
/// The descriptors require different scalar types.
ImageScalarType,
/// The multisampling requirements of the descriptors differ.
ImageMultisampled,
/// The descriptors require different image view types.
ImageViewType,
/// The multisampling requirements of the descriptors differ.
Multisampled,
}
impl Error for DescriptorRequirementsIncompatible {}
impl Display for DescriptorRequirementsIncompatible {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DescriptorRequirementsIncompatible::DescriptorType => {
write!(
f,
"the allowed descriptor types of the two descriptors do not overlap"
)
DescriptorRequirementsIncompatible::DescriptorType => write!(
fmt,
"the allowed descriptor types of the two descriptors do not overlap",
),
DescriptorRequirementsIncompatible::ImageFormat => {
write!(fmt, "the descriptors require different formats",)
}
DescriptorRequirementsIncompatible::Format => {
write!(f, "the descriptors require different formats")
DescriptorRequirementsIncompatible::ImageMultisampled => write!(
fmt,
"the multisampling requirements of the descriptors differ",
),
DescriptorRequirementsIncompatible::ImageScalarType => {
write!(fmt, "the descriptors require different scalar types",)
}
DescriptorRequirementsIncompatible::ImageViewType => {
write!(f, "the descriptors require different image view types")
}
DescriptorRequirementsIncompatible::Multisampled => {
write!(
f,
"the multisampling requirements of the descriptors differ"
)
write!(fmt, "the descriptors require different image view types",)
}
}
}
@ -928,7 +951,7 @@ impl ShaderInterfaceEntryType {
}
}
/// The numeric base type of a shader interface variable.
/// The numeric base type of a shader variable.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ShaderScalarType {
Float,
@ -936,6 +959,23 @@ pub enum ShaderScalarType {
Uint,
}
// https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap43.html#formats-numericformat
impl From<NumericType> for ShaderScalarType {
fn from(val: NumericType) -> Self {
match val {
NumericType::SFLOAT => Self::Float,
NumericType::UFLOAT => Self::Float,
NumericType::SINT => Self::Sint,
NumericType::UINT => Self::Uint,
NumericType::SNORM => Self::Float,
NumericType::UNORM => Self::Float,
NumericType::SSCALED => Self::Float,
NumericType::USCALED => Self::Float,
NumericType::SRGB => Self::Float,
}
}
}
/// Error that can happen when the interface mismatches between two shader stages.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ShaderInterfaceMismatchError {

View File

@ -14,12 +14,11 @@ use crate::image::view::ImageViewType;
use crate::shader::ShaderScalarType;
use crate::DeviceSize;
use crate::{
format::Format,
pipeline::layout::PipelineLayoutPcRange,
shader::{
spirv::{
Capability, Decoration, Dim, ExecutionMode, ExecutionModel, Id, ImageFormat,
Instruction, Spirv, StorageClass,
Capability, Decoration, Dim, ExecutionMode, ExecutionModel, Id, Instruction, Spirv,
StorageClass,
},
DescriptorIdentifier, DescriptorRequirements, EntryPointInfo, GeometryShaderExecution,
GeometryShaderInput, ShaderExecution, ShaderInterface, ShaderInterfaceEntry,
@ -391,8 +390,46 @@ fn inspect_entry_point(
sampled_image,
ref image_operands,
..
} => {
if let Some((variable, Some(index))) = instruction_chain(
result,
global,
spirv,
[inst_sampled_image, inst_load],
sampled_image,
) {
variable
.reqs
.sampler_no_unnormalized_coordinates
.insert(index);
}
}
| &Instruction::ImageSampleDrefImplicitLod {
&Instruction::ImageSampleProjExplicitLod {
sampled_image,
ref image_operands,
..
}
| &Instruction::ImageSparseSampleProjExplicitLod {
sampled_image,
ref image_operands,
..
} => {
if let Some((variable, Some(index))) = instruction_chain(
result,
global,
spirv,
[inst_sampled_image, inst_load],
sampled_image,
) {
variable
.reqs
.sampler_no_unnormalized_coordinates
.insert(index);
}
}
&Instruction::ImageSampleDrefImplicitLod {
sampled_image,
ref image_operands,
..
@ -419,21 +456,15 @@ fn inspect_entry_point(
[inst_sampled_image, inst_load],
sampled_image,
) {
variable.reqs.sampler_no_unnormalized.insert(index);
variable
.reqs
.sampler_no_unnormalized_coordinates
.insert(index);
variable.reqs.sampler_compare.insert(index);
}
}
&Instruction::ImageSampleProjExplicitLod {
sampled_image,
ref image_operands,
..
}
| &Instruction::ImageSparseSampleProjExplicitLod {
sampled_image,
ref image_operands,
..
}
| &Instruction::ImageSampleDrefExplicitLod {
&Instruction::ImageSampleDrefExplicitLod {
sampled_image,
ref image_operands,
..
@ -460,7 +491,11 @@ fn inspect_entry_point(
[inst_sampled_image, inst_load],
sampled_image,
) {
variable.reqs.sampler_no_unnormalized.insert(index);
variable
.reqs
.sampler_no_unnormalized_coordinates
.insert(index);
variable.reqs.sampler_compare.insert(index);
}
}
@ -486,7 +521,10 @@ fn inspect_entry_point(
|| image_operands.const_offsets.is_some()
|| image_operands.offset.is_some()
{
variable.reqs.sampler_no_unnormalized.insert(index);
variable
.reqs
.sampler_no_unnormalized_coordinates
.insert(index);
}
}
}
@ -497,11 +535,19 @@ fn inspect_entry_point(
instruction_chain(result, global, spirv, [], image);
}
&Instruction::ImageRead { image, .. } => {
if let Some((variable, Some(index))) =
instruction_chain(result, global, spirv, [inst_load], image)
{
variable.reqs.storage_read.insert(index);
}
}
&Instruction::ImageWrite { image, .. } => {
if let Some((variable, Some(index))) =
instruction_chain(result, global, spirv, [inst_load], image)
{
variable.reqs.mutable.insert(index);
variable.reqs.storage_write.insert(index);
}
}
@ -536,7 +582,7 @@ fn inspect_entry_point(
if let Some((variable, Some(index))) =
instruction_chain(result, global, spirv, [], pointer)
{
variable.reqs.mutable.insert(index);
variable.reqs.storage_write.insert(index);
}
}
@ -635,6 +681,7 @@ fn descriptor_requirements_of(spirv: &Spirv, variable_id: Id) -> DescriptorVaria
}
&Instruction::TypeImage {
sampled_type,
ref dim,
arrayed,
ms,
@ -642,25 +689,39 @@ fn descriptor_requirements_of(spirv: &Spirv, variable_id: Id) -> DescriptorVaria
ref image_format,
..
} => {
let multisampled = ms != 0;
assert!(sampled != 0, "Vulkan requires that variables of type OpTypeImage have a Sampled operand of 1 or 2");
let format: Option<Format> = image_format.clone().into();
reqs.image_format = image_format.clone().into();
reqs.image_multisampled = ms != 0;
reqs.image_scalar_type = Some(match spirv.id(sampled_type).instruction() {
&Instruction::TypeInt {
width, signedness, ..
} => {
assert!(width == 32); // TODO: 64-bit components
match signedness {
0 => ShaderScalarType::Uint,
1 => ShaderScalarType::Sint,
_ => unreachable!(),
}
}
&Instruction::TypeFloat { width, .. } => {
assert!(width == 32); // TODO: 64-bit components
ShaderScalarType::Float
}
_ => unreachable!(),
});
match dim {
Dim::SubpassData => {
assert!(
*image_format == ImageFormat::Unknown,
reqs.image_format.is_none(),
"If Dim is SubpassData, Image Format must be Unknown"
);
assert!(sampled == 2, "If Dim is SubpassData, Sampled must be 2");
assert!(arrayed == 0, "If Dim is SubpassData, Arrayed must be 0");
reqs.descriptor_types = vec![DescriptorType::InputAttachment];
reqs.multisampled = multisampled;
}
Dim::Buffer => {
reqs.format = format;
if sampled == 1 {
reqs.descriptor_types = vec![DescriptorType::UniformTexelBuffer];
} else {
@ -668,7 +729,7 @@ fn descriptor_requirements_of(spirv: &Spirv, variable_id: Id) -> DescriptorVaria
}
}
_ => {
let image_view_type = Some(match (dim, arrayed) {
reqs.image_view_type = Some(match (dim, arrayed) {
(Dim::Dim1D, 0) => ImageViewType::Dim1d,
(Dim::Dim1D, 1) => ImageViewType::Dim1dArray,
(Dim::Dim2D, 0) => ImageViewType::Dim2d,
@ -685,10 +746,6 @@ fn descriptor_requirements_of(spirv: &Spirv, variable_id: Id) -> DescriptorVaria
_ => unreachable!(),
});
reqs.format = format;
reqs.multisampled = multisampled;
reqs.image_view_type = image_view_type;
if reqs.descriptor_types.is_empty() {
if sampled == 1 {
reqs.descriptor_types = vec![DescriptorType::SampledImage];

View File

@ -660,6 +660,7 @@ impl<W> SwapchainBuilder<W> {
usage,
flags,
None,
None,
)?
.is_none()
{

View File

@ -291,33 +291,33 @@ pub unsafe trait GpuFuture: DeviceOwned {
{
Box::new(self) as _
}
/// Turn the current future into a `Box<dyn GpuFuture + Send>`.
///
/// This is a helper function that calls `Box::new(yourFuture) as Box<dyn GpuFuture + Send>`.
fn boxed_send(self) -> Box<dyn GpuFuture + Send>
where
Self: Sized + Send + 'static,
where
Self: Sized + Send + 'static,
{
Box::new(self) as _
}
/// Turn the current future into a `Box<dyn GpuFuture + Sync>`.
///
/// This is a helper function that calls `Box::new(yourFuture) as Box<dyn GpuFuture + Sync>`.
fn boxed_sync(self) -> Box<dyn GpuFuture + Sync>
where
Self: Sized + Sync + 'static,
where
Self: Sized + Sync + 'static,
{
Box::new(self) as _
}
/// Turn the current future into a `Box<dyn GpuFuture + Send + Sync>`.
///
/// This is a helper function that calls `Box::new(yourFuture) as Box<dyn GpuFuture + Send + Sync>`.
fn boxed_send_sync(self) -> Box<dyn GpuFuture + Send + Sync>
where
Self: Sized + Send + Sync + 'static,
where
Self: Sized + Send + Sync + 'static,
{
Box::new(self) as _
}