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
137                        .lock()
138                        .unwrap()
139                        .free(block_id, alloc.offset, mem_reqs.size);
140                } else {
141                    self.device.free_memory(alloc.memory, None);
142                }
143                self.device.destroy_image(image, None);
144                return Err(HalError::BackendError(format!(
145                    "vkBindImageMemory failed: {e}"
146                )));
147            }
148        }
149
150        let aspect_mask = if desc.format == TextureFormat::Depth32Float {
151            vk::ImageAspectFlags::DEPTH
152        } else {
153            vk::ImageAspectFlags::COLOR
154        };
155
156        let subresource_range = vk::ImageSubresourceRange::default()
157            .aspect_mask(aspect_mask)
158            .base_mip_level(0)
159            .level_count(mip_levels)
160            .base_array_layer(0)
161            .layer_count(1);
162
163        let view_info = vk::ImageViewCreateInfo::default()
164            .image(image)
165            .view_type(vk::ImageViewType::TYPE_2D)
166            .format(vk_format)
167            .subresource_range(subresource_range);
168
169        let view = unsafe {
170            self.device
171                .create_image_view(&view_info, None)
172                .map_err(|e| HalError::BackendError(format!("vkCreateImageView failed: {e}")))?
173        };
174
175        Ok(VulkanTexture {
176            image,
177            view,
178            memory: alloc.memory,
179            block_id: alloc.block_id,
180            offset: alloc.offset,
181            allocated_size: mem_reqs.size,
182            width: desc.width,
183            height: desc.height,
184            mip_levels,
185            sample_count: desc.sample_count.max(1),
186            format: vk_format,
187            layout: std::sync::Mutex::new(vk::ImageLayout::UNDEFINED),
188            owned: true,
189        })
190    }
191
192    pub fn destroy_texture(&self, texture: VulkanTexture) {
193        if !texture.owned {
194            return;
195        }
196        unsafe {
197            if texture.view != vk::ImageView::null() {
198                self.device.destroy_image_view(texture.view, None);
199            }
200            if texture.image != vk::Image::null() {
201                self.device.destroy_image(texture.image, None);
202            }
203            if texture.memory != vk::DeviceMemory::null() {
204                if let Some(block_id) = texture.block_id {
205                    self.allocator.lock().unwrap().free(
206                        block_id,
207                        texture.offset,
208                        texture.allocated_size,
209                    );
210                } else {
211                    self.device.free_memory(texture.memory, None);
212                }
213            }
214        }
215    }
216
217    pub fn register_bindless_texture(&self, texture: &VulkanTexture) -> Result<u32, HalError> {
218        let heap = self.bindless_heap.as_ref().ok_or_else(|| {
219            HalError::BackendError("Bindless descriptor heap is not enabled".to_string())
220        })?;
221        let slot = heap.allocate_texture_slot()?;
222        heap.write_texture_descriptor(
223            &self.device,
224            slot,
225            texture.view,
226            vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
227        );
228        Ok(slot)
229    }
230
231    pub fn unregister_bindless_texture(&self, slot: u32) {
232        if let Some(heap) = &self.bindless_heap {
233            heap.free_texture_slot(slot);
234        }
235    }
236
237    pub fn register_bindless_storage_image(
238        &self,
239        texture: &VulkanTexture,
240    ) -> Result<u32, HalError> {
241        let heap = self.bindless_heap.as_ref().ok_or_else(|| {
242            HalError::BackendError("Bindless descriptor heap is not enabled".to_string())
243        })?;
244        let slot = heap.allocate_storage_image_slot()?;
245        heap.write_storage_image_descriptor(
246            &self.device,
247            slot,
248            texture.view,
249            vk::ImageLayout::GENERAL,
250        );
251        Ok(slot)
252    }
253
254    pub fn unregister_bindless_storage_image(&self, slot: u32) {
255        if let Some(heap) = &self.bindless_heap {
256            heap.free_storage_image_slot(slot);
257        }
258    }
259
260    pub fn write_texture_descriptors_batch(
261        &self,
262        writes: &[(u32, &VulkanTexture)],
263    ) -> Result<(), HalError> {
264        let heap = self.bindless_heap.as_ref().ok_or_else(|| {
265            HalError::BackendError("Bindless descriptor heap is not enabled".to_string())
266        })?;
267        let raw_writes: Vec<(u32, ash::vk::ImageView, ash::vk::ImageLayout)> = writes
268            .iter()
269            .map(|(slot, tex)| {
270                (
271                    *slot,
272                    tex.view,
273                    ash::vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
274                )
275            })
276            .collect();
277        heap.write_texture_descriptors_batch(&self.device, &raw_writes);
278        Ok(())
279    }
280
281    pub fn create_sampler(&self, desc: &SamplerDesc) -> Result<VulkanSampler, HalError> {
282        let vk_filter = |f: FilterMode| match f {
283            FilterMode::Nearest => vk::Filter::NEAREST,
284            FilterMode::Linear => vk::Filter::LINEAR,
285        };
286        let vk_mipmap_mode = |f: FilterMode| match f {
287            FilterMode::Nearest => vk::SamplerMipmapMode::NEAREST,
288            FilterMode::Linear => vk::SamplerMipmapMode::LINEAR,
289        };
290        let vk_address_mode = |a: AddressMode| match a {
291            AddressMode::ClampToEdge => vk::SamplerAddressMode::CLAMP_TO_EDGE,
292            AddressMode::Repeat => vk::SamplerAddressMode::REPEAT,
293            AddressMode::MirrorRepeat => vk::SamplerAddressMode::MIRRORED_REPEAT,
294        };
295
296        let create_info = vk::SamplerCreateInfo::default()
297            .mag_filter(vk_filter(desc.mag_filter))
298            .min_filter(vk_filter(desc.min_filter))
299            .mipmap_mode(vk_mipmap_mode(desc.mipmap_filter))
300            .address_mode_u(vk_address_mode(desc.address_mode_u))
301            .address_mode_v(vk_address_mode(desc.address_mode_v))
302            .address_mode_w(vk_address_mode(desc.address_mode_w))
303            .mip_lod_bias(0.0)
304            .anisotropy_enable(false)
305            .max_anisotropy(1.0)
306            .compare_enable(false)
307            .compare_op(vk::CompareOp::NEVER)
308            .min_lod(0.0)
309            .max_lod(vk::LOD_CLAMP_NONE)
310            .border_color(vk::BorderColor::FLOAT_TRANSPARENT_BLACK)
311            .unnormalized_coordinates(false);
312
313        let sampler = unsafe {
314            self.device
315                .create_sampler(&create_info, None)
316                .map_err(|e| HalError::BackendError(format!("vkCreateSampler failed: {e}")))?
317        };
318
319        Ok(VulkanSampler { sampler })
320    }
321
322    pub fn destroy_sampler(&self, sampler: VulkanSampler) {
323        unsafe {
324            self.device.destroy_sampler(sampler.sampler, None);
325        }
326    }
327
328    pub fn register_bindless_sampler(&self, sampler: &VulkanSampler) -> Result<u32, HalError> {
329        let heap = self.bindless_heap.as_ref().ok_or_else(|| {
330            HalError::BackendError("Bindless descriptor heap is not enabled".to_string())
331        })?;
332        let slot = heap.allocate_sampler_slot()?;
333        heap.write_sampler_descriptor(&self.device, slot, sampler.sampler);
334        Ok(slot)
335    }
336
337    pub fn unregister_bindless_sampler(&self, slot: u32) {
338        if let Some(heap) = &self.bindless_heap {
339            heap.free_sampler_slot(slot);
340        }
341    }
342}