Skip to content

Commit 88e6f64

Browse files
committed
Sampled image
1 parent 4e2116f commit 88e6f64

File tree

4 files changed

+194
-0
lines changed

4 files changed

+194
-0
lines changed

guide/src/memory/images.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,101 @@ auto vma::create_image(ImageCreateInfo const& create_info,
8181
};
8282
}
8383
```
84+
85+
For creating sampled images, we need both the image bytes and size (extent). Wrap that into a struct:
86+
87+
```cpp
88+
struct Bitmap {
89+
std::span<std::byte const> bytes{};
90+
glm::ivec2 size{};
91+
};
92+
```
93+
94+
The creation process is similar to device buffers: requiring a staging copy, but it also needs layout transitions. In short:
95+
96+
1. Create the image and staging buffer
97+
1. Transition the layout from Undefined to TransferDst
98+
1. Record a buffer image copy operation
99+
1. Transition the layout from TransferDst to ShaderReadOnlyOptimal
100+
101+
```cpp
102+
auto vma::create_sampled_image(ImageCreateInfo const& create_info,
103+
CommandBlock command_block, Bitmap const& bitmap)
104+
-> Image {
105+
// create image.
106+
// no mip-mapping right now: 1 level.
107+
auto const mip_levels = 1u;
108+
auto const usize = glm::uvec2{bitmap.size};
109+
auto const extent = vk::Extent2D{usize.x, usize.y};
110+
auto const usage =
111+
vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled;
112+
auto ret = create_image(create_info, usage, mip_levels,
113+
vk::Format::eR8G8B8A8Srgb, extent);
114+
115+
// create staging buffer.
116+
auto const buffer_ci = BufferCreateInfo{
117+
.allocator = create_info.allocator,
118+
.usage = vk::BufferUsageFlagBits::eTransferSrc,
119+
.queue_family = create_info.queue_family,
120+
};
121+
auto const staging_buffer = create_buffer(buffer_ci, BufferMemoryType::Host,
122+
bitmap.bytes.size_bytes());
123+
124+
// can't do anything if either creation failed.
125+
if (!ret.get().image || !staging_buffer.get().buffer) { return {}; }
126+
127+
// copy bytes into staging buffer.
128+
std::memcpy(staging_buffer.get().mapped, bitmap.bytes.data(),
129+
bitmap.bytes.size_bytes());
130+
131+
// transition image for transfer.
132+
auto dependency_info = vk::DependencyInfo{};
133+
auto subresource_range = vk::ImageSubresourceRange{};
134+
subresource_range.setAspectMask(vk::ImageAspectFlagBits::eColor)
135+
.setLayerCount(1)
136+
.setLevelCount(mip_levels);
137+
auto barrier = vk::ImageMemoryBarrier2{};
138+
barrier.setImage(ret.get().image)
139+
.setSrcQueueFamilyIndex(create_info.queue_family)
140+
.setDstQueueFamilyIndex(create_info.queue_family)
141+
.setOldLayout(vk::ImageLayout::eUndefined)
142+
.setNewLayout(vk::ImageLayout::eTransferDstOptimal)
143+
.setSubresourceRange(subresource_range)
144+
.setSrcStageMask(vk::PipelineStageFlagBits2::eTopOfPipe)
145+
.setSrcAccessMask(vk::AccessFlagBits2::eNone)
146+
.setDstStageMask(vk::PipelineStageFlagBits2::eTransfer)
147+
.setDstAccessMask(vk::AccessFlagBits2::eMemoryRead |
148+
vk::AccessFlagBits2::eMemoryWrite);
149+
dependency_info.setImageMemoryBarriers(barrier);
150+
command_block.command_buffer().pipelineBarrier2(dependency_info);
151+
152+
auto buffer_image_copy = vk::BufferImageCopy2{};
153+
auto subresource_layers = vk::ImageSubresourceLayers{};
154+
subresource_layers.setAspectMask(vk::ImageAspectFlagBits::eColor)
155+
.setLayerCount(1)
156+
.setLayerCount(mip_levels);
157+
buffer_image_copy.setImageSubresource(subresource_layers)
158+
.setImageExtent(vk::Extent3D{extent.width, extent.height, 1});
159+
auto copy_info = vk::CopyBufferToImageInfo2{};
160+
copy_info.setDstImage(ret.get().image)
161+
.setDstImageLayout(vk::ImageLayout::eTransferDstOptimal)
162+
.setSrcBuffer(staging_buffer.get().buffer)
163+
.setRegions(buffer_image_copy);
164+
command_block.command_buffer().copyBufferToImage2(copy_info);
165+
166+
// transition image for sampling.
167+
barrier.setOldLayout(barrier.newLayout)
168+
.setNewLayout(vk::ImageLayout::eShaderReadOnlyOptimal)
169+
.setSrcStageMask(barrier.dstStageMask)
170+
.setSrcAccessMask(barrier.dstAccessMask)
171+
.setDstStageMask(vk::PipelineStageFlagBits2::eAllGraphics)
172+
.setDstAccessMask(vk::AccessFlagBits2::eMemoryRead |
173+
vk::AccessFlagBits2::eMemoryWrite);
174+
dependency_info.setImageMemoryBarriers(barrier);
175+
command_block.command_buffer().pipelineBarrier2(dependency_info);
176+
177+
command_block.submit_and_wait();
178+
179+
return ret;
180+
}
181+
```

src/bitmap.hpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#pragma once
2+
#include <glm/vec2.hpp>
3+
#include <cstddef>
4+
#include <span>
5+
6+
namespace lvk {
7+
struct Bitmap {
8+
std::span<std::byte const> bytes{};
9+
glm::ivec2 size{};
10+
};
11+
} // namespace lvk

src/vma.cpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,4 +175,84 @@ auto vma::create_image(ImageCreateInfo const& create_info,
175175
.levels = levels,
176176
};
177177
}
178+
179+
auto vma::create_sampled_image(ImageCreateInfo const& create_info,
180+
CommandBlock command_block, Bitmap const& bitmap)
181+
-> Image {
182+
// create image.
183+
// no mip-mapping right now: 1 level.
184+
auto const mip_levels = 1u;
185+
auto const usize = glm::uvec2{bitmap.size};
186+
auto const extent = vk::Extent2D{usize.x, usize.y};
187+
auto const usage =
188+
vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled;
189+
auto ret = create_image(create_info, usage, mip_levels,
190+
vk::Format::eR8G8B8A8Srgb, extent);
191+
192+
// create staging buffer.
193+
auto const buffer_ci = BufferCreateInfo{
194+
.allocator = create_info.allocator,
195+
.usage = vk::BufferUsageFlagBits::eTransferSrc,
196+
.queue_family = create_info.queue_family,
197+
};
198+
auto const staging_buffer = create_buffer(buffer_ci, BufferMemoryType::Host,
199+
bitmap.bytes.size_bytes());
200+
201+
// can't do anything if either creation failed.
202+
if (!ret.get().image || !staging_buffer.get().buffer) { return {}; }
203+
204+
// copy bytes into staging buffer.
205+
std::memcpy(staging_buffer.get().mapped, bitmap.bytes.data(),
206+
bitmap.bytes.size_bytes());
207+
208+
// transition image for transfer.
209+
auto dependency_info = vk::DependencyInfo{};
210+
auto subresource_range = vk::ImageSubresourceRange{};
211+
subresource_range.setAspectMask(vk::ImageAspectFlagBits::eColor)
212+
.setLayerCount(1)
213+
.setLevelCount(mip_levels);
214+
auto barrier = vk::ImageMemoryBarrier2{};
215+
barrier.setImage(ret.get().image)
216+
.setSrcQueueFamilyIndex(create_info.queue_family)
217+
.setDstQueueFamilyIndex(create_info.queue_family)
218+
.setOldLayout(vk::ImageLayout::eUndefined)
219+
.setNewLayout(vk::ImageLayout::eTransferDstOptimal)
220+
.setSubresourceRange(subresource_range)
221+
.setSrcStageMask(vk::PipelineStageFlagBits2::eTopOfPipe)
222+
.setSrcAccessMask(vk::AccessFlagBits2::eNone)
223+
.setDstStageMask(vk::PipelineStageFlagBits2::eTransfer)
224+
.setDstAccessMask(vk::AccessFlagBits2::eMemoryRead |
225+
vk::AccessFlagBits2::eMemoryWrite);
226+
dependency_info.setImageMemoryBarriers(barrier);
227+
command_block.command_buffer().pipelineBarrier2(dependency_info);
228+
229+
auto buffer_image_copy = vk::BufferImageCopy2{};
230+
auto subresource_layers = vk::ImageSubresourceLayers{};
231+
subresource_layers.setAspectMask(vk::ImageAspectFlagBits::eColor)
232+
.setLayerCount(1)
233+
.setLayerCount(mip_levels);
234+
buffer_image_copy.setImageSubresource(subresource_layers)
235+
.setImageExtent(vk::Extent3D{extent.width, extent.height, 1});
236+
auto copy_info = vk::CopyBufferToImageInfo2{};
237+
copy_info.setDstImage(ret.get().image)
238+
.setDstImageLayout(vk::ImageLayout::eTransferDstOptimal)
239+
.setSrcBuffer(staging_buffer.get().buffer)
240+
.setRegions(buffer_image_copy);
241+
command_block.command_buffer().copyBufferToImage2(copy_info);
242+
243+
// transition image for sampling.
244+
barrier.setOldLayout(barrier.newLayout)
245+
.setNewLayout(vk::ImageLayout::eShaderReadOnlyOptimal)
246+
.setSrcStageMask(barrier.dstStageMask)
247+
.setSrcAccessMask(barrier.dstAccessMask)
248+
.setDstStageMask(vk::PipelineStageFlagBits2::eAllGraphics)
249+
.setDstAccessMask(vk::AccessFlagBits2::eMemoryRead |
250+
vk::AccessFlagBits2::eMemoryWrite);
251+
dependency_info.setImageMemoryBarriers(barrier);
252+
command_block.command_buffer().pipelineBarrier2(dependency_info);
253+
254+
command_block.submit_and_wait();
255+
256+
return ret;
257+
}
178258
} // namespace lvk

src/vma.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22
#include <vk_mem_alloc.h>
3+
#include <bitmap.hpp>
34
#include <command_block.hpp>
45
#include <scoped.hpp>
56
#include <vulkan/vulkan.hpp>
@@ -81,4 +82,8 @@ struct ImageCreateInfo {
8182
vk::ImageUsageFlags usage, std::uint32_t levels,
8283
vk::Format format, vk::Extent2D extent)
8384
-> Image;
85+
86+
[[nodiscard]] auto create_sampled_image(ImageCreateInfo const& create_info,
87+
CommandBlock command_block,
88+
Bitmap const& bitmap) -> Image;
8489
} // namespace lvk::vma

0 commit comments

Comments
 (0)