2
0

RmlUi_Renderer_VK.h 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. /*
  2. * This source file is part of RmlUi, the HTML/CSS Interface Middleware
  3. *
  4. * For the latest information, see http://github.com/mikke89/RmlUi
  5. *
  6. * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
  7. * Copyright (c) 2019 The RmlUi Team, and contributors
  8. *
  9. * Permission is hereby granted, free of charge, to any person obtaining a copy
  10. * of this software and associated documentation files (the "Software"), to deal
  11. * in the Software without restriction, including without limitation the rights
  12. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. * copies of the Software, and to permit persons to whom the Software is
  14. * furnished to do so, subject to the following conditions:
  15. *
  16. * The above copyright notice and this permission notice shall be included in
  17. * all copies or substantial portions of the Software.
  18. *
  19. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. * THE SOFTWARE.
  26. *
  27. */
  28. #ifndef RMLUI_BACKENDS_RENDERER_VK_H
  29. #define RMLUI_BACKENDS_RENDERER_VK_H
  30. #include <RmlUi/Core/RenderInterface.h>
  31. /**
  32. * Include third-party dependencies.
  33. */
  34. #ifdef RMLUI_PLATFORM_WIN32
  35. #include "RmlUi_Include_Windows.h"
  36. #define VK_USE_PLATFORM_WIN32_KHR
  37. #endif
  38. #if (_MSC_VER > 0)
  39. #define RMLUI_DISABLE_ALL_COMPILER_WARNINGS_PUSH _Pragma("warning(push, 0)")
  40. #define RMLUI_DISABLE_ALL_COMPILER_WARNINGS_POP _Pragma("warning(pop)")
  41. #elif __clang__
  42. #define RMLUI_DISABLE_ALL_COMPILER_WARNINGS_PUSH \
  43. _Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wall\"") _Pragma("clang diagnostic ignored \"-Wextra\"") \
  44. _Pragma("clang diagnostic ignored \"-Wnullability-extension\"") \
  45. _Pragma("clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"") \
  46. _Pragma("clang diagnostic ignored \"-Wnullability-completeness\"")
  47. #define RMLUI_DISABLE_ALL_COMPILER_WARNINGS_POP _Pragma("clang diagnostic pop")
  48. #elif __GNUC__
  49. #define RMLUI_DISABLE_ALL_COMPILER_WARNINGS_PUSH \
  50. _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"") \
  51. _Pragma("GCC diagnostic ignored \"-Wunused-function\"") _Pragma("GCC diagnostic ignored \"-Wunused-parameter\"") \
  52. _Pragma("GCC diagnostic ignored \"-Wunused-variable\"") _Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"") \
  53. _Pragma("GCC diagnostic ignored \"-Wswitch\"") _Pragma("GCC diagnostic ignored \"-Wpedantic\"") \
  54. _Pragma("GCC diagnostic ignored \"-Wattributes\"") _Pragma("GCC diagnostic ignored \"-Wignored-qualifiers\"") \
  55. _Pragma("GCC diagnostic ignored \"-Wparentheses\"")
  56. #define RMLUI_DISABLE_ALL_COMPILER_WARNINGS_POP _Pragma("GCC diagnostic pop")
  57. #else
  58. #define RMLUI_DISABLE_ALL_COMPILER_WARNINGS_PUSH
  59. #define RMLUI_DISABLE_ALL_COMPILER_WARNINGS_POP
  60. #endif
  61. RMLUI_DISABLE_ALL_COMPILER_WARNINGS_PUSH
  62. #include "RmlUi_Vulkan/vulkan.h"
  63. #define VMA_STATIC_VULKAN_FUNCTIONS 0
  64. #define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
  65. #include "RmlUi_Vulkan/vk_mem_alloc.h"
  66. RMLUI_DISABLE_ALL_COMPILER_WARNINGS_POP
  67. #ifdef RMLUI_DEBUG
  68. #define RMLUI_VK_ASSERTMSG(statement, msg) RMLUI_ASSERTMSG(statement, msg)
  69. // Uncomment the following line to enable additional Vulkan debugging.
  70. //#define RMLUI_VK_DEBUG
  71. #else
  72. #define RMLUI_VK_ASSERTMSG(statement, msg) static_cast<void>(statement)
  73. #endif
  74. /**
  75. * Vulkan render interface for RmlUi
  76. *
  77. * My aim is to create compact, but easy to use class
  78. * I understand that it isn't good architectural choice to keep all things in one class
  79. * But I follow to RMLUI design and for implementing one GAPI backend it just needs one class
  80. * For user looks cool, but for programmer...
  81. *
  82. * It's better to try operate with very clean 'one-class' architecture rather than create own library for Vulkan
  83. * With many different classes, with not trivial signatures and etc
  84. * And as a result we should document that library so it's just a headache for all of us
  85. *
  86. * Reminder to users: If you want to implement your Vulkan renderer check previous commits of this work, because current system works only with new
  87. * and delete operations every frame (CPU side), on GPU we implemented the pre-allocated buffer with virtual allocs (Vma) so there's no problems and
  88. * all fine. I wrote all ideas and implementation for that.
  89. *
  90. * @author wh1t3lord
  91. */
  92. class RenderInterface_VK : public Rml::RenderInterface {
  93. public:
  94. static constexpr uint32_t kSwapchainBackBufferCount = 3;
  95. static constexpr VkDeviceSize kVideoMemoryForAllocation = 4 * 1024 * 1024; // [bytes]
  96. RenderInterface_VK();
  97. ~RenderInterface_VK();
  98. using CreateSurfaceCallback = bool (*)(VkInstance instance, VkSurfaceKHR* out_surface);
  99. bool Initialize(CreateSurfaceCallback create_surface_callback);
  100. void Shutdown();
  101. void BeginFrame();
  102. void EndFrame();
  103. void SetViewport(int width, int height);
  104. bool IsSwapchainValid();
  105. void RecreateSwapchain();
  106. // -- Inherited from Rml::RenderInterface --
  107. /// Called by RmlUi when it wants to render geometry that it does not wish to optimise.
  108. void RenderGeometry(Rml::Vertex* vertices, int num_vertices, int* indices, int num_indices, Rml::TextureHandle texture,
  109. const Rml::Vector2f& translation) override;
  110. /// Called by RmlUi when it wants to compile geometry it believes will be static for the forseeable future.
  111. Rml::CompiledGeometryHandle CompileGeometry(Rml::Vertex* vertices, int num_vertices, int* indices, int num_indices,
  112. Rml::TextureHandle texture) override;
  113. /// Called by RmlUi when it wants to render application-compiled geometry.
  114. void RenderCompiledGeometry(Rml::CompiledGeometryHandle geometry, const Rml::Vector2f& translation) override;
  115. /// Called by RmlUi when it wants to release application-compiled geometry.
  116. void ReleaseCompiledGeometry(Rml::CompiledGeometryHandle geometry) override;
  117. /// Called by RmlUi when it wants to enable or disable scissoring to clip content.
  118. void EnableScissorRegion(bool enable) override;
  119. /// Called by RmlUi when it wants to change the scissor region.
  120. void SetScissorRegion(int x, int y, int width, int height) override;
  121. /// Called by RmlUi when a texture is required by the library.
  122. bool LoadTexture(Rml::TextureHandle& texture_handle, Rml::Vector2i& texture_dimensions, const Rml::String& source) override;
  123. /// Called by RmlUi when a texture is required to be built from an internally-generated sequence of pixels.
  124. bool GenerateTexture(Rml::TextureHandle& texture_handle, const Rml::byte* source, const Rml::Vector2i& source_dimensions) override;
  125. /// Called by RmlUi when a loaded texture is no longer required.
  126. void ReleaseTexture(Rml::TextureHandle texture_handle) override;
  127. /// Called by RmlUi when it wants to set the current transform matrix to a new matrix.
  128. void SetTransform(const Rml::Matrix4f* transform) override;
  129. private:
  130. enum class shader_type_t : int { Vertex, Fragment, Unknown = -1 };
  131. enum class shader_id_t : int { Vertex, Fragment_WithoutTextures, Fragment_WithTextures };
  132. struct shader_vertex_user_data_t {
  133. // Member objects are order-sensitive to match shader.
  134. Rml::Matrix4f m_transform;
  135. Rml::Vector2f m_translate;
  136. };
  137. struct texture_data_t {
  138. VkImage m_p_vk_image;
  139. VkImageView m_p_vk_image_view;
  140. VkSampler m_p_vk_sampler;
  141. VkDescriptorSet m_p_vk_descriptor_set;
  142. VmaAllocation m_p_vma_allocation;
  143. };
  144. struct geometry_handle_t {
  145. bool m_has_texture;
  146. int m_num_indices;
  147. texture_data_t* m_p_texture;
  148. VkDescriptorBufferInfo m_p_vertex;
  149. VkDescriptorBufferInfo m_p_index;
  150. VkDescriptorBufferInfo m_p_shader;
  151. // @ this is for freeing our logical blocks for VMA
  152. // see https://gpuopen-librariesandsdks.github.io/VulkanMemoryAllocator/html/virtual_allocator.html
  153. VmaVirtualAllocation m_p_vertex_allocation;
  154. VmaVirtualAllocation m_p_index_allocation;
  155. VmaVirtualAllocation m_p_shader_allocation;
  156. };
  157. struct buffer_data_t {
  158. VkBuffer m_p_vk_buffer;
  159. VmaAllocation m_p_vma_allocation;
  160. };
  161. class UploadResourceManager {
  162. public:
  163. UploadResourceManager() : m_p_device{}, m_p_fence{}, m_p_command_buffer{}, m_p_command_pool{}, m_p_graphics_queue{} {}
  164. ~UploadResourceManager() {}
  165. void Initialize(VkDevice p_device, VkQueue p_queue, uint32_t queue_family_index)
  166. {
  167. RMLUI_VK_ASSERTMSG(p_queue, "you have to pass a valid VkQueue");
  168. RMLUI_VK_ASSERTMSG(p_device, "you have to pass a valid VkDevice for creation resources");
  169. m_p_device = p_device;
  170. m_p_graphics_queue = p_queue;
  171. Create_All(queue_family_index);
  172. }
  173. void Shutdown()
  174. {
  175. vkDestroyFence(m_p_device, m_p_fence, nullptr);
  176. vkDestroyCommandPool(m_p_device, m_p_command_pool, nullptr);
  177. }
  178. template <typename Func>
  179. void UploadToGPU(Func&& p_user_commands) noexcept
  180. {
  181. RMLUI_VK_ASSERTMSG(m_p_command_buffer, "you didn't initialize VkCommandBuffer");
  182. VkCommandBufferBeginInfo info_command = {};
  183. info_command.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
  184. info_command.pNext = nullptr;
  185. info_command.pInheritanceInfo = nullptr;
  186. info_command.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
  187. VkResult status = vkBeginCommandBuffer(m_p_command_buffer, &info_command);
  188. RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkBeginCommandBuffer");
  189. p_user_commands(m_p_command_buffer);
  190. status = vkEndCommandBuffer(m_p_command_buffer);
  191. RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "faield to vkEndCommandBuffer");
  192. Submit();
  193. Wait();
  194. }
  195. private:
  196. void Create_Fence() noexcept
  197. {
  198. VkFenceCreateInfo info = {};
  199. info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
  200. info.pNext = nullptr;
  201. info.flags = 0;
  202. VkResult status = vkCreateFence(m_p_device, &info, nullptr, &m_p_fence);
  203. RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkCreateFence");
  204. }
  205. void Create_CommandBuffer() noexcept
  206. {
  207. RMLUI_VK_ASSERTMSG(m_p_command_pool, "you have to initialize VkCommandPool before calling this method!");
  208. VkCommandBufferAllocateInfo info = {};
  209. info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
  210. info.pNext = nullptr;
  211. info.commandPool = m_p_command_pool;
  212. info.commandBufferCount = 1;
  213. info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
  214. VkResult status = vkAllocateCommandBuffers(m_p_device, &info, &m_p_command_buffer);
  215. RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkAllocateCommandBuffers");
  216. }
  217. void Create_CommandPool(uint32_t queue_family_index) noexcept
  218. {
  219. VkCommandPoolCreateInfo info = {};
  220. info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
  221. info.pNext = nullptr;
  222. info.queueFamilyIndex = queue_family_index;
  223. info.flags = 0;
  224. VkResult status = vkCreateCommandPool(m_p_device, &info, nullptr, &m_p_command_pool);
  225. RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkCreateCommandPool");
  226. }
  227. void Create_All(uint32_t queue_family_index) noexcept
  228. {
  229. Create_Fence();
  230. Create_CommandPool(queue_family_index);
  231. Create_CommandBuffer();
  232. }
  233. void Wait() noexcept
  234. {
  235. RMLUI_VK_ASSERTMSG(m_p_fence, "you must initialize your VkFence");
  236. vkWaitForFences(m_p_device, 1, &m_p_fence, VK_TRUE, UINT64_MAX);
  237. vkResetFences(m_p_device, 1, &m_p_fence);
  238. vkResetCommandPool(m_p_device, m_p_command_pool, 0);
  239. }
  240. void Submit() noexcept
  241. {
  242. VkSubmitInfo info = {};
  243. info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
  244. info.pNext = nullptr;
  245. info.waitSemaphoreCount = 0;
  246. info.signalSemaphoreCount = 0;
  247. info.pSignalSemaphores = nullptr;
  248. info.pWaitSemaphores = nullptr;
  249. info.pWaitDstStageMask = nullptr;
  250. info.pCommandBuffers = &m_p_command_buffer;
  251. info.commandBufferCount = 1;
  252. auto status = vkQueueSubmit(m_p_graphics_queue, 1, &info, m_p_fence);
  253. RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkQueueSubmit");
  254. }
  255. private:
  256. VkDevice m_p_device;
  257. VkFence m_p_fence;
  258. VkCommandBuffer m_p_command_buffer;
  259. VkCommandPool m_p_command_pool;
  260. VkQueue m_p_graphics_queue;
  261. };
  262. // @ main manager for "allocating" vertex, index, uniform stuff
  263. class MemoryPool {
  264. public:
  265. MemoryPool();
  266. ~MemoryPool();
  267. void Initialize(VkDeviceSize byte_size, VkDeviceSize device_min_uniform_alignment, VmaAllocator p_allocator, VkDevice p_device) noexcept;
  268. void Shutdown() noexcept;
  269. bool Alloc_GeneralBuffer(VkDeviceSize size, void** p_data, VkDescriptorBufferInfo* p_out, VmaVirtualAllocation* p_alloc) noexcept;
  270. bool Alloc_VertexBuffer(uint32_t number_of_elements, uint32_t stride_in_bytes, void** p_data, VkDescriptorBufferInfo* p_out,
  271. VmaVirtualAllocation* p_alloc) noexcept;
  272. bool Alloc_IndexBuffer(uint32_t number_of_elements, uint32_t stride_in_bytes, void** p_data, VkDescriptorBufferInfo* p_out,
  273. VmaVirtualAllocation* p_alloc) noexcept;
  274. void SetDescriptorSet(uint32_t binding_index, uint32_t size, VkDescriptorType descriptor_type, VkDescriptorSet p_set) noexcept;
  275. void SetDescriptorSet(uint32_t binding_index, VkDescriptorBufferInfo* p_info, VkDescriptorType descriptor_type,
  276. VkDescriptorSet p_set) noexcept;
  277. void SetDescriptorSet(uint32_t binding_index, VkSampler p_sampler, VkImageLayout layout, VkImageView p_view, VkDescriptorType descriptor_type,
  278. VkDescriptorSet p_set) noexcept;
  279. void Free_GeometryHandle(geometry_handle_t* p_valid_geometry_handle) noexcept;
  280. void Free_GeometryHandle_ShaderDataOnly(geometry_handle_t* p_valid_geometry_handle) noexcept;
  281. private:
  282. VkDeviceSize m_memory_total_size;
  283. VkDeviceSize m_device_min_uniform_alignment;
  284. char* m_p_data;
  285. VkBuffer m_p_buffer;
  286. VmaAllocation m_p_buffer_alloc;
  287. VkDevice m_p_device;
  288. VmaAllocator m_p_vk_allocator;
  289. VmaVirtualBlock m_p_block;
  290. };
  291. // If we need additional command buffers, we can add them to this list and retrieve them from the ring.
  292. enum class CommandBufferName { Primary, Count };
  293. // The command buffer ring stores a unique set of named command buffers for each bufferd frame.
  294. // Explanation of how to use Vulkan efficiently: https://vkguide.dev/docs/chapter-4/double_buffering/
  295. class CommandBufferRing {
  296. public:
  297. static constexpr uint32_t kNumFramesToBuffer = kSwapchainBackBufferCount;
  298. static constexpr uint32_t kNumCommandBuffersPerFrame = static_cast<uint32_t>(CommandBufferName::Count);
  299. CommandBufferRing();
  300. void Initialize(VkDevice p_device, uint32_t queue_index_graphics) noexcept;
  301. void Shutdown();
  302. void OnBeginFrame();
  303. VkCommandBuffer GetCommandBufferForActiveFrame(CommandBufferName named_command_buffer);
  304. private:
  305. struct CommandBuffersPerFrame {
  306. Rml::Array<VkCommandPool, kNumCommandBuffersPerFrame> m_command_pools;
  307. Rml::Array<VkCommandBuffer, kNumCommandBuffersPerFrame> m_command_buffers;
  308. };
  309. VkDevice m_p_device;
  310. uint32_t m_frame_index;
  311. CommandBuffersPerFrame* m_p_current_frame;
  312. Rml::Array<CommandBuffersPerFrame, kNumFramesToBuffer> m_frames;
  313. };
  314. class DescriptorPoolManager {
  315. public:
  316. DescriptorPoolManager() : m_allocated_descriptor_count{}, m_p_descriptor_pool{} {}
  317. ~DescriptorPoolManager()
  318. {
  319. RMLUI_VK_ASSERTMSG(m_allocated_descriptor_count <= 0, "something is wrong. You didn't free some VkDescriptorSet");
  320. }
  321. void Initialize(VkDevice p_device, uint32_t count_uniform_buffer, uint32_t count_image_sampler, uint32_t count_sampler,
  322. uint32_t count_storage_buffer) noexcept
  323. {
  324. RMLUI_VK_ASSERTMSG(p_device, "you can't pass an invalid VkDevice here");
  325. Rml::Array<VkDescriptorPoolSize, 5> sizes;
  326. sizes[0] = {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, count_uniform_buffer};
  327. sizes[1] = {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, count_uniform_buffer};
  328. sizes[2] = {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, count_image_sampler};
  329. sizes[3] = {VK_DESCRIPTOR_TYPE_SAMPLER, count_sampler};
  330. sizes[4] = {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, count_storage_buffer};
  331. VkDescriptorPoolCreateInfo info = {};
  332. info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
  333. info.pNext = nullptr;
  334. info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
  335. info.maxSets = 1000;
  336. info.poolSizeCount = static_cast<uint32_t>(sizes.size());
  337. info.pPoolSizes = sizes.data();
  338. auto status = vkCreateDescriptorPool(p_device, &info, nullptr, &m_p_descriptor_pool);
  339. RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkCreateDescriptorPool");
  340. }
  341. void Shutdown(VkDevice p_device)
  342. {
  343. RMLUI_VK_ASSERTMSG(p_device, "you can't pass an invalid VkDevice here");
  344. vkDestroyDescriptorPool(p_device, m_p_descriptor_pool, nullptr);
  345. }
  346. uint32_t Get_AllocatedDescriptorCount() const noexcept { return m_allocated_descriptor_count; }
  347. bool Alloc_Descriptor(VkDevice p_device, VkDescriptorSetLayout* p_layouts, VkDescriptorSet* p_sets,
  348. uint32_t descriptor_count_for_creation = 1) noexcept
  349. {
  350. RMLUI_VK_ASSERTMSG(p_layouts, "you have to pass a valid and initialized VkDescriptorSetLayout (probably you must create it)");
  351. RMLUI_VK_ASSERTMSG(p_device, "you must pass a valid VkDevice here");
  352. VkDescriptorSetAllocateInfo info = {};
  353. info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
  354. info.pNext = nullptr;
  355. info.descriptorPool = m_p_descriptor_pool;
  356. info.descriptorSetCount = descriptor_count_for_creation;
  357. info.pSetLayouts = p_layouts;
  358. auto status = vkAllocateDescriptorSets(p_device, &info, p_sets);
  359. RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkAllocateDescriptorSets");
  360. m_allocated_descriptor_count += descriptor_count_for_creation;
  361. return status == VkResult::VK_SUCCESS;
  362. }
  363. void Free_Descriptors(VkDevice p_device, VkDescriptorSet* p_sets, uint32_t descriptor_count = 1) noexcept
  364. {
  365. RMLUI_VK_ASSERTMSG(p_device, "you must pass a valid VkDevice here");
  366. if (p_sets)
  367. {
  368. m_allocated_descriptor_count -= descriptor_count;
  369. vkFreeDescriptorSets(p_device, m_p_descriptor_pool, descriptor_count, p_sets);
  370. }
  371. }
  372. private:
  373. int m_allocated_descriptor_count;
  374. VkDescriptorPool m_p_descriptor_pool;
  375. };
  376. struct PhysicalDeviceWrapper {
  377. VkPhysicalDevice m_p_physical_device;
  378. VkPhysicalDeviceProperties m_physical_device_properties;
  379. };
  380. using PhysicalDeviceWrapperList = Rml::Vector<PhysicalDeviceWrapper>;
  381. using LayerPropertiesList = Rml::Vector<VkLayerProperties>;
  382. using ExtensionPropertiesList = Rml::Vector<VkExtensionProperties>;
  383. private:
  384. bool CreateTexture(Rml::TextureHandle& texture_handle, const Rml::byte* source, const Rml::Vector2i& dimensions, const Rml::String& name);
  385. void Initialize_Instance() noexcept;
  386. void Initialize_Device() noexcept;
  387. void Initialize_PhysicalDevice(VkPhysicalDeviceProperties& out_physical_device_properties) noexcept;
  388. void Initialize_Swapchain(VkExtent2D window_extent) noexcept;
  389. void Initialize_Surface(CreateSurfaceCallback create_surface_callback) noexcept;
  390. void Initialize_QueueIndecies() noexcept;
  391. void Initialize_Queues() noexcept;
  392. void Initialize_SyncPrimitives() noexcept;
  393. void Initialize_Resources(const VkPhysicalDeviceProperties& physical_device_properties) noexcept;
  394. void Initialize_Allocator() noexcept;
  395. void Destroy_Instance() noexcept;
  396. void Destroy_Device() noexcept;
  397. void Destroy_Swapchain() noexcept;
  398. void Destroy_Surface() noexcept;
  399. void Destroy_SyncPrimitives() noexcept;
  400. void Destroy_Resources() noexcept;
  401. void Destroy_Allocator() noexcept;
  402. void QueryInstanceLayers(LayerPropertiesList& result) noexcept;
  403. void QueryInstanceExtensions(ExtensionPropertiesList& result, const LayerPropertiesList& instance_layer_properties) noexcept;
  404. bool AddLayerToInstance(Rml::Vector<const char*>& result, const LayerPropertiesList& instance_layer_properties,
  405. const char* p_instance_layer_name) noexcept;
  406. bool AddExtensionToInstance(Rml::Vector<const char*>& result, const ExtensionPropertiesList& instance_extension_properties,
  407. const char* p_instance_extension_name) noexcept;
  408. void CreatePropertiesFor_Instance(Rml::Vector<const char*>& instance_layer_names, Rml::Vector<const char*>& instance_extension_names) noexcept;
  409. bool IsLayerPresent(const LayerPropertiesList& properties, const char* p_layer_name) noexcept;
  410. bool IsExtensionPresent(const ExtensionPropertiesList& properties, const char* p_extension_name) noexcept;
  411. bool AddExtensionToDevice(Rml::Vector<const char*>& result, const ExtensionPropertiesList& device_extension_properties,
  412. const char* p_device_extension_name) noexcept;
  413. void CreatePropertiesFor_Device(ExtensionPropertiesList& result) noexcept;
  414. void CreateReportDebugCallback() noexcept;
  415. void Destroy_ReportDebugCallback() noexcept;
  416. uint32_t GetUserAPIVersion() const noexcept;
  417. uint32_t GetRequiredVersionAndValidateMachine() noexcept;
  418. void CollectPhysicalDevices(PhysicalDeviceWrapperList& out_physical_devices) noexcept;
  419. const PhysicalDeviceWrapper* ChoosePhysicalDevice(const PhysicalDeviceWrapperList& physical_devices, VkPhysicalDeviceType device_type) noexcept;
  420. VkSurfaceFormatKHR ChooseSwapchainFormat() noexcept;
  421. VkSurfaceTransformFlagBitsKHR CreatePretransformSwapchain() noexcept;
  422. VkCompositeAlphaFlagBitsKHR ChooseSwapchainCompositeAlpha() noexcept;
  423. int Choose_SwapchainImageCount(uint32_t user_swapchain_count_for_creation = kSwapchainBackBufferCount, bool if_failed_choose_min = true) noexcept;
  424. VkPresentModeKHR GetPresentMode(VkPresentModeKHR type = VkPresentModeKHR::VK_PRESENT_MODE_FIFO_KHR) noexcept;
  425. VkSurfaceCapabilitiesKHR GetSurfaceCapabilities() noexcept;
  426. VkExtent2D GetValidSurfaceExtent() noexcept;
  427. void CreateShaders() noexcept;
  428. void CreateDescriptorSetLayout() noexcept;
  429. void CreatePipelineLayout() noexcept;
  430. void CreateDescriptorSets() noexcept;
  431. void CreateSamplers() noexcept;
  432. void Create_Pipelines() noexcept;
  433. void CreateRenderPass() noexcept;
  434. void CreateSwapchainFrameBuffers() noexcept;
  435. // This method is called in Views, so don't call it manually
  436. void CreateSwapchainImages() noexcept;
  437. void CreateSwapchainImageViews() noexcept;
  438. void Create_DepthStencilImage() noexcept;
  439. void Create_DepthStencilImageViews() noexcept;
  440. void CreateResourcesDependentOnSize() noexcept;
  441. buffer_data_t CreateResource_StagingBuffer(VkDeviceSize size, VkBufferUsageFlags flags) noexcept;
  442. void DestroyResource_StagingBuffer(const buffer_data_t& data) noexcept;
  443. void Destroy_Textures() noexcept;
  444. void Destroy_Geometries() noexcept;
  445. void Destroy_Texture(const texture_data_t& p_texture) noexcept;
  446. void DestroyResourcesDependentOnSize() noexcept;
  447. void DestroySwapchainImageViews() noexcept;
  448. void DestroySwapchainFrameBuffers() noexcept;
  449. void DestroyRenderPass() noexcept;
  450. void Destroy_Pipelines() noexcept;
  451. void DestroyDescriptorSets() noexcept;
  452. void DestroyPipelineLayout() noexcept;
  453. void DestroySamplers() noexcept;
  454. void Wait() noexcept;
  455. void Update_PendingForDeletion_Textures_By_Frames() noexcept;
  456. void Update_PendingForDeletion_Geometries() noexcept;
  457. void Submit() noexcept;
  458. void Present() noexcept;
  459. VkFormat Get_SupportedDepthFormat();
  460. private:
  461. bool m_is_transform_enabled;
  462. bool m_is_apply_to_regular_geometry_stencil;
  463. bool m_is_use_scissor_specified;
  464. bool m_is_use_stencil_pipeline;
  465. int m_width;
  466. int m_height;
  467. uint32_t m_queue_index_present;
  468. uint32_t m_queue_index_graphics;
  469. uint32_t m_queue_index_compute;
  470. uint32_t m_semaphore_index;
  471. uint32_t m_semaphore_index_previous;
  472. uint32_t m_image_index;
  473. VkInstance m_p_instance;
  474. VkDevice m_p_device;
  475. VkPhysicalDevice m_p_physical_device;
  476. VkSurfaceKHR m_p_surface;
  477. VkSwapchainKHR m_p_swapchain;
  478. VmaAllocator m_p_allocator;
  479. // @ obtained from command list see PrepareRenderBuffer method
  480. VkCommandBuffer m_p_current_command_buffer;
  481. VkDescriptorSetLayout m_p_descriptor_set_layout_vertex_transform;
  482. VkDescriptorSetLayout m_p_descriptor_set_layout_texture;
  483. VkPipelineLayout m_p_pipeline_layout;
  484. VkPipeline m_p_pipeline_with_textures;
  485. VkPipeline m_p_pipeline_without_textures;
  486. VkPipeline m_p_pipeline_stencil_for_region_where_geometry_will_be_drawn;
  487. VkPipeline m_p_pipeline_stencil_for_regular_geometry_that_applied_to_region_with_textures;
  488. VkPipeline m_p_pipeline_stencil_for_regular_geometry_that_applied_to_region_without_textures;
  489. VkDescriptorSet m_p_descriptor_set;
  490. VkRenderPass m_p_render_pass;
  491. VkSampler m_p_sampler_linear;
  492. VkRect2D m_scissor;
  493. // @ means it captures the window size full width and full height, offset equals both x and y to 0
  494. VkRect2D m_scissor_original;
  495. VkViewport m_viewport;
  496. VkQueue m_p_queue_present;
  497. VkQueue m_p_queue_graphics;
  498. VkQueue m_p_queue_compute;
  499. #ifdef RMLUI_VK_DEBUG
  500. VkDebugReportCallbackEXT m_debug_report_callback_instance;
  501. #endif
  502. VkSurfaceFormatKHR m_swapchain_format;
  503. shader_vertex_user_data_t m_user_data_for_vertex_shader;
  504. texture_data_t m_texture_depthstencil;
  505. Rml::Matrix4f m_projection;
  506. Rml::Vector<VkFence> m_executed_fences;
  507. Rml::Vector<VkSemaphore> m_semaphores_image_available;
  508. Rml::Vector<VkSemaphore> m_semaphores_finished_render;
  509. Rml::Vector<VkFramebuffer> m_swapchain_frame_buffers;
  510. Rml::Vector<VkImage> m_swapchain_images;
  511. Rml::Vector<VkImageView> m_swapchain_image_views;
  512. Rml::Vector<VkShaderModule> m_shaders;
  513. Rml::Array<Rml::Vector<texture_data_t*>, kSwapchainBackBufferCount> m_pending_for_deletion_textures_by_frames;
  514. // vma handles that thing, so there's no need for frame splitting
  515. Rml::Vector<geometry_handle_t*> m_pending_for_deletion_geometries;
  516. CommandBufferRing m_command_buffer_ring;
  517. MemoryPool m_memory_pool;
  518. UploadResourceManager m_upload_manager;
  519. DescriptorPoolManager m_manager_descriptors;
  520. };
  521. #endif