From 0340719cfb6c4580fd20fb8f0a9893cea1fe3860 Mon Sep 17 00:00:00 2001 From: Panos Karabelas Date: Tue, 12 Nov 2024 20:42:55 +0000 Subject: [PATCH] [rhi] texture compression and gpu uploading will now happen after all textures have been set to a material, so before all that, texture packing can now take place --- editor/ImGui/Implementation/ImGui_RHI.h | 2 +- editor/Widgets/FileDialog.cpp | 2 +- editor/Widgets/IconLoader.cpp | 2 +- runtime/RHI/RHI_Texture.cpp | 159 +++++++++++----------- runtime/RHI/RHI_Texture.h | 68 ++++----- runtime/RHI/Vulkan/Vulkan_CommandList.cpp | 2 +- runtime/RHI/Vulkan/Vulkan_Texture.cpp | 2 +- runtime/Rendering/Material.cpp | 13 ++ runtime/Resource/IResource.h | 8 +- runtime/Resource/Import/ModelImporter.cpp | 5 +- 10 files changed, 142 insertions(+), 121 deletions(-) diff --git a/editor/ImGui/Implementation/ImGui_RHI.h b/editor/ImGui/Implementation/ImGui_RHI.h index 0964f27bc..5d3153966 100644 --- a/editor/ImGui/Implementation/ImGui_RHI.h +++ b/editor/ImGui/Implementation/ImGui_RHI.h @@ -332,7 +332,7 @@ namespace ImGui::RHI is_frame_texture = Renderer::GetRenderTarget(Renderer_RenderTarget::frame_output)->GetObjectId() == texture->GetObjectId(); // during engine startup, some textures might be loading in different threads - if (texture->IsReadyForUse()) + if (texture->IsGpuReady()) { cmd_list->SetTexture(Renderer_BindingsSrv::tex, texture); diff --git a/editor/Widgets/FileDialog.cpp b/editor/Widgets/FileDialog.cpp index b5fc089a7..e44c4bac0 100644 --- a/editor/Widgets/FileDialog.cpp +++ b/editor/Widgets/FileDialog.cpp @@ -305,7 +305,7 @@ void FileDialog::ShowMiddle() // Image if (RHI_Texture* texture = item.GetTexture()) { - if (texture->IsReadyForUse()) // This is possible for when the editor is reading from drive + if (texture->IsGpuReady()) // This is possible for when the editor is reading from drive { // Compute thumbnail size ImVec2 image_size = ImVec2(static_cast(texture->GetWidth()), static_cast(texture->GetHeight())); diff --git a/editor/Widgets/IconLoader.cpp b/editor/Widgets/IconLoader.cpp index 9dd07ccd8..d3623ce10 100644 --- a/editor/Widgets/IconLoader.cpp +++ b/editor/Widgets/IconLoader.cpp @@ -68,7 +68,7 @@ Icon::Icon(IconType type, const string& file_path) RHI_Texture* Icon::GetTexture() const { - if (m_texture && m_texture->IsReadyForUse()) + if (m_texture && m_texture->IsGpuReady()) { return m_texture.get(); } diff --git a/runtime/RHI/RHI_Texture.cpp b/runtime/RHI/RHI_Texture.cpp index f42740ff1..77988e6f4 100644 --- a/runtime/RHI/RHI_Texture.cpp +++ b/runtime/RHI/RHI_Texture.cpp @@ -143,7 +143,7 @@ namespace Spartan m_bits_per_channel = rhi_format_to_bits_per_channel(m_format); RHI_Texture::RHI_CreateResource(); - m_is_ready_for_use = true; + m_is_gpu_ready = true; if (!compressonator::registered) { @@ -160,8 +160,6 @@ namespace Spartan RHI_Texture::~RHI_Texture() { - m_slices.clear(); - m_slices.shrink_to_fit(); RHI_DestroyResource(); } @@ -215,9 +213,7 @@ namespace Spartan } } - // the bytes have been saved, so we can now free some memory - m_slices.clear(); - m_slices.shrink_to_fit(); + ClearBytes(); } // write properties @@ -236,94 +232,95 @@ namespace Spartan bool RHI_Texture::LoadFromFile(const string& file_path) { - m_type = RHI_Texture_Type::Type2D; - m_depth = 1; - m_flags |= RHI_Texture_Srv; - m_object_name = FileSystem::GetFileNameFromFilePath(file_path); - if (!FileSystem::IsFile(file_path)) { SP_LOG_ERROR("Invalid file path \"%s\".", file_path.c_str()); return false; } - m_slices.clear(); - m_slices.shrink_to_fit(); + m_type = RHI_Texture_Type::Type2D; + m_depth = 1; + m_flags |= RHI_Texture_Srv; + m_object_name = FileSystem::GetFileNameFromFilePath(file_path); + + ClearBytes(); // load from drive + if (FileSystem::IsEngineTextureFile(file_path)) { - if (FileSystem::IsEngineTextureFile(file_path)) + auto file = make_unique(file_path, FileStream_Read); + if (!file->IsOpen()) { - auto file = make_unique(file_path, FileStream_Read); - if (!file->IsOpen()) - { - SP_LOG_ERROR("Failed to load \"%s\".", file_path.c_str()); - return false; - } + SP_LOG_ERROR("Failed to load \"%s\".", file_path.c_str()); + return false; + } - // read mip info - file->Read(&m_object_size); - file->Read(&m_depth); - file->Read(&m_mip_count); + // read mip info + file->Read(&m_object_size); + file->Read(&m_depth); + file->Read(&m_mip_count); - // read mip data - m_slices.resize(m_depth); - for (RHI_Texture_Slice& slice : m_slices) + // read mip data + m_slices.resize(m_depth); + for (RHI_Texture_Slice& slice : m_slices) + { + slice.mips.resize(m_mip_count); + for (RHI_Texture_Mip& mip : slice.mips) { - slice.mips.resize(m_mip_count); - for (RHI_Texture_Mip& mip : slice.mips) - { - file->Read(&mip.bytes); - } + file->Read(&mip.bytes); } - - // read properties - file->Read(&m_width); - file->Read(&m_height); - file->Read(&m_channel_count); - file->Read(&m_bits_per_channel); - file->Read(reinterpret_cast(&m_type)); - file->Read(reinterpret_cast(&m_format)); - file->Read(&m_flags); - SetObjectId(file->ReadAs()); - SetResourceFilePath(file->ReadAs()); } - else if (FileSystem::IsSupportedImageFile(file_path)) + + // read properties + file->Read(&m_width); + file->Read(&m_height); + file->Read(&m_channel_count); + file->Read(&m_bits_per_channel); + file->Read(reinterpret_cast(&m_type)); + file->Read(reinterpret_cast(&m_format)); + file->Read(&m_flags); + SetObjectId(file->ReadAs()); + SetResourceFilePath(file->ReadAs()); + } + else if (FileSystem::IsSupportedImageFile(file_path)) + { + vector file_paths = { file_path }; + + // if this is an array, try to find all the textures + if (m_type == RHI_Texture_Type::Type2DArray) { - vector file_paths = { file_path }; + string file_path_extension = FileSystem::GetExtensionFromFilePath(file_path); + string file_path_no_extension = FileSystem::GetFilePathWithoutExtension(file_path); + string file_path_no_digit = file_path_no_extension.substr(0, file_path_no_extension.size() - 1); - // if this is an array, try to find all the textures - if (m_type == RHI_Texture_Type::Type2DArray) + uint32_t index = 1; + string file_path_guess = file_path_no_digit + to_string(index) + file_path_extension; + while (FileSystem::Exists(file_path_guess)) { - string file_path_extension = FileSystem::GetExtensionFromFilePath(file_path); - string file_path_no_extension = FileSystem::GetFilePathWithoutExtension(file_path); - string file_path_no_digit = file_path_no_extension.substr(0, file_path_no_extension.size() - 1); - - uint32_t index = 1; - string file_path_guess = file_path_no_digit + to_string(index) + file_path_extension; - while (FileSystem::Exists(file_path_guess)) - { - file_paths.emplace_back(file_path_guess); - file_path_guess = file_path_no_digit + to_string(++index) + file_path_extension; - } + file_paths.emplace_back(file_path_guess); + file_path_guess = file_path_no_digit + to_string(++index) + file_path_extension; } + } - // load texture - for (uint32_t slice_index = 0; slice_index < static_cast(file_paths.size()); slice_index++) + // load texture + for (uint32_t slice_index = 0; slice_index < static_cast(file_paths.size()); slice_index++) + { + if (!ImageImporterExporter::Load(file_paths[slice_index], slice_index, this)) { - if (!ImageImporterExporter::Load(file_paths[slice_index], slice_index, this)) - { - SP_LOG_ERROR("Failed to load \"%s\".", file_path.c_str()); - return false; - } + SP_LOG_ERROR("Failed to load \"%s\".", file_path.c_str()); + return false; } - - // set resource file path so it can be used by the resource cache. - SetResourceFilePath(file_path); } + + // set resource file path so it can be used by the resource cache. + SetResourceFilePath(file_path); + } + + if (!(m_flags & RHI_Texture_DontPrepareForGpu)) + { + PrepareForGpu(); } - PrepareForGpu(); ComputeMemoryUsage(); return true; @@ -432,7 +429,7 @@ namespace Spartan if (cmd_list != nullptr) { // wait in case this texture loading in another thread - while (!IsReadyForUse()) + while (!IsGpuReady()) { SP_LOG_INFO("Waiting for texture \"%s\" to finish loading...", m_object_name.c_str()); this_thread::sleep_for(chrono::milliseconds(16)); @@ -449,23 +446,33 @@ namespace Spartan } } + void RHI_Texture::ClearBytes() + { + m_slices.clear(); + m_slices.shrink_to_fit(); + } + void RHI_Texture::PrepareForGpu() { - // compress texture (if not alraedy compressed) - if ((m_flags & RHI_Texture_Compress) && !IsCompressedFormat(m_format)) + SP_ASSERT(m_slices.size() > 0); + SP_ASSERT(m_slices[0].mips.size() > 0); + + // compress + bool compress = m_flags & RHI_Texture_Compress; + bool not_compressed = !IsCompressedFormat(m_format); + if (compress && not_compressed) { compressonator::compress(this); } - // create gpu resource + // upload to gpu SP_ASSERT_MSG(RHI_CreateResource(), "Failed to create GPU resource"); - m_is_ready_for_use = true; + m_is_gpu_ready = true; // clear data if (!(m_flags & RHI_Texture_KeepData)) { - m_slices.clear(); - m_slices.shrink_to_fit(); + ClearBytes(); } ComputeMemoryUsage(); diff --git a/runtime/RHI/RHI_Texture.h b/runtime/RHI/RHI_Texture.h index 0fadefdfe..1348c39b5 100644 --- a/runtime/RHI/RHI_Texture.h +++ b/runtime/RHI/RHI_Texture.h @@ -91,59 +91,59 @@ namespace Spartan bool LoadFromFile(const std::string& file_path) override; //======================================================= - uint32_t GetWidth() const { return m_width; } - void SetWidth(const uint32_t width) { m_width = width; } - - uint32_t GetHeight() const { return m_height; } - void SetHeight(const uint32_t height) { m_height = height; } - - uint32_t GetBitsPerChannel() const { return m_bits_per_channel; } - void SetBitsPerChannel(const uint32_t bits) { m_bits_per_channel = bits; } - uint32_t GetBytesPerChannel() const { return m_bits_per_channel / 8; } - uint32_t GetBytesPerPixel() const { return (m_bits_per_channel / 8) * m_channel_count; } - - uint32_t GetChannelCount() const { return m_channel_count; } - void SetChannelCount(const uint32_t channel_count) { m_channel_count = channel_count; } - - RHI_Format GetFormat() const { return m_format; } - void SetFormat(const RHI_Format format) { m_format = format; } + uint32_t GetWidth() const { return m_width; } + void SetWidth(const uint32_t width) { m_width = width; } + + uint32_t GetHeight() const { return m_height; } + void SetHeight(const uint32_t height) { m_height = height; } + + uint32_t GetBitsPerChannel() const { return m_bits_per_channel; } + void SetBitsPerChannel(const uint32_t bits) { m_bits_per_channel = bits; } + uint32_t GetBytesPerChannel() const { return m_bits_per_channel / 8; } + uint32_t GetBytesPerPixel() const { return (m_bits_per_channel / 8) * m_channel_count; } + + uint32_t GetChannelCount() const { return m_channel_count; } + void SetChannelCount(const uint32_t channel_count) { m_channel_count = channel_count; } + + RHI_Format GetFormat() const { return m_format; } + void SetFormat(const RHI_Format format) { m_format = format; } // external memory void* GetExternalMemoryHandle() const { return m_rhi_external_memory; } void SetExternalMemoryHandle(void* handle) { m_rhi_external_memory = handle; } // misc + void ClearBytes(); void PrepareForGpu(); void SaveAsImage(const std::string& file_path); static bool IsCompressedFormat(const RHI_Format format); static size_t CalculateMipSize(uint32_t width, uint32_t height, uint32_t depth, RHI_Format format, uint32_t bits_per_channel, uint32_t channel_count); // data - uint32_t GetMipCount() const { return m_mip_count; } - uint32_t GetDepth() const { return m_depth; } - bool HasData() const { return !m_slices.empty() && !m_slices[0].mips.empty() && !m_slices[0].mips[0].bytes.empty(); }; - std::vector& GetData() { return m_slices; } + uint32_t GetMipCount() const { return m_mip_count; } + uint32_t GetDepth() const { return m_depth; } + bool HasData() const { return !m_slices.empty() && !m_slices[0].mips.empty() && !m_slices[0].mips[0].bytes.empty(); }; RHI_Texture_Mip& CreateMip(const uint32_t array_index); RHI_Texture_Mip& GetMip(const uint32_t array_index, const uint32_t mip_index); RHI_Texture_Slice& GetSlice(const uint32_t array_index); // flags - bool IsSrv() const { return m_flags & RHI_Texture_Srv; } - bool IsUav() const { return m_flags & RHI_Texture_Uav; } - bool IsVrs() const { return m_flags & RHI_Texture_Vrs; } - bool IsRt() const { return m_flags & RHI_Texture_Rtv; } - bool IsDsv() const { return IsRt() && IsDepthStencilFormat(); } - bool IsRtv() const { return IsRt() && IsColorFormat(); } - bool HasPerMipViews() const { return m_flags & RHI_Texture_PerMipViews; } - bool IsGrayscale() const { return m_flags & RHI_Texture_Greyscale; } + bool IsSrv() const { return m_flags & RHI_Texture_Srv; } + bool IsUav() const { return m_flags & RHI_Texture_Uav; } + bool IsVrs() const { return m_flags & RHI_Texture_Vrs; } + bool IsRt() const { return m_flags & RHI_Texture_Rtv; } + bool IsDsv() const { return IsRt() && IsDepthStencilFormat(); } + bool IsRtv() const { return IsRt() && IsColorFormat(); } + bool HasPerMipViews() const { return m_flags & RHI_Texture_PerMipViews; } + bool IsGrayscale() const { return m_flags & RHI_Texture_Greyscale; } bool IsSemiTransparent() const { return m_flags & RHI_Texture_Transparent; } bool HasExternalMemory() const { return m_flags & RHI_Texture_ExternalMemory; } // format type - bool IsDepthFormat() const { return m_format == RHI_Format::D16_Unorm || m_format == RHI_Format::D32_Float || m_format == RHI_Format::D32_Float_S8X24_Uint; } - bool IsStencilFormat() const { return m_format == RHI_Format::D32_Float_S8X24_Uint; } + bool IsDepthFormat() const { return m_format == RHI_Format::D16_Unorm || m_format == RHI_Format::D32_Float || m_format == RHI_Format::D32_Float_S8X24_Uint; } + bool IsStencilFormat() const { return m_format == RHI_Format::D32_Float_S8X24_Uint; } bool IsDepthStencilFormat() const { return IsDepthFormat() || IsStencilFormat(); } - bool IsColorFormat() const { return !IsDepthStencilFormat(); } + bool IsColorFormat() const { return !IsDepthStencilFormat(); } // layout void SetLayout(const RHI_Image_Layout layout, RHI_CommandList* cmd_list, uint32_t mip_index = rhi_all_mips, uint32_t mip_range = 0); @@ -154,10 +154,10 @@ namespace Spartan const auto& GetViewport() const { return m_viewport; } // rhi - RHI_Texture_Type GetType() const { return m_type; } + RHI_Texture_Type GetType() const { return m_type; } void*& GetRhiResource() { return m_rhi_resource; } - void* GetRhiSrv() const { return m_rhi_srv; } - void* GetRhiSrvMip(const uint32_t i) const { return m_rhi_srv_mips[i]; } + void* GetRhiSrv() const { return m_rhi_srv; } + void* GetRhiSrvMip(const uint32_t i) const { return m_rhi_srv_mips[i]; } void* GetRhiDsv(const uint32_t i = 0) const { return m_rhi_dsv[i]; } void* GetRhiRtv(const uint32_t i = 0) const { return m_rhi_rtv[i]; } void RHI_DestroyResource(); diff --git a/runtime/RHI/Vulkan/Vulkan_CommandList.cpp b/runtime/RHI/Vulkan/Vulkan_CommandList.cpp index bfa9d2fa4..b17b6e2d7 100644 --- a/runtime/RHI/Vulkan/Vulkan_CommandList.cpp +++ b/runtime/RHI/Vulkan/Vulkan_CommandList.cpp @@ -1434,7 +1434,7 @@ namespace Spartan } // if the texture is null or it's still loading, ignore it - if (!texture || !texture->IsReadyForUse()) + if (!texture || !texture->IsGpuReady()) return; // get some texture info diff --git a/runtime/RHI/Vulkan/Vulkan_Texture.cpp b/runtime/RHI/Vulkan/Vulkan_Texture.cpp index 9487b8d7f..e0cf386bc 100644 --- a/runtime/RHI/Vulkan/Vulkan_Texture.cpp +++ b/runtime/RHI/Vulkan/Vulkan_Texture.cpp @@ -313,7 +313,7 @@ namespace Spartan stage(this); if ((m_flags & RHI_Texture_KeepData) == 0) { - m_slices.clear(); + ClearBytes(); } } diff --git a/runtime/Rendering/Material.cpp b/runtime/Rendering/Material.cpp index df6c5f387..8f5bf8c80 100644 --- a/runtime/Rendering/Material.cpp +++ b/runtime/Rendering/Material.cpp @@ -361,6 +361,19 @@ namespace Spartan // //SetTexture(MaterialTextureType::AlphaMask, nullptr); } + + for (RHI_Texture* texture : m_textures) + { + if (texture) + { + // it's important to check if it's ready GPU ready as the same + // texture can be shared among multiple materials or material slots + if (!texture->IsGpuReady()) + { + texture->PrepareForGpu(); + } + } + } } uint32_t Material::GetUsedSlotCount() const diff --git a/runtime/Resource/IResource.h b/runtime/Resource/IResource.h index 264b40fb3..a8edb7f02 100644 --- a/runtime/Resource/IResource.h +++ b/runtime/Resource/IResource.h @@ -106,7 +106,7 @@ namespace Spartan void SetFlags(const uint32_t flags) { m_flags = flags; } // ready to use - bool IsReadyForUse() const { return m_is_ready_for_use; } + bool IsGpuReady() const { return m_is_gpu_ready; } // io virtual bool SaveToFile(const std::string& file_path) { return true; } @@ -117,9 +117,9 @@ namespace Spartan static constexpr ResourceType TypeToEnum(); protected: - ResourceType m_resource_type = ResourceType::Max; - std::atomic m_is_ready_for_use = false; - uint32_t m_flags = 0; + ResourceType m_resource_type = ResourceType::Max; + std::atomic m_is_gpu_ready = false; + uint32_t m_flags = 0; private: std::string m_resource_directory; diff --git a/runtime/Resource/Import/ModelImporter.cpp b/runtime/Resource/Import/ModelImporter.cpp index aa3ac8b2e..17b244ebf 100644 --- a/runtime/Resource/Import/ModelImporter.cpp +++ b/runtime/Resource/Import/ModelImporter.cpp @@ -308,6 +308,9 @@ namespace Spartan load_material_texture(mesh, file_path, is_gltf, material, material_assimp, MaterialTextureType::Height, aiTextureType_HEIGHT, aiTextureType_NONE); load_material_texture(mesh, file_path, is_gltf, material, material_assimp, MaterialTextureType::AlphaMask, aiTextureType_OPACITY, aiTextureType_NONE); + // this will pack textures, compress them, and upload them to the GPU + material->PrepareForGPU(); + // name aiString name_assimp; aiGetMaterialString(material_assimp, AI_MATKEY_NAME, &name_assimp); @@ -403,8 +406,6 @@ namespace Spartan } } - material->PrepareForGPU(); - return material; } }