7#include "gfx_pipeline_image_vulkan_intf.hpp"
8#include "gfx_device_vulkan_impl.hpp"
9#include "../macros.hpp"
10#include <vulkan/vulkan.hpp>
12hi_export_module(hikogui.GFX : gfx_pipeline_image_impl);
14hi_export
namespace hi {
inline namespace v1 {
16hi_inline
void gfx_pipeline_image::draw_in_command_buffer(vk::CommandBuffer commandBuffer, draw_context
const&
context)
18 gfx_pipeline::draw_in_command_buffer(commandBuffer,
context);
20 hi_axiom_not_null(device());
21 device()->flushAllocation(vertexBufferAllocation, 0, vertexBufferData.size() *
sizeof(vertex));
22 device()->image_pipeline->prepare_atlas_for_rendering();
28 device()->image_pipeline->draw_in_command_buffer(commandBuffer);
33 pushConstants.viewportScale = sfloat_rg32{2.0f / extent.width, 2.0f / extent.height};
34 pushConstants.atlasExtent = sfloat_rg32{device_shared::atlas_image_axis_size, device_shared::atlas_image_axis_size};
35 pushConstants.atlasScale =
36 sfloat_rg32{1.0f / device_shared::atlas_image_axis_size, 1.0f / device_shared::atlas_image_axis_size};
37 commandBuffer.pushConstants(
39 vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment,
41 sizeof(push_constants),
46 device()->cmdBeginDebugUtilsLabelEXT(commandBuffer,
"draw images");
48 device()->cmdEndDebugUtilsLabelEXT(commandBuffer);
53 hi_axiom_not_null(device());
54 return device()->image_pipeline->shader_stages;
61 vk::DescriptorType::eSampler,
63 vk::ShaderStageFlagBits::eFragment},
65 vk::DescriptorType::eSampledImage,
67 vk::ShaderStageFlagBits::eFragment}};
72 hi_axiom_not_null(device());
81 vk::DescriptorType::eSampler,
91 vk::DescriptorType::eSampledImage,
98hi_inline
size_t gfx_pipeline_image::getDescriptorSetVersion()
const
100 hi_axiom_not_null(device());
101 return device()->image_pipeline->atlas_textures.size();
106 return push_constants::pushConstantRanges();
109hi_inline vk::VertexInputBindingDescription gfx_pipeline_image::createVertexInputBindingDescription()
const
111 return vertex::inputBindingDescription();
116 return vertex::inputAttributeDescriptions();
119hi_inline
void gfx_pipeline_image::build_vertex_buffers()
125 vk::BufferCreateFlags(),
127 vk::BufferUsageFlagBits::eVertexBuffer,
128 vk::SharingMode::eExclusive};
134 hi_axiom_not_null(device());
136 device()->setDebugUtilsObjectNameEXT(vertexBuffer,
"image-pipeline vertex buffer");
137 vertexBufferData = device()->mapMemory<vertex>(vertexBufferAllocation);
140hi_inline
void gfx_pipeline_image::teardown_vertex_buffers()
142 hi_axiom_not_null(device());
143 device()->unmapMemory(vertexBufferAllocation);
144 device()->destroyBuffer(vertexBuffer, vertexBufferAllocation);
148gfx_pipeline_image::texture_map::transitionLayout(
const gfx_device& device, vk::Format format, vk::ImageLayout
nextLayout)
151 device.transition_layout(image, format, layout,
nextLayout);
157 device(
nullptr), width(width), height(height), pages()
159 if (surface ==
nullptr) {
168 auto const lock = std::scoped_lock(gfx_system_mutex);
169 if ((this->device = surface->device()) !=
nullptr) {
170 auto const[num_columns, num_rows] = size_in_int_pages();
171 this->pages = device->image_pipeline->allocate_pages(num_columns * num_rows);
179 auto const lock = std::scoped_lock(gfx_system_mutex);
184hi_inline gfx_pipeline_image::paged_image::paged_image(gfx_surface
const *surface, png
const& image)
noexcept :
188 auto const lock = std::scoped_lock(gfx_system_mutex);
193hi_inline gfx_pipeline_image::paged_image::paged_image(paged_image&&
other)
noexcept :
194 state(
other.state.exchange(state_type::uninitialized)),
195 device(std::exchange(
other.device,
nullptr)),
197 height(
other.height),
202hi_inline gfx_pipeline_image::paged_image& gfx_pipeline_image::paged_image::operator=(paged_image&&
other)
noexcept
204 hi_return_on_self_assignment(
other);
208 device->image_pipeline->free_pages(pages);
211 state =
other.state.exchange(state_type::uninitialized);
212 device = std::exchange(
other.device,
nullptr);
214 height =
other.height;
219hi_inline gfx_pipeline_image::paged_image::~paged_image()
222 device->image_pipeline->free_pages(pages);
228 hi_assert(image.width() == width
and image.height() == height);
231 auto const lock = std::scoped_lock(gfx_system_mutex);
233 state = state_type::drawing;
235 auto staging_image = device->image_pipeline->get_staging_pixmap(image.width(), image.height());
237 device->image_pipeline->update_atlas_with_staging_pixmap(*
this);
239 state = state_type::uploaded;
245 hi_assert(image.width() == width
and image.height() == height);
248 auto const lock = std::scoped_lock(gfx_system_mutex);
250 state = state_type::drawing;
252 auto staging_image = device->image_pipeline->get_staging_pixmap(image.width(), image.height());
254 device->image_pipeline->update_atlas_with_staging_pixmap(*
this);
256 state = state_type::uploaded;
260hi_inline gfx_pipeline_image::device_shared::device_shared(gfx_device
const& device) : device(device)
266hi_inline gfx_pipeline_image::device_shared::~device_shared() {}
277 while (
num_pages > _atlas_free_pages.size()) {
283 auto const page = _atlas_free_pages.back();
285 _atlas_free_pages.pop_back();
292 _atlas_free_pages.insert(_atlas_free_pages.end(), pages.begin(), pages.end());
297 staging_texture.transitionLayout(device, vk::Format::eR16G16B16A16Sfloat, vk::ImageLayout::eGeneral);
299 return staging_texture.pixmap.subimage(1, 1, staging_image_width - 2, staging_image_height - 2);
310 constexpr auto page_stride = gfx_pipeline_image::paged_image::page_size + 2;
312 auto const image_nr =
page / gfx_pipeline_image::device_shared::atlas_num_pages_per_image;
313 auto const image_page =
page % gfx_pipeline_image::device_shared::atlas_num_pages_per_image;
329 auto const width_in_pages = (image.width + gfx_pipeline_image::paged_image::page_size - 1) / gfx_pipeline_image::paged_image::page_size;
336hi_inline
void gfx_pipeline_image::device_shared::make_staging_border_transparent(aarectangle
border_rectangle)
noexcept
346 hi_assert(
left == 0);
348 hi_assert(
right >= 2);
355 for (
auto x = 0
_uz; x != width; ++x) {
361 for (
auto y = 0
_uz; y != height; ++y) {
362 auto row = staging_texture.pixmap[y];
368hi_inline
void gfx_pipeline_image::device_shared::clear_staging_between_border_and_upload(
384 auto row = staging_texture.pixmap[y];
386 row[x] = sfloat_rgba16{};
392 auto row = staging_texture.pixmap[y];
394 row[x] = sfloat_rgba16{};
399hi_inline
void gfx_pipeline_image::device_shared::prepare_staging_for_upload(paged_image
const& image)
noexcept
401 auto const image_rectangle = aarectangle{point2{1.0f, 1.0f}, image.size()};
411 static_assert(std::is_same_v<
decltype(staging_texture.pixmap)::value_type, sfloat_rgba16>);
412 device.flushAllocation(staging_texture.allocation, 0,
upload_height * staging_texture.pixmap.stride() * 8);
413 staging_texture.transitionLayout(device, vk::Format::eR16G16B16A16Sfloat, vk::ImageLayout::eTransferSrcOptimal);
416hi_inline
void gfx_pipeline_image::device_shared::update_atlas_with_staging_pixmap(paged_image
const& image)
noexcept
418 prepare_staging_for_upload(image);
421 for (
std::size_t index = 0; index < size(image.pages); index++) {
422 auto const page = image.pages.
at(index);
438 vk::ImageSubresourceLayers{vk::ImageAspectFlagBits::eColor, 0, 0, 1},
440 vk::ImageSubresourceLayers{vk::ImageAspectFlagBits::eColor, 0, 0, 1},
442 vk::Extent3D{width, height, 1});
452 atlas_texture.transitionLayout(device, vk::Format::eR16G16B16A16Sfloat, vk::ImageLayout::eTransferDstOptimal);
455 staging_texture.image,
456 vk::ImageLayout::eTransferSrcOptimal,
458 vk::ImageLayout::eTransferDstOptimal,
466 atlas_texture.transitionLayout(device, vk::Format::eR16G16B16A16Sfloat, vk::ImageLayout::eShaderReadOnlyOptimal);
470hi_inline
void gfx_pipeline_image::device_shared::draw_in_command_buffer(vk::CommandBuffer
const& commandBuffer)
472 commandBuffer.bindIndexBuffer(device.quadIndexBuffer, 0, vk::IndexType::eUint16);
475hi_inline
void gfx_pipeline_image::device_shared::build_shaders()
477 vertex_shader_module = device.loadShader(
URL(
"resource:image_vulkan.vert.spv"));
478 fragment_shader_module = device.loadShader(
URL(
"resource:image_vulkan.frag.spv"));
481 {vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eVertex, vertex_shader_module,
"main"},
482 {vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eFragment, fragment_shader_module,
"main"}};
485hi_inline
void gfx_pipeline_image::device_shared::teardown_shaders(gfx_device
const *
vulkanDevice)
492hi_inline
void gfx_pipeline_image::device_shared::add_atlas_image()
498 vk::ImageCreateFlags(),
500 vk::Format::eR16G16B16A16Sfloat,
501 vk::Extent3D(atlas_image_axis_size, atlas_image_axis_size, 1),
504 vk::SampleCountFlagBits::e1,
505 vk::ImageTiling::eOptimal,
506 vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled,
507 vk::SharingMode::eExclusive,
510 vk::ImageLayout::eUndefined};
521 {vk::ImageViewCreateFlags(),
523 vk::ImageViewType::e2D,
525 vk::ComponentMapping(),
527 vk::ImageAspectFlagBits::eColor,
538 for (
int i = 0; i < atlas_num_pages_per_image; i++) {
543 for (
std::size_t i = 0; i < size(atlas_descriptor_image_infos); i++) {
546 atlas_descriptor_image_infos.at(i) = {
548 i < atlas_textures.size() ? atlas_textures.at(i).view : atlas_textures.at(0).view,
549 vk::ImageLayout::eShaderReadOnlyOptimal};
553hi_inline
void gfx_pipeline_image::device_shared::build_atlas()
557 vk::ImageCreateFlags(),
559 vk::Format::eR16G16B16A16Sfloat,
560 vk::Extent3D(staging_image_width, staging_image_height, 1),
563 vk::SampleCountFlagBits::e1,
564 vk::ImageTiling::eLinear,
565 vk::ImageUsageFlagBits::eTransferSrc,
566 vk::SharingMode::eExclusive,
569 vk::ImageLayout::ePreinitialized};
575 device.setDebugUtilsObjectNameEXT(image,
"image-pipeline staging image");
576 auto const data = device.mapMemory<sfloat_rgba16>(allocation);
585 vk::SamplerCreateFlags(),
588 vk::SamplerMipmapMode::eNearest,
589 vk::SamplerAddressMode::eRepeat,
590 vk::SamplerAddressMode::eRepeat,
591 vk::SamplerAddressMode::eRepeat,
596 vk::CompareOp::eNever,
599 vk::BorderColor::eFloatTransparentBlack,
604 atlas_sampler_descriptor_image_info = {atlas_sampler, vk::ImageView(), vk::ImageLayout::eUndefined};
611hi_inline
void gfx_pipeline_image::device_shared::teardown_atlas(gfx_device
const *
old_device)
620 atlas_textures.clear();
622 old_device->unmapMemory(staging_texture.allocation);
623 old_device->destroyImage(staging_texture.image, staging_texture.allocation);
632 hi_assert(image.state == paged_image::state_type::uploaded);
638 auto const size_in_float_pages =
f32x4{image.size_in_float_pages()};
639 auto const size_in_int_pages =
i32x4{ceil(size_in_float_pages)};
649 auto left_bottom =
box.p0;
650 auto right_bottom =
box.p1;
652 auto it = image.pages.begin();
658 auto new_p0 = left_bottom;
679 left_bottom = left_top;
680 right_bottom = right_top;
@ bottom
Align to the bottom.
@ right
Align the text to the right side.
@ left
Align the text to the left side.
@ other
The gui_event does not have associated data.
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
The HikoGUI namespace.
Definition recursive_iterator.hpp:15
std::ptrdiff_t ssize_t
Signed size/index into an array.
Definition misc.hpp:32
hi_inline point3 get_atlas_position(std::size_t page) noexcept
Get the coordinate in the atlas from a page index.
Definition gfx_pipeline_image_vulkan_impl.hpp:307
hi_inline point2 get_staging_position(const gfx_pipeline_image::paged_image &image, std::size_t page_index)
Get the position in the staging texture map to copy from.
Definition gfx_pipeline_image_vulkan_impl.hpp:327
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:378
Class which represents an axis-aligned rectangle.
Definition aarectangle.hpp:33
A high-level geometric extent.
Definition extent2.hpp:32
A rectangle / parallelogram in 3D space.
Definition rectangle.hpp:25
This is a image that is uploaded into the texture atlas.
Definition gfx_pipeline_image_vulkan_intf.hpp:83
void upload(pixmap_span< sfloat_rgba16 const > image) noexcept
Upload image to atlas.
Definition gfx_pipeline_image_vulkan_impl.hpp:243
void free_pages(std::vector< std::size_t > const &pages) noexcept
Deallocate pages back to the atlas.
Definition gfx_pipeline_image_vulkan_impl.hpp:290
hi::pixmap_span< sfloat_rgba16 > get_staging_pixmap()
Get the full staging pixel map excluding border.
Definition gfx_pipeline_image_vulkan_impl.hpp:295
std::vector< std::size_t > allocate_pages(std::size_t num_pages) noexcept
Allocate pages from the atlas.
Definition gfx_pipeline_image_vulkan_impl.hpp:275
void place_vertices(vector_span< vertex > &vertices, aarectangle const &clipping_rectangle, quad const &box, paged_image const &image) noexcept
Place vertices for a single image.
Definition gfx_pipeline_image_vulkan_impl.hpp:626
void destroy(gfx_device const *vulkanDevice)
Definition gfx_pipeline_image_vulkan_impl.hpp:268
void prepare_atlas_for_rendering()
Prepare the atlas so that it can be used as a texture map by the shaders.
Definition gfx_pipeline_image_vulkan_impl.hpp:463
A non-owning 2D pixel-based image.
Definition pixmap_span.hpp:34
Universal Resource Locator.
Definition URL.hpp:51