Toast Engine
Loading...
Searching...
No Matches
rmlui_renderer_vk.h
1// NOLINTBEGIN
2/*
3 * This source file is part of RmlUi, the HTML/CSS Interface Middleware
4 * https://github.com/mikke89/RmlUi — vendored at tag 6.2 and adapted for toast-engine,
5 * see README.md in this directory for the list of local changes.
6 *
7 * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
8 * Copyright (c) 2019-2023 The RmlUi Team, and contributors
9 *
10 * Licensed under the MIT license, see the RmlUi repository for the full text.
11 */
12
13#pragma once
14
15#include <RmlUi/Core/RenderInterface.h>
16#include <algorithm>
17#include <array>
18#include <deque>
19#include <memory>
20#include <mutex>
21#include <unordered_map>
22#include <utility>
23#include <vector>
24#include <vk_mem_alloc.h>
25#include <vulkan/vulkan.h>
26
27#ifdef RMLUI_DEBUG
28#define RMLUI_VK_ASSERTMSG(statement, msg) RMLUI_ASSERTMSG(statement, msg)
29#else
30#define RMLUI_VK_ASSERTMSG(statement, msg) static_cast<void>(statement)
31#endif
32
33namespace rmlui_vk_sync {
34
36 VkPipelineStageFlags2 stages;
37 VkAccessFlags2 access;
38};
39
40inline constexpr VkAccessFlags2 kWriteAccess = VK_ACCESS_2_SHADER_WRITE_BIT | VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT |
41 VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT |
42 VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_2_TRANSFER_WRITE_BIT |
43 VK_ACCESS_2_HOST_WRITE_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT;
44
45inline LayoutScope ScopeForLayout(VkImageLayout layout) {
46 switch (layout) {
47 case VK_IMAGE_LAYOUT_UNDEFINED: return {VK_PIPELINE_STAGE_2_NONE, VK_ACCESS_2_NONE};
48 case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
49 return {
50 VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
51 VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT
52 };
53 case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
54 return {
55 VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT,
56 VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT
57 };
58 case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
59 return {VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, VK_ACCESS_2_SHADER_SAMPLED_READ_BIT};
60 case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
61 return {VK_PIPELINE_STAGE_2_COPY_BIT | VK_PIPELINE_STAGE_2_BLIT_BIT, VK_ACCESS_2_TRANSFER_READ_BIT};
62 case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
63 return {
64 VK_PIPELINE_STAGE_2_COPY_BIT | VK_PIPELINE_STAGE_2_BLIT_BIT | VK_PIPELINE_STAGE_2_CLEAR_BIT,
65 VK_ACCESS_2_TRANSFER_WRITE_BIT
66 };
67 default: return {VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT};
68 }
69}
70
72inline void TransitionImage(
73 VkCommandBuffer cmd, VkImage image, const VkImageSubresourceRange& range, VkImageLayout old_layout, VkImageLayout new_layout
74) {
75 const LayoutScope src = ScopeForLayout(old_layout);
76 const LayoutScope dst = ScopeForLayout(new_layout);
77
78 VkImageMemoryBarrier2 barrier = {};
79 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;
80 barrier.srcStageMask = src.stages;
81 barrier.srcAccessMask = src.access & kWriteAccess;
82 barrier.dstStageMask = dst.stages;
83 barrier.dstAccessMask = dst.access;
84 barrier.oldLayout = old_layout;
85 barrier.newLayout = new_layout;
86 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
87 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
88 barrier.image = image;
89 barrier.subresourceRange = range;
90
91 VkDependencyInfo dependency = {};
92 dependency.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
93 dependency.imageMemoryBarrierCount = 1;
94 dependency.pImageMemoryBarriers = &barrier;
95 vkCmdPipelineBarrier2(cmd, &dependency);
96}
97
98} // namespace rmlui_vk_sync
99
100class RenderInterface_VK : public Rml::RenderInterface {
101public:
102 // Matches the engine renderer's frames-in-flight; per-frame resources cycle on this
103 static constexpr uint32_t kFramesInFlight = 3;
104 static constexpr VkDeviceSize kVideoMemoryForAllocation = 64 * 1024 * 1024; // [bytes]
105
108 float m_v[6][4] = {};
109 };
110
111 RenderInterface_VK();
112 ~RenderInterface_VK();
113
117 bool Initialize(
118 VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, uint32_t queue_family_index, VkQueue queue,
119 VkFormat color_format, VkFormat depth_stencil_format, std::mutex* submit_mutex,
120 VkPipelineCache pipeline_cache = VK_NULL_HANDLE
121 );
122 void Shutdown();
123
126 std::shared_ptr<const void> OnFrameBegin(uint32_t frame_index);
127
129 VkCommandBuffer BeginRecording(VkExtent2D extent);
131 VkCommandBuffer EndRecording();
132
134 VkImageView GetLastOutputView() const { return m_p_last_output_view; }
135
136 void SetViewport(int width, int height);
137
138 // -- Inherited from Rml::RenderInterface --
139
140 Rml::CompiledGeometryHandle CompileGeometry(Rml::Span<const Rml::Vertex> vertices, Rml::Span<const int> indices) override;
141 void RenderGeometry(Rml::CompiledGeometryHandle handle, Rml::Vector2f translation, Rml::TextureHandle texture) override;
142 void ReleaseGeometry(Rml::CompiledGeometryHandle geometry) override;
143
144 Rml::TextureHandle LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source) override;
145 Rml::TextureHandle GenerateTexture(Rml::Span<const Rml::byte> source_data, Rml::Vector2i source_dimensions) override;
146 void ReleaseTexture(Rml::TextureHandle texture_handle) override;
147
148 void EnableScissorRegion(bool enable) override;
149 void SetScissorRegion(Rml::Rectanglei region) override;
150
151 void SetTransform(const Rml::Matrix4f* transform) override;
152
153 // RmlUi 6 effects API
154
155 void EnableClipMask(bool enable) override;
156 void RenderToClipMask(
157 Rml::ClipMaskOperation operation, Rml::CompiledGeometryHandle geometry, Rml::Vector2f translation
158 ) override;
159
160 Rml::LayerHandle PushLayer() override;
161 void CompositeLayers(
162 Rml::LayerHandle source, Rml::LayerHandle destination, Rml::BlendMode blend_mode,
163 Rml::Span<const Rml::CompiledFilterHandle> filters
164 ) override;
165 void PopLayer() override;
166
167 Rml::TextureHandle SaveLayerAsTexture() override;
168 Rml::CompiledFilterHandle SaveLayerAsMaskImage() override;
169
170 Rml::CompiledFilterHandle CompileFilter(const Rml::String& name, const Rml::Dictionary& parameters) override;
171 void ReleaseFilter(Rml::CompiledFilterHandle filter) override;
172
173 Rml::CompiledShaderHandle CompileShader(const Rml::String& name, const Rml::Dictionary& parameters) override;
174 void RenderShader(
175 Rml::CompiledShaderHandle shader, Rml::CompiledGeometryHandle geometry, Rml::Vector2f translation,
176 Rml::TextureHandle texture
177 ) override;
178 void ReleaseShader(Rml::CompiledShaderHandle shader) override;
179
180private:
181 enum class shader_type_t : int {
182 Vertex,
183 Fragment,
184 Unknown = -1
185 };
186 enum class shader_id_t : int {
187 Vertex,
188 Fragment_WithoutTextures,
189 Fragment_WithTextures
190 };
191
192 struct shader_vertex_user_data_t {
193 // Member objects are order-sensitive to match shader.
194 Rml::Matrix4f m_transform;
195 Rml::Vector2f m_translate;
196 };
197
198 struct texture_data_t {
199 VkImage m_p_vk_image;
200 VkImageView m_p_vk_image_view;
201 VkSampler m_p_vk_sampler;
202 VkDescriptorSet m_p_vk_descriptor_set;
203 VmaAllocation m_p_vma_allocation;
204 };
205
206 struct geometry_handle_t {
207 int m_num_indices;
208
210 bool m_transparent = false;
211
212 VkDescriptorBufferInfo m_p_vertex;
213 VkDescriptorBufferInfo m_p_index;
214
215 // @ this is for freeing our logical blocks for VMA
216 // see https://gpuopen-librariesandsdks.github.io/VulkanMemoryAllocator/html/virtual_allocator.html
217 VmaVirtualAllocation m_p_vertex_allocation;
218 VmaVirtualAllocation m_p_index_allocation;
219 };
220
221 struct buffer_data_t {
222 VkBuffer m_p_vk_buffer;
223 VmaAllocation m_p_vma_allocation;
224 };
225
227 struct effect_image_t {
228 VkImage m_p_image = nullptr;
229 VkImageView m_p_view = nullptr;
230 VmaAllocation m_p_allocation = nullptr;
231 VkDescriptorSet m_p_descriptor_set = nullptr;
232 VkImageLayout m_layout = VK_IMAGE_LAYOUT_UNDEFINED;
233 };
234
235 [[nodiscard]]
236 static bool IsValidEffectImage(const effect_image_t& image);
237
239 struct LayerPool {
240 uint64_t m_generation = 0;
241 uint64_t m_last_used_frame = 0;
242 VkExtent2D m_extent {};
243 std::deque<effect_image_t> m_layers;
244 effect_image_t m_postprocess[3];
245 effect_image_t m_blend_mask;
246 Rml::Vector<effect_image_t> m_outputs;
247 effect_image_t m_stencil;
248 uint32_t m_outputs_used = 0;
249 };
250
251 enum class FilterType {
252 Invalid = 0,
253 Passthrough,
254 Blur,
255 DropShadow,
256 ColorMatrix,
257 MaskImage
258 };
259
260 struct CompiledFilter {
261 FilterType m_type = FilterType::Invalid;
262 float m_blend_factor = 1.f; // Passthrough
263 float m_sigma = 0.f; // Blur / DropShadow
264 Rml::Vector2f m_offset; // DropShadow
265 Rml::ColourbPremultiplied m_color; // DropShadow
266 Rml::Matrix4f m_color_matrix; // ColorMatrix
267 };
268
269 struct CompiledShader {
270 // gradient parameters live in the memory pool for the shader's whole lifetime
271 VkDescriptorBufferInfo m_buffer = {};
272 VmaVirtualAllocation m_allocation = nullptr;
273 };
274
276 struct gradient_data_std140_t {
277 int32_t m_func = 0;
278 int32_t m_num_stops = 0;
279 float m_p[2] = {};
280 float m_v[2] = {};
281 float m_padding[2] = {};
282 float m_stop_colors[16][4] = {};
283 float m_stop_positions[16] = {};
284 };
285
286 static_assert(sizeof(gradient_data_std140_t) == 352, "must match the std140 layout in ui_effects.slang");
287
288 class UploadResourceManager {
289 public:
290 UploadResourceManager()
291 : m_p_device {},
292 m_p_fence {},
293 m_p_command_buffer {},
294 m_p_command_pool {},
295 m_p_graphics_queue {},
296 m_p_submit_mutex {} { }
297
298 ~UploadResourceManager() { }
299
300 void Initialize(VkDevice p_device, VkQueue p_queue, uint32_t queue_family_index, std::mutex* p_submit_mutex) {
301 RMLUI_VK_ASSERTMSG(p_queue, "you have to pass a valid VkQueue");
302 RMLUI_VK_ASSERTMSG(p_device, "you have to pass a valid VkDevice for creation resources");
303
304 m_p_device = p_device;
305 m_p_graphics_queue = p_queue;
306 m_p_submit_mutex = p_submit_mutex;
307
308 Create_All(queue_family_index);
309 }
310
311 void Shutdown() {
312 vkDestroyFence(m_p_device, m_p_fence, nullptr);
313 vkDestroyCommandPool(m_p_device, m_p_command_pool, nullptr);
314 }
315
316 template<typename Func>
317 void UploadToGPU(Func&& p_user_commands) noexcept {
318 RMLUI_VK_ASSERTMSG(m_p_command_buffer, "you didn't initialize VkCommandBuffer");
319
320 VkCommandBufferBeginInfo info_command = {};
321
322 info_command.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
323 info_command.pNext = nullptr;
324 info_command.pInheritanceInfo = nullptr;
325 info_command.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
326
327 VkResult status = vkBeginCommandBuffer(m_p_command_buffer, &info_command);
328
329 RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkBeginCommandBuffer");
330
331 p_user_commands(m_p_command_buffer);
332
333 status = vkEndCommandBuffer(m_p_command_buffer);
334
335 RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "faield to vkEndCommandBuffer");
336
337 Submit();
338 Wait();
339 }
340
341 private:
342 void Create_Fence() noexcept {
343 VkFenceCreateInfo info = {};
344 info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
345 info.pNext = nullptr;
346 info.flags = 0;
347
348 VkResult status = vkCreateFence(m_p_device, &info, nullptr, &m_p_fence);
349
350 RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkCreateFence");
351 }
352
353 void Create_CommandBuffer() noexcept {
354 RMLUI_VK_ASSERTMSG(m_p_command_pool, "you have to initialize VkCommandPool before calling this method!");
355
356 VkCommandBufferAllocateInfo info = {};
357 info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
358 info.pNext = nullptr;
359 info.commandPool = m_p_command_pool;
360 info.commandBufferCount = 1;
361 info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
362
363 VkResult status = vkAllocateCommandBuffers(m_p_device, &info, &m_p_command_buffer);
364
365 RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkAllocateCommandBuffers");
366 }
367
368 void Create_CommandPool(uint32_t queue_family_index) noexcept {
369 VkCommandPoolCreateInfo info = {};
370
371 info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
372 info.pNext = nullptr;
373 info.queueFamilyIndex = queue_family_index;
374 info.flags = 0;
375
376 VkResult status = vkCreateCommandPool(m_p_device, &info, nullptr, &m_p_command_pool);
377
378 RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkCreateCommandPool");
379 }
380
381 void Create_All(uint32_t queue_family_index) noexcept {
382 Create_Fence();
383 Create_CommandPool(queue_family_index);
384 Create_CommandBuffer();
385 }
386
387 void Wait() noexcept {
388 RMLUI_VK_ASSERTMSG(m_p_fence, "you must initialize your VkFence");
389
390 vkWaitForFences(m_p_device, 1, &m_p_fence, VK_TRUE, UINT64_MAX);
391 vkResetFences(m_p_device, 1, &m_p_fence);
392 vkResetCommandPool(m_p_device, m_p_command_pool, 0);
393 }
394
395 void Submit() noexcept {
396 VkCommandBufferSubmitInfo command_info = {};
397 command_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO;
398 command_info.commandBuffer = m_p_command_buffer;
399
400 VkSubmitInfo2 info = {};
401 info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2;
402 info.commandBufferInfoCount = 1;
403 info.pCommandBufferInfos = &command_info;
404
405 // The render thread submits on the same queue; queues need external synchronization
406 std::unique_lock<std::mutex> lock;
407 if (m_p_submit_mutex) {
408 lock = std::unique_lock<std::mutex>(*m_p_submit_mutex);
409 }
410
411 auto status = vkQueueSubmit2(m_p_graphics_queue, 1, &info, m_p_fence);
412
413 RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkQueueSubmit2");
414 }
415
416 private:
417 VkDevice m_p_device;
418 VkFence m_p_fence;
419 VkCommandBuffer m_p_command_buffer;
420 VkCommandPool m_p_command_pool;
421 VkQueue m_p_graphics_queue;
422 std::mutex* m_p_submit_mutex;
423 };
424
425 // @ main manager for "allocating" vertex, index, uniform stuff
426 class MemoryPool {
427 public:
428 MemoryPool();
429 ~MemoryPool();
430
431 void Initialize(
432 VkDeviceSize byte_size, VkDeviceSize device_min_uniform_alignment, VmaAllocator p_allocator, VkDevice p_device
433 ) noexcept;
434 void Shutdown() noexcept;
435
436 bool Alloc_GeneralBuffer(
437 VkDeviceSize size, void** p_data, VkDescriptorBufferInfo* p_out, VmaVirtualAllocation* p_alloc
438 ) noexcept;
439 bool Alloc_VertexBuffer(
440 uint32_t number_of_elements, uint32_t stride_in_bytes, void** p_data, VkDescriptorBufferInfo* p_out,
441 VmaVirtualAllocation* p_alloc
442 ) noexcept;
443 bool Alloc_IndexBuffer(
444 uint32_t number_of_elements, uint32_t stride_in_bytes, void** p_data, VkDescriptorBufferInfo* p_out,
445 VmaVirtualAllocation* p_alloc
446 ) noexcept;
447 void Flush(const VkDescriptorBufferInfo& range) noexcept;
448
449 void
450 SetDescriptorSet(uint32_t binding_index, uint32_t size, VkDescriptorType descriptor_type, VkDescriptorSet p_set) noexcept;
451 void SetDescriptorSet(
452 uint32_t binding_index, VkDescriptorBufferInfo* p_info, VkDescriptorType descriptor_type, VkDescriptorSet p_set
453 ) noexcept;
454 void SetDescriptorSet(
455 uint32_t binding_index, VkSampler p_sampler, VkImageLayout layout, VkImageView p_view, VkDescriptorType descriptor_type,
456 VkDescriptorSet p_set
457 ) noexcept;
458
459 void Free_GeometryHandle(geometry_handle_t* p_valid_geometry_handle) noexcept;
460 void Free_Allocation(VmaVirtualAllocation allocation) noexcept;
461
462 private:
463 VkDeviceSize m_memory_total_size;
464 VkDeviceSize m_device_min_uniform_alignment;
465 char* m_p_data;
466 VkBuffer m_p_buffer;
467 VmaAllocation m_p_buffer_alloc;
468 VkDevice m_p_device;
469 VmaAllocator m_p_vk_allocator;
470 VmaVirtualBlock m_p_block;
471 };
472
473 // Hands out secondary command buffers for main-thread recording, one pool per buffered
474 // frame; pools reset in OnFrameBegin once the render thread is done with that slot
475 class CommandBufferRing {
476 public:
477 CommandBufferRing();
478
479 void Initialize(VkDevice p_device, uint32_t queue_index_graphics) noexcept;
480 void Shutdown(MemoryPool& memory_pool);
481
483 std::shared_ptr<const void> AcquireSlot(MemoryPool& memory_pool);
484 VkCommandBuffer GetNextSecondaryBuffer();
485
486 void AddCurrentSlotAllocation(VmaVirtualAllocation allocation);
487 void AddCurrentSlotGeneration(uint64_t generation);
488 void AddCurrentSlotResource(std::shared_ptr<const void> resource);
489 bool IsGenerationReferenced(uint64_t generation) const;
490
491 private:
492 struct Slot {
493 VkCommandPool m_command_pool = nullptr;
494 Rml::Vector<VkCommandBuffer> m_command_buffers;
495 uint32_t m_used = 0;
496 std::shared_ptr<const int> m_guard;
497 Rml::Vector<VmaVirtualAllocation> m_transient_allocations;
498 Rml::Vector<uint64_t> m_generations;
499 Rml::Vector<std::shared_ptr<const void>> m_resources;
500 };
501
502 Slot& CreateSlot();
503
504 VkDevice m_p_device;
505 uint32_t m_queue_index;
506 Slot* m_p_current_slot;
507 Rml::Vector<Rml::UniquePtr<Slot>> m_slots;
508 };
509
510 class DescriptorPoolManager {
511 public:
512 DescriptorPoolManager() = default;
513
514 ~DescriptorPoolManager() {
515 RMLUI_VK_ASSERTMSG(m_allocations.empty(), "something is wrong. You didn't free some VkDescriptorSet");
516 }
517
518 void Initialize(
519 VkDevice p_device, uint32_t count_uniform_buffer, uint32_t count_image_sampler, uint32_t count_sampler,
520 uint32_t count_storage_buffer
521 ) noexcept {
522 RMLUI_VK_ASSERTMSG(p_device, "you can't pass an invalid VkDevice here");
523 m_p_device = p_device;
524 m_pool_sizes = {
525 VkDescriptorPoolSize {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, count_uniform_buffer},
526 VkDescriptorPoolSize { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, count_uniform_buffer},
527 VkDescriptorPoolSize {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, count_image_sampler},
528 VkDescriptorPoolSize { VK_DESCRIPTOR_TYPE_SAMPLER, count_sampler},
529 VkDescriptorPoolSize { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, count_storage_buffer},
530 };
531 m_max_sets_per_pool =
532 std::max<uint32_t>(count_uniform_buffer + count_image_sampler + count_sampler + count_storage_buffer, 64);
533 RMLUI_VK_ASSERTMSG(CreatePool(), "failed to create the initial descriptor pool");
534 }
535
536 void Shutdown(VkDevice p_device) {
537 RMLUI_VK_ASSERTMSG(p_device, "you can't pass an invalid VkDevice here");
538 for (VkDescriptorPool pool : m_pools) {
539 vkDestroyDescriptorPool(p_device, pool, nullptr);
540 }
541 m_allocations.clear();
542 m_pools.clear();
543 m_p_device = nullptr;
544 }
545
546 uint32_t Get_AllocatedDescriptorCount() const noexcept { return static_cast<uint32_t>(m_allocations.size()); }
547
548 bool Alloc_Descriptor(
549 VkDevice p_device, VkDescriptorSetLayout* p_layouts, VkDescriptorSet* p_sets, uint32_t descriptor_count_for_creation = 1
550 ) noexcept {
551 RMLUI_VK_ASSERTMSG(
552 p_layouts, "you have to pass a valid and initialized VkDescriptorSetLayout (probably you must create it)"
553 );
554 RMLUI_VK_ASSERTMSG(p_device, "you must pass a valid VkDevice here");
555 if (!p_layouts || !p_sets || descriptor_count_for_creation == 0) {
556 return false;
557 }
558
559 VkDescriptorSetAllocateInfo info = {};
560 info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
561 info.pNext = nullptr;
562 info.descriptorSetCount = descriptor_count_for_creation;
563 info.pSetLayouts = p_layouts;
564
565 auto try_allocate = [&](VkDescriptorPool pool) {
566 info.descriptorPool = pool;
567 const VkResult status = vkAllocateDescriptorSets(p_device, &info, p_sets);
568 if (status == VK_SUCCESS) {
569 for (uint32_t i = 0; i < descriptor_count_for_creation; i++) {
570 m_allocations.push_back({p_sets[i], pool});
571 }
572 }
573 return status;
574 };
575
576 for (VkDescriptorPool pool : m_pools) {
577 const VkResult status = try_allocate(pool);
578 if (status == VK_SUCCESS) {
579 return true;
580 }
581 if (status != VK_ERROR_OUT_OF_POOL_MEMORY && status != VK_ERROR_FRAGMENTED_POOL) {
582 RMLUI_VK_ASSERTMSG(false, "failed to vkAllocateDescriptorSets");
583 return false;
584 }
585 }
586
587 if (!CreatePool()) {
588 return false;
589 }
590 return try_allocate(m_pools.back()) == VK_SUCCESS;
591 }
592
593 void Free_Descriptors(VkDevice p_device, VkDescriptorSet* p_sets, uint32_t descriptor_count = 1) noexcept {
594 RMLUI_VK_ASSERTMSG(p_device, "you must pass a valid VkDevice here");
595
596 if (!p_sets) {
597 return;
598 }
599
600 for (uint32_t i = 0; i < descriptor_count; i++) {
601 const VkDescriptorSet set = p_sets[i];
602 if (!set) {
603 continue;
604 }
605
606 auto allocation = std::find_if(m_allocations.begin(), m_allocations.end(), [set](const Allocation& candidate) {
607 return candidate.m_set == set;
608 });
609 if (allocation == m_allocations.end()) {
610 RMLUI_VK_ASSERTMSG(false, "descriptor set was not allocated by this manager");
611 continue;
612 }
613
614 vkFreeDescriptorSets(p_device, allocation->m_pool, 1, &set);
615 m_allocations.erase(allocation);
616 p_sets[i] = nullptr;
617 }
618 }
619
620 private:
621 struct Allocation {
622 VkDescriptorSet m_set = nullptr;
623 VkDescriptorPool m_pool = nullptr;
624 };
625
626 bool CreatePool() noexcept {
627 VkDescriptorPoolCreateInfo info = {};
628 info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
629 info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
630 info.maxSets = m_max_sets_per_pool;
631 info.poolSizeCount = static_cast<uint32_t>(m_pool_sizes.size());
632 info.pPoolSizes = m_pool_sizes.data();
633
634 VkDescriptorPool pool = nullptr;
635 const VkResult status = vkCreateDescriptorPool(m_p_device, &info, nullptr, &pool);
636 if (status != VK_SUCCESS) {
637 RMLUI_VK_ASSERTMSG(false, "failed to vkCreateDescriptorPool");
638 return false;
639 }
640 m_pools.push_back(pool);
641 return true;
642 }
643
644 VkDevice m_p_device = nullptr;
645 uint32_t m_max_sets_per_pool = 0;
646 std::array<VkDescriptorPoolSize, 5> m_pool_sizes {};
647 Rml::Vector<VkDescriptorPool> m_pools;
648 Rml::Vector<Allocation> m_allocations;
649 };
650
651private:
652 Rml::TextureHandle CreateTexture(Rml::Span<const Rml::byte> source, Rml::Vector2i dimensions, const Rml::String& name);
653
654 void Initialize_Resources_Toast(VkQueue queue) noexcept;
655 void Destroy_Resources() noexcept;
656
657 void CreateShaders() noexcept;
658 void CreateDescriptorSetLayout() noexcept;
659 void CreatePipelineLayout() noexcept;
660 void CreateDescriptorSets() noexcept;
661 void CreateSamplers() noexcept;
662 void Create_Pipelines() noexcept;
663
664 buffer_data_t CreateResource_StagingBuffer(VkDeviceSize size, VkBufferUsageFlags flags) noexcept;
665 void DestroyResource_StagingBuffer(const buffer_data_t& data) noexcept;
666
667 void Destroy_Texture(const texture_data_t& p_texture) noexcept;
668 bool RetainResource(const void* resource);
669 void ReleaseResource(const void* resource);
670
671 void Destroy_Pipelines() noexcept;
672 void DestroyDescriptorSets() noexcept;
673 void DestroyPipelineLayout() noexcept;
674 void DestroySamplers() noexcept;
675
676 void CreateEffectResources() noexcept;
677 void DestroyEffectResources() noexcept;
678
679 LayerPool& AcquirePool(VkExtent2D extent);
680 void DestroyPool(LayerPool& pool) noexcept;
681 void RetireUnusedPools() noexcept;
682 effect_image_t CreateEffectImage(VkExtent2D extent, VkFormat format, VkImageUsageFlags usage) noexcept;
683 void DestroyEffectImage(effect_image_t& image) noexcept;
684
685 void TransitionEffectImage(effect_image_t& image, VkImageLayout new_layout);
686 void BeginLayerScope(effect_image_t& target, bool clear_color, bool clear_stencil);
687 void BeginEffectScope(effect_image_t& target, bool clear_color, VkRect2D render_area, bool use_clip_stencil = false);
688 void EndScope();
689
690 void SuspendLayerScope();
691 void ResumeLayerScope();
692
693 void RenderFullscreenPass(
694 VkPipeline pipeline, effect_image_t& destination, effect_image_t& source, effect_image_t* mask, const effects_push_t& push,
695 bool clear_destination, VkRect2D render_area, VkViewport viewport, bool use_clip_stencil = false
696 );
697
698 void RenderFilters(Rml::Span<const Rml::CompiledFilterHandle> filter_handles);
699 void RenderBlur(float sigma, LayerPool& pool, int source_destination, int temp);
700
701 VkRect2D CurrentScissor() const;
703 void ClearClipStencil(uint32_t value);
704 VkDescriptorSet GetEffectDescriptorSet(effect_image_t& image) noexcept;
705
706private:
707 bool m_is_apply_to_regular_geometry_stencil;
708 bool m_is_use_scissor_specified;
709 bool m_is_use_stencil_pipeline;
710
711 int m_width;
712 int m_height;
713
714 uint32_t m_queue_index_graphics;
715 uint32_t m_frame_index;
716
717 VkInstance m_p_instance;
718 VkDevice m_p_device;
719 VkPipelineCache m_p_pipeline_cache = VK_NULL_HANDLE;
720 VkPhysicalDevice m_p_physical_device;
721 VmaAllocator m_p_allocator;
722 // @ the secondary command buffer between BeginRecording/EndRecording
723 VkCommandBuffer m_p_current_command_buffer;
724
725 VkFormat m_color_attachment_format;
726 VkFormat m_depth_stencil_attachment_format;
727 std::mutex* m_p_submit_mutex;
728
729 VkDescriptorSetLayout m_p_descriptor_set_layout_vertex_transform;
730 VkDescriptorSetLayout m_p_descriptor_set_layout_texture;
731 VkPipelineLayout m_p_pipeline_layout;
732 VkPipeline m_p_pipeline_with_textures;
733 VkPipeline m_p_pipeline_without_textures;
734 VkPipeline m_p_pipeline_stencil_for_region_where_geometry_will_be_drawn;
735 VkPipeline m_p_pipeline_stencil_for_regular_geometry_that_applied_to_region_with_textures;
736 VkPipeline m_p_pipeline_stencil_for_regular_geometry_that_applied_to_region_without_textures;
737 VkDescriptorSet m_p_descriptor_set;
738 VkSampler m_p_sampler_linear;
739 VkRect2D m_scissor;
740
741 // @ means it captures the window size full width and full height, offset equals both x and y to 0
742 VkRect2D m_scissor_original;
743 VkViewport m_viewport;
744
745 shader_vertex_user_data_t m_user_data_for_vertex_shader;
746
747 Rml::Matrix4f m_projection;
748 Rml::Vector<VkShaderModule> m_shaders;
749 std::unordered_map<const void*, std::shared_ptr<const void>> m_live_resources;
750
751 CommandBufferRing m_command_buffer_ring;
752 MemoryPool m_memory_pool;
753 UploadResourceManager m_upload_manager;
754 DescriptorPoolManager m_manager_descriptors;
755
756 uint64_t m_pool_generation = 0;
757 uint64_t m_pool_frame = 0;
758 Rml::Vector<Rml::UniquePtr<LayerPool>> m_pools;
759 Rml::Vector<Rml::UniquePtr<LayerPool>> m_retired_pools;
760 LayerPool* m_p_current_pool = nullptr;
761
762 // recording state
763 Rml::Vector<effect_image_t*> m_layer_stack;
764 uint32_t m_stack_layers_used = 0;
765 bool m_scope_active = false;
766 effect_image_t* m_p_scope_target = nullptr;
767 int m_stencil_test_value = 1;
768 VkImageView m_p_last_output_view = nullptr;
769
770 VkSampler m_p_sampler_clamp = nullptr;
771
772 // fullscreen effect pipelines
773 // layout = [texture set, mask set] + 96B push constants
774 VkDescriptorSetLayout m_p_layout_effect_texture = nullptr;
775 VkPipelineLayout m_p_pipeline_layout_effects = nullptr;
776 VkPipeline m_p_pipeline_passthrough_blend = nullptr;
777 VkPipeline m_p_pipeline_passthrough_noblend = nullptr;
778 VkPipeline m_p_pipeline_passthrough_blend_stencil = nullptr;
779 VkPipeline m_p_pipeline_passthrough_noblend_stencil = nullptr;
780 VkPipeline m_p_pipeline_colormatrix = nullptr;
781 VkPipeline m_p_pipeline_blendmask = nullptr;
782 VkPipeline m_p_pipeline_blur = nullptr;
783 VkPipeline m_p_pipeline_dropshadow = nullptr;
784
785 // gradient pipeline
786 // layout = [vertex transform set, gradient data set]
787 VkDescriptorSetLayout m_p_layout_gradient_data = nullptr;
788 VkPipelineLayout m_p_pipeline_layout_gradient = nullptr;
789 VkPipeline m_p_pipeline_gradient = nullptr;
790 VkPipeline m_p_pipeline_gradient_stencil = nullptr;
791 VkDescriptorSet m_p_descriptor_set_gradient = nullptr;
792
793 // clip mask writes reuse the stencil pipeline
794 // Intersect needs an increment variant
795 VkPipeline m_p_pipeline_clip_write_incr = nullptr;
796 bool m_clip_incr = false;
797 uint32_t m_clip_write_value = 1;
798
799 VkShaderModule m_p_effects_shader_module = nullptr;
800};
801
802// NOLINTEND
VkImageView GetLastOutputView() const
View of the image the last recording rendered into.
Definition rmlui_renderer_vk.h:134
VkCommandBuffer BeginRecording(VkExtent2D extent)
Begins recording a secondary command buffer for one context render target.
Definition rmlui_renderer_vk.cpp:606
bool Initialize(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, uint32_t queue_family_index, VkQueue queue, VkFormat color_format, VkFormat depth_stencil_format, std::mutex *submit_mutex, VkPipelineCache pipeline_cache=VK_NULL_HANDLE)
Definition rmlui_renderer_vk.cpp:757
std::shared_ptr< const void > OnFrameBegin(uint32_t frame_index)
Definition rmlui_renderer_vk.cpp:591
VkCommandBuffer EndRecording()
Ends recording and returns the secondary command buffer for execution on the render thread.
Definition rmlui_renderer_vk.cpp:694
Mirrors the EffectsPush block in ui_effects.slang.
Definition rmlui_renderer_vk.h:107
Definition rmlui_renderer_vk.h:35