yuzu-emu
/
yuzu
Archived
1
0
Fork 0

gpu_thread: Fix deadlock with threading idle state check.

This commit is contained in:
bunnei 2019-02-09 11:37:11 -05:00
parent 63aa08acbe
commit 84ad81ee67
2 changed files with 11 additions and 7 deletions

View File

@ -40,7 +40,7 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
auto WaitForWakeup = [&]() { auto WaitForWakeup = [&]() {
std::unique_lock<std::mutex> lock{state.signal_mutex}; std::unique_lock<std::mutex> lock{state.signal_mutex};
state.signal_condition.wait(lock, [&] { return !state.IsIdle() || !state.is_running; }); state.signal_condition.wait(lock, [&] { return !state.is_idle || !state.is_running; });
}; };
// Wait for first GPU command before acquiring the window context // Wait for first GPU command before acquiring the window context
@ -70,8 +70,10 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
state.pop_queue->pop(); state.pop_queue->pop();
} }
state.UpdateIdleState();
// Signal that the GPU thread has finished processing commands // Signal that the GPU thread has finished processing commands
if (state.IsIdle()) { if (state.is_idle) {
state.idle_condition.notify_one(); state.idle_condition.notify_one();
} }
@ -126,13 +128,14 @@ void ThreadManager::PushCommand(CommandData&& command_data, bool wait_for_idle,
{ {
std::lock_guard<std::mutex> lock{state.signal_mutex}; std::lock_guard<std::mutex> lock{state.signal_mutex};
if ((allow_on_cpu && state.IsIdle()) || IsGpuThread()) { if ((allow_on_cpu && state.is_idle) || IsGpuThread()) {
// Execute the command synchronously on the current thread // Execute the command synchronously on the current thread
ExecuteCommand(&command_data, renderer, dma_pusher); ExecuteCommand(&command_data, renderer, dma_pusher);
return; return;
} }
// Push the command to the GPU thread // Push the command to the GPU thread
state.UpdateIdleState();
state.push_queue->emplace(command_data); state.push_queue->emplace(command_data);
} }
@ -142,7 +145,7 @@ void ThreadManager::PushCommand(CommandData&& command_data, bool wait_for_idle,
if (wait_for_idle) { if (wait_for_idle) {
// Wait for the GPU to be idle (all commands to be executed) // Wait for the GPU to be idle (all commands to be executed)
std::unique_lock<std::mutex> lock{state.idle_mutex}; std::unique_lock<std::mutex> lock{state.idle_mutex};
state.idle_condition.wait(lock, [this] { return state.IsIdle(); }); state.idle_condition.wait(lock, [this] { return static_cast<bool>(state.is_idle); });
} }
} }

View File

@ -70,6 +70,7 @@ using CommandData = std::variant<SubmitListCommand, SwapBuffersCommand, FlushReg
/// Struct used to synchronize the GPU thread /// Struct used to synchronize the GPU thread
struct SynchState final { struct SynchState final {
std::atomic<bool> is_running{true}; std::atomic<bool> is_running{true};
std::atomic<bool> is_idle{true};
std::condition_variable signal_condition; std::condition_variable signal_condition;
std::mutex signal_mutex; std::mutex signal_mutex;
std::condition_variable idle_condition; std::condition_variable idle_condition;
@ -84,9 +85,9 @@ struct SynchState final {
CommandQueue* push_queue{&command_queues[0]}; CommandQueue* push_queue{&command_queues[0]};
CommandQueue* pop_queue{&command_queues[1]}; CommandQueue* pop_queue{&command_queues[1]};
/// Returns true if the GPU thread should be idle, meaning there are no commands to process void UpdateIdleState() {
bool IsIdle() const { std::lock_guard<std::mutex> lock{idle_mutex};
return command_queues[0].empty() && command_queues[1].empty(); is_idle = command_queues[0].empty() && command_queues[1].empty();
} }
}; };