Index: source/graphics/TextureConverter.h =================================================================== --- source/graphics/TextureConverter.h +++ source/graphics/TextureConverter.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -22,10 +22,11 @@ #include "TextureManager.h" -#include +#if CONFIG2_NVTT +#include "ps/Future.h" #include -#include -#include +#include +#endif class MD5; @@ -202,23 +203,18 @@ bool IsBusy(); private: - static void RunThread(CTextureConverter* data); - PIVFS m_VFS; bool m_HighQuality; #if CONFIG2_NVTT - std::thread m_WorkerThread; - std::mutex m_WorkerMutex; - std::condition_variable m_WorkerCV; -#endif // CONFIG2_NVTT - struct ConversionRequest; struct ConversionResult; - std::deque> m_RequestQueue; // protected by m_WorkerMutex - std::deque> m_ResultQueue; // protected by m_WorkerMutex - bool m_Shutdown; // protected by m_WorkerMutex + // Completely arbitrary constant - there is some main-thread cost to loading textures, so probably + // should not be too high. Some results in the queue may already be ready. + static constexpr std::deque>>::size_type MAX_QUEUE_SIZE{6}; + std::deque>> m_ResultQueue; +#endif }; #endif // INCLUDED_TEXTURECONVERTER Index: source/graphics/TextureConverter.cpp =================================================================== --- source/graphics/TextureConverter.cpp +++ source/graphics/TextureConverter.cpp @@ -28,7 +28,7 @@ #include "ps/CLogger.h" #include "ps/CStr.h" #include "ps/Profiler2.h" -#include "ps/Threading.h" +#include "ps/TaskManager.h" #include "ps/Util.h" #include "ps/XML/Xeromyces.h" @@ -293,42 +293,20 @@ } CTextureConverter::CTextureConverter(PIVFS vfs, bool highQuality) : - m_VFS(vfs), m_HighQuality(highQuality), m_Shutdown(false) + m_VFS(vfs), m_HighQuality(highQuality) { #if CONFIG2_NVTT // Verify that we are running with at least the version we were compiled with, // to avoid bugs caused by ABI changes ENSURE(nvtt::version() >= NVTT_VERSION); - - m_WorkerThread = std::thread(Threading::HandleExceptions::Wrapper, this); - - // Maybe we should share some centralised pool of worker threads? - // For now we'll just stick with a single thread for this specific use. #endif // CONFIG2_NVTT } CTextureConverter::~CTextureConverter() { #if CONFIG2_NVTT - // Tell the thread to shut down - { - std::lock_guard lock(m_WorkerMutex); - m_Shutdown = true; - } - - while (true) - { - // Wake the thread up so that it shutdowns. - // If we send the message once, there is a chance it will be missed, - // so keep sending until shtudown becomes false again, indicating that the thread has shut down. - std::lock_guard lock(m_WorkerMutex); - m_WorkerCV.notify_all(); - if (!m_Shutdown) - break; - } - - // Wait for it to shut down cleanly - m_WorkerThread.join(); + for (Future>& res : m_ResultQueue) + res.CancelOrWait(); #endif // CONFIG2_NVTT } @@ -478,13 +456,23 @@ delete[] rgba; } + m_ResultQueue.emplace_back(Threading::TaskManager::Instance().PushTask([request] { - std::lock_guard lock(m_WorkerMutex); - m_RequestQueue.push_back(request); - } + PROFILE2("convert texture"); + // Set up the result object + std::unique_ptr result = std::make_unique(); + result->dest = request->dest; + result->texture = request->texture; - // Wake up the worker thread - m_WorkerCV.notify_all(); + request->outputOptions.setOutputHandler(&result->output); + + // Perform the compression + nvtt::Compressor compressor; + result->ret = compressor.process(request->inputOptions, request->compressionOptions, + request->outputOptions); + + return result; + }, Threading::TaskPriority::LOW)); return true; @@ -497,24 +485,15 @@ bool CTextureConverter::Poll(CTexturePtr& texture, VfsPath& dest, bool& ok) { #if CONFIG2_NVTT - std::shared_ptr result; - - // Grab the first result (if any) - { - std::lock_guard lock(m_WorkerMutex); - if (!m_ResultQueue.empty()) - { - result = m_ResultQueue.front(); - m_ResultQueue.pop_front(); - } - } - - if (!result) + if (m_ResultQueue.empty() || !m_ResultQueue.front().IsReady()) { // no work to do return false; } + std::unique_ptr result = m_ResultQueue.front().Get(); + m_ResultQueue.pop_front(); + if (!result->ret) { // conversion had failed @@ -548,66 +527,8 @@ bool CTextureConverter::IsBusy() { #if CONFIG2_NVTT - std::lock_guard lock(m_WorkerMutex); - return !m_RequestQueue.empty(); + return m_ResultQueue.size() >= MAX_QUEUE_SIZE; #else // CONFIG2_NVTT return false; #endif // !CONFIG2_NVTT } - -void CTextureConverter::RunThread(CTextureConverter* textureConverter) -{ -#if CONFIG2_NVTT - debug_SetThreadName("TextureConverter"); - g_Profiler2.RegisterCurrentThread("texconv"); - // Wait until the main thread wakes us up - while (true) - { - // We may have several textures in the incoming queue, process them all before going back to sleep. - if (!textureConverter->IsBusy()) { - std::unique_lock wait_lock(textureConverter->m_WorkerMutex); - // Use the no-condition variant because spurious wake-ups don't matter that much here. - textureConverter->m_WorkerCV.wait(wait_lock); - } - - g_Profiler2.RecordSyncMarker(); - PROFILE2_EVENT("wakeup"); - - std::shared_ptr request; - - { - std::lock_guard wait_lock(textureConverter->m_WorkerMutex); - if (textureConverter->m_Shutdown) - break; - // If we weren't woken up for shutdown, we must have been woken up for - // a new request, so grab it from the queue - request = textureConverter->m_RequestQueue.front(); - textureConverter->m_RequestQueue.pop_front(); - } - - // Set up the result object - std::shared_ptr result = std::make_shared(); - result->dest = request->dest; - result->texture = request->texture; - - request->outputOptions.setOutputHandler(&result->output); - -// TIMER(L"TextureConverter compress"); - - { - PROFILE2("compress"); - - // Perform the compression - nvtt::Compressor compressor; - result->ret = compressor.process(request->inputOptions, request->compressionOptions, request->outputOptions); - } - - // Push the result onto the queue - std::lock_guard wait_lock(textureConverter->m_WorkerMutex); - textureConverter->m_ResultQueue.push_back(result); - } - - std::lock_guard wait_lock(textureConverter->m_WorkerMutex); - textureConverter->m_Shutdown = false; -#endif // CONFIG2_NVTT -} Index: source/tools/replayprofile/extract.pl =================================================================== --- source/tools/replayprofile/extract.pl +++ source/tools/replayprofile/extract.pl @@ -19,7 +19,7 @@ my ($to_json, $from_json); GetOptions ("to-json" => \$to_json, "from-json" => \$from_json); -open my $f, '../../../binaries/system/profile.txt' or die $!; +open my $f, '../../../profile.txt' or die $!; my $turn = 0; my %s; my %f;