mirror of
https://github.com/vulkano-rs/vulkano.git
synced 2024-11-25 00:04:15 +00:00
Better validation of image view creation, descriptor set updates, draw-time resources and others (#1806)
This commit is contained in:
parent
b25598ee70
commit
0c06394e6c
@ -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(),
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
},
|
||||
),
|
||||
|
@ -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
|
||||
)*
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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),
|
||||
|
@ -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),
|
||||
};
|
||||
|
@ -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 view’s 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 image’s 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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -499,7 +499,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum DescriptorSetCreationError {
|
||||
DescriptorSetUpdateError(DescriptorSetUpdateError),
|
||||
OomError(OomError),
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
|
@ -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
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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]
|
||||
|
@ -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 {
|
||||
|
@ -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];
|
||||
|
@ -660,6 +660,7 @@ impl<W> SwapchainBuilder<W> {
|
||||
usage,
|
||||
flags,
|
||||
None,
|
||||
None,
|
||||
)?
|
||||
.is_none()
|
||||
{
|
||||
|
@ -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 _
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user