diff --git a/lib/graphics_engine/CMakeLists.txt b/lib/graphics_engine/CMakeLists.txt index abffb68e3..fae7444a2 100644 --- a/lib/graphics_engine/CMakeLists.txt +++ b/lib/graphics_engine/CMakeLists.txt @@ -38,6 +38,7 @@ set(GE_SOURCES src/ge_vulkan_scene_manager.cpp src/ge_vulkan_shader_manager.cpp src/ge_vulkan_texture.cpp + src/ge_vulkan_texture_descriptor.cpp src/ge_gl_texture.cpp src/ge_spm.cpp ) diff --git a/lib/graphics_engine/include/ge_vulkan_texture_descriptor.hpp b/lib/graphics_engine/include/ge_vulkan_texture_descriptor.hpp new file mode 100644 index 000000000..3fad1bd32 --- /dev/null +++ b/lib/graphics_engine/include/ge_vulkan_texture_descriptor.hpp @@ -0,0 +1,104 @@ +#ifndef HEADER_GE_VULKAN_TEXTURE_DESCRIPTOR_HPP +#define HEADER_GE_VULKAN_TEXTURE_DESCRIPTOR_HPP + +#include "vulkan_wrapper.h" + +#include "IrrCompileConfig.h" +namespace irr +{ + namespace video { class ITexture; } +} + +#include +#include +#include +#include + +namespace GE +{ +class GEVulkanDriver; +enum GEVulkanSampler : unsigned; + +class GEVulkanTextureDescriptor +{ + typedef std::array, + _IRR_MATERIAL_MAX_TEXTURES_> TextureList; + + std::map m_texture_list; + + std::shared_ptr m_white_image, m_transparent_image; + + VkDescriptorSetLayout m_descriptor_set_layout; + + VkDescriptorPool m_descriptor_pool; + + std::vector m_descriptor_sets; + + const unsigned m_max_texture_list; + + const unsigned m_max_layer; + + const unsigned m_binding; + + GEVulkanSampler m_sampler_use; + + GEVulkanDriver* m_vk; + + bool m_recreate_next_frame; + + bool m_needs_update_descriptor; +public: + // ------------------------------------------------------------------------ + GEVulkanTextureDescriptor(unsigned max_texture_list, unsigned max_layer, + bool single_descriptor, unsigned binding = 0); + // ------------------------------------------------------------------------ + ~GEVulkanTextureDescriptor(); + // ------------------------------------------------------------------------ + void handleDeletedTextures() + { + bool has_deleted_image_view = false; + for (auto& p : m_texture_list) + { + for (auto& t : p.first) + { + if (*(t.get()) == VK_NULL_HANDLE) + { + has_deleted_image_view = true; + break; + } + } + } + if (has_deleted_image_view || m_recreate_next_frame) + { + m_texture_list.clear(); + m_needs_update_descriptor = true; + m_recreate_next_frame = false; + } + } + // ------------------------------------------------------------------------ + int getTextureID(const irr::video::ITexture** list); + // ------------------------------------------------------------------------ + void setSamplerUse(GEVulkanSampler sampler) + { + if (m_sampler_use == sampler) + return; + m_sampler_use = sampler; + m_needs_update_descriptor = true; + } + // ------------------------------------------------------------------------ + void updateDescriptor(); + // ------------------------------------------------------------------------ + unsigned getMaxTextureList() const { return m_max_texture_list; } + // ------------------------------------------------------------------------ + unsigned getMaxLayer() const { return m_max_layer; } + // ------------------------------------------------------------------------ + VkDescriptorSetLayout* getDescriptorSetLayout() + { return &m_descriptor_set_layout; } + // ------------------------------------------------------------------------ + VkDescriptorSet* getDescriptorSet() + { return m_descriptor_sets.data(); } +}; // GEVulkanTextureDescriptor + +} + +#endif diff --git a/lib/graphics_engine/src/ge_vulkan_depth_texture.cpp b/lib/graphics_engine/src/ge_vulkan_depth_texture.cpp index 6352d9dcb..dfac2f8ad 100644 --- a/lib/graphics_engine/src/ge_vulkan_depth_texture.cpp +++ b/lib/graphics_engine/src/ge_vulkan_depth_texture.cpp @@ -13,7 +13,6 @@ GEVulkanDepthTexture::GEVulkanDepthTexture(GEVulkanDriver* vk, m_vulkan_device = m_vk->getDevice(); m_image = VK_NULL_HANDLE; m_vma_allocation = VK_NULL_HANDLE; - m_image_view = VK_NULL_HANDLE; m_has_mipmaps = false; m_locked_data = NULL; m_size = m_orig_size = m_max_size = size; diff --git a/lib/graphics_engine/src/ge_vulkan_depth_texture.hpp b/lib/graphics_engine/src/ge_vulkan_depth_texture.hpp index 4799a6fcf..e32cd9d08 100644 --- a/lib/graphics_engine/src/ge_vulkan_depth_texture.hpp +++ b/lib/graphics_engine/src/ge_vulkan_depth_texture.hpp @@ -38,7 +38,8 @@ public: // ------------------------------------------------------------------------ virtual void regenerateMipMapLevels(void* mipmap_data = NULL) {} // ------------------------------------------------------------------------ - virtual u64 getTextureHandler() const { return (u64)m_image_view; } + virtual u64 getTextureHandler() const + { return (u64)*(m_image_view.get()); } // ------------------------------------------------------------------------ virtual unsigned int getTextureSize() const { return m_texture_size; } // ------------------------------------------------------------------------ @@ -46,6 +47,9 @@ public: // ------------------------------------------------------------------------ virtual void updateTexture(void* data, irr::video::ECOLOR_FORMAT format, u32 w, u32 h, u32 x, u32 y) {} + // ------------------------------------------------------------------------ + virtual std::shared_ptr getImageView() const + { return m_image_view; } }; // GEVulkanDepthTexture } diff --git a/lib/graphics_engine/src/ge_vulkan_texture.cpp b/lib/graphics_engine/src/ge_vulkan_texture.cpp index 5d6e8091f..4e235b00d 100644 --- a/lib/graphics_engine/src/ge_vulkan_texture.cpp +++ b/lib/graphics_engine/src/ge_vulkan_texture.cpp @@ -24,8 +24,8 @@ GEVulkanTexture::GEVulkanTexture(const std::string& path, m_locked_data(NULL), m_vulkan_device(getVKDriver()->getDevice()), m_image(VK_NULL_HANDLE), m_vma_allocation(VK_NULL_HANDLE), - m_image_view(VK_NULL_HANDLE), m_texture_size(0), - m_disable_reload(false), m_has_mipmaps(true), + m_texture_size(0), m_disable_reload(false), + m_has_mipmaps(true), m_internal_format(VK_FORMAT_R8G8B8A8_UNORM), m_vk(getVKDriver()) { @@ -50,8 +50,8 @@ GEVulkanTexture::GEVulkanTexture(video::IImage* img, const std::string& name) m_locked_data(NULL), m_vulkan_device(getVKDriver()->getDevice()), m_image(VK_NULL_HANDLE), m_vma_allocation(VK_NULL_HANDLE), - m_image_view(VK_NULL_HANDLE), m_texture_size(0), - m_disable_reload(true), m_has_mipmaps(true), + m_texture_size(0), m_disable_reload(true), + m_has_mipmaps(true), m_internal_format(VK_FORMAT_R8G8B8A8_UNORM), m_vk(getVKDriver()) { @@ -76,8 +76,7 @@ GEVulkanTexture::GEVulkanTexture(const std::string& name, unsigned int size, : video::ITexture(name.c_str()), m_image_mani(nullptr), m_locked_data(NULL), m_vulkan_device(getVKDriver()->getDevice()), m_image(VK_NULL_HANDLE), m_vma_allocation(VK_NULL_HANDLE), - m_image_view(VK_NULL_HANDLE), m_texture_size(0), - m_disable_reload(true), m_has_mipmaps(true), + m_texture_size(0), m_disable_reload(true), m_has_mipmaps(true), m_internal_format(single_channel ? VK_FORMAT_R8_UNORM : VK_FORMAT_R8G8B8A8_UNORM), m_vk(getVKDriver()) @@ -102,7 +101,7 @@ GEVulkanTexture::~GEVulkanTexture() m_image_view_lock.lock(); m_image_view_lock.unlock(); - if (m_image_view != VK_NULL_HANDLE || m_image != VK_NULL_HANDLE || + if (m_image_view || m_image != VK_NULL_HANDLE || m_vma_allocation != VK_NULL_HANDLE) m_vk->waitIdle(); @@ -364,16 +363,26 @@ bool GEVulkanTexture::createImageView(VkImageAspectFlags aspect_flags) view_info.components.a = VK_COMPONENT_SWIZZLE_R; } + std::shared_ptr image_view = std::make_shared(); VkResult result = vkCreateImageView(m_vulkan_device, &view_info, NULL, - &m_image_view); - return (result == VK_SUCCESS); + image_view.get()); + if (result == VK_SUCCESS) + { + m_image_view = image_view; + return true; + } + return false; } // createImageView // ---------------------------------------------------------------------------- void GEVulkanTexture::clearVulkanData() { - if (m_image_view != VK_NULL_HANDLE) - vkDestroyImageView(m_vulkan_device, m_image_view, NULL); + if (m_image_view) + { + vkDestroyImageView(m_vulkan_device, *m_image_view.get(), NULL); + *(m_image_view.get()) = VK_NULL_HANDLE; + m_image_view.reset(); + } if (m_image != VK_NULL_HANDLE) vmaDestroyImage(m_vk->getVmaAllocator(), m_image, m_vma_allocation); } // clearVulkanData @@ -696,7 +705,7 @@ void GEVulkanTexture::reload() m_image_view_lock.lock(); m_image_view_lock.unlock(); - if (m_image_view != VK_NULL_HANDLE || m_image != VK_NULL_HANDLE || + if (m_image_view || m_image != VK_NULL_HANDLE || m_vma_allocation != VK_NULL_HANDLE) m_vk->waitIdle(); diff --git a/lib/graphics_engine/src/ge_vulkan_texture.hpp b/lib/graphics_engine/src/ge_vulkan_texture.hpp index e7becd189..86cab2f32 100644 --- a/lib/graphics_engine/src/ge_vulkan_texture.hpp +++ b/lib/graphics_engine/src/ge_vulkan_texture.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -33,7 +34,7 @@ protected: VmaAllocation m_vma_allocation; - VkImageView m_image_view; + std::shared_ptr m_image_view; unsigned int m_texture_size; @@ -165,7 +166,7 @@ public: { m_image_view_lock.lock(); m_image_view_lock.unlock(); - return (u64)m_image_view; + return m_image_view ? (u64)*(m_image_view.get()) : 0; } // ------------------------------------------------------------------------ virtual unsigned int getTextureSize() const @@ -180,6 +181,13 @@ public: virtual void updateTexture(void* data, irr::video::ECOLOR_FORMAT format, u32 w, u32 h, u32 x, u32 y); // ------------------------------------------------------------------------ + virtual std::shared_ptr getImageView() const + { + m_image_view_lock.lock(); + m_image_view_lock.unlock(); + return m_image_view; + } + // ------------------------------------------------------------------------ VkFormat getInternalFormat() const { return m_internal_format; } }; // GEVulkanTexture diff --git a/lib/graphics_engine/src/ge_vulkan_texture_descriptor.cpp b/lib/graphics_engine/src/ge_vulkan_texture_descriptor.cpp new file mode 100644 index 000000000..ec646e5c8 --- /dev/null +++ b/lib/graphics_engine/src/ge_vulkan_texture_descriptor.cpp @@ -0,0 +1,228 @@ +#include "ge_vulkan_texture_descriptor.hpp" + +#include "ge_main.hpp" +#include "ge_vulkan_driver.hpp" +#include "ge_vulkan_texture.hpp" + +#include +#include + +namespace GE +{ +// ---------------------------------------------------------------------------- +GEVulkanTextureDescriptor::GEVulkanTextureDescriptor(unsigned max_texture_list, + unsigned max_layer, + bool single_descriptor, + unsigned binding) + : m_max_texture_list(max_texture_list), + m_max_layer(max_layer), m_binding(binding) +{ + if (m_max_layer > _IRR_MATERIAL_MAX_TEXTURES_) + { + throw std::runtime_error( + "Too large max_layer for GEVulkanTextureDescriptor"); + } + + m_vk = getVKDriver(); + + // m_descriptor_set_layout + VkDescriptorSetLayoutBinding texture_layout_binding = {}; + texture_layout_binding.binding = m_binding; + texture_layout_binding.descriptorCount = + single_descriptor ? m_max_texture_list * m_max_layer : m_max_layer; + texture_layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + texture_layout_binding.pImmutableSamplers = NULL; + texture_layout_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + + VkDescriptorSetLayoutCreateInfo setinfo = {}; + setinfo.flags = 0; + setinfo.pNext = NULL; + setinfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + setinfo.pBindings = &texture_layout_binding; + setinfo.bindingCount = 1; + if (vkCreateDescriptorSetLayout(m_vk->getDevice(), &setinfo, + NULL, &m_descriptor_set_layout) != VK_SUCCESS) + { + throw std::runtime_error("vkCreateDescriptorSetLayout failed for " + "GEVulkanTextureDescriptor"); + } + + // m_descriptor_pool + VkDescriptorPoolSize pool_size; + pool_size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + pool_size.descriptorCount = m_max_texture_list * m_max_layer; + + VkDescriptorPoolCreateInfo pool_info = {}; + pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + pool_info.flags = 0; + pool_info.maxSets = single_descriptor ? 1 : m_max_texture_list; + pool_info.poolSizeCount = 1; + pool_info.pPoolSizes = &pool_size; + if (vkCreateDescriptorPool(m_vk->getDevice(), &pool_info, NULL, + &m_descriptor_pool) != VK_SUCCESS) + { + throw std::runtime_error("vkCreateDescriptorPool failed for " + "GEVulkanTextureDescriptor"); + } + + // m_descriptor_sets + if (single_descriptor) + m_descriptor_sets.resize(1); + else + m_descriptor_sets.resize(m_max_texture_list); + std::vector layouts(m_descriptor_sets.size(), + m_descriptor_set_layout); + + VkDescriptorSetAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + alloc_info.descriptorPool = m_descriptor_pool; + alloc_info.descriptorSetCount = layouts.size(); + alloc_info.pSetLayouts = layouts.data(); + + if (vkAllocateDescriptorSets(m_vk->getDevice(), &alloc_info, + m_descriptor_sets.data()) != VK_SUCCESS) + { + throw std::runtime_error("vkAllocateDescriptorSets failed for " + "GEVulkanTextureDescriptor"); + } + + m_sampler_use = GVS_NEAREST; + m_recreate_next_frame = false; + m_needs_update_descriptor = false; + + GEVulkanTexture* tex = static_cast( + m_vk->getWhiteTexture()); + m_white_image = tex->getImageView(); + tex = static_cast(m_vk->getTransparentTexture()); + m_transparent_image = tex->getImageView(); +} // GEVulkanTextureDescriptor + +// ---------------------------------------------------------------------------- +GEVulkanTextureDescriptor::~GEVulkanTextureDescriptor() +{ + vkDestroyDescriptorSetLayout(m_vk->getDevice(), m_descriptor_set_layout, + NULL); + vkDestroyDescriptorPool(m_vk->getDevice(), m_descriptor_pool, NULL); +} // ~GEVulkanTextureDescriptor + +// ---------------------------------------------------------------------------- +void GEVulkanTextureDescriptor::updateDescriptor() +{ + if (!m_needs_update_descriptor) + return; + m_needs_update_descriptor = false; + if (m_texture_list.empty()) + return; + + std::vector image_infos; + image_infos.resize(m_texture_list.size() * m_max_layer); + for (auto& p : m_texture_list) + { + const size_t max_size = std::min((size_t)m_max_layer, p.first.size()); + for (unsigned i = 0; i < max_size; i++) + { + VkDescriptorImageInfo info; + info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + info.sampler = m_vk->getSampler(m_sampler_use); + info.imageView = *(p.first[i].get()); + if (info.imageView == VK_NULL_HANDLE) + info.imageView = *m_transparent_image.get(); + image_infos[p.second * m_max_layer + i] = info; + } + } + + bool single_descriptor = (m_descriptor_sets.size() == 1); + if (single_descriptor) + { + VkDescriptorImageInfo dummy_info; + dummy_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + dummy_info.imageView = *m_transparent_image.get(); + dummy_info.sampler = m_vk->getSampler(m_sampler_use); + image_infos.resize(m_max_texture_list * m_max_layer, dummy_info); + } + + m_vk->waitIdle(); + if (single_descriptor) + { + VkWriteDescriptorSet write_descriptor_set = {}; + write_descriptor_set.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write_descriptor_set.dstBinding = m_binding; + write_descriptor_set.dstArrayElement = 0; + write_descriptor_set.descriptorType = + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + write_descriptor_set.descriptorCount = m_max_texture_list * m_max_layer; + write_descriptor_set.pBufferInfo = 0; + write_descriptor_set.dstSet = m_descriptor_sets[0]; + write_descriptor_set.pImageInfo = image_infos.data(); + + vkUpdateDescriptorSets(m_vk->getDevice(), 1, &write_descriptor_set, 0, + NULL); + } + else + { + std::vector all_sets; + for (unsigned i = 0; i < image_infos.size(); i += m_max_layer) + { + const unsigned set_idx = i / m_max_layer; + VkWriteDescriptorSet write_descriptor_set = {}; + write_descriptor_set.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write_descriptor_set.dstBinding = m_binding; + write_descriptor_set.dstArrayElement = 0; + write_descriptor_set.descriptorType = + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + write_descriptor_set.descriptorCount = m_max_layer; + write_descriptor_set.pBufferInfo = 0; + write_descriptor_set.dstSet = m_descriptor_sets[set_idx]; + write_descriptor_set.pImageInfo = &image_infos[i]; + all_sets.push_back(write_descriptor_set); + } + vkUpdateDescriptorSets(m_vk->getDevice(), all_sets.size(), + all_sets.data(), 0, NULL); + } +} // updateDescriptor + +// ---------------------------------------------------------------------------- +int GEVulkanTextureDescriptor::getTextureID(const irr::video::ITexture** list) +{ + TextureList key = + {{ + m_white_image, + m_transparent_image, + m_transparent_image, + m_transparent_image, + m_transparent_image, + m_transparent_image, + m_transparent_image, + m_transparent_image + }}; + for (unsigned i = 0; i < m_max_layer; i++) + { + if (list[i]) + { + key[i] = static_cast( + list[i])->getImageView(); + } + } + auto it = m_texture_list.find(key); + if (it != m_texture_list.end()) + return it->second; + else + { + int cur_id = m_texture_list.size(); + if (cur_id >= m_max_texture_list) + { + printf("Too many texture used in current frames\n"); + m_recreate_next_frame = true; + return m_max_texture_list - 1; + } + + m_texture_list[key] = cur_id; + m_needs_update_descriptor = true; + // Reset the list earlier if almost full + if (cur_id > int((float)m_max_texture_list * 0.8f)) + m_recreate_next_frame = true; + return cur_id; + } +} // getTextureID + +}