ursus_core/vulkan/resources/texture/
mod.rs1mod image_alloc;
2mod mipmap;
3mod staging;
4
5use crate::render::gfx::types::Format;
6use crate::vulkan::core::memory::destroy_image_resources;
7use crate::vulkan::core::{DeviceContext, SubmitContext};
8use ash::vk;
9use image_alloc::AllocatedTextureImage;
10use staging::StagingBuffer;
11
12pub struct GpuTexture {
13 pub image: vk::Image,
14 pub view: vk::ImageView,
15 pub memory: vk::DeviceMemory,
16 pub format: vk::Format,
17 pub width: u32,
18 pub height: u32,
19 pub name: String,
20 device: ash::Device,
21}
22
23pub struct TextureSource<'a> {
24 pub pixels: &'a [u8],
25 pub width: u32,
26 pub height: u32,
27 pub format: Format,
28 pub name: &'a str,
29}
30
31impl GpuTexture {
32 pub fn upload(ctx: DeviceContext, submit: SubmitContext, source: TextureSource) -> anyhow::Result<Self> {
34 let mip_levels = compute_mip_levels(source.width, source.height);
35 let usage =
36 vk::ImageUsageFlags::TRANSFER_DST | vk::ImageUsageFlags::TRANSFER_SRC | vk::ImageUsageFlags::SAMPLED;
37
38 Self::upload_impl(ctx, submit, source, mip_levels, usage, true)
39 }
40
41 pub fn upload_no_mip(ctx: DeviceContext, submit: SubmitContext, source: TextureSource) -> anyhow::Result<Self> {
43 let usage = vk::ImageUsageFlags::TRANSFER_DST | vk::ImageUsageFlags::SAMPLED;
44
45 Self::upload_impl(ctx, submit, source, 1, usage, false)
46 }
47
48 fn upload_impl(
49 ctx: DeviceContext,
50 submit: SubmitContext,
51 source: TextureSource,
52 mip_levels: u32,
53 usage: vk::ImageUsageFlags,
54 with_mipchain: bool,
55 ) -> anyhow::Result<Self> {
56 let name = source.name.to_string();
57
58 let staging = StagingBuffer::upload(ctx, source.pixels)?;
59 let alloc = AllocatedTextureImage::create(ctx, source.format, source.width, source.height, mip_levels, usage)?;
60
61 one_shot(ctx.device, submit, |cmd| {
62 if with_mipchain {
63 mipmap::upload_with_mipchain(
64 ctx.device,
65 cmd,
66 alloc.image,
67 staging.buffer,
68 source.width,
69 source.height,
70 mip_levels,
71 );
72 } else {
73 mipmap::upload_single_level(ctx.device, cmd, alloc.image, staging.buffer, source.width, source.height);
74 }
75 })?;
76
77 let view = alloc.create_view(source.format, mip_levels)?;
78 let (image, memory) = alloc.into_raw();
79
80 log::debug!(
81 "GpuTexture '{}': {}x{} {:?} ({} mip levels)",
82 name,
83 source.width,
84 source.height,
85 source.format,
86 mip_levels
87 );
88
89 Ok(Self {
90 image,
91 view,
92 memory,
93 format: source.format.to_vk(),
94 width: source.width,
95 height: source.height,
96 name,
97 device: ctx.device.clone(),
98 })
99 }
100}
101
102impl Drop for GpuTexture {
103 fn drop(&mut self) {
104 unsafe { destroy_image_resources(&self.device, self.image, self.view, self.memory) }
105 }
106}
107
108fn compute_mip_levels(width: u32, height: u32) -> u32 {
111 if width == 0 || height == 0 {
112 return 1;
113 }
114 (width.max(height) as f32).log2().floor() as u32 + 1
115}
116
117fn one_shot(device: &ash::Device, submit: SubmitContext, f: impl FnOnce(vk::CommandBuffer)) -> anyhow::Result<()> {
118 let alloc_info = vk::CommandBufferAllocateInfo::default()
119 .command_pool(submit.command_pool)
120 .level(vk::CommandBufferLevel::PRIMARY)
121 .command_buffer_count(1);
122 let cmd = unsafe { device.allocate_command_buffers(&alloc_info)?[0] };
123
124 unsafe {
125 device.begin_command_buffer(
126 cmd,
127 &vk::CommandBufferBeginInfo::default().flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT),
128 )?;
129 }
130
131 f(cmd);
132
133 unsafe {
134 device.end_command_buffer(cmd)?;
135 device.queue_submit(
136 submit.queue,
137 &[vk::SubmitInfo::default().command_buffers(std::slice::from_ref(&cmd))],
138 vk::Fence::null(),
139 )?;
140 device.queue_wait_idle(submit.queue)?;
141 device.free_command_buffers(submit.command_pool, &[cmd]);
142 }
143
144 Ok(())
145}