diff --git a/editor/ImGui/Implementation/ImGui_RHI.h b/editor/ImGui/Implementation/ImGui_RHI.h index 5d3153966..4cbdd1af4 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->IsGpuReady()) + if (texture->GetResourceState() == ResourceState::Ready) { cmd_list->SetTexture(Renderer_BindingsSrv::tex, texture); diff --git a/editor/Widgets/FileDialog.cpp b/editor/Widgets/FileDialog.cpp index e44c4bac0..8dd3642e7 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->IsGpuReady()) // This is possible for when the editor is reading from drive + if (texture->GetResourceState() == ResourceState::Ready) // 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 d3623ce10..d41f64ea1 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->IsGpuReady()) + if (m_texture && m_texture->GetResourceState() == ResourceState::Ready) { return m_texture.get(); } diff --git a/runtime/Core/ThreadPool.cpp b/runtime/Core/ThreadPool.cpp index e0acd1dba..906bac8e6 100644 --- a/runtime/Core/ThreadPool.cpp +++ b/runtime/Core/ThreadPool.cpp @@ -120,19 +120,31 @@ namespace Spartan threads.clear(); } - void ThreadPool::AddTask(Task&& task) + future ThreadPool::AddTask(Task&& task) { - // Lock tasks mutex + // create a packaged task that will give us a future + auto packaged_task = make_shared>(forward(task)); + + // get the future before we move the packaged_task into the lambda + future future = packaged_task->get_future(); + + // lock tasks mutex unique_lock lock(mutex_tasks); - - // Save the task - tasks.emplace_back(bind(std::forward(task))); - - // Unlock the mutex + + // save the task - wrap the packaged_task in a lambda that will execute it + tasks.emplace_back([packaged_task]() + { + (*packaged_task)(); + }); + + // unlock the mutex lock.unlock(); - - // Wake up a thread + + // wake up a thread condition_var.notify_one(); + + // return the future that can be used to wait for task completion + return future; } void ThreadPool::ParallelLoop(function&& function, const uint32_t work_total) diff --git a/runtime/Core/ThreadPool.h b/runtime/Core/ThreadPool.h index 57c21ee5e..65982e39e 100644 --- a/runtime/Core/ThreadPool.h +++ b/runtime/Core/ThreadPool.h @@ -21,10 +21,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #pragma once -//= INCLUDES =========== -#include "Definitions.h" +//= INCLUDES ======== +#include #include -//====================== +//=================== namespace Spartan { @@ -37,7 +37,7 @@ namespace Spartan static void Shutdown(); // add a task - static void AddTask(Task&& task); + static std::future AddTask(Task&& task); // spread execution of a given function across all available threads static void ParallelLoop(std::function&& function, const uint32_t work_total); diff --git a/runtime/RHI/RHI_Texture.cpp b/runtime/RHI/RHI_Texture.cpp index 55c4a7924..492e3048d 100644 --- a/runtime/RHI/RHI_Texture.cpp +++ b/runtime/RHI/RHI_Texture.cpp @@ -498,7 +498,7 @@ namespace Spartan if (cmd_list != nullptr) { // wait in case this texture loading in another thread - while (!IsGpuReady()) + while (m_resource_state != ResourceState::Ready) { SP_LOG_INFO("Waiting for texture \"%s\" to finish loading...", m_object_name.c_str()); this_thread::sleep_for(chrono::milliseconds(16)); @@ -523,10 +523,13 @@ namespace Spartan void RHI_Texture::PrepareForGpu() { - SP_ASSERT_MSG(!IsGpuReady(), "The texture is already optimized"); + SP_ASSERT_MSG(m_resource_state != ResourceState::Processing, "The texture is already being processed"); + SP_ASSERT_MSG(m_resource_state != ResourceState::Ready, "The texture is already optimized"); SP_ASSERT(m_slices.size() > 0); SP_ASSERT(m_slices[0].mips.size() > 0); + m_resource_state = ResourceState::Processing; + if (!IsCompressedFormat()) // the bistro world loads compressed textures with mips { // generate mip chain diff --git a/runtime/RHI/Vulkan/Vulkan_CommandList.cpp b/runtime/RHI/Vulkan/Vulkan_CommandList.cpp index b17b6e2d7..ac7622968 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->IsGpuReady()) + if (!texture || texture->GetResourceState() != ResourceState::Ready) return; // get some texture info diff --git a/runtime/Rendering/Material.cpp b/runtime/Rendering/Material.cpp index 10f544d66..bca295b6c 100644 --- a/runtime/Rendering/Material.cpp +++ b/runtime/Rendering/Material.cpp @@ -26,6 +26,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "../RHI/RHI_Texture.h" #include "../World/World.h" #include "../Core/ProgressTracker.h" +#include "../Core/ThreadPool.h" SP_WARNINGS_OFF #include "../IO/pugixml.hpp" SP_WARNINGS_ON @@ -323,7 +324,10 @@ namespace Spartan void Material::Optimize(const bool is_gltf) { - SP_ASSERT_MSG(!IsGpuReady(), "The material is already optimized"); + SP_ASSERT_MSG(m_resource_state != ResourceState::Processing, "The material is already being processed"); + SP_ASSERT_MSG(m_resource_state != ResourceState::Ready, "The material is already optimized"); + + m_resource_state = ResourceState::Processing; RHI_Texture* texture_color = GetTexture(MaterialTextureType::Color); RHI_Texture* texture_alpha_mask = GetTexture(MaterialTextureType::AlphaMask); @@ -431,14 +435,17 @@ namespace Spartan } // prepare all textures - for (RHI_Texture* texture : m_textures) + future texture_preparation_task = ThreadPool::AddTask([this]() { - if (texture && !texture->IsGpuReady()) + for (RHI_Texture* texture : m_textures) { - // todo: this could be given to the thread pool - texture->PrepareForGpu(); + if (texture && texture->GetResourceState() == ResourceState::Max) + { + texture->PrepareForGpu(); + } } - } + }); + texture_preparation_task.wait(); // determine if the material is optimized bool is_optimized = GetTexture(MaterialTextureType::Packed) != nullptr; diff --git a/runtime/Rendering/Mesh.h b/runtime/Rendering/Mesh.h index ba42cdaae..394b8b227 100644 --- a/runtime/Rendering/Mesh.h +++ b/runtime/Rendering/Mesh.h @@ -25,9 +25,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include "Material.h" -#include "../Resource/IResource.h" -#include "../Math/BoundingBox.h" #include "../RHI/RHI_Vertex.h" +#include "../Math/BoundingBox.h" +#include "../Resource/IResource.h" //================================ namespace Spartan diff --git a/runtime/Resource/IResource.h b/runtime/Resource/IResource.h index b6756a59a..2c53bef64 100644 --- a/runtime/Resource/IResource.h +++ b/runtime/Resource/IResource.h @@ -45,7 +45,7 @@ namespace Spartan enum class ResourceState { - Processed, + Processing, Ready, Max }; @@ -100,9 +100,6 @@ namespace Spartan uint32_t GetFlags() const { return m_flags; } void SetFlags(const uint32_t flags) { m_flags = flags; } - // ready to use - bool IsGpuReady() const { return m_resource_state == ResourceState::Ready; } - // io virtual bool SaveToFile(const std::string& file_path) { return true; } virtual bool LoadFromFile(const std::string& file_path) { return true; } @@ -111,6 +108,8 @@ namespace Spartan template static constexpr ResourceType TypeToEnum(); + ResourceState GetResourceState() const { return m_resource_state; } + protected: ResourceType m_resource_type = ResourceType::Max; std::atomic m_resource_state = ResourceState::Max;