7#include "gfx_pipeline_SDF_vulkan.hpp"
8#include "gfx_surface_vulkan.hpp"
9#include "gfx_device_vulkan_impl.hpp"
10#include "draw_context.hpp"
11#include "../macros.hpp"
13namespace hi {
inline namespace v1 {
15inline void gfx_pipeline_SDF::draw_in_command_buffer(vk::CommandBuffer commandBuffer, draw_context
const&
context)
17 gfx_pipeline::draw_in_command_buffer(commandBuffer,
context);
19 hi_axiom_not_null(device());
20 device()->flushAllocation(vertexBufferAllocation, 0, vertexBufferData.size() *
sizeof(vertex));
26 device()->SDF_pipeline->drawInCommandBuffer(commandBuffer);
32 pushConstants.has_subpixels =
context.subpixel_orientation != subpixel_orientation::unknown;
34 constexpr float third = 1.0f / 3.0f;
35 switch (
context.subpixel_orientation) {
36 case subpixel_orientation::unknown:
37 pushConstants.red_subpixel_offset = vector2{0.0f, 0.0f};
38 pushConstants.blue_subpixel_offset = vector2{0.0f, 0.0f};
40 case subpixel_orientation::horizontal_rgb:
41 pushConstants.red_subpixel_offset = vector2{-
third, 0.0f};
42 pushConstants.blue_subpixel_offset = vector2{
third, 0.0f};
44 case subpixel_orientation::horizontal_bgr:
45 pushConstants.red_subpixel_offset = vector2{
third, 0.0f};
46 pushConstants.blue_subpixel_offset = vector2{-
third, 0.0f};
48 case subpixel_orientation::vertical_rgb:
49 pushConstants.red_subpixel_offset = vector2{0.0f,
third};
50 pushConstants.blue_subpixel_offset = vector2{0.0f, -
third};
52 case subpixel_orientation::vertical_bgr:
53 pushConstants.red_subpixel_offset = vector2{0.0f, -
third};
54 pushConstants.blue_subpixel_offset = vector2{0.0f,
third};
60 commandBuffer.pushConstants(
62 vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment,
64 sizeof(push_constants),
69 device()->cmdBeginDebugUtilsLabelEXT(commandBuffer,
"draw glyphs");
71 device()->cmdEndDebugUtilsLabelEXT(commandBuffer);
76 hi_axiom_not_null(device());
77 return device()->SDF_pipeline->shaderStages;
91 vk::BlendFactor::eOne,
94 vk::BlendFactor::eOne,
97 vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB |
98 vk::ColorComponentFlagBits::eA}};
105 vk::DescriptorType::eSampler,
107 vk::ShaderStageFlagBits::eFragment},
109 vk::DescriptorType::eSampledImage,
111 vk::ShaderStageFlagBits::eFragment}};
116 hi_axiom_not_null(device());
125 vk::DescriptorType::eSampler,
135 vk::DescriptorType::eSampledImage,
143inline size_t gfx_pipeline_SDF::getDescriptorSetVersion()
const
145 hi_axiom_not_null(device());
146 return device()->SDF_pipeline->atlasTextures.size();
151 return push_constants::pushConstantRanges();
154inline vk::VertexInputBindingDescription gfx_pipeline_SDF::createVertexInputBindingDescription()
const
156 return vertex::inputBindingDescription();
161 return vertex::inputAttributeDescriptions();
164inline void gfx_pipeline_SDF::build_vertex_buffers()
170 vk::BufferCreateFlags(),
172 vk::BufferUsageFlagBits::eVertexBuffer,
173 vk::SharingMode::eExclusive};
179 hi_axiom_not_null(device());
181 device()->setDebugUtilsObjectNameEXT(vertexBuffer,
"sdf-pipeline vertex buffer");
182 vertexBufferData = device()->mapMemory<vertex>(vertexBufferAllocation);
185inline void gfx_pipeline_SDF::teardown_vertex_buffers()
187 hi_axiom_not_null(device());
188 device()->unmapMemory(vertexBufferAllocation);
189 device()->destroyBuffer(vertexBuffer, vertexBufferAllocation);
192inline void gfx_pipeline_SDF::texture_map::transitionLayout(
const gfx_device &device, vk::Format format, vk::ImageLayout
nextLayout)
197 device.transition_layout(image, format, layout,
nextLayout);
202inline gfx_pipeline_SDF::device_shared::device_shared(gfx_device
const& device) : device(device)
208inline gfx_pipeline_SDF::device_shared::~device_shared() {}
210inline void gfx_pipeline_SDF::device_shared::destroy(gfx_device
const *vulkanDevice)
212 hi_assert_not_null(vulkanDevice);
214 teardownShaders(vulkanDevice);
215 teardownAtlas(vulkanDevice);
218[[nodiscard]]
inline glyph_atlas_info gfx_pipeline_SDF::device_shared::allocate_rect(
extent2 draw_extent,
scale2 draw_scale)
noexcept
220 auto image_width = ceil_cast<int>(draw_extent.width());
221 auto image_height = ceil_cast<int>(draw_extent.height());
225 if (atlas_allocation_position.x() + image_width > atlasImageWidth) {
226 atlas_allocation_position.x() = 0;
227 atlas_allocation_position.y() = atlas_allocation_position.y() + atlasAllocationMaxHeight;
228 atlasAllocationMaxHeight = 0;
233 if (atlas_allocation_position.y() + image_height > atlasImageHeight) {
234 atlas_allocation_position.x() = 0;
235 atlas_allocation_position.y() = 0;
236 atlas_allocation_position.z() = atlas_allocation_position.z() + 1;
237 atlasAllocationMaxHeight = 0;
239 if (atlas_allocation_position.z() >= atlasMaximumNrImages) {
240 hi_log_fatal(
"gfx_pipeline_SDF atlas overflow, too many glyphs in use.");
243 if (atlas_allocation_position.z() >= atlasTextures.size()) {
248 auto r = glyph_atlas_info{atlas_allocation_position, draw_extent, draw_scale,
scale2{atlasTextureCoordinateMultiplier}};
249 atlas_allocation_position.x() = atlas_allocation_position.x() + image_width;
250 atlasAllocationMaxHeight =
std::max(atlasAllocationMaxHeight, image_height);
254inline void gfx_pipeline_SDF::device_shared::uploadStagingPixmapToAtlas(glyph_atlas_info
const& location)
257 device.flushAllocation(
258 stagingTexture.allocation, 0, (stagingTexture.pixmap.height() * stagingTexture.pixmap.stride()) *
sizeof(sdf_r8));
260 stagingTexture.transitionLayout(device, vk::Format::eR8Snorm, vk::ImageLayout::eTransferSrcOptimal);
265 {vk::ImageAspectFlagBits::eColor, 0, 0, 1},
267 {vk::ImageAspectFlagBits::eColor, 0, 0, 1},
268 {floor_cast<int32_t>(location.position.x()), floor_cast<int32_t>(location.position.y()), 0},
269 {ceil_cast<uint32_t>(location.size.width()), ceil_cast<uint32_t>(location.size.height()), 1}}};
271 auto& atlasTexture = atlasTextures.
at(floor_cast<std::size_t>(location.position.z()));
272 atlasTexture.transitionLayout(device, vk::Format::eR8Snorm, vk::ImageLayout::eTransferDstOptimal);
275 stagingTexture.image,
276 vk::ImageLayout::eTransferSrcOptimal,
278 vk::ImageLayout::eTransferDstOptimal,
282inline void gfx_pipeline_SDF::device_shared::prepareStagingPixmapForDrawing()
284 stagingTexture.transitionLayout(device, vk::Format::eR8Snorm, vk::ImageLayout::eGeneral);
287inline void gfx_pipeline_SDF::device_shared::prepare_atlas_for_rendering()
289 hilet lock = std::scoped_lock(gfx_system_mutex);
290 for (
auto& atlasTexture : atlasTextures) {
291 atlasTexture.transitionLayout(device, vk::Format::eR8Snorm, vk::ImageLayout::eShaderReadOnlyOptimal);
311inline void gfx_pipeline_SDF::device_shared::add_glyph_to_atlas(hi::font
const &font, glyph_id glyph, glyph_atlas_info& info)
noexcept
313 hilet glyph_metrics = font.get_metrics(glyph);
314 hilet glyph_path = font.get_path(glyph);
315 hilet glyph_bounding_box = glyph_metrics.bounding_rectangle;
317 hilet draw_scale =
scale2{drawfontSize, drawfontSize};
318 hilet draw_bounding_box = draw_scale * glyph_bounding_box;
325 hilet draw_offset = point2{drawBorder, drawBorder} - get<0>(draw_bounding_box);
326 hilet draw_extent = draw_bounding_box.size() + 2.0f * drawBorder;
327 hilet image_size = ceil(draw_extent);
330 hilet draw_path = (
translate2{draw_offset} * draw_scale) * glyph_path;
333 hilet lock = std::scoped_lock(gfx_system_mutex);
334 prepareStagingPixmapForDrawing();
335 info = allocate_rect(image_size, image_size / draw_bounding_box.size());
337 stagingTexture.pixmap.subimage(0, 0, ceil_cast<size_t>(info.size.width()), ceil_cast<size_t>(info.size.height()));
338 fill(pixmap, draw_path);
339 uploadStagingPixmapToAtlas(info);
342inline bool gfx_pipeline_SDF::device_shared::place_vertices(
343 vector_span<vertex>& vertices,
346 hi::font
const &font, glyph_id glyph,
349 hilet[atlas_rect, glyph_was_added] = this->get_glyph_from_atlas(font, glyph);
353 auto image_index = atlas_rect->position.z();
354 auto t0 = point3(get<0>(atlas_rect->texture_coordinates), image_index);
355 auto t1 = point3(get<1>(atlas_rect->texture_coordinates), image_index);
356 auto t2 = point3(get<2>(atlas_rect->texture_coordinates), image_index);
357 auto t3 = point3(get<3>(atlas_rect->texture_coordinates), image_index);
359 vertices.emplace_back(box_with_border.p0, clipping_rectangle, t0, colors.p0);
360 vertices.emplace_back(box_with_border.p1, clipping_rectangle, t1, colors.p1);
361 vertices.emplace_back(box_with_border.p2, clipping_rectangle, t2, colors.p2);
362 vertices.emplace_back(box_with_border.p3, clipping_rectangle, t3, colors.p3);
363 return glyph_was_added;
366inline void gfx_pipeline_SDF::device_shared::drawInCommandBuffer(vk::CommandBuffer
const& commandBuffer)
368 commandBuffer.bindIndexBuffer(device.quadIndexBuffer, 0, vk::IndexType::eUint16);
371inline void gfx_pipeline_SDF::device_shared::buildShaders()
373 specializationConstants.sdf_r8maxDistance = sdf_r8::max_distance;
374 specializationConstants.atlasImageWidth = atlasImageWidth;
376 fragmentShaderSpecializationMapEntries = specialization_constants::specializationConstantMapEntries();
377 fragmentShaderSpecializationInfo = specializationConstants.specializationInfo(fragmentShaderSpecializationMapEntries);
379 vertexShaderModule = device.loadShader(
URL(
"resource:SDF_vulkan.vert.spv"));
380 fragmentShaderModule = device.loadShader(
URL(
"resource:SDF_vulkan.frag.spv"));
383 {vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eVertex, vertexShaderModule,
"main"},
384 {vk::PipelineShaderStageCreateFlags(),
385 vk::ShaderStageFlagBits::eFragment,
386 fragmentShaderModule,
388 &fragmentShaderSpecializationInfo}};
391inline void gfx_pipeline_SDF::device_shared::teardownShaders(gfx_device
const *vulkanDevice)
393 hi_assert_not_null(vulkanDevice);
395 vulkanDevice->destroy(vertexShaderModule);
396 vulkanDevice->destroy(fragmentShaderModule);
399inline void gfx_pipeline_SDF::device_shared::addAtlasImage()
401 hilet current_image_index = atlasTextures.size();
404 vk::ImageCreateInfo
const imageCreateInfo = {
405 vk::ImageCreateFlags(),
407 vk::Format::eR8Snorm,
408 vk::Extent3D(atlasImageWidth, atlasImageHeight, 1),
411 vk::SampleCountFlagBits::e1,
412 vk::ImageTiling::eOptimal,
413 vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled,
414 vk::SharingMode::eExclusive,
417 vk::ImageLayout::eUndefined};
418 VmaAllocationCreateInfo allocationCreateInfo = {};
419 auto allocation_name = std::format(
"sdf-pipeline atlas image {}", current_image_index);
420 allocationCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT;
421 allocationCreateInfo.pUserData =
const_cast<char *
>(allocation_name.c_str());
422 allocationCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
424 hilet[atlasImage, atlasImageAllocation] = device.createImage(imageCreateInfo, allocationCreateInfo);
425 device.setDebugUtilsObjectNameEXT(atlasImage, allocation_name.c_str());
427 hilet clearValue = vk::ClearColorValue{
std::array{-1.0f, -1.0f, -1.0f, -1.0f}};
428 hilet clearRange =
std::array{vk::ImageSubresourceRange{vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}};
430 device.transition_layout(
431 atlasImage, imageCreateInfo.format, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal);
432 device.clearColorImage(atlasImage, vk::ImageLayout::eTransferDstOptimal, clearValue, clearRange);
434 hilet atlasImageView = device.createImageView(
435 {vk::ImageViewCreateFlags(),
437 vk::ImageViewType::e2D,
438 imageCreateInfo.format,
439 vk::ComponentMapping(),
441 vk::ImageAspectFlagBits::eColor,
448 atlasTextures.push_back({atlasImage, atlasImageAllocation, atlasImageView});
451 for (
int i = 0; i < ssize(atlasDescriptorImageInfos); i++) {
454 atlasDescriptorImageInfos.at(i) = {
456 i < atlasTextures.size() ? atlasTextures.at(i).view : atlasTextures.at(0).view,
457 vk::ImageLayout::eShaderReadOnlyOptimal};
461inline void gfx_pipeline_SDF::device_shared::buildAtlas()
464 vk::ImageCreateInfo
const imageCreateInfo = {
465 vk::ImageCreateFlags(),
467 vk::Format::eR8Snorm,
468 vk::Extent3D(stagingImageWidth, stagingImageHeight, 1),
471 vk::SampleCountFlagBits::e1,
472 vk::ImageTiling::eLinear,
473 vk::ImageUsageFlagBits::eTransferSrc,
474 vk::SharingMode::eExclusive,
477 vk::ImageLayout::ePreinitialized};
478 VmaAllocationCreateInfo allocationCreateInfo = {};
479 allocationCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT;
480 allocationCreateInfo.pUserData =
const_cast<char *
>(
"sdf-pipeline staging image");
481 allocationCreateInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
482 hilet[image, allocation] = device.createImage(imageCreateInfo, allocationCreateInfo);
483 device.setDebugUtilsObjectNameEXT(image,
"sdf-pipeline staging image");
484 hilet data = device.mapMemory<sdf_r8>(allocation);
492 vk::SamplerCreateInfo
const samplerCreateInfo = {
493 vk::SamplerCreateFlags(),
496 vk::SamplerMipmapMode::eNearest,
497 vk::SamplerAddressMode::eClampToEdge,
498 vk::SamplerAddressMode::eClampToEdge,
499 vk::SamplerAddressMode::eClampToEdge,
504 vk::CompareOp::eNever,
507 vk::BorderColor::eFloatTransparentBlack,
510 atlasSampler = device.createSampler(samplerCreateInfo);
511 device.setDebugUtilsObjectNameEXT(atlasSampler,
"sdf-pipeline atlas sampler");
513 atlasSamplerDescriptorImageInfo = {atlasSampler, vk::ImageView(), vk::ImageLayout::eUndefined};
520inline void gfx_pipeline_SDF::device_shared::teardownAtlas(gfx_device
const *vulkanDevice)
522 hi_assert_not_null(vulkanDevice);
524 vulkanDevice->destroy(atlasSampler);
526 for (
const auto& atlasImage : atlasTextures) {
527 vulkanDevice->destroy(atlasImage.view);
528 vulkanDevice->destroyImage(atlasImage.image, atlasImage.allocation);
530 atlasTextures.clear();
532 vulkanDevice->unmapMemory(stagingTexture.allocation);
533 vulkanDevice->destroyImage(stagingTexture.image, stagingTexture.allocation);
DOXYGEN BUG.
Definition algorithm.hpp:16
unfair_recursive_mutex gfx_system_mutex
Global mutex for GUI elements, like gfx_system, gfx_device, Windows and Widgets.
Definition gfx_system_globals.hpp:18
geometry/margins.hpp
Definition lookahead_iterator.hpp:5
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
constexpr quad scale_from_center(quad const &lhs, scale2 const &rhs) noexcept
scale the quad.
Definition transform.hpp:459
A color for each corner of a quad.
Definition quad_color.hpp:20
Class which represents an axis-aligned rectangle.
Definition aarectangle.hpp:29
A high-level geometric extent.
Definition extent2.hpp:29
Definition translate2.hpp:14
A non-owning 2D pixel-based image.
Definition pixmap_span.hpp:34
Universal Resource Locator.
Definition URL.hpp:51