aboutsummaryrefslogtreecommitdiff
path: root/stockton-render/src/draw/buffers/dedicated_image.rs
blob: 878d3047e3271bb90b7281b8176a3887057de9cc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
//! A dedicated image. Used for depth buffers.

use crate::draw::texture::PIXEL_SIZE;
use crate::types::*;

use std::mem::ManuallyDrop;

use anyhow::{Context, Result};
use hal::{
    format::{Format, Swizzle},
    image::{SubresourceRange, Usage, Usage as ImgUsage, ViewKind},
    memory,
    memory::Properties,
    MemoryTypeId,
};
use thiserror::Error;

/// Holds an image that's loaded into GPU memory dedicated only to that image, bypassing the memory allocator.
pub struct DedicatedLoadedImage {
    /// The GPU Image handle
    image: ManuallyDrop<ImageT>,

    /// The full view of the image
    pub image_view: ManuallyDrop<ImageViewT>,

    /// The memory backing the image
    memory: ManuallyDrop<MemoryT>,
}

#[derive(Debug, Error)]
pub enum ImageLoadError {
    #[error("No suitable memory type for image memory")]
    NoMemoryTypes,
}

impl DedicatedLoadedImage {
    pub fn new(
        device: &mut DeviceT,
        adapter: &Adapter,
        format: Format,
        usage: Usage,
        resources: SubresourceRange,
        width: usize,
        height: usize,
    ) -> Result<DedicatedLoadedImage> {
        let (memory, image_ref) = {
            // Round up the size to align properly
            let initial_row_size = PIXEL_SIZE * width;
            let limits = adapter.physical_device.properties().limits;
            let row_alignment_mask = limits.optimal_buffer_copy_pitch_alignment as u32 - 1;

            let row_size =
                ((initial_row_size as u32 + row_alignment_mask) & !row_alignment_mask) as usize;
            debug_assert!(row_size as usize >= initial_row_size);

            // Make the image
            let mut image_ref = unsafe {
                use hal::image::{Kind, Tiling, ViewCapabilities};

                device.create_image(
                    Kind::D2(width as u32, height as u32, 1, 1),
                    1,
                    format,
                    Tiling::Optimal,
                    usage,
                    memory::SparseFlags::empty(),
                    ViewCapabilities::empty(),
                )
            }
            .context("Error creating image")?;

            // Allocate memory
            let memory = unsafe {
                let requirements = device.get_image_requirements(&image_ref);

                let memory_type_id = adapter
                    .physical_device
                    .memory_properties()
                    .memory_types
                    .iter()
                    .enumerate()
                    .find(|&(id, memory_type)| {
                        requirements.type_mask & (1 << id) != 0
                            && memory_type.properties.contains(Properties::DEVICE_LOCAL)
                    })
                    .map(|(id, _)| MemoryTypeId(id))
                    .ok_or(ImageLoadError::NoMemoryTypes)?;

                let memory = device
                    .allocate_memory(memory_type_id, requirements.size)
                    .context("Error allocating memory for image")?;

                device
                    .bind_image_memory(&memory, 0, &mut image_ref)
                    .context("Error binding memory to image")?;

                memory
            };

            (memory, image_ref)
        };

        // Create ImageView and sampler
        let image_view = unsafe {
            device.create_image_view(
                &image_ref,
                ViewKind::D2,
                format,
                Swizzle::NO,
                ImgUsage::DEPTH_STENCIL_ATTACHMENT,
                resources,
            )
        }
        .context("Error creating image view")?;

        Ok(DedicatedLoadedImage {
            image: ManuallyDrop::new(image_ref),
            image_view: ManuallyDrop::new(image_view),
            memory: ManuallyDrop::new(memory),
        })
    }

    /// Properly frees/destroys all the objects in this struct
    /// Dropping without doing this is a bad idea
    pub fn deactivate(self, device: &mut DeviceT) {
        unsafe {
            use core::ptr::read;

            device.destroy_image_view(ManuallyDrop::into_inner(read(&self.image_view)));
            device.destroy_image(ManuallyDrop::into_inner(read(&self.image)));
            device.free_memory(ManuallyDrop::into_inner(read(&self.memory)));
        }
    }
}