HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
gfx_pipeline_image_vulkan_impl.hpp
1// Copyright Take Vos 2019-2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "gfx_pipeline_image_vulkan.hpp"
8#include "gfx_device_vulkan_impl.hpp"
9#include "../macros.hpp"
10
11namespace hi { inline namespace v1 {
12
13inline void gfx_pipeline_image::draw_in_command_buffer(vk::CommandBuffer commandBuffer, draw_context const& context)
14{
15 gfx_pipeline::draw_in_command_buffer(commandBuffer, context);
16
17 hi_axiom_not_null(device());
18 device()->flushAllocation(vertexBufferAllocation, 0, vertexBufferData.size() * sizeof(vertex));
19 device()->image_pipeline->prepare_atlas_for_rendering();
20
23 hi_assert(tmpvertexBuffers.size() == tmpOffsets.size());
24
25 device()->image_pipeline->draw_in_command_buffer(commandBuffer);
26
27 commandBuffer.bindVertexBuffers(0, tmpvertexBuffers, tmpOffsets);
28
29 pushConstants.windowExtent = extent2{narrow_cast<float>(extent.width), narrow_cast<float>(extent.height)};
30 pushConstants.viewportScale = sfloat_rg32{2.0f / extent.width, 2.0f / extent.height};
31 pushConstants.atlasExtent = sfloat_rg32{device_shared::atlas_image_axis_size, device_shared::atlas_image_axis_size};
32 pushConstants.atlasScale =
33 sfloat_rg32{1.0f / device_shared::atlas_image_axis_size, 1.0f / device_shared::atlas_image_axis_size};
34 commandBuffer.pushConstants(
35 pipelineLayout,
36 vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment,
37 0,
38 sizeof(push_constants),
39 &pushConstants);
40
41 hilet numberOfRectangles = vertexBufferData.size() / 4;
43 device()->cmdBeginDebugUtilsLabelEXT(commandBuffer, "draw images");
44 commandBuffer.drawIndexed(narrow_cast<uint32_t>(numberOfTriangles * 3), 1, 0, 0, 0);
45 device()->cmdEndDebugUtilsLabelEXT(commandBuffer);
46}
47
48inline std::vector<vk::PipelineShaderStageCreateInfo> gfx_pipeline_image::createShaderStages() const
49{
50 hi_axiom_not_null(device());
51 return device()->image_pipeline->shader_stages;
52}
53
54inline std::vector<vk::DescriptorSetLayoutBinding> gfx_pipeline_image::createDescriptorSetLayoutBindings() const
55{
56 return {
57 {0, // binding
58 vk::DescriptorType::eSampler,
59 1, // descriptorCount
60 vk::ShaderStageFlagBits::eFragment},
61 {1, // binding
62 vk::DescriptorType::eSampledImage,
63 narrow_cast<uint32_t>(device_shared::atlas_maximum_num_images), // descriptorCount
64 vk::ShaderStageFlagBits::eFragment}};
65}
66
67inline std::vector<vk::WriteDescriptorSet> gfx_pipeline_image::createWriteDescriptorSet() const
68{
69 hi_axiom_not_null(device());
70 hilet& sharedImagePipeline = device()->image_pipeline;
71
72 return {
73 {
74 descriptorSet,
75 0, // destBinding
76 0, // arrayElement
77 1, // descriptorCount
78 vk::DescriptorType::eSampler,
79 &sharedImagePipeline->atlas_sampler_descriptor_image_info,
80 nullptr, // bufferInfo
81 nullptr // texelBufferView
82 },
83 {
84 descriptorSet,
85 1, // destBinding
86 0, // arrayElement
87 narrow_cast<uint32_t>(sharedImagePipeline->atlas_descriptor_image_infos.size()), // descriptorCount
88 vk::DescriptorType::eSampledImage,
89 sharedImagePipeline->atlas_descriptor_image_infos.data(),
90 nullptr, // bufferInfo
91 nullptr // texelBufferView
92 }};
93}
94
95inline size_t gfx_pipeline_image::getDescriptorSetVersion() const
96{
97 hi_axiom_not_null(device());
98 return device()->image_pipeline->atlas_textures.size();
99}
100
101inline std::vector<vk::PushConstantRange> gfx_pipeline_image::createPushConstantRanges() const
102{
103 return push_constants::pushConstantRanges();
104}
105
106inline vk::VertexInputBindingDescription gfx_pipeline_image::createVertexInputBindingDescription() const
107{
108 return vertex::inputBindingDescription();
109}
110
111inline std::vector<vk::VertexInputAttributeDescription> gfx_pipeline_image::createVertexInputAttributeDescriptions() const
112{
113 return vertex::inputAttributeDescriptions();
114}
115
116inline void gfx_pipeline_image::build_vertex_buffers()
117{
118 using vertexIndexType = uint16_t;
119 constexpr ssize_t numberOfVertices = 1 << (sizeof(vertexIndexType) * CHAR_BIT);
120
121 vk::BufferCreateInfo const bufferCreateInfo = {
122 vk::BufferCreateFlags(),
123 sizeof(vertex) * numberOfVertices,
124 vk::BufferUsageFlagBits::eVertexBuffer,
125 vk::SharingMode::eExclusive};
128 allocationCreateInfo.pUserData = const_cast<char *>("image-pipeline vertex buffer");
130
131 hi_axiom_not_null(device());
132 std::tie(vertexBuffer, vertexBufferAllocation) = device()->createBuffer(bufferCreateInfo, allocationCreateInfo);
133 device()->setDebugUtilsObjectNameEXT(vertexBuffer, "image-pipeline vertex buffer");
134 vertexBufferData = device()->mapMemory<vertex>(vertexBufferAllocation);
135}
136
137inline void gfx_pipeline_image::teardown_vertex_buffers()
138{
139 hi_axiom_not_null(device());
140 device()->unmapMemory(vertexBufferAllocation);
141 device()->destroyBuffer(vertexBuffer, vertexBufferAllocation);
142}
143
144inline void
145gfx_pipeline_image::texture_map::transitionLayout(const gfx_device& device, vk::Format format, vk::ImageLayout nextLayout)
146{
147 if (layout != nextLayout) {
148 device.transition_layout(image, format, layout, nextLayout);
149 layout = nextLayout;
150 }
151}
152
153inline gfx_pipeline_image::paged_image::paged_image(gfx_surface const *surface, std::size_t width, std::size_t height) noexcept :
154 device(nullptr), width(width), height(height), pages()
155{
156 if (surface == nullptr) {
157 // During initialization of a widget, the window may not have a surface yet.
158 // As it needs to determine the size of the surface based on the size of the containing widgets.
159 // Return an empty image.
160 return;
161 }
162
163 // Like before the surface may not be assigned to a device either.
164 // In that case also return an empty image.
165 hilet lock = std::scoped_lock(gfx_system_mutex);
166 if ((this->device = surface->device()) != nullptr) {
167 hilet[num_columns, num_rows] = size_in_int_pages();
168 this->pages = device->image_pipeline->allocate_pages(num_columns * num_rows);
169 }
170}
171
172inline gfx_pipeline_image::paged_image::paged_image(gfx_surface const *surface, pixmap_span<sfloat_rgba16 const> image) noexcept :
173 paged_image(surface, narrow_cast<std::size_t>(image.width()), narrow_cast<std::size_t>(image.height()))
174{
175 if (this->device) {
176 hilet lock = std::scoped_lock(gfx_system_mutex);
177 this->upload(image);
178 }
179}
180
181inline gfx_pipeline_image::paged_image::paged_image(gfx_surface const *surface, png const& image) noexcept :
182 paged_image(surface, narrow_cast<std::size_t>(image.width()), narrow_cast<std::size_t>(image.height()))
183{
184 if (this->device) {
185 hilet lock = std::scoped_lock(gfx_system_mutex);
186 this->upload(image);
187 }
188}
189
190inline gfx_pipeline_image::paged_image::paged_image(paged_image&& other) noexcept :
191 state(other.state.exchange(state_type::uninitialized)),
192 device(std::exchange(other.device, nullptr)),
193 width(other.width),
194 height(other.height),
195 pages(std::move(other.pages))
196{
197}
198
199inline gfx_pipeline_image::paged_image& gfx_pipeline_image::paged_image::operator=(paged_image&& other) noexcept
200{
201 hi_return_on_self_assignment(other);
202
203 // If the old image had pages, free them.
204 if (device) {
205 device->image_pipeline->free_pages(pages);
206 }
207
208 state = other.state.exchange(state_type::uninitialized);
209 device = std::exchange(other.device, nullptr);
210 width = other.width;
211 height = other.height;
212 pages = std::move(other.pages);
213 return *this;
214}
215
216inline gfx_pipeline_image::paged_image::~paged_image()
217{
218 if (device) {
219 device->image_pipeline->free_pages(pages);
220 }
221}
222
223inline void gfx_pipeline_image::paged_image::upload(png const& image) noexcept
224{
225 hi_assert(image.width() == width and image.height() == height);
226
227 if (device) {
228 hilet lock = std::scoped_lock(gfx_system_mutex);
229
230 state = state_type::drawing;
231
232 auto staging_image = device->image_pipeline->get_staging_pixmap(image.width(), image.height());
233 image.decode_image(staging_image);
234 device->image_pipeline->update_atlas_with_staging_pixmap(*this);
235
236 state = state_type::uploaded;
237 }
238}
239
241{
242 hi_assert(image.width() == width and image.height() == height);
243
244 if (device) {
245 hilet lock = std::scoped_lock(gfx_system_mutex);
246
247 state = state_type::drawing;
248
249 auto staging_image = device->image_pipeline->get_staging_pixmap(image.width(), image.height());
250 copy(image, staging_image);
251 device->image_pipeline->update_atlas_with_staging_pixmap(*this);
252
253 state = state_type::uploaded;
254 }
255}
256
257inline gfx_pipeline_image::device_shared::device_shared(gfx_device const& device) : device(device)
258{
259 build_shaders();
260 build_atlas();
261}
262
263inline gfx_pipeline_image::device_shared::~device_shared() {}
264
266{
267 hi_assert_not_null(old_device);
268 teardown_shaders(old_device);
269 teardown_atlas(old_device);
270}
271
273{
274 while (num_pages > _atlas_free_pages.size()) {
275 add_atlas_image();
276 }
277
278 auto r = std::vector<std::size_t>();
279 for (int i = 0; i < num_pages; i++) {
280 hilet page = _atlas_free_pages.back();
281 r.push_back(page);
282 _atlas_free_pages.pop_back();
283 }
284 return r;
285}
286
288{
289 _atlas_free_pages.insert(_atlas_free_pages.end(), pages.begin(), pages.end());
290}
291
293{
294 staging_texture.transitionLayout(device, vk::Format::eR16G16B16A16Sfloat, vk::ImageLayout::eGeneral);
295
296 return staging_texture.pixmap.subimage(1, 1, staging_image_width - 2, staging_image_height - 2);
297}
298
304[[nodiscard]] inline point3 get_atlas_position(std::size_t page) noexcept
305{
306 // The amount of pixels per page, that is the page plus two borders.
307 constexpr auto page_stride = gfx_pipeline_image::paged_image::page_size + 2;
308
309 hilet image_nr = page / gfx_pipeline_image::device_shared::atlas_num_pages_per_image;
310 hilet image_page = page % gfx_pipeline_image::device_shared::atlas_num_pages_per_image;
311
312 return point3{
313 narrow_cast<float>((image_page % gfx_pipeline_image::device_shared::atlas_num_pages_per_axis) * page_stride + 1),
314 narrow_cast<float>((image_page / gfx_pipeline_image::device_shared::atlas_num_pages_per_axis) * page_stride + 1),
316}
317
324inline point2 get_staging_position(const gfx_pipeline_image::paged_image& image, std::size_t page_index)
325{
326 hilet width_in_pages = (image.width + gfx_pipeline_image::paged_image::page_size - 1) / gfx_pipeline_image::paged_image::page_size;
327
328 return point2{
329 narrow_cast<float>((page_index % width_in_pages) * gfx_pipeline_image::paged_image::page_size + 1),
330 narrow_cast<float>((page_index / width_in_pages) * gfx_pipeline_image::paged_image::page_size + 1)};
331}
332
333inline void gfx_pipeline_image::device_shared::make_staging_border_transparent(aarectangle border_rectangle) noexcept
334{
335 hilet width = ceil_cast<std::size_t>(border_rectangle.width());
336 hilet height = ceil_cast<std::size_t>(border_rectangle.height());
341
342 hi_assert(bottom == 0);
343 hi_assert(left == 0);
344 hi_assert(top >= 2);
345 hi_assert(right >= 2);
346
347 // Add a border below and above the image.
348 auto border_bottom_row = staging_texture.pixmap[bottom];
349 auto border_top_row = staging_texture.pixmap[top - 1];
350 auto image_bottom_row = staging_texture.pixmap[bottom + 1];
351 auto image_top_row = staging_texture.pixmap[top - 2];
352 for (auto x = 0_uz; x != width; ++x) {
353 border_bottom_row[x] = make_transparent(image_bottom_row[x]);
354 border_top_row[x] = make_transparent(image_top_row[x]);
355 }
356
357 // Add a border to the left and right of the image.
358 for (auto y = 0_uz; y != height; ++y) {
359 auto row = staging_texture.pixmap[y];
360 row[left] = make_transparent(row[left + 1]);
361 row[right - 2] = make_transparent(row[right - 1]);
362 }
363}
364
365inline void gfx_pipeline_image::device_shared::clear_staging_between_border_and_upload(
366 aarectangle border_rectangle,
367 aarectangle upload_rectangle) noexcept
368{
369 hi_assert(border_rectangle.left() == 0.0f and border_rectangle.bottom() == 0.0f);
370 hi_assert(upload_rectangle.left() == 0.0f and upload_rectangle.bottom() == 0.0f);
371
376 hi_assert(border_right <= upload_right);
377 hi_assert(border_top <= upload_top);
378
379 // Clear the area to the right of the border.
380 for (auto y = 0_uz; y != border_top; ++y) {
381 auto row = staging_texture.pixmap[y];
382 for (auto x = border_right; x != upload_right; ++x) {
383 row[x] = {};
384 }
385 }
386
387 // Clear the area above the border.
388 for (auto y = border_top; y != upload_top; ++y) {
389 auto row = staging_texture.pixmap[y];
390 for (auto x = 0_uz; x != upload_right; ++x) {
391 row[x] = {};
392 }
393 }
394}
395
396inline void gfx_pipeline_image::device_shared::prepare_staging_for_upload(paged_image const& image) noexcept
397{
398 hilet image_rectangle = aarectangle{point2{1.0f, 1.0f}, image.size()};
400 hilet upload_width = ceil(image.width, paged_image::page_size) + 2;
401 hilet upload_height = ceil(image.height, paged_image::page_size) + 2;
403
404 make_staging_border_transparent(border_rectangle);
405 clear_staging_between_border_and_upload(border_rectangle, upload_rectangle);
406
407 // Flush the given image, everything that may be uploaded.
408 static_assert(std::is_same_v<decltype(staging_texture.pixmap)::value_type, sfloat_rgba16>);
409 device.flushAllocation(staging_texture.allocation, 0, upload_height * staging_texture.pixmap.stride() * 8);
410 staging_texture.transitionLayout(device, vk::Format::eR16G16B16A16Sfloat, vk::ImageLayout::eTransferSrcOptimal);
411}
412
413inline void gfx_pipeline_image::device_shared::update_atlas_with_staging_pixmap(paged_image const& image) noexcept
414{
415 prepare_staging_for_upload(image);
416
418 for (std::size_t index = 0; index < size(image.pages); index++) {
419 hilet page = image.pages.at(index);
420
421 hilet src_position = get_staging_position(image, index);
423
424 // Copy including a 1 pixel border.
425 constexpr auto width = narrow_cast<int32_t>(paged_image::page_size + 2);
426 constexpr auto height = narrow_cast<int32_t>(paged_image::page_size + 2);
427 hilet src_x = floor_cast<int32_t>(src_position.x() - 1.0f);
428 hilet src_y = floor_cast<int32_t>(src_position.y() - 1.0f);
429 hilet dst_x = floor_cast<int32_t>(dst_position.x() - 1.0f);
430 hilet dst_y = floor_cast<int32_t>(dst_position.y() - 1.0f);
432
434 regionsToCopy.emplace_back(
435 vk::ImageSubresourceLayers{vk::ImageAspectFlagBits::eColor, 0, 0, 1},
436 vk::Offset3D{src_x, src_y, 0},
437 vk::ImageSubresourceLayers{vk::ImageAspectFlagBits::eColor, 0, 0, 1},
438 vk::Offset3D{dst_x, dst_y, 0},
439 vk::Extent3D{width, height, 1});
440 }
441
442 for (std::size_t atlas_texture_index = 0; atlas_texture_index < size(atlas_textures); atlas_texture_index++) {
444 if (regions_to_copy.empty()) {
445 continue;
446 }
447
448 auto& atlas_texture = atlas_textures.at(atlas_texture_index);
449 atlas_texture.transitionLayout(device, vk::Format::eR16G16B16A16Sfloat, vk::ImageLayout::eTransferDstOptimal);
450
451 device.copyImage(
452 staging_texture.image,
453 vk::ImageLayout::eTransferSrcOptimal,
454 atlas_texture.image,
455 vk::ImageLayout::eTransferDstOptimal,
457 }
458}
459
461{
462 for (auto& atlas_texture : atlas_textures) {
463 atlas_texture.transitionLayout(device, vk::Format::eR16G16B16A16Sfloat, vk::ImageLayout::eShaderReadOnlyOptimal);
464 }
465}
466
467inline void gfx_pipeline_image::device_shared::draw_in_command_buffer(vk::CommandBuffer const& commandBuffer)
468{
469 commandBuffer.bindIndexBuffer(device.quadIndexBuffer, 0, vk::IndexType::eUint16);
470}
471
472inline void gfx_pipeline_image::device_shared::build_shaders()
473{
474 vertex_shader_module = device.loadShader(URL("resource:shaders/image.vert.spv"));
475 fragment_shader_module = device.loadShader(URL("resource:shaders/image.frag.spv"));
476
477 shader_stages = {
478 {vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eVertex, vertex_shader_module, "main"},
479 {vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eFragment, fragment_shader_module, "main"}};
480}
481
482inline void gfx_pipeline_image::device_shared::teardown_shaders(gfx_device const *vulkanDevice)
483{
484 hi_assert_not_null(vulkanDevice);
485 vulkanDevice->destroy(vertex_shader_module);
486 vulkanDevice->destroy(fragment_shader_module);
487}
488
489inline void gfx_pipeline_image::device_shared::add_atlas_image()
490{
491 hilet current_image_index = size(atlas_textures);
492
493 // Create atlas image
494 vk::ImageCreateInfo const imageCreateInfo = {
495 vk::ImageCreateFlags(),
496 vk::ImageType::e2D,
497 vk::Format::eR16G16B16A16Sfloat,
498 vk::Extent3D(atlas_image_axis_size, atlas_image_axis_size, 1),
499 1, // mipLevels
500 1, // arrayLayers
501 vk::SampleCountFlagBits::e1,
502 vk::ImageTiling::eOptimal,
503 vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled,
504 vk::SharingMode::eExclusive,
505 0,
506 nullptr,
507 vk::ImageLayout::eUndefined};
509 auto allocation_name = std::format("image-pipeline atlas image {}", current_image_index);
511 allocationCreateInfo.pUserData = const_cast<char *>(allocation_name.c_str());
513
515 device.setDebugUtilsObjectNameEXT(atlasImage, allocation_name.c_str());
516
517 hilet atlasImageView = device.createImageView(
518 {vk::ImageViewCreateFlags(),
520 vk::ImageViewType::e2D,
521 imageCreateInfo.format,
522 vk::ComponentMapping(),
523 {
524 vk::ImageAspectFlagBits::eColor,
525 0, // baseMipLevel
526 1, // levelCount
527 0, // baseArrayLayer
528 1 // layerCount
529 }});
530
531 atlas_textures.push_back({atlasImage, atlasImageAllocation, atlasImageView});
532
533 // Add pages for this image to free list.
534 hilet page_offset = current_image_index * atlas_num_pages_per_image;
535 for (int i = 0; i < atlas_num_pages_per_image; i++) {
536 _atlas_free_pages.push_back({page_offset + i});
537 }
538
539 // Build image descriptor info.
540 for (std::size_t i = 0; i < size(atlas_descriptor_image_infos); i++) {
541 // Point the descriptors to each imageView,
542 // repeat the first imageView if there are not enough.
543 atlas_descriptor_image_infos.at(i) = {
544 vk::Sampler(),
545 i < atlas_textures.size() ? atlas_textures.at(i).view : atlas_textures.at(0).view,
546 vk::ImageLayout::eShaderReadOnlyOptimal};
547 }
548}
549
550inline void gfx_pipeline_image::device_shared::build_atlas()
551{
552 // Create staging image
553 vk::ImageCreateInfo const imageCreateInfo = {
554 vk::ImageCreateFlags(),
555 vk::ImageType::e2D,
556 vk::Format::eR16G16B16A16Sfloat,
557 vk::Extent3D(staging_image_width, staging_image_height, 1),
558 1, // mipLevels
559 1, // arrayLayers
560 vk::SampleCountFlagBits::e1,
561 vk::ImageTiling::eLinear,
562 vk::ImageUsageFlagBits::eTransferSrc,
563 vk::SharingMode::eExclusive,
564 0,
565 nullptr,
566 vk::ImageLayout::ePreinitialized};
569 allocationCreateInfo.pUserData = const_cast<char *>("image-pipeline staging image");
571 hilet[image, allocation] = device.createImage(imageCreateInfo, allocationCreateInfo);
572 device.setDebugUtilsObjectNameEXT(image, "image-pipeline staging image");
573 hilet data = device.mapMemory<sfloat_rgba16>(allocation);
574
575 staging_texture = {
576 image,
577 allocation,
578 vk::ImageView(),
579 hi::pixmap_span<sfloat_rgba16>{data.data(), imageCreateInfo.extent.width, imageCreateInfo.extent.height}};
580
581 vk::SamplerCreateInfo const samplerCreateInfo = {
582 vk::SamplerCreateFlags(),
583 vk::Filter::eLinear, // magFilter
584 vk::Filter::eLinear, // minFilter
585 vk::SamplerMipmapMode::eNearest, // mipmapMode
586 vk::SamplerAddressMode::eRepeat, // addressModeU
587 vk::SamplerAddressMode::eRepeat, // addressModeV
588 vk::SamplerAddressMode::eRepeat, // addressModeW
589 0.0, // mipLodBias
590 VK_FALSE, // anisotropyEnable
591 0.0, // maxAnisotropy
592 VK_FALSE, // compareEnable
593 vk::CompareOp::eNever,
594 0.0, // minLod
595 0.0, // maxLod
596 vk::BorderColor::eFloatTransparentBlack,
597 VK_FALSE // unnormazlizedCoordinates
598 };
599 atlas_sampler = device.createSampler(samplerCreateInfo);
600
601 atlas_sampler_descriptor_image_info = {atlas_sampler, vk::ImageView(), vk::ImageLayout::eUndefined};
602
603 // There needs to be at least one atlas image, so the array of samplers can point to
604 // the single image.
605 add_atlas_image();
606}
607
608inline void gfx_pipeline_image::device_shared::teardown_atlas(gfx_device const *old_device)
609{
610 hi_assert_not_null(old_device);
611 old_device->destroy(atlas_sampler);
612
613 for (const auto& atlas_texture : atlas_textures) {
614 old_device->destroy(atlas_texture.view);
615 old_device->destroyImage(atlas_texture.image, atlas_texture.allocation);
616 }
617 atlas_textures.clear();
618
619 old_device->unmapMemory(staging_texture.allocation);
620 old_device->destroyImage(staging_texture.image, staging_texture.allocation);
621}
622
625 aarectangle const& clipping_rectangle,
626 quad const& box,
627 paged_image const& image) noexcept
628{
629 hi_assert(image.state == paged_image::state_type::uploaded);
630
631 constexpr auto page_size2 =
632 f32x4{i32x4{narrow_cast<int32_t>(paged_image::page_size), narrow_cast<int32_t>(paged_image::page_size)}};
633
634 hilet image_size = image.size();
635 hilet size_in_float_pages = f32x4{image.size_in_float_pages()};
636 hilet size_in_int_pages = i32x4{ceil(size_in_float_pages)};
637 hilet num_columns = narrow_cast<std::size_t>(size_in_int_pages.x());
638 hilet num_rows = narrow_cast<std::size_t>(size_in_int_pages.y());
639
640 hilet page_to_quad_ratio = rcp(size_in_float_pages);
643 hilet left_increment = page_to_quad_ratio_y * box.left();
644 hilet right_increment = page_to_quad_ratio_y * box.right();
645
646 auto left_bottom = box.p0;
647 auto right_bottom = box.p1;
648 auto bottom_increment = page_to_quad_ratio_x * (right_bottom - left_bottom);
649 auto it = image.pages.begin();
650 for (std::size_t page_index = 0, row_nr = 0; row_nr != num_rows; ++row_nr) {
651 hilet left_top = left_bottom + left_increment;
652 hilet right_top = right_bottom + right_increment;
653 hilet top_increment = page_to_quad_ratio_x * (right_top - left_top);
654
655 auto new_p0 = left_bottom;
656 auto new_p2 = left_top;
657 for (std::size_t column_nr = 0; column_nr != num_columns; ++column_nr, ++page_index, ++it) {
659 hilet new_p3 = new_p2 + top_increment;
660
661 // The new quad, limited to the right-top corner of the original quad.
662 hilet atlas_position = get_atlas_position(*it);
663
664 hilet xy = f32x4{i32x4{narrow_cast<int32_t>(column_nr), narrow_cast<int32_t>(row_nr)} * paged_image::page_size};
665 hilet uv_rectangle = rectangle{atlas_position, extent2{page_size2}};
666
667 vertices.emplace_back(new_p0, clipping_rectangle, get<0>(uv_rectangle));
668 vertices.emplace_back(new_p1, clipping_rectangle, get<1>(uv_rectangle));
669 vertices.emplace_back(new_p2, clipping_rectangle, get<2>(uv_rectangle));
670 vertices.emplace_back(new_p3, clipping_rectangle, get<3>(uv_rectangle));
671
672 new_p0 = new_p1;
673 new_p2 = new_p3;
674 }
675
676 left_bottom = left_top;
677 right_bottom = right_top;
679 }
680}
681
682}} // namespace hi::v1
@ bottom
Align to the bottom.
@ top
Align to the top.
@ 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.hpp:16
geometry/margins.hpp
Definition lookahead_iterator.hpp:5
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:324
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:304
std::ptrdiff_t ssize_t
Signed size/index into an array.
Definition misc.hpp:33
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:377
Definition png.hpp:27
Class which represents an axis-aligned rectangle.
Definition aarectangle.hpp:29
A high-level geometric extent.
Definition extent2.hpp:29
A rectangle / parallelogram in 3D space.
Definition rectangle.hpp:21
Definition scale3.hpp:14
This is a image that is uploaded into the texture atlas.
Definition gfx_pipeline_image_vulkan.hpp:79
void upload(pixmap_span< sfloat_rgba16 const > image) noexcept
Upload image to atlas.
Definition gfx_pipeline_image_vulkan_impl.hpp:240
void destroy(gfx_device const *vulkanDevice)
Definition gfx_pipeline_image_vulkan_impl.hpp:265
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:623
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:272
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:460
hi::pixmap_span< sfloat_rgba16 > get_staging_pixmap()
Get the full staging pixel map excluding border.
Definition gfx_pipeline_image_vulkan_impl.hpp:292
void free_pages(std::vector< std::size_t > const &pages) noexcept
Deallocate pages back to the atlas.
Definition gfx_pipeline_image_vulkan_impl.hpp:287
A non-owning 2D pixel-based image.
Definition pixmap_span.hpp:34
Universal Resource Locator.
Definition URL.hpp:51
T ceil(T... args)
T lock(T... args)
T move(T... args)
T tie(T... args)