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
135
136
use {
    crate::{
        graphics::vulkan::{raii, Allocator, Block},
        trace,
    },
    anyhow::{Context, Result},
    ash::vk,
    std::sync::Arc,
};

/// A block allocation that frees itself when dropped.
#[derive(Debug)]
pub struct OwnedBlock {
    block: Block,
    allocator: Arc<Allocator>,
}

impl OwnedBlock {
    /// Creates an image and allocates memory to back it.
    ///
    /// The image is bound to the memory prior to return, so the caller can use
    /// it right away.
    pub fn allocate_image(
        allocator: Arc<Allocator>,
        image_create_info: &vk::ImageCreateInfo,
        flags: vk::MemoryPropertyFlags,
    ) -> Result<(Self, raii::Image)> {
        let image = raii::Image::new(
            allocator.logical_device.clone(),
            image_create_info,
        )
        .with_context(trace!("Unable to create image!"))?;

        let (requirements, dedicated) = {
            let mut dedicated = vk::MemoryDedicatedRequirements::default();
            let requirements = unsafe {
                let mut out = vk::MemoryRequirements2::default()
                    .push_next(&mut dedicated);

                allocator.logical_device.get_image_memory_requirements2(
                    &vk::ImageMemoryRequirementsInfo2 {
                        image: image.raw,
                        ..Default::default()
                    },
                    &mut out,
                );

                out.memory_requirements
            };
            (
                requirements,
                dedicated.prefers_dedicated_allocation == vk::TRUE
                    || dedicated.requires_dedicated_allocation == vk::TRUE,
            )
        };

        let block = allocator
            .allocate_memory(&requirements, flags, dedicated)
            .with_context(trace!("Unable to allocate memory for image!"))?;

        unsafe {
            allocator
                .logical_device
                .bind_image_memory(image.raw, block.memory(), block.offset())
                .with_context(trace!("Unable to bind image memory!"))?;
        };

        Ok((Self { block, allocator }, image))
    }

    /// Creates a buffer and allocates memory to back it.
    ///
    /// The buffer is bound to the memory prior to return, so the caller can use
    /// it right away.
    pub fn allocate_buffer(
        allocator: Arc<Allocator>,
        buffer_create_info: &vk::BufferCreateInfo,
        flags: vk::MemoryPropertyFlags,
    ) -> Result<(OwnedBlock, raii::Buffer)> {
        let buffer = raii::Buffer::new(
            allocator.logical_device.clone(),
            buffer_create_info,
        )
        .with_context(trace!("Unable to create buffer!"))?;

        let (requirements, dedicated) = {
            let mut dedicated = vk::MemoryDedicatedRequirements::default();
            let requirements = unsafe {
                let mut out = vk::MemoryRequirements2::default()
                    .push_next(&mut dedicated);

                allocator.logical_device.get_buffer_memory_requirements2(
                    &vk::BufferMemoryRequirementsInfo2 {
                        buffer: buffer.raw,
                        ..Default::default()
                    },
                    &mut out,
                );

                out.memory_requirements
            };
            (
                requirements,
                dedicated.prefers_dedicated_allocation == vk::TRUE
                    || dedicated.requires_dedicated_allocation == vk::TRUE,
            )
        };

        let block = allocator
            .allocate_memory(&requirements, flags, dedicated)
            .with_context(trace!("Unable to allocate memory for buffer!"))?;

        unsafe {
            allocator
                .logical_device
                .bind_buffer_memory(buffer.raw, block.memory(), block.offset())
                .with_context(trace!("Unable to bind buffer to memory!"))?;
        };

        Ok((Self { block, allocator }, buffer))
    }
}

impl std::ops::Deref for OwnedBlock {
    type Target = Block;

    fn deref(&self) -> &Self::Target {
        &self.block
    }
}

impl Drop for OwnedBlock {
    fn drop(&mut self) {
        self.allocator.free(&self.block);
    }
}