sim2d/streaming_renderer/texture/
mod.rs

1mod atlas;
2mod loader;
3
4use {
5    anyhow::{Context, Result},
6    ash::vk,
7    demo_vk::graphics::vulkan::{OwnedBlock, VulkanContext, raii},
8};
9
10pub use self::{atlas::TextureAtlas, loader::TextureLoader};
11
12/// A 2D image for use when rendering.
13///
14/// # Safety
15///
16/// Textures own their own Vulkan resources and will destroy them when dropped.
17/// The application is responsible for synchronizing access to Texture
18/// resources with the GPU and ensuring nothing is dropped early.
19pub struct Texture {
20    mip_levels: u32,
21    width: u32,
22    height: u32,
23    image_view: raii::ImageView,
24    image: raii::Image,
25    block: OwnedBlock,
26}
27
28#[bon::bon]
29impl Texture {
30    #[builder]
31    pub fn new(
32        ctx: &VulkanContext,
33        dimensions: (u32, u32),
34        format: vk::Format,
35        image_usage_flags: vk::ImageUsageFlags,
36        memory_property_flags: vk::MemoryPropertyFlags,
37        #[builder(default = 1)] mip_levels: u32,
38    ) -> Result<Self> {
39        let (width, height) = dimensions;
40
41        let (block, image) = OwnedBlock::allocate_image(
42            ctx.allocator.clone(),
43            &vk::ImageCreateInfo {
44                flags: vk::ImageCreateFlags::empty(),
45                image_type: vk::ImageType::TYPE_2D,
46                format,
47                extent: vk::Extent3D {
48                    width,
49                    height,
50                    depth: 1,
51                },
52                mip_levels,
53                array_layers: 1,
54                samples: vk::SampleCountFlags::TYPE_1,
55                tiling: vk::ImageTiling::OPTIMAL,
56                usage: image_usage_flags,
57                sharing_mode: vk::SharingMode::EXCLUSIVE,
58                queue_family_index_count: 1,
59                p_queue_family_indices: &ctx.graphics_queue_family_index,
60                initial_layout: vk::ImageLayout::UNDEFINED,
61                ..Default::default()
62            },
63            memory_property_flags,
64        )
65        .context("Unable to create texture image!")?;
66
67        let is_depth = image_usage_flags
68            .contains(vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT);
69        let image_view = raii::ImageView::new(
70            "Texture Image View",
71            ctx.device.clone(),
72            &vk::ImageViewCreateInfo {
73                image: image.raw,
74                view_type: vk::ImageViewType::TYPE_2D,
75                format,
76                components: vk::ComponentMapping::default(),
77                subresource_range: vk::ImageSubresourceRange {
78                    aspect_mask: if is_depth {
79                        vk::ImageAspectFlags::DEPTH
80                    } else {
81                        vk::ImageAspectFlags::COLOR
82                    },
83                    base_mip_level: 0,
84                    level_count: mip_levels,
85                    base_array_layer: 0,
86                    layer_count: 1,
87                },
88                ..Default::default()
89            },
90        )
91        .context("Unable to create texture image view")?;
92
93        Ok(Self {
94            mip_levels,
95            width,
96            height,
97            image_view,
98            image,
99            block,
100        })
101    }
102
103    /// Returns the underlying Vulkan image.
104    pub fn image(&self) -> &raii::Image {
105        &self.image
106    }
107
108    /// Returns the underlying Vulkan image view.
109    pub fn view(&self) -> &raii::ImageView {
110        &self.image_view
111    }
112
113    /// Returns the underlying Vulkan memory block.
114    pub fn memory(&self) -> &OwnedBlock {
115        &self.block
116    }
117
118    pub fn width(&self) -> u32 {
119        self.width
120    }
121
122    pub fn height(&self) -> u32 {
123        self.height
124    }
125
126    pub fn extent(&self) -> vk::Extent2D {
127        vk::Extent2D {
128            width: self.width,
129            height: self.height,
130        }
131    }
132
133    #[builder]
134    pub fn pipeline_barrier(
135        &self,
136        ctx: &VulkanContext,
137        command_buffer: vk::CommandBuffer,
138        old_layout: vk::ImageLayout,
139        new_layout: vk::ImageLayout,
140        src_access_mask: vk::AccessFlags,
141        dst_access_mask: vk::AccessFlags,
142        src_stage_mask: vk::PipelineStageFlags,
143        dst_stage_mask: vk::PipelineStageFlags,
144    ) {
145        let is_depth_texture = dst_access_mask
146            .contains(vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ)
147            || dst_access_mask
148                .contains(vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE);
149
150        let image_memory_barrier = vk::ImageMemoryBarrier {
151            old_layout,
152            new_layout,
153            src_access_mask,
154            dst_access_mask,
155            image: self.image.raw,
156            subresource_range: vk::ImageSubresourceRange {
157                aspect_mask: if is_depth_texture {
158                    vk::ImageAspectFlags::DEPTH
159                } else {
160                    vk::ImageAspectFlags::COLOR
161                },
162                base_mip_level: 0,
163                level_count: self.mip_levels,
164                base_array_layer: 0,
165                layer_count: 1,
166            },
167            ..Default::default()
168        };
169        unsafe {
170            ctx.cmd_pipeline_barrier(
171                command_buffer,
172                src_stage_mask,
173                dst_stage_mask,
174                vk::DependencyFlags::empty(),
175                &[],
176                &[],
177                &[image_memory_barrier],
178            );
179        }
180    }
181}