use crate::device::Device;
use crate::format::ClearValue;
use crate::format::Format;
use crate::image::sys::ImageCreationError;
use crate::image::sys::UnsafeImage;
use crate::image::sys::UnsafeImageCreateInfo;
use crate::image::traits::ImageAccess;
use crate::image::traits::ImageClearValue;
use crate::image::traits::ImageContent;
use crate::image::ImageDescriptorLayouts;
use crate::image::ImageDimensions;
use crate::image::ImageInner;
use crate::image::ImageLayout;
use crate::image::ImageUsage;
use crate::image::SampleCount;
use crate::memory::pool::alloc_dedicated_with_exportable_fd;
use crate::memory::pool::AllocFromRequirementsFilter;
use crate::memory::pool::AllocLayout;
use crate::memory::pool::MappingRequirement;
use crate::memory::pool::MemoryPool;
use crate::memory::pool::MemoryPoolAlloc;
use crate::memory::pool::PotentialDedicatedAllocation;
use crate::memory::pool::StdMemoryPoolAlloc;
use crate::memory::DedicatedAllocation;
use crate::memory::ExternalMemoryHandleType;
use crate::memory::{DeviceMemoryExportError, ExternalMemoryHandleTypes};
use crate::sync::AccessError;
use crate::DeviceSize;
use std::fs::File;
use std::hash::Hash;
use std::hash::Hasher;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::sync::Arc;
#[derive(Debug)]
pub struct AttachmentImage<A = PotentialDedicatedAllocation<StdMemoryPoolAlloc>> {
image: UnsafeImage,
memory: A,
format: Format,
attachment_layout: ImageLayout,
initialized: AtomicBool,
gpu_lock: AtomicUsize,
}
impl AttachmentImage {
#[inline]
pub fn new(
device: Arc<Device>,
dimensions: [u32; 2],
format: Format,
) -> Result<Arc<AttachmentImage>, ImageCreationError> {
AttachmentImage::new_impl(
device,
dimensions,
1,
format,
ImageUsage::none(),
SampleCount::Sample1,
)
}
#[inline]
pub fn input_attachment(
device: Arc<Device>,
dimensions: [u32; 2],
format: Format,
) -> Result<Arc<AttachmentImage>, ImageCreationError> {
let base_usage = ImageUsage {
input_attachment: true,
..ImageUsage::none()
};
AttachmentImage::new_impl(
device,
dimensions,
1,
format,
base_usage,
SampleCount::Sample1,
)
}
#[inline]
pub fn multisampled(
device: Arc<Device>,
dimensions: [u32; 2],
samples: SampleCount,
format: Format,
) -> Result<Arc<AttachmentImage>, ImageCreationError> {
AttachmentImage::new_impl(device, dimensions, 1, format, ImageUsage::none(), samples)
}
#[inline]
pub fn multisampled_input_attachment(
device: Arc<Device>,
dimensions: [u32; 2],
samples: SampleCount,
format: Format,
) -> Result<Arc<AttachmentImage>, ImageCreationError> {
let base_usage = ImageUsage {
input_attachment: true,
..ImageUsage::none()
};
AttachmentImage::new_impl(device, dimensions, 1, format, base_usage, samples)
}
#[inline]
pub fn with_usage(
device: Arc<Device>,
dimensions: [u32; 2],
format: Format,
usage: ImageUsage,
) -> Result<Arc<AttachmentImage>, ImageCreationError> {
AttachmentImage::new_impl(device, dimensions, 1, format, usage, SampleCount::Sample1)
}
#[inline]
pub fn multisampled_with_usage(
device: Arc<Device>,
dimensions: [u32; 2],
samples: SampleCount,
format: Format,
usage: ImageUsage,
) -> Result<Arc<AttachmentImage>, ImageCreationError> {
AttachmentImage::new_impl(device, dimensions, 1, format, usage, samples)
}
#[inline]
pub fn multisampled_with_usage_with_layers(
device: Arc<Device>,
dimensions: [u32; 2],
array_layers: u32,
samples: SampleCount,
format: Format,
usage: ImageUsage,
) -> Result<Arc<AttachmentImage>, ImageCreationError> {
AttachmentImage::new_impl(device, dimensions, array_layers, format, usage, samples)
}
#[inline]
pub fn sampled(
device: Arc<Device>,
dimensions: [u32; 2],
format: Format,
) -> Result<Arc<AttachmentImage>, ImageCreationError> {
let base_usage = ImageUsage {
sampled: true,
..ImageUsage::none()
};
AttachmentImage::new_impl(
device,
dimensions,
1,
format,
base_usage,
SampleCount::Sample1,
)
}
#[inline]
pub fn sampled_input_attachment(
device: Arc<Device>,
dimensions: [u32; 2],
format: Format,
) -> Result<Arc<AttachmentImage>, ImageCreationError> {
let base_usage = ImageUsage {
sampled: true,
input_attachment: true,
..ImageUsage::none()
};
AttachmentImage::new_impl(
device,
dimensions,
1,
format,
base_usage,
SampleCount::Sample1,
)
}
#[inline]
pub fn sampled_multisampled(
device: Arc<Device>,
dimensions: [u32; 2],
samples: SampleCount,
format: Format,
) -> Result<Arc<AttachmentImage>, ImageCreationError> {
let base_usage = ImageUsage {
sampled: true,
..ImageUsage::none()
};
AttachmentImage::new_impl(device, dimensions, 1, format, base_usage, samples)
}
#[inline]
pub fn sampled_multisampled_input_attachment(
device: Arc<Device>,
dimensions: [u32; 2],
samples: SampleCount,
format: Format,
) -> Result<Arc<AttachmentImage>, ImageCreationError> {
let base_usage = ImageUsage {
sampled: true,
input_attachment: true,
..ImageUsage::none()
};
AttachmentImage::new_impl(device, dimensions, 1, format, base_usage, samples)
}
#[inline]
pub fn transient(
device: Arc<Device>,
dimensions: [u32; 2],
format: Format,
) -> Result<Arc<AttachmentImage>, ImageCreationError> {
let base_usage = ImageUsage {
transient_attachment: true,
..ImageUsage::none()
};
AttachmentImage::new_impl(
device,
dimensions,
1,
format,
base_usage,
SampleCount::Sample1,
)
}
#[inline]
pub fn transient_input_attachment(
device: Arc<Device>,
dimensions: [u32; 2],
format: Format,
) -> Result<Arc<AttachmentImage>, ImageCreationError> {
let base_usage = ImageUsage {
transient_attachment: true,
input_attachment: true,
..ImageUsage::none()
};
AttachmentImage::new_impl(
device,
dimensions,
1,
format,
base_usage,
SampleCount::Sample1,
)
}
#[inline]
pub fn transient_multisampled(
device: Arc<Device>,
dimensions: [u32; 2],
samples: SampleCount,
format: Format,
) -> Result<Arc<AttachmentImage>, ImageCreationError> {
let base_usage = ImageUsage {
transient_attachment: true,
..ImageUsage::none()
};
AttachmentImage::new_impl(device, dimensions, 1, format, base_usage, samples)
}
#[inline]
pub fn transient_multisampled_input_attachment(
device: Arc<Device>,
dimensions: [u32; 2],
samples: SampleCount,
format: Format,
) -> Result<Arc<AttachmentImage>, ImageCreationError> {
let base_usage = ImageUsage {
transient_attachment: true,
input_attachment: true,
..ImageUsage::none()
};
AttachmentImage::new_impl(device, dimensions, 1, format, base_usage, samples)
}
fn new_impl(
device: Arc<Device>,
dimensions: [u32; 2],
array_layers: u32,
format: Format,
base_usage: ImageUsage,
samples: SampleCount,
) -> Result<Arc<AttachmentImage>, ImageCreationError> {
let aspects = format.aspects();
let is_depth = aspects.depth || aspects.stencil;
if format.compression().is_some() {
panic!() }
let image = UnsafeImage::new(
device.clone(),
UnsafeImageCreateInfo {
dimensions: ImageDimensions::Dim2d {
width: dimensions[0],
height: dimensions[1],
array_layers,
},
format: Some(format),
samples,
usage: ImageUsage {
color_attachment: !is_depth,
depth_stencil_attachment: is_depth,
..base_usage
},
..Default::default()
},
)?;
let mem_reqs = image.memory_requirements();
let memory = MemoryPool::alloc_from_requirements(
&Device::standard_pool(&device),
&mem_reqs,
AllocLayout::Optimal,
MappingRequirement::DoNotMap,
Some(DedicatedAllocation::Image(&image)),
|t| {
if t.is_device_local() {
AllocFromRequirementsFilter::Preferred
} else {
AllocFromRequirementsFilter::Allowed
}
},
)?;
debug_assert!((memory.offset() % mem_reqs.alignment) == 0);
unsafe {
image.bind_memory(memory.memory(), memory.offset())?;
}
Ok(Arc::new(AttachmentImage {
image,
memory,
format,
attachment_layout: if is_depth {
ImageLayout::DepthStencilAttachmentOptimal
} else {
ImageLayout::ColorAttachmentOptimal
},
initialized: AtomicBool::new(false),
gpu_lock: AtomicUsize::new(0),
}))
}
pub fn new_with_exportable_fd(
device: Arc<Device>,
dimensions: [u32; 2],
array_layers: u32,
format: Format,
base_usage: ImageUsage,
samples: SampleCount,
) -> Result<Arc<AttachmentImage>, ImageCreationError> {
let aspects = format.aspects();
let is_depth = aspects.depth || aspects.stencil;
let image = UnsafeImage::new(
device.clone(),
UnsafeImageCreateInfo {
dimensions: ImageDimensions::Dim2d {
width: dimensions[0],
height: dimensions[1],
array_layers,
},
format: Some(format),
samples,
usage: ImageUsage {
color_attachment: !is_depth,
depth_stencil_attachment: is_depth,
..base_usage
},
external_memory_handle_types: ExternalMemoryHandleTypes {
opaque_fd: true,
..ExternalMemoryHandleTypes::none()
},
mutable_format: true,
..Default::default()
},
)?;
let mem_reqs = image.memory_requirements();
let memory = alloc_dedicated_with_exportable_fd(
device.clone(),
&mem_reqs,
AllocLayout::Optimal,
MappingRequirement::DoNotMap,
DedicatedAllocation::Image(&image),
|t| {
if t.is_device_local() {
AllocFromRequirementsFilter::Preferred
} else {
AllocFromRequirementsFilter::Allowed
}
},
)?;
debug_assert!((memory.offset() % mem_reqs.alignment) == 0);
unsafe {
image.bind_memory(memory.memory(), memory.offset())?;
}
Ok(Arc::new(AttachmentImage {
image,
memory,
format,
attachment_layout: if is_depth {
ImageLayout::DepthStencilAttachmentOptimal
} else {
ImageLayout::ColorAttachmentOptimal
},
initialized: AtomicBool::new(false),
gpu_lock: AtomicUsize::new(0),
}))
}
pub fn export_posix_fd(&self) -> Result<File, DeviceMemoryExportError> {
self.memory
.memory()
.export_fd(ExternalMemoryHandleType::OpaqueFd)
}
pub fn mem_size(&self) -> DeviceSize {
self.memory.memory().allocation_size()
}
}
unsafe impl<A> ImageAccess for AttachmentImage<A>
where
A: MemoryPoolAlloc,
{
#[inline]
fn inner(&self) -> ImageInner {
ImageInner {
image: &self.image,
first_layer: 0,
num_layers: self.image.dimensions().array_layers() as usize,
first_mipmap_level: 0,
num_mipmap_levels: 1,
}
}
#[inline]
fn initial_layout_requirement(&self) -> ImageLayout {
self.attachment_layout
}
#[inline]
fn final_layout_requirement(&self) -> ImageLayout {
self.attachment_layout
}
#[inline]
fn descriptor_layouts(&self) -> Option<ImageDescriptorLayouts> {
Some(ImageDescriptorLayouts {
storage_image: ImageLayout::General,
combined_image_sampler: ImageLayout::ShaderReadOnlyOptimal,
sampled_image: ImageLayout::ShaderReadOnlyOptimal,
input_attachment: ImageLayout::ShaderReadOnlyOptimal,
})
}
#[inline]
fn conflict_key(&self) -> u64 {
self.image.key()
}
#[inline]
fn try_gpu_lock(
&self,
_: bool,
uninitialized_safe: bool,
expected_layout: ImageLayout,
) -> Result<(), AccessError> {
if expected_layout != self.attachment_layout && expected_layout != ImageLayout::Undefined {
if self.initialized.load(Ordering::SeqCst) {
return Err(AccessError::UnexpectedImageLayout {
requested: expected_layout,
allowed: self.attachment_layout,
});
} else {
return Err(AccessError::UnexpectedImageLayout {
requested: expected_layout,
allowed: ImageLayout::Undefined,
});
}
}
if !uninitialized_safe && expected_layout != ImageLayout::Undefined {
if !self.initialized.load(Ordering::SeqCst) {
return Err(AccessError::ImageNotInitialized {
requested: expected_layout,
});
}
}
if self
.gpu_lock
.compare_exchange(0, 1, Ordering::SeqCst, Ordering::SeqCst)
.unwrap_or_else(|e| e)
== 0
{
Ok(())
} else {
Err(AccessError::AlreadyInUse)
}
}
#[inline]
unsafe fn increase_gpu_lock(&self) {
let val = self.gpu_lock.fetch_add(1, Ordering::SeqCst);
debug_assert!(val >= 1);
}
#[inline]
unsafe fn unlock(&self, new_layout: Option<ImageLayout>) {
if let Some(new_layout) = new_layout {
debug_assert_eq!(new_layout, self.attachment_layout);
self.initialized.store(true, Ordering::SeqCst);
}
let prev_val = self.gpu_lock.fetch_sub(1, Ordering::SeqCst);
debug_assert!(prev_val >= 1);
}
#[inline]
unsafe fn layout_initialized(&self) {
self.initialized.store(true, Ordering::SeqCst);
}
#[inline]
fn is_layout_initialized(&self) -> bool {
self.initialized.load(Ordering::SeqCst)
}
#[inline]
fn current_mip_levels_access(&self) -> std::ops::Range<u32> {
0..self.mip_levels()
}
#[inline]
fn current_array_layers_access(&self) -> std::ops::Range<u32> {
0..self.dimensions().array_layers()
}
}
unsafe impl<A> ImageClearValue<ClearValue> for AttachmentImage<A>
where
A: MemoryPoolAlloc,
{
#[inline]
fn decode(&self, value: ClearValue) -> Option<ClearValue> {
Some(self.format.decode_clear_value(value))
}
}
unsafe impl<P, A> ImageContent<P> for AttachmentImage<A>
where
A: MemoryPoolAlloc,
{
#[inline]
fn matches_format(&self) -> bool {
true }
}
impl<A> PartialEq for AttachmentImage<A>
where
A: MemoryPoolAlloc,
{
#[inline]
fn eq(&self, other: &Self) -> bool {
self.inner() == other.inner()
}
}
impl<A> Eq for AttachmentImage<A> where A: MemoryPoolAlloc {}
impl<A> Hash for AttachmentImage<A>
where
A: MemoryPoolAlloc,
{
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.inner().hash(state);
}
}
pub enum ClearAttachment {
Color(ClearValue, u32),
Depth(f32),
Stencil(u32),
DepthStencil((f32, u32)),
}
impl From<ClearAttachment> for ash::vk::ClearAttachment {
fn from(v: ClearAttachment) -> Self {
match v {
ClearAttachment::Color(clear_value, color_attachment) => ash::vk::ClearAttachment {
aspect_mask: ash::vk::ImageAspectFlags::COLOR,
color_attachment,
clear_value: ash::vk::ClearValue {
color: match clear_value {
ClearValue::Float(val) => ash::vk::ClearColorValue { float32: val },
ClearValue::Int(val) => ash::vk::ClearColorValue { int32: val },
ClearValue::Uint(val) => ash::vk::ClearColorValue { uint32: val },
_ => ash::vk::ClearColorValue { float32: [0.0; 4] },
},
},
},
ClearAttachment::Depth(depth) => ash::vk::ClearAttachment {
aspect_mask: ash::vk::ImageAspectFlags::DEPTH,
color_attachment: 0,
clear_value: ash::vk::ClearValue {
depth_stencil: ash::vk::ClearDepthStencilValue { depth, stencil: 0 },
},
},
ClearAttachment::Stencil(stencil) => ash::vk::ClearAttachment {
aspect_mask: ash::vk::ImageAspectFlags::STENCIL,
color_attachment: 0,
clear_value: ash::vk::ClearValue {
depth_stencil: ash::vk::ClearDepthStencilValue {
depth: 0.0,
stencil,
},
},
},
ClearAttachment::DepthStencil((depth, stencil)) => ash::vk::ClearAttachment {
aspect_mask: ash::vk::ImageAspectFlags::DEPTH | ash::vk::ImageAspectFlags::STENCIL,
color_attachment: 0,
clear_value: ash::vk::ClearValue {
depth_stencil: ash::vk::ClearDepthStencilValue { depth, stencil },
},
},
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ClearRect {
pub rect_offset: [u32; 2],
pub rect_extent: [u32; 2],
pub base_array_layer: u32,
pub layer_count: u32,
}
#[cfg(test)]
mod tests {
use super::AttachmentImage;
use crate::format::Format;
#[test]
fn create_regular() {
let (device, _) = gfx_dev_and_queue!();
let _img = AttachmentImage::new(device, [32, 32], Format::R8G8B8A8_UNORM).unwrap();
}
#[test]
fn create_transient() {
let (device, _) = gfx_dev_and_queue!();
let _img = AttachmentImage::transient(device, [32, 32], Format::R8G8B8A8_UNORM).unwrap();
}
#[test]
fn d16_unorm_always_supported() {
let (device, _) = gfx_dev_and_queue!();
let _img = AttachmentImage::new(device, [32, 32], Format::D16_UNORM).unwrap();
}
}