/* * This source file is part of RmlUi, the HTML/CSS Interface Middleware * * For the latest information, see http://github.com/mikke89/RmlUi * * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd * Copyright (c) 2019-2023 The RmlUi Team, and contributors * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #ifndef RMLUI_BACKENDS_RENDERER_VK_H #define RMLUI_BACKENDS_RENDERER_VK_H #include #ifdef RMLUI_PLATFORM_WIN32 #include "RmlUi_Include_Windows.h" #define VK_USE_PLATFORM_WIN32_KHR #endif #include "RmlUi_Include_Vulkan.h" #ifdef RMLUI_DEBUG #define RMLUI_VK_ASSERTMSG(statement, msg) RMLUI_ASSERTMSG(statement, msg) // Uncomment the following line to enable additional Vulkan debugging. // #define RMLUI_VK_DEBUG #else #define RMLUI_VK_ASSERTMSG(statement, msg) static_cast(statement) #endif // your specified api version, but in future it will be dynamic ^_^ #define RMLUI_VK_API_VERSION VK_API_VERSION_1_0 /** * Vulkan render interface for RmlUi * * My aim is to create compact, but easy to use class * I understand that it isn't good architectural choice to keep all things in one class * But I follow to RMLUI design and for implementing one GAPI backend it just needs one class * For user looks cool, but for programmer... * * It's better to try operate with very clean 'one-class' architecture rather than create own library for Vulkan * With many different classes, with not trivial signatures and etc * And as a result we should document that library so it's just a headache for all of us * * Reminder to users: If you want to implement your Vulkan renderer check previous commits of this work, because current system works only with new * 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 * all fine. I wrote all ideas and implementation for that. * * @author wh1t3lord (https://github.com/wh1t3lord) */ class RenderInterface_VK : public Rml::RenderInterface { public: static constexpr uint32_t kSwapchainBackBufferCount = 3; static constexpr VkDeviceSize kVideoMemoryForAllocation = 4 * 1024 * 1024; // [bytes] RenderInterface_VK(); ~RenderInterface_VK(); using CreateSurfaceCallback = bool (*)(VkInstance instance, VkSurfaceKHR* out_surface); bool Initialize(Rml::Vector required_extensions, CreateSurfaceCallback create_surface_callback); void Shutdown(); void BeginFrame(); void EndFrame(); void SetViewport(int width, int height); bool IsSwapchainValid(); void RecreateSwapchain(); // -- Inherited from Rml::RenderInterface -- /// Called by RmlUi when it wants to compile geometry it believes will be static for the forseeable future. Rml::CompiledGeometryHandle CompileGeometry(Rml::Span vertices, Rml::Span indices) override; /// Called by RmlUi when it wants to render application-compiled geometry. void RenderGeometry(Rml::CompiledGeometryHandle handle, Rml::Vector2f translation, Rml::TextureHandle texture) override; /// Called by RmlUi when it wants to release application-compiled geometry. void ReleaseGeometry(Rml::CompiledGeometryHandle geometry) override; /// Called by RmlUi when a texture is required by the library. Rml::TextureHandle LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source) override; /// Called by RmlUi when a texture is required to be built from an internally-generated sequence of pixels. Rml::TextureHandle GenerateTexture(Rml::Span source_data, Rml::Vector2i source_dimensions) override; /// Called by RmlUi when a loaded texture is no longer required. void ReleaseTexture(Rml::TextureHandle texture_handle) override; /// Called by RmlUi when it wants to enable or disable scissoring to clip content. void EnableScissorRegion(bool enable) override; /// Called by RmlUi when it wants to change the scissor region. void SetScissorRegion(Rml::Rectanglei region) override; /// Called by RmlUi when it wants to set the current transform matrix to a new matrix. void SetTransform(const Rml::Matrix4f* transform) override; private: enum class shader_type_t : int { Vertex, Fragment, Unknown = -1 }; enum class shader_id_t : int { Vertex, Fragment_WithoutTextures, Fragment_WithTextures }; struct shader_vertex_user_data_t { // Member objects are order-sensitive to match shader. Rml::Matrix4f m_transform; Rml::Vector2f m_translate; }; struct texture_data_t { VkImage m_p_vk_image; VkImageView m_p_vk_image_view; VkSampler m_p_vk_sampler; VkDescriptorSet m_p_vk_descriptor_set; VmaAllocation m_p_vma_allocation; }; struct geometry_handle_t { int m_num_indices; VkDescriptorBufferInfo m_p_vertex; VkDescriptorBufferInfo m_p_index; VkDescriptorBufferInfo m_p_shader; // @ this is for freeing our logical blocks for VMA // see https://gpuopen-librariesandsdks.github.io/VulkanMemoryAllocator/html/virtual_allocator.html VmaVirtualAllocation m_p_vertex_allocation; VmaVirtualAllocation m_p_index_allocation; VmaVirtualAllocation m_p_shader_allocation; }; struct buffer_data_t { VkBuffer m_p_vk_buffer; VmaAllocation m_p_vma_allocation; }; class UploadResourceManager { public: UploadResourceManager() : m_p_device{}, m_p_fence{}, m_p_command_buffer{}, m_p_command_pool{}, m_p_graphics_queue{} {} ~UploadResourceManager() {} void Initialize(VkDevice p_device, VkQueue p_queue, uint32_t queue_family_index) { RMLUI_VK_ASSERTMSG(p_queue, "you have to pass a valid VkQueue"); RMLUI_VK_ASSERTMSG(p_device, "you have to pass a valid VkDevice for creation resources"); m_p_device = p_device; m_p_graphics_queue = p_queue; Create_All(queue_family_index); } void Shutdown() { vkDestroyFence(m_p_device, m_p_fence, nullptr); vkDestroyCommandPool(m_p_device, m_p_command_pool, nullptr); } template void UploadToGPU(Func&& p_user_commands) noexcept { RMLUI_VK_ASSERTMSG(m_p_command_buffer, "you didn't initialize VkCommandBuffer"); VkCommandBufferBeginInfo info_command = {}; info_command.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; info_command.pNext = nullptr; info_command.pInheritanceInfo = nullptr; info_command.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; VkResult status = vkBeginCommandBuffer(m_p_command_buffer, &info_command); RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkBeginCommandBuffer"); p_user_commands(m_p_command_buffer); status = vkEndCommandBuffer(m_p_command_buffer); RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "faield to vkEndCommandBuffer"); Submit(); Wait(); } private: void Create_Fence() noexcept { VkFenceCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; info.pNext = nullptr; info.flags = 0; VkResult status = vkCreateFence(m_p_device, &info, nullptr, &m_p_fence); RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkCreateFence"); } void Create_CommandBuffer() noexcept { RMLUI_VK_ASSERTMSG(m_p_command_pool, "you have to initialize VkCommandPool before calling this method!"); VkCommandBufferAllocateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; info.pNext = nullptr; info.commandPool = m_p_command_pool; info.commandBufferCount = 1; info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; VkResult status = vkAllocateCommandBuffers(m_p_device, &info, &m_p_command_buffer); RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkAllocateCommandBuffers"); } void Create_CommandPool(uint32_t queue_family_index) noexcept { VkCommandPoolCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; info.pNext = nullptr; info.queueFamilyIndex = queue_family_index; info.flags = 0; VkResult status = vkCreateCommandPool(m_p_device, &info, nullptr, &m_p_command_pool); RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkCreateCommandPool"); } void Create_All(uint32_t queue_family_index) noexcept { Create_Fence(); Create_CommandPool(queue_family_index); Create_CommandBuffer(); } void Wait() noexcept { RMLUI_VK_ASSERTMSG(m_p_fence, "you must initialize your VkFence"); vkWaitForFences(m_p_device, 1, &m_p_fence, VK_TRUE, UINT64_MAX); vkResetFences(m_p_device, 1, &m_p_fence); vkResetCommandPool(m_p_device, m_p_command_pool, 0); } void Submit() noexcept { VkSubmitInfo info = {}; info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; info.pNext = nullptr; info.waitSemaphoreCount = 0; info.signalSemaphoreCount = 0; info.pSignalSemaphores = nullptr; info.pWaitSemaphores = nullptr; info.pWaitDstStageMask = nullptr; info.pCommandBuffers = &m_p_command_buffer; info.commandBufferCount = 1; auto status = vkQueueSubmit(m_p_graphics_queue, 1, &info, m_p_fence); RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkQueueSubmit"); } private: VkDevice m_p_device; VkFence m_p_fence; VkCommandBuffer m_p_command_buffer; VkCommandPool m_p_command_pool; VkQueue m_p_graphics_queue; }; // @ main manager for "allocating" vertex, index, uniform stuff class MemoryPool { public: MemoryPool(); ~MemoryPool(); void Initialize(VkDeviceSize byte_size, VkDeviceSize device_min_uniform_alignment, VmaAllocator p_allocator, VkDevice p_device) noexcept; void Shutdown() noexcept; bool Alloc_GeneralBuffer(VkDeviceSize size, void** p_data, VkDescriptorBufferInfo* p_out, VmaVirtualAllocation* p_alloc) noexcept; bool Alloc_VertexBuffer(uint32_t number_of_elements, uint32_t stride_in_bytes, void** p_data, VkDescriptorBufferInfo* p_out, VmaVirtualAllocation* p_alloc) noexcept; bool Alloc_IndexBuffer(uint32_t number_of_elements, uint32_t stride_in_bytes, void** p_data, VkDescriptorBufferInfo* p_out, VmaVirtualAllocation* p_alloc) noexcept; void SetDescriptorSet(uint32_t binding_index, uint32_t size, VkDescriptorType descriptor_type, VkDescriptorSet p_set) noexcept; void SetDescriptorSet(uint32_t binding_index, VkDescriptorBufferInfo* p_info, VkDescriptorType descriptor_type, VkDescriptorSet p_set) noexcept; void SetDescriptorSet(uint32_t binding_index, VkSampler p_sampler, VkImageLayout layout, VkImageView p_view, VkDescriptorType descriptor_type, VkDescriptorSet p_set) noexcept; void Free_GeometryHandle(geometry_handle_t* p_valid_geometry_handle) noexcept; void Free_GeometryHandle_ShaderDataOnly(geometry_handle_t* p_valid_geometry_handle) noexcept; private: VkDeviceSize m_memory_total_size; VkDeviceSize m_device_min_uniform_alignment; char* m_p_data; VkBuffer m_p_buffer; VmaAllocation m_p_buffer_alloc; VkDevice m_p_device; VmaAllocator m_p_vk_allocator; VmaVirtualBlock m_p_block; }; // If we need additional command buffers, we can add them to this list and retrieve them from the ring. enum class CommandBufferName { Primary, Count }; // The command buffer ring stores a unique set of named command buffers for each bufferd frame. // Explanation of how to use Vulkan efficiently: https://vkguide.dev/docs/chapter-4/double_buffering/ class CommandBufferRing { public: static constexpr uint32_t kNumFramesToBuffer = kSwapchainBackBufferCount; static constexpr uint32_t kNumCommandBuffersPerFrame = static_cast(CommandBufferName::Count); CommandBufferRing(); void Initialize(VkDevice p_device, uint32_t queue_index_graphics) noexcept; void Shutdown(); void OnBeginFrame(); VkCommandBuffer GetCommandBufferForActiveFrame(CommandBufferName named_command_buffer); private: struct CommandBuffersPerFrame { Rml::Array m_command_pools; Rml::Array m_command_buffers; }; VkDevice m_p_device; uint32_t m_frame_index; CommandBuffersPerFrame* m_p_current_frame; Rml::Array m_frames; }; class DescriptorPoolManager { public: DescriptorPoolManager() : m_allocated_descriptor_count{}, m_p_descriptor_pool{} {} ~DescriptorPoolManager() { RMLUI_VK_ASSERTMSG(m_allocated_descriptor_count <= 0, "something is wrong. You didn't free some VkDescriptorSet"); } void Initialize(VkDevice p_device, uint32_t count_uniform_buffer, uint32_t count_image_sampler, uint32_t count_sampler, uint32_t count_storage_buffer) noexcept { RMLUI_VK_ASSERTMSG(p_device, "you can't pass an invalid VkDevice here"); Rml::Array sizes; sizes[0] = {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, count_uniform_buffer}; sizes[1] = {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, count_uniform_buffer}; sizes[2] = {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, count_image_sampler}; sizes[3] = {VK_DESCRIPTOR_TYPE_SAMPLER, count_sampler}; sizes[4] = {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, count_storage_buffer}; VkDescriptorPoolCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; info.pNext = nullptr; info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; info.maxSets = 1000; info.poolSizeCount = static_cast(sizes.size()); info.pPoolSizes = sizes.data(); auto status = vkCreateDescriptorPool(p_device, &info, nullptr, &m_p_descriptor_pool); RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkCreateDescriptorPool"); } void Shutdown(VkDevice p_device) { RMLUI_VK_ASSERTMSG(p_device, "you can't pass an invalid VkDevice here"); vkDestroyDescriptorPool(p_device, m_p_descriptor_pool, nullptr); } uint32_t Get_AllocatedDescriptorCount() const noexcept { return m_allocated_descriptor_count; } bool Alloc_Descriptor(VkDevice p_device, VkDescriptorSetLayout* p_layouts, VkDescriptorSet* p_sets, uint32_t descriptor_count_for_creation = 1) noexcept { RMLUI_VK_ASSERTMSG(p_layouts, "you have to pass a valid and initialized VkDescriptorSetLayout (probably you must create it)"); RMLUI_VK_ASSERTMSG(p_device, "you must pass a valid VkDevice here"); VkDescriptorSetAllocateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; info.pNext = nullptr; info.descriptorPool = m_p_descriptor_pool; info.descriptorSetCount = descriptor_count_for_creation; info.pSetLayouts = p_layouts; auto status = vkAllocateDescriptorSets(p_device, &info, p_sets); RMLUI_VK_ASSERTMSG(status == VkResult::VK_SUCCESS, "failed to vkAllocateDescriptorSets"); m_allocated_descriptor_count += descriptor_count_for_creation; return status == VkResult::VK_SUCCESS; } void Free_Descriptors(VkDevice p_device, VkDescriptorSet* p_sets, uint32_t descriptor_count = 1) noexcept { RMLUI_VK_ASSERTMSG(p_device, "you must pass a valid VkDevice here"); if (p_sets) { m_allocated_descriptor_count -= descriptor_count; vkFreeDescriptorSets(p_device, m_p_descriptor_pool, descriptor_count, p_sets); } } private: int m_allocated_descriptor_count; VkDescriptorPool m_p_descriptor_pool; }; struct PhysicalDeviceWrapper { VkPhysicalDevice m_p_physical_device; VkPhysicalDeviceProperties m_physical_device_properties; }; using PhysicalDeviceWrapperList = Rml::Vector; using LayerPropertiesList = Rml::Vector; using ExtensionPropertiesList = Rml::Vector; private: Rml::TextureHandle CreateTexture(Rml::Span source, Rml::Vector2i dimensions, const Rml::String& name); void Initialize_Instance(Rml::Vector required_extensions) noexcept; void Initialize_Device() noexcept; void Initialize_PhysicalDevice(VkPhysicalDeviceProperties& out_physical_device_properties) noexcept; void Initialize_Swapchain(VkExtent2D window_extent) noexcept; void Initialize_Surface(CreateSurfaceCallback create_surface_callback) noexcept; void Initialize_QueueIndecies() noexcept; void Initialize_Queues() noexcept; void Initialize_SyncPrimitives() noexcept; void Initialize_Resources(const VkPhysicalDeviceProperties& physical_device_properties) noexcept; void Initialize_Allocator() noexcept; void Destroy_Instance() noexcept; void Destroy_Device() noexcept; void Destroy_Swapchain() noexcept; void Destroy_Surface() noexcept; void Destroy_SyncPrimitives() noexcept; void Destroy_Resources() noexcept; void Destroy_Allocator() noexcept; void QueryInstanceLayers(LayerPropertiesList& result) noexcept; void QueryInstanceExtensions(ExtensionPropertiesList& result, const LayerPropertiesList& instance_layer_properties) noexcept; bool AddLayerToInstance(Rml::Vector& result, const LayerPropertiesList& instance_layer_properties, const char* p_instance_layer_name) noexcept; bool AddExtensionToInstance(Rml::Vector& result, const ExtensionPropertiesList& instance_extension_properties, const char* p_instance_extension_name) noexcept; void CreatePropertiesFor_Instance(Rml::Vector& instance_layer_names, Rml::Vector& instance_extension_names) noexcept; bool IsLayerPresent(const LayerPropertiesList& properties, const char* p_layer_name) noexcept; bool IsExtensionPresent(const ExtensionPropertiesList& properties, const char* p_extension_name) noexcept; bool AddExtensionToDevice(Rml::Vector& result, const ExtensionPropertiesList& device_extension_properties, const char* p_device_extension_name) noexcept; void CreatePropertiesFor_Device(ExtensionPropertiesList& result) noexcept; void CreateReportDebugCallback() noexcept; void Destroy_ReportDebugCallback() noexcept; uint32_t GetUserAPIVersion() const noexcept; uint32_t GetRequiredVersionAndValidateMachine() noexcept; void CollectPhysicalDevices(PhysicalDeviceWrapperList& out_physical_devices) noexcept; const PhysicalDeviceWrapper* ChoosePhysicalDevice(const PhysicalDeviceWrapperList& physical_devices, VkPhysicalDeviceType device_type) noexcept; VkSurfaceFormatKHR ChooseSwapchainFormat() noexcept; VkSurfaceTransformFlagBitsKHR CreatePretransformSwapchain() noexcept; VkCompositeAlphaFlagBitsKHR ChooseSwapchainCompositeAlpha() noexcept; int Choose_SwapchainImageCount(uint32_t user_swapchain_count_for_creation = kSwapchainBackBufferCount, bool if_failed_choose_min = true) noexcept; VkPresentModeKHR GetPresentMode(VkPresentModeKHR type = VkPresentModeKHR::VK_PRESENT_MODE_FIFO_KHR) noexcept; VkSurfaceCapabilitiesKHR GetSurfaceCapabilities() noexcept; VkExtent2D GetValidSurfaceExtent() noexcept; void CreateShaders() noexcept; void CreateDescriptorSetLayout() noexcept; void CreatePipelineLayout() noexcept; void CreateDescriptorSets() noexcept; void CreateSamplers() noexcept; void Create_Pipelines() noexcept; void CreateRenderPass() noexcept; void CreateSwapchainFrameBuffers(const VkExtent2D& real_render_image_size) noexcept; // This method is called in Views, so don't call it manually void CreateSwapchainImages() noexcept; void CreateSwapchainImageViews() noexcept; void Create_DepthStencilImage() noexcept; void Create_DepthStencilImageViews() noexcept; void CreateResourcesDependentOnSize(const VkExtent2D& real_render_image_size) noexcept; buffer_data_t CreateResource_StagingBuffer(VkDeviceSize size, VkBufferUsageFlags flags) noexcept; void DestroyResource_StagingBuffer(const buffer_data_t& data) noexcept; void Destroy_Textures() noexcept; void Destroy_Geometries() noexcept; void Destroy_Texture(const texture_data_t& p_texture) noexcept; void DestroyResourcesDependentOnSize() noexcept; void DestroySwapchainImageViews() noexcept; void DestroySwapchainFrameBuffers() noexcept; void DestroyRenderPass() noexcept; void Destroy_Pipelines() noexcept; void DestroyDescriptorSets() noexcept; void DestroyPipelineLayout() noexcept; void DestroySamplers() noexcept; void Wait() noexcept; void Update_PendingForDeletion_Textures_By_Frames() noexcept; void Update_PendingForDeletion_Geometries() noexcept; void Submit() noexcept; void Present() noexcept; VkFormat Get_SupportedDepthFormat(); private: bool m_is_transform_enabled; bool m_is_apply_to_regular_geometry_stencil; bool m_is_use_scissor_specified; bool m_is_use_stencil_pipeline; int m_width; int m_height; uint32_t m_queue_index_present; uint32_t m_queue_index_graphics; uint32_t m_queue_index_compute; uint32_t m_semaphore_index; uint32_t m_semaphore_index_previous; uint32_t m_image_index; VkInstance m_p_instance; VkDevice m_p_device; VkPhysicalDevice m_p_physical_device; VkSurfaceKHR m_p_surface; VkSwapchainKHR m_p_swapchain; VmaAllocator m_p_allocator; // @ obtained from command list see PrepareRenderBuffer method VkCommandBuffer m_p_current_command_buffer; VkDescriptorSetLayout m_p_descriptor_set_layout_vertex_transform; VkDescriptorSetLayout m_p_descriptor_set_layout_texture; VkPipelineLayout m_p_pipeline_layout; VkPipeline m_p_pipeline_with_textures; VkPipeline m_p_pipeline_without_textures; VkPipeline m_p_pipeline_stencil_for_region_where_geometry_will_be_drawn; VkPipeline m_p_pipeline_stencil_for_regular_geometry_that_applied_to_region_with_textures; VkPipeline m_p_pipeline_stencil_for_regular_geometry_that_applied_to_region_without_textures; VkDescriptorSet m_p_descriptor_set; VkRenderPass m_p_render_pass; VkSampler m_p_sampler_linear; VkRect2D m_scissor; // @ means it captures the window size full width and full height, offset equals both x and y to 0 VkRect2D m_scissor_original; VkViewport m_viewport; VkQueue m_p_queue_present; VkQueue m_p_queue_graphics; VkQueue m_p_queue_compute; #ifdef RMLUI_VK_DEBUG VkDebugUtilsMessengerEXT m_debug_messenger; #endif VkSurfaceFormatKHR m_swapchain_format; shader_vertex_user_data_t m_user_data_for_vertex_shader; texture_data_t m_texture_depthstencil; Rml::Matrix4f m_projection; Rml::Vector m_executed_fences; Rml::Vector m_semaphores_image_available; Rml::Vector m_semaphores_finished_render; Rml::Vector m_swapchain_frame_buffers; Rml::Vector m_swapchain_images; Rml::Vector m_swapchain_image_views; Rml::Vector m_shaders; Rml::Array, kSwapchainBackBufferCount> m_pending_for_deletion_textures_by_frames; // vma handles that thing, so there's no need for frame splitting Rml::Vector m_pending_for_deletion_geometries; CommandBufferRing m_command_buffer_ring; MemoryPool m_memory_pool; UploadResourceManager m_upload_manager; DescriptorPoolManager m_manager_descriptors; }; #endif