atagen

monolith

public
Star0
Watch0
b001ce7817edaa5aa390e22c424edecab96d7f0a
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git
crates/render-vk/src/image.rsRust254 lines7.8 KB
1
use crate::Error;
2
use crate::device::Shared;
3
use crate::format::{components, vk_format};
4
use ash::vk;
5
use gpu_allocator::MemoryLocation;
6
use gpu_allocator::vulkan::{Allocation, AllocationCreateDesc, AllocationScheme};
7
use smithay::backend::allocator::Fourcc;
8
use smithay::backend::renderer::{Texture, TextureMapping};
9
use std::fmt;
10
use std::marker::PhantomData;
11
use std::sync::{Arc, Mutex};
12
13
pub(crate) enum Memory {
14
    Allocated(Allocation),
15
    /// Memory imported from a dmabuf, owned directly.
16
    Imported(vk::DeviceMemory),
17
}
18
19
pub(crate) struct ImageInner {
20
    pub shared: Arc<Shared>,
21
    pub image: vk::Image,
22
    pub view: vk::ImageView,
23
    pub memory: Mutex<Option<Memory>>,
24
    pub fourcc: Fourcc,
25
    pub format: vk::Format,
26
    pub width: u32,
27
    pub height: u32,
28
    pub usage: vk::ImageUsageFlags,
29
    /// The rows are stored bottom to top, as a dmabuf flagged `Y_INVERT`.
30
    pub y_inverted: bool,
31
    pub layout: Mutex<vk::ImageLayout>,
32
    /// Shared with other devices or processes through a dmabuf. Every use
33
    /// acquires the image from the foreign queue family and releases it after.
34
    pub external: bool,
35
}
36
37
impl fmt::Debug for ImageInner {
38
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39
        f.debug_struct("ImageInner")
40
            .field("image", &self.image)
41
            .field("fourcc", &self.fourcc)
42
            .field("size", &(self.width, self.height))
43
            .field("external", &self.external)
44
            .finish()
45
    }
