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