mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-04-01 01:20:40 +00:00
VideoCommon: Add AsyncShaderCompiler class implementation
This commit is contained in:
parent
5bad2ee4a4
commit
0a9574eaa1
215
Source/Core/VideoCommon/AsyncShaderCompiler.cpp
Normal file
215
Source/Core/VideoCommon/AsyncShaderCompiler.cpp
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
// Copyright 2017 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "VideoCommon/AsyncShaderCompiler.h"
|
||||||
|
#include <thread>
|
||||||
|
#include "Common/Assert.h"
|
||||||
|
#include "Common/Logging/Log.h"
|
||||||
|
|
||||||
|
namespace VideoCommon
|
||||||
|
{
|
||||||
|
AsyncShaderCompiler::AsyncShaderCompiler()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncShaderCompiler::~AsyncShaderCompiler()
|
||||||
|
{
|
||||||
|
// Pending work can be left at shutdown.
|
||||||
|
// The work item classes are expected to clean up after themselves.
|
||||||
|
_assert_(!HasWorkerThreads());
|
||||||
|
_assert_(m_completed_work.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncShaderCompiler::QueueWorkItem(WorkItemPtr item)
|
||||||
|
{
|
||||||
|
// If no worker threads are available, compile synchronously.
|
||||||
|
if (!HasWorkerThreads())
|
||||||
|
{
|
||||||
|
item->Compile();
|
||||||
|
m_completed_work.push_back(std::move(item));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(m_pending_work_lock);
|
||||||
|
m_pending_work.push_back(std::move(item));
|
||||||
|
m_worker_thread_wake.notify_one();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncShaderCompiler::RetrieveWorkItems()
|
||||||
|
{
|
||||||
|
std::deque<WorkItemPtr> completed_work;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(m_completed_work_lock);
|
||||||
|
m_completed_work.swap(completed_work);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!completed_work.empty())
|
||||||
|
{
|
||||||
|
completed_work.front()->Retrieve();
|
||||||
|
completed_work.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AsyncShaderCompiler::HasPendingWork()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(m_pending_work_lock);
|
||||||
|
return !m_pending_work.empty() || m_busy_workers.load() != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncShaderCompiler::WaitUntilCompletion()
|
||||||
|
{
|
||||||
|
while (HasPendingWork())
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncShaderCompiler::WaitUntilCompletion(
|
||||||
|
const std::function<void(size_t, size_t)>& progress_callback)
|
||||||
|
{
|
||||||
|
if (!HasPendingWork())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Wait a second before opening a progress dialog.
|
||||||
|
// This way, if the operation completes quickly, we don't annoy the user.
|
||||||
|
constexpr u32 CHECK_INTERVAL_MS = 50;
|
||||||
|
constexpr auto CHECK_INTERVAL = std::chrono::milliseconds(CHECK_INTERVAL_MS);
|
||||||
|
for (u32 i = 0; i < (1000 / CHECK_INTERVAL_MS); i++)
|
||||||
|
{
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(CHECK_INTERVAL));
|
||||||
|
if (!HasPendingWork())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab the number of pending items. We use this to work out how many are left.
|
||||||
|
size_t total_items = 0;
|
||||||
|
{
|
||||||
|
// Safe to hold both locks here, since nowhere else does.
|
||||||
|
std::lock_guard<std::mutex> pending_guard(m_pending_work_lock);
|
||||||
|
std::lock_guard<std::mutex> completed_guard(m_completed_work_lock);
|
||||||
|
total_items = m_completed_work.size() + m_pending_work.size() + m_busy_workers.load() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update progress while the compiles complete.
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
size_t remaining_items;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> pending_guard(m_pending_work_lock);
|
||||||
|
if (m_pending_work.empty() && !m_busy_workers.load())
|
||||||
|
break;
|
||||||
|
remaining_items = m_pending_work.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
progress_callback(total_items - remaining_items, total_items);
|
||||||
|
std::this_thread::sleep_for(CHECK_INTERVAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncShaderCompiler::StartWorkerThreads(u32 num_worker_threads)
|
||||||
|
{
|
||||||
|
for (u32 i = 0; i < num_worker_threads; i++)
|
||||||
|
{
|
||||||
|
void* thread_param = nullptr;
|
||||||
|
if (!WorkerThreadInitMainThread(&thread_param))
|
||||||
|
{
|
||||||
|
WARN_LOG(VIDEO, "Failed to initialize shader compiler worker thread.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_worker_thread_start_result.store(false);
|
||||||
|
|
||||||
|
std::thread thr(&AsyncShaderCompiler::WorkerThreadEntryPoint, this, thread_param);
|
||||||
|
m_init_event.Wait();
|
||||||
|
|
||||||
|
if (!m_worker_thread_start_result.load())
|
||||||
|
{
|
||||||
|
WARN_LOG(VIDEO, "Failed to start shader compiler worker thread.");
|
||||||
|
thr.join();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_worker_threads.push_back(std::move(thr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AsyncShaderCompiler::HasWorkerThreads() const
|
||||||
|
{
|
||||||
|
return !m_worker_threads.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncShaderCompiler::StopWorkerThreads()
|
||||||
|
{
|
||||||
|
// Signal worker threads to stop, and wake all of them.
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(m_pending_work_lock);
|
||||||
|
m_exit_flag.Set();
|
||||||
|
m_worker_thread_wake.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for worker threads to exit.
|
||||||
|
for (std::thread& thr : m_worker_threads)
|
||||||
|
thr.join();
|
||||||
|
m_worker_threads.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AsyncShaderCompiler::WorkerThreadInitMainThread(void** param)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AsyncShaderCompiler::WorkerThreadInitWorkerThread(void* param)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncShaderCompiler::WorkerThreadExit(void* param)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncShaderCompiler::WorkerThreadEntryPoint(void* param)
|
||||||
|
{
|
||||||
|
// Initialize worker thread with backend-specific method.
|
||||||
|
if (!WorkerThreadInitWorkerThread(param))
|
||||||
|
{
|
||||||
|
WARN_LOG(VIDEO, "Failed to initialize shader compiler worker.");
|
||||||
|
m_worker_thread_start_result.store(false);
|
||||||
|
m_init_event.Set();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_worker_thread_start_result.store(true);
|
||||||
|
m_init_event.Set();
|
||||||
|
|
||||||
|
WorkerThreadRun();
|
||||||
|
|
||||||
|
WorkerThreadExit(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncShaderCompiler::WorkerThreadRun()
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> pending_lock(m_pending_work_lock);
|
||||||
|
while (!m_exit_flag.IsSet())
|
||||||
|
{
|
||||||
|
m_worker_thread_wake.wait(pending_lock);
|
||||||
|
|
||||||
|
while (!m_pending_work.empty() && !m_exit_flag.IsSet())
|
||||||
|
{
|
||||||
|
m_busy_workers++;
|
||||||
|
WorkItemPtr item(std::move(m_pending_work.front()));
|
||||||
|
m_pending_work.pop_front();
|
||||||
|
pending_lock.unlock();
|
||||||
|
|
||||||
|
if (item->Compile())
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> completed_guard(m_completed_work_lock);
|
||||||
|
m_completed_work.push_back(std::move(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
pending_lock.lock();
|
||||||
|
m_busy_workers--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace VideoCommon
|
83
Source/Core/VideoCommon/AsyncShaderCompiler.h
Normal file
83
Source/Core/VideoCommon/AsyncShaderCompiler.h
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
// Copyright 2017 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <deque>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/Event.h"
|
||||||
|
#include "Common/Flag.h"
|
||||||
|
|
||||||
|
namespace VideoCommon
|
||||||
|
{
|
||||||
|
class AsyncShaderCompiler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class WorkItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~WorkItem() = default;
|
||||||
|
virtual bool Compile() = 0;
|
||||||
|
virtual void Retrieve() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
using WorkItemPtr = std::unique_ptr<WorkItem>;
|
||||||
|
|
||||||
|
AsyncShaderCompiler();
|
||||||
|
virtual ~AsyncShaderCompiler();
|
||||||
|
|
||||||
|
template <typename T, typename... Params>
|
||||||
|
static WorkItemPtr CreateWorkItem(Params... params)
|
||||||
|
{
|
||||||
|
return std::unique_ptr<WorkItem>(new T(params...));
|
||||||
|
}
|
||||||
|
|
||||||
|
void QueueWorkItem(WorkItemPtr item);
|
||||||
|
void RetrieveWorkItems();
|
||||||
|
bool HasPendingWork();
|
||||||
|
|
||||||
|
// Simpler version without progress updates.
|
||||||
|
void WaitUntilCompletion();
|
||||||
|
|
||||||
|
// Calls progress_callback periodically, with completed_items, and total_items.
|
||||||
|
void WaitUntilCompletion(const std::function<void(size_t, size_t)>& progress_callback);
|
||||||
|
|
||||||
|
// Needed because of calling virtual methods in shutdown procedure.
|
||||||
|
void StartWorkerThreads(u32 num_worker_threads);
|
||||||
|
bool HasWorkerThreads() const;
|
||||||
|
void StopWorkerThreads();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool WorkerThreadInitMainThread(void** param);
|
||||||
|
virtual bool WorkerThreadInitWorkerThread(void* param);
|
||||||
|
virtual void WorkerThreadExit(void* param);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void WorkerThreadEntryPoint(void* param);
|
||||||
|
void WorkerThreadRun();
|
||||||
|
|
||||||
|
Common::Flag m_exit_flag;
|
||||||
|
Common::Event m_init_event;
|
||||||
|
|
||||||
|
std::vector<std::thread> m_worker_threads;
|
||||||
|
std::atomic_bool m_worker_thread_start_result{false};
|
||||||
|
|
||||||
|
std::deque<WorkItemPtr> m_pending_work;
|
||||||
|
std::mutex m_pending_work_lock;
|
||||||
|
std::condition_variable m_worker_thread_wake;
|
||||||
|
std::atomic_size_t m_busy_workers{0};
|
||||||
|
|
||||||
|
std::deque<WorkItemPtr> m_completed_work;
|
||||||
|
std::mutex m_completed_work_lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace VideoCommon
|
@ -1,6 +1,7 @@
|
|||||||
set(SRCS
|
set(SRCS
|
||||||
AbstractTexture.cpp
|
AbstractTexture.cpp
|
||||||
AsyncRequests.cpp
|
AsyncRequests.cpp
|
||||||
|
AsyncShaderCompiler.cpp
|
||||||
BoundingBox.cpp
|
BoundingBox.cpp
|
||||||
BPFunctions.cpp
|
BPFunctions.cpp
|
||||||
BPMemory.cpp
|
BPMemory.cpp
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="AbstractTexture.cpp" />
|
<ClCompile Include="AbstractTexture.cpp" />
|
||||||
<ClCompile Include="AsyncRequests.cpp" />
|
<ClCompile Include="AsyncRequests.cpp" />
|
||||||
|
<ClCompile Include="AsyncShaderCompiler.cpp" />
|
||||||
<ClCompile Include="AVIDump.cpp" />
|
<ClCompile Include="AVIDump.cpp" />
|
||||||
<ClCompile Include="BoundingBox.cpp" />
|
<ClCompile Include="BoundingBox.cpp" />
|
||||||
<ClCompile Include="BPFunctions.cpp" />
|
<ClCompile Include="BPFunctions.cpp" />
|
||||||
@ -94,6 +95,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="AbstractTexture.h" />
|
<ClInclude Include="AbstractTexture.h" />
|
||||||
<ClInclude Include="AsyncRequests.h" />
|
<ClInclude Include="AsyncRequests.h" />
|
||||||
|
<ClInclude Include="AsyncShaderCompiler.h" />
|
||||||
<ClInclude Include="AVIDump.h" />
|
<ClInclude Include="AVIDump.h" />
|
||||||
<ClInclude Include="BoundingBox.h" />
|
<ClInclude Include="BoundingBox.h" />
|
||||||
<ClInclude Include="BPFunctions.h" />
|
<ClInclude Include="BPFunctions.h" />
|
||||||
|
@ -176,6 +176,9 @@
|
|||||||
<ClCompile Include="ShaderGenCommon.cpp">
|
<ClCompile Include="ShaderGenCommon.cpp">
|
||||||
<Filter>Shader Generators</Filter>
|
<Filter>Shader Generators</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="AsyncShaderCompiler.cpp">
|
||||||
|
<Filter>Util</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="CommandProcessor.h" />
|
<ClInclude Include="CommandProcessor.h" />
|
||||||
@ -332,8 +335,11 @@
|
|||||||
<ClInclude Include="AbstractTexture.h">
|
<ClInclude Include="AbstractTexture.h">
|
||||||
<Filter>Base</Filter>
|
<Filter>Base</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="AsyncShaderCompiler.h">
|
||||||
|
<Filter>Util</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Text Include="CMakeLists.txt" />
|
<Text Include="CMakeLists.txt" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user