bort_vk/
image.rs

1use crate::{
2    AllocationAccess, AllocatorAccess, Device, DeviceOwned, ImageAccess, ImageDimensions,
3    MemoryAllocation, MemoryAllocator, PhysicalDevice,
4};
5use ash::{
6    prelude::VkResult,
7    vk::{self, Handle},
8};
9use bort_vma::{Alloc, AllocationCreateInfo};
10use std::sync::Arc;
11
12// ~~ Image ~~
13
14pub struct Image {
15    handle: vk::Image,
16    image_properties: ImageProperties,
17    memory_allocation: MemoryAllocation,
18}
19
20impl Image {
21    pub fn new(
22        alloc_access: Arc<dyn AllocatorAccess>,
23        image_properties: ImageProperties,
24        allocation_info: AllocationCreateInfo,
25    ) -> VkResult<Self> {
26        let (handle, vma_allocation) = unsafe {
27            alloc_access
28                .vma_allocator()
29                .create_image(&image_properties.create_info_builder(), &allocation_info)
30        }?;
31
32        let memory_allocation = MemoryAllocation::from_vma_allocation(vma_allocation, alloc_access);
33
34        Ok(Self {
35            handle,
36            image_properties,
37            memory_allocation,
38        })
39    }
40
41    pub unsafe fn new_from_create_info(
42        alloc_access: Arc<dyn AllocatorAccess>,
43        image_create_info_builder: vk::ImageCreateInfoBuilder,
44        allocation_info: AllocationCreateInfo,
45    ) -> VkResult<Self> {
46        let image_properties =
47            ImageProperties::from_create_info_builder(&image_create_info_builder);
48
49        let (handle, vma_allocation) = unsafe {
50            alloc_access
51                .vma_allocator()
52                .create_image(&image_create_info_builder, &allocation_info)
53        }?;
54
55        let memory_allocation = MemoryAllocation::from_vma_allocation(vma_allocation, alloc_access);
56
57        Ok(Self {
58            handle,
59            image_properties,
60            memory_allocation,
61        })
62    }
63
64    /// Create a (preferably) lazily-allocated transient attachment image.
65    pub fn new_tranient(
66        memory_allocator: Arc<MemoryAllocator>,
67        dimensions: ImageDimensions,
68        format: vk::Format,
69        additional_usage: vk::ImageUsageFlags,
70    ) -> VkResult<Self> {
71        let (image_properties, allocation_info) =
72            transient_image_info(dimensions, format, additional_usage);
73
74        Self::new(memory_allocator, image_properties, allocation_info)
75    }
76
77    // Getters
78
79    #[inline]
80    pub fn properties(&self) -> &ImageProperties {
81        &self.image_properties
82    }
83
84    #[inline]
85    pub fn allocator_access(&self) -> &Arc<dyn AllocatorAccess> {
86        &self.memory_allocation.allocator_access()
87    }
88
89    #[inline]
90    pub fn memory_allocation(&self) -> &MemoryAllocation {
91        &self.memory_allocation
92    }
93}
94
95impl ImageAccess for Image {
96    #[inline]
97    fn handle(&self) -> vk::Image {
98        self.handle
99    }
100
101    #[inline]
102    fn dimensions(&self) -> ImageDimensions {
103        self.image_properties.dimensions
104    }
105}
106
107impl AllocationAccess for Image {
108    fn memory_allocation_mut(&mut self) -> &mut MemoryAllocation {
109        &mut self.memory_allocation
110    }
111}
112
113impl DeviceOwned for Image {
114    #[inline]
115    fn device(&self) -> &Arc<Device> {
116        &self.memory_allocation.device()
117    }
118
119    #[inline]
120    fn handle_raw(&self) -> u64 {
121        self.handle.as_raw()
122    }
123}
124
125impl Drop for Image {
126    fn drop(&mut self) {
127        unsafe {
128            self.allocator_access()
129                .clone()
130                .vma_allocator()
131                .destroy_image(self.handle, self.memory_allocation.inner_mut());
132        }
133    }
134}
135
136// ~~ Presets ~~
137
138/// Properties for a device local and preferably lazily-allocated transient attachment image.
139pub fn transient_image_info(
140    dimensions: ImageDimensions,
141    format: vk::Format,
142    additional_usage: vk::ImageUsageFlags,
143) -> (ImageProperties, AllocationCreateInfo) {
144    let image_properties = ImageProperties::new_default(
145        format,
146        dimensions,
147        vk::ImageUsageFlags::TRANSIENT_ATTACHMENT | additional_usage,
148    );
149
150    let allocation_info = AllocationCreateInfo {
151        //usage: bort_vma::MemoryUsage::GpuLazy,
152        required_flags: vk::MemoryPropertyFlags::DEVICE_LOCAL,
153        preferred_flags: vk::MemoryPropertyFlags::LAZILY_ALLOCATED,
154        ..AllocationCreateInfo::default()
155    };
156
157    (image_properties, allocation_info)
158}
159
160// ~~ Image Properties ~~
161
162/// Note: default values for `format`, `dimensions` and `usage` are nothing!
163#[derive(Debug, Clone)]
164pub struct ImageProperties {
165    pub flags: vk::ImageCreateFlags,
166    pub format: vk::Format,
167    pub dimensions: ImageDimensions,
168    pub mip_levels: u32,
169    pub samples: vk::SampleCountFlags,
170    pub tiling: vk::ImageTiling,
171    pub usage: vk::ImageUsageFlags,
172    pub sharing_mode: vk::SharingMode,
173    pub queue_family_indices: Vec<u32>,
174    pub initial_layout: vk::ImageLayout,
175}
176
177impl Default for ImageProperties {
178    fn default() -> Self {
179        Self {
180            mip_levels: 1,
181            samples: vk::SampleCountFlags::TYPE_1,
182            tiling: vk::ImageTiling::OPTIMAL,
183            sharing_mode: vk::SharingMode::EXCLUSIVE,
184            queue_family_indices: Vec::new(),
185            initial_layout: vk::ImageLayout::UNDEFINED,
186            flags: vk::ImageCreateFlags::empty(),
187
188            // nonsense defaults. make sure you override these!
189            format: vk::Format::default(),
190            dimensions: ImageDimensions::default(),
191            usage: vk::ImageUsageFlags::empty(),
192        }
193    }
194}
195
196impl ImageProperties {
197    pub fn subresource_range(&self) -> vk::ImageSubresourceRange {
198        let aspect_mask = aspect_mask_from_format(self.format);
199        vk::ImageSubresourceRange {
200            aspect_mask,
201            base_mip_level: 0,
202            level_count: self.mip_levels,
203            base_array_layer: 0,
204            layer_count: self.dimensions.array_layers(),
205        }
206    }
207
208    #[inline]
209    pub fn new_default(
210        format: vk::Format,
211        dimensions: ImageDimensions,
212        usage: vk::ImageUsageFlags,
213    ) -> Self {
214        Self {
215            format,
216            dimensions,
217            usage,
218            ..Self::default()
219        }
220    }
221
222    pub fn create_info_builder(&self) -> vk::ImageCreateInfoBuilder {
223        vk::ImageCreateInfo::builder()
224            .flags(self.flags)
225            .image_type(self.dimensions.image_type())
226            .format(self.format)
227            .extent(self.dimensions.extent_3d())
228            .mip_levels(self.mip_levels)
229            .array_layers(self.dimensions.array_layers())
230            .samples(self.samples)
231            .tiling(self.tiling)
232            .usage(self.usage)
233            .sharing_mode(self.sharing_mode)
234            .initial_layout(self.initial_layout)
235            .queue_family_indices(&self.queue_family_indices)
236    }
237
238    fn from_create_info_builder(value: &vk::ImageCreateInfoBuilder) -> Self {
239        let dimensions =
240            ImageDimensions::new_from_extent_and_layers(value.extent, value.array_layers);
241
242        let mut queue_family_indices = Vec::<u32>::new();
243        for i in 0..value.queue_family_index_count {
244            let queue_family_index = unsafe { *value.p_queue_family_indices.offset(i as isize) };
245            queue_family_indices.push(queue_family_index);
246        }
247
248        Self {
249            mip_levels: value.mip_levels,
250            samples: value.samples,
251            tiling: value.tiling,
252            sharing_mode: value.sharing_mode,
253            queue_family_indices,
254            initial_layout: value.initial_layout,
255            flags: value.flags,
256            format: value.format,
257            dimensions,
258            usage: value.usage,
259        }
260    }
261}
262
263// Helper Functions
264
265/// Returns a depth stencil format guarenteed by the vulkan spec to be supported as a depth stencil
266/// attachment. Prefers VK_FORMAT_D24_UNORM_S8_UINT.
267///
268/// According to the [vulkan spec](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap47.html#formats-properties):
269///
270/// _VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT feature must be supported for at least one of_ ...
271/// _VK_FORMAT_D24_UNORM_S8_UINT and VK_FORMAT_D32_SFLOAT_S8_UINT._
272pub fn guaranteed_depth_stencil_format(physical_device: &PhysicalDevice) -> vk::Format {
273    let d24_s8_props = unsafe {
274        physical_device
275            .instance()
276            .inner()
277            .get_physical_device_format_properties(
278                physical_device.handle(),
279                vk::Format::D24_UNORM_S8_UINT,
280            )
281    };
282
283    if d24_s8_props
284        .optimal_tiling_features
285        .contains(vk::FormatFeatureFlags::DEPTH_STENCIL_ATTACHMENT)
286    {
287        return vk::Format::D24_UNORM_S8_UINT;
288    } else {
289        return vk::Format::D32_SFLOAT_S8_UINT;
290    }
291}
292
293/// Returns a pure depth format guarenteed by the vulkan spec to be supported as a depth stencil
294/// attachment. Prefers VK_FORMAT_D32_SFLOAT.
295///
296/// According to the [vulkan spec](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap47.html#formats-properties):
297///
298/// _VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT feature must be supported for at least one of
299/// VK_FORMAT_X8_D24_UNORM_PACK32 and VK_FORMAT_D32_SFLOAT_
300pub fn guaranteed_pure_depth_format(physical_device: &PhysicalDevice) -> vk::Format {
301    let d32_props = unsafe {
302        physical_device
303            .instance()
304            .inner()
305            .get_physical_device_format_properties(physical_device.handle(), vk::Format::D32_SFLOAT)
306    };
307
308    if d32_props
309        .optimal_tiling_features
310        .contains(vk::FormatFeatureFlags::DEPTH_STENCIL_ATTACHMENT)
311    {
312        return vk::Format::D32_SFLOAT;
313    } else {
314        return vk::Format::X8_D24_UNORM_PACK32;
315    }
316}
317
318pub fn extent_2d_from_width_height(dimensions: [u32; 2]) -> vk::Extent2D {
319    vk::Extent2D {
320        width: dimensions[0],
321        height: dimensions[1],
322    }
323}
324
325/// Doesn't support planes/metadata.
326pub fn aspect_mask_from_format(format: vk::Format) -> vk::ImageAspectFlags {
327    let mut aspect = vk::ImageAspectFlags::empty();
328
329    if !matches!(
330        format,
331        vk::Format::D16_UNORM
332            | vk::Format::X8_D24_UNORM_PACK32
333            | vk::Format::D32_SFLOAT
334            | vk::Format::S8_UINT
335            | vk::Format::D16_UNORM_S8_UINT
336            | vk::Format::D24_UNORM_S8_UINT
337            | vk::Format::D32_SFLOAT_S8_UINT
338    ) {
339        aspect |= vk::ImageAspectFlags::COLOR;
340    }
341
342    if matches!(
343        format,
344        vk::Format::D16_UNORM
345            | vk::Format::X8_D24_UNORM_PACK32
346            | vk::Format::D32_SFLOAT
347            | vk::Format::D16_UNORM_S8_UINT
348            | vk::Format::D24_UNORM_S8_UINT
349            | vk::Format::D32_SFLOAT_S8_UINT
350    ) {
351        aspect |= vk::ImageAspectFlags::DEPTH;
352    }
353
354    if matches!(
355        format,
356        vk::Format::S8_UINT
357            | vk::Format::D16_UNORM_S8_UINT
358            | vk::Format::D24_UNORM_S8_UINT
359            | vk::Format::D32_SFLOAT_S8_UINT
360    ) {
361        aspect |= vk::ImageAspectFlags::STENCIL;
362    }
363
364    aspect
365}