Skip to main content

lgpu_hal/vulkan/
texture.rs

1//! Vulkan texture and sampler resource management.
2
3use super::VulkanDevice;
4use crate::{
5    AddressMode, FilterMode, HalError, SamplerDesc, TextureDesc, TextureFormat, TextureUsage,
6};
7use ash::vk;
8
9pub struct VulkanTexture {
10    pub image: vk::Image,
11    pub view: vk::ImageView,
12    pub memory: vk::DeviceMemory,
13    pub block_id: Option<usize>,
14    pub offset: u64,
15    pub allocated_size: u64,
16    pub width: u32,
17    pub height: u32,
18    pub mip_levels: u32,
19    pub sample_count: u32,
20    pub format: vk::Format,
21    pub layout: std::sync::Mutex<vk::ImageLayout>,
22    pub owned: bool,
23}
24
25unsafe impl Send for VulkanTexture {}
26unsafe impl Sync for VulkanTexture {}
27
28pub struct VulkanSampler {
29    pub sampler: vk::Sampler,
30}
31
32unsafe impl Send for VulkanSampler {}
33unsafe impl Sync for VulkanSampler {}
34
35impl VulkanTexture {
36    #[inline]
37    pub fn current_layout(&self) -> vk::ImageLayout {
38        *self.layout.lock().unwrap()
39    }
40
41    #[inline]
42    pub fn transition_layout(&self, new_layout: vk::ImageLayout) -> vk::ImageLayout {
43        let mut lock = self.layout.lock().unwrap();
44        let old = *lock;
45        *lock = new_layout;
46        old
47    }
48}
49
50impl VulkanDevice {
51    pub fn create_texture(&self, desc: &TextureDesc) -> Result<VulkanTexture, HalError> {
52        if desc.width == 0 || desc.height == 0 || desc.depth == 0 {
53            return Err(HalError::InvalidParameter(
54                "Texture dimensions must be greater than 0".to_string(),
55            ));
56        }
57
58        let vk_format = desc.format.to_vk();
59
60        let mut usage = vk::ImageUsageFlags::empty();
61        if desc.usage.contains(TextureUsage::COPY_SRC) {
62            usage |= vk::ImageUsageFlags::TRANSFER_SRC;
63        }
64        if desc.usage.contains(TextureUsage::COPY_DST) {
65            usage |= vk::ImageUsageFlags::TRANSFER_DST;
66        }
67        if desc.usage.contains(TextureUsage::TEXTURE_BINDING) {
68            usage |= vk::ImageUsageFlags::SAMPLED;
69        }
70        if desc.usage.contains(TextureUsage::STORAGE_BINDING) {
71            usage |= vk::ImageUsageFlags::STORAGE;
72        }
73        if desc.usage.contains(TextureUsage::RENDER_ATTACHMENT) {
74            if desc.format == TextureFormat::Depth32Float {
75                usage |= vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT;
76            } else {
77                usage |= vk::ImageUsageFlags::COLOR_ATTACHMENT;
78            }
79        }
80
81        let mip_levels = desc.mip_levels.max(1);
82        let sample_count = match desc.sample_count {
83            2 => vk::SampleCountFlags::TYPE_2,
84            4 => vk::SampleCountFlags::TYPE_4,
85            8 => vk::SampleCountFlags::TYPE_8,
86            16 => vk::SampleCountFlags::TYPE_16,
87            32 => vk::SampleCountFlags::TYPE_32,
88            64 => vk::SampleCountFlags::TYPE_64,
89            _ => vk::SampleCountFlags::TYPE_1,
90        };
91
92        let image_info = vk::ImageCreateInfo::default()
93            .image_type(vk::ImageType::TYPE_2D)
94            .format(vk_format)
95            .extent(vk::Extent3D {
96                width: desc.width,
97                height: desc.height,
98                depth: desc.depth.max(1),
99            })
100            .mip_levels(mip_levels)
101            .array_layers(1)
102            .samples(sample_count)
103            .tiling(vk::ImageTiling::OPTIMAL)
104            .usage(usage)
105            .sharing_mode(vk::SharingMode::EXCLUSIVE)
106            .initial_layout(vk::ImageLayout::UNDEFINED);
107
108        let image = unsafe {
109            self.device
110                .create_image(&image_info, None)
111                .map_err(|e| HalError::BackendError(format!("vkCreateImage failed: {e}")))?
112        };
113
114        let mem_reqs = unsafe { self.device.get_image_memory_requirements(image) };
115        let (mem_type_index, is_coherent) = self.find_memory_type(
116            mem_reqs.memory_type_bits,
117            vk::MemoryPropertyFlags::DEVICE_LOCAL,
118        )?;
119
120        let alloc = self.allocator.lock().unwrap().allocate(
121            &self.device,
122            mem_reqs.size,
123            mem_reqs.alignment,
124            mem_type_index,
125            false,
126            is_coherent,
127            super::allocator::AllocationType::Optimal,
128        )?;
129
130        unsafe {
131            if let Err(e) = self
132                .device
133                .bind_image_memory(image, alloc.memory, alloc.offset)
134            {
135                if let Some(block_id) = alloc.block_id {
136                    self.allocator.lock().unwrap().free(
137                        block_id,
138                        alloc.offset,
139                        mem_reqs.size,
140                        &self.device,
141                    );
142                } else {
143                    self.device.free_memory(alloc.memory, None);
144                }
145                self.device.destroy_image(image, None);
146                return Err(HalError::BackendError(format!(
147                    "vkBindImageMemory failed: {e}"
148                )));
149            }
150        }
151
152        let aspect_mask = if desc.format == TextureFormat::Depth32Float {
153            vk::ImageAspectFlags::DEPTH
154        } else {
155            vk::ImageAspectFlags::COLOR
156        };
157
158        let subresource_range = vk::ImageSubresourceRange::default()
159            .aspect_mask(aspect_mask)
160            .base_mip_level(0)
161            .level_count(mip_levels)
162            .base_array_layer(0)
163            .layer_count(1);
164
165        let view_info = vk::ImageViewCreateInfo::default()
166            .image(image)
167            .view_type(vk::ImageViewType::TYPE_2D)
168            .format(vk_format)
169            .subresource_range(subresource_range);
170
171        let view = unsafe {
172            self.device
173                .create_image_view(&view_info, None)
174                .map_err(|e| HalError::BackendError(format!("vkCreateImageView failed: {e}")))?
175        };
176
177        Ok(VulkanTexture {
178            image,
179            view,
180            memory: alloc.memory,
181            block_id: alloc.block_id,
182            offset: alloc.offset,
183            allocated_size: mem_reqs.size,
184            width: desc.width,
185            height: desc.height,
186            mip_levels,
187            sample_count: desc.sample_count.max(1),
188            format: vk_format,
189            layout: std::sync::Mutex::new(vk::ImageLayout::UNDEFINED),
190            owned: true,
191        })
192    }
193
194    pub fn destroy_texture(&self, texture: VulkanTexture) {
195        if !texture.owned {
196            return;
197        }
198        unsafe {
199            if texture.view != vk::ImageView::null() {
200                self.device.destroy_image_view(texture.view, None);
201            }
202            if texture.image != vk::Image::null() {
203                self.device.destroy_image(texture.image, None);
204            }
205            if texture.memory != vk::DeviceMemory::null() {
206                if let Some(block_id) = texture.block_id {
207                    self.allocator.lock().unwrap().free(
208                        block_id,
209                        texture.offset,
210                        texture.allocated_size,
211                        &self.device,
212                    );
213                } else {
214                    self.device.free_memory(texture.memory, None);
215                }
216            }
217        }
218    }
219
220    pub fn register_bindless_texture(&self, texture: &VulkanTexture) -> Result<u32, HalError> {
221        let heap = self.bindless_heap.as_ref().ok_or_else(|| {
222            HalError::BackendError("Bindless descriptor heap is not enabled".to_string())
223        })?;
224        let slot = heap.allocate_texture_slot()?;
225        heap.write_texture_descriptor(
226            &self.device,
227            slot,
228            texture.view,
229            vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
230        );
231        Ok(slot)
232    }
233
234    pub fn unregister_bindless_texture(&self, slot: u32) {
235        if let Some(heap) = &self.bindless_heap {
236            heap.free_texture_slot(slot);
237        }
238    }
239
240    pub fn register_bindless_storage_image(
241        &self,
242        texture: &VulkanTexture,
243    ) -> Result<u32, HalError> {
244        let heap = self.bindless_heap.as_ref().ok_or_else(|| {
245            HalError::BackendError("Bindless descriptor heap is not enabled".to_string())
246        })?;
247        let slot = heap.allocate_storage_image_slot()?;
248        heap.write_storage_image_descriptor(
249            &self.device,
250            slot,
251            texture.view,
252            vk::ImageLayout::GENERAL,
253        );
254        Ok(slot)
255    }
256
257    pub fn unregister_bindless_storage_image(&self, slot: u32) {
258        if let Some(heap) = &self.bindless_heap {
259            heap.free_storage_image_slot(slot);
260        }
261    }
262
263    pub fn write_texture_descriptors_batch(
264        &self,
265        writes: &[(u32, &VulkanTexture)],
266    ) -> Result<(), HalError> {
267        let heap = self.bindless_heap.as_ref().ok_or_else(|| {
268            HalError::BackendError("Bindless descriptor heap is not enabled".to_string())
269        })?;
270        let raw_writes: Vec<(u32, ash::vk::ImageView, ash::vk::ImageLayout)> = writes
271            .iter()
272            .map(|(slot, tex)| {
273                (
274                    *slot,
275                    tex.view,
276                    ash::vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
277                )
278            })
279            .collect();
280        heap.write_texture_descriptors_batch(&self.device, &raw_writes);
281        Ok(())
282    }
283
284    pub fn create_sampler(&self, desc: &SamplerDesc) -> Result<VulkanSampler, HalError> {
285        let vk_filter = |f: FilterMode| match f {
286            FilterMode::Nearest => vk::Filter::NEAREST,
287            FilterMode::Linear => vk::Filter::LINEAR,
288        };
289        let vk_mipmap_mode = |f: FilterMode| match f {
290            FilterMode::Nearest => vk::SamplerMipmapMode::NEAREST,
291            FilterMode::Linear => vk::SamplerMipmapMode::LINEAR,
292        };
293        let vk_address_mode = |a: AddressMode| match a {
294            AddressMode::ClampToEdge => vk::SamplerAddressMode::CLAMP_TO_EDGE,
295            AddressMode::Repeat => vk::SamplerAddressMode::REPEAT,
296            AddressMode::MirrorRepeat => vk::SamplerAddressMode::MIRRORED_REPEAT,
297        };
298
299        let create_info = vk::SamplerCreateInfo::default()
300            .mag_filter(vk_filter(desc.mag_filter))
301            .min_filter(vk_filter(desc.min_filter))
302            .mipmap_mode(vk_mipmap_mode(desc.mipmap_filter))
303            .address_mode_u(vk_address_mode(desc.address_mode_u))
304            .address_mode_v(vk_address_mode(desc.address_mode_v))
305            .address_mode_w(vk_address_mode(desc.address_mode_w))
306            .mip_lod_bias(0.0)
307            .anisotropy_enable(false)
308            .max_anisotropy(1.0)
309            .compare_enable(false)
310            .compare_op(vk::CompareOp::NEVER)
311            .min_lod(0.0)
312            .max_lod(vk::LOD_CLAMP_NONE)
313            .border_color(vk::BorderColor::FLOAT_TRANSPARENT_BLACK)
314            .unnormalized_coordinates(false);
315
316        let sampler = unsafe {
317            self.device
318                .create_sampler(&create_info, None)
319                .map_err(|e| HalError::BackendError(format!("vkCreateSampler failed: {e}")))?
320        };
321
322        Ok(VulkanSampler { sampler })
323    }
324
325    pub fn destroy_sampler(&self, sampler: VulkanSampler) {
326        unsafe {
327            self.device.destroy_sampler(sampler.sampler, None);
328        }
329    }
330
331    pub fn register_bindless_sampler(&self, sampler: &VulkanSampler) -> Result<u32, HalError> {
332        let heap = self.bindless_heap.as_ref().ok_or_else(|| {
333            HalError::BackendError("Bindless descriptor heap is not enabled".to_string())
334        })?;
335        let slot = heap.allocate_sampler_slot()?;
336        heap.write_sampler_descriptor(&self.device, slot, sampler.sampler);
337        Ok(slot)
338    }
339
340    pub fn unregister_bindless_sampler(&self, slot: u32) {
341        if let Some(heap) = &self.bindless_heap {
342            heap.free_sampler_slot(slot);
343        }
344    }
345}