46
}
47
48
impl Drop for ImageInner {
49
    fn drop(&mut self) {
50
        let d = &self.shared.device.device;
51
        unsafe {
52
            d.destroy_image_view(self.view, None);
53
            d.destroy_image(self.image, None);
54
        }
55
        match self.memory.lock().unwrap_or_else(|e| e.into_inner()).take() {
56
            Some(Memory::Allocated(a)) => {
57
                let _ = self
58
                    .shared
59
                    .allocator
60
                    .lock()
61
                    .unwrap_or_else(|e| e.into_inner())
62
                    .free(a);
63
            }
64
            Some(Memory::Imported(m)) => unsafe { d.free_memory(m, None) },
65
            None => {}
66
        }
67
    }
68
}
69
70
impl ImageInner {
71
    pub fn create(
72
        shared: &Arc<Shared>,
73
        fourcc: Fourcc,
74
        width: u32,
75
        height: u32,
76
        usage: vk::ImageUsageFlags,
77
    ) -> Result<Arc<Self>, Error> {
78
        let fmt = vk_format(fourcc).ok_or(Error::UnsupportedFormat(fourcc))?;
79
        if width == 0 || height == 0 {
80
            return Err(Error::InvalidSize(width as i32, height as i32));
81
        }
82
        let d = &shared.device.device;
83
        let info = vk::ImageCreateInfo::default()
84
            .image_type(vk::ImageType::TYPE_2D)
85
            .format(fmt.format)
86
            .extent(vk::Extent3D {
87
                width,
88
                height,
89
                depth: 1,
90
            })
91
            .mip_levels(1)
92
            .array_layers(1)
93
            .samples(vk::SampleCountFlags::TYPE_1)
94
            .tiling(vk::ImageTiling::OPTIMAL)
95
            .usage(usage)
96
            .initial_layout(vk::ImageLayout::UNDEFINED);
97
        let image = unsafe { d.create_image(&info, None)? };
98
        let requirements = unsafe { d.get_image_memory_requirements(image) };
99
        let allocation = shared
100
            .allocator
101
            .lock()
102
            .unwrap_or_else(|e| e.into_inner())
103
            .allocate(&AllocationCreateDesc {
104
                name: "image",
105
                requirements,
106
                location: MemoryLocation::GpuOnly,
107
                linear: false,
108
                allocation_scheme: AllocationScheme::GpuAllocatorManaged,
109
            });
110
        let allocation = match allocation {
111
            Ok(a) => a,
112
            Err(e) => {
113
                unsafe { d.destroy_image(image, None) };
114
                return Err(e.into());
115
            }
116
        };
117
        let memory = Memory::Allocated(allocation);
118
        let (mem, offset) = match &memory {
119
            Memory::Allocated(a) => (unsafe { a.memory() }, a.offset()),
120
            Memory::Imported(m) => (*m, 0),
121
        };
122
        Self::finish(
123
            shared, image, memory, mem, offset, fourcc, width, height, usage, false, false,
124
        )
125
    }
126
127
    /// Binds memory, creates the view and wraps everything up. Takes
128
    /// ownership of `image` and `memory`, releasing both on failure.
129
    #[expect(clippy::too_many_arguments)]
130
    pub fn finish(
131
        shared: &Arc<Shared>,
132
        image: vk::Image,
133
        memory: Memory,
134
        mem: vk::DeviceMemory,
135
        offset: u64,
136
        fourcc: Fourcc,
137
        width: u32,
138
        height: u32,
139
        usage: vk::ImageUsageFlags,
140
        external: bool,
141
        y_inverted: bool,
142
    ) -> Result<Arc<Self>, Error> {
143
        let fmt = vk_format(fourcc).ok_or(Error::UnsupportedFormat(fourcc))?;
144
        let d = &shared.device.device;
145
        let mut inner = Self {
146
            shared: shared.clone(),
147
            image,
148
            view: vk::ImageView::null(),
149
            memory: Mutex::new(Some(memory)),
150
            fourcc,
151
            format: fmt.format,
152
            width,
153
            height,
154
            usage,
155
            y_inverted,
156
            layout: Mutex::new(if external {
157
                vk::ImageLayout::GENERAL
158
            } else {
159
                vk::ImageLayout::UNDEFINED
160
            }),
161
            external,
162
        };
163
        unsafe { d.bind_image_memory(image, mem, offset)? };
164
        let view_info = vk::ImageViewCreateInfo::default()
165
            .image(image)
166
            .view_type(vk::ImageViewType::TYPE_2D)
167
            .format(fmt.format)
168
            .components(components(fmt.opaque))
169
            .subresource_range(color_range());
170
        inner.view = unsafe { d.create_image_view(&view_info, None)? };
171
        Ok(Arc::new(inner))
172
    }
173
174
    /// Whether sampling the image always gives alpha one.
175
    pub fn opaque(&self) -> bool {
176
        vk_format(self.fourcc).is_some_and(|f| f.opaque)
177
    }
178
179
    pub fn layout(&self) -> vk::ImageLayout {
180
        *self.layout.lock().unwrap_or_else(|e| e.into_inner())
181
    }
182
183
    pub fn set_layout(&self, layout: vk::ImageLayout) {
184
        *self.layout.lock().unwrap_or_else(|e| e.into_inner()) = layout;
185
    }
186
}
187
188
pub(crate) fn color_range() -> vk::ImageSubresourceRange {
189
    vk::ImageSubresourceRange::default()
190
        .aspect_mask(vk::ImageAspectFlags::COLOR)
191
        .level_count(1)
192
        .layer_count(1)
193
}
194
195
/// A sampled texture.
196
#[derive(Clone, Debug)]
197
pub struct VulkanTexture(pub(crate) Arc<ImageInner>);
198
199
/// An image the renderer can draw into, from [`smithay::backend::renderer::Offscreen`].
200
#[derive(Debug)]
201
pub struct VulkanImage(pub(crate) Arc<ImageInner>);
202
203
impl VulkanImage {
204
    /// The image's contents as a texture, for drawing it elsewhere.
205
    pub fn texture(&self) -> VulkanTexture {
206
        VulkanTexture(self.0.clone())
207
    }
208
}
209
210
/// A bound render target, borrowing whatever it was bound from.
211
#[derive(Debug)]
212
pub struct VulkanTarget<'a>(
213
    pub(crate) Arc<ImageInner>,
214
    pub(crate) PhantomData<&'a mut ()>,
215
);
216
217
/// Pixels read back from the GPU.
218
#[derive(Debug)]
219
pub struct VulkanMapping {
220
    pub(crate) data: Vec<u8>,
221
    pub(crate) width: u32,
222
    pub(crate) height: u32,
223
    pub(crate) fourcc: Fourcc,
224
}
225
226
macro_rules! texture_impl {
227
    ($t:ty, |$s:ident| $inner:expr) => {
228
        impl Texture for $t {
229
            fn width(&self) -> u32 {
230
                let $s = self;
231
                $inner.width
232
            }
233
            fn height(&self) -> u32 {
234
                let $s = self;
235
                $inner.height
236
            }
237
            fn format(&self) -> Option<Fourcc> {
238
                let $s = self;
239
                Some($inner.fourcc)
240
            }
241
        }
242
    };
243
}
244
245
texture_impl!(VulkanTexture, |s| s.0);
246
texture_impl!(VulkanImage, |s| s.0);
247
texture_impl!(VulkanTarget<'_>, |s| s.0);
248
texture_impl!(VulkanMapping, |s| s);
249
250
impl TextureMapping for VulkanMapping {
251
    fn flipped(&self) -> bool {
252
        false
253
    }
254
}