1use 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}