Merge pull request #2912 from FernandoS27/async-fixes

General fixes to Async GPU
This commit is contained in:
bunnei 2019-10-16 10:34:48 -04:00 committed by GitHub
commit ef9b31783d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 67 additions and 52 deletions

View file

@ -256,6 +256,8 @@ struct System::Impl {
is_powered_on = false; is_powered_on = false;
exit_lock = false; exit_lock = false;
gpu_core->WaitIdle();
// Shutdown emulation session // Shutdown emulation session
renderer.reset(); renderer.reset();
GDBStub::Shutdown(); GDBStub::Shutdown();

View file

@ -5,6 +5,7 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/core.h" #include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
#include "core/hle/service/nvdrv/devices/nvmap.h" #include "core/hle/service/nvdrv/devices/nvmap.h"
#include "core/perf_stats.h" #include "core/perf_stats.h"
@ -38,7 +39,10 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
transform, crop_rect}; transform, crop_rect};
system.GetPerfStats().EndGameFrame(); system.GetPerfStats().EndGameFrame();
system.GetPerfStats().EndSystemFrame();
system.GPU().SwapBuffers(&framebuffer); system.GPU().SwapBuffers(&framebuffer);
system.FrameLimiter().DoFrameLimiting(system.CoreTiming().GetGlobalTimeUs());
system.GetPerfStats().BeginSystemFrame();
} }
} // namespace Service::Nvidia::Devices } // namespace Service::Nvidia::Devices

View file

@ -63,16 +63,26 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
return NvResult::BadParameter; return NvResult::BadParameter;
} }
u32 event_id = params.value & 0x00FF;
if (event_id >= MaxNvEvents) {
std::memcpy(output.data(), &params, sizeof(params));
return NvResult::BadParameter;
}
auto event = events_interface.events[event_id];
auto& gpu = system.GPU(); auto& gpu = system.GPU();
// This is mostly to take into account unimplemented features. As synced // This is mostly to take into account unimplemented features. As synced
// gpu is always synced. // gpu is always synced.
if (!gpu.IsAsync()) { if (!gpu.IsAsync()) {
event.writable->Signal();
return NvResult::Success; return NvResult::Success;
} }
auto lock = gpu.LockSync(); auto lock = gpu.LockSync();
const u32 current_syncpoint_value = gpu.GetSyncpointValue(params.syncpt_id); const u32 current_syncpoint_value = gpu.GetSyncpointValue(params.syncpt_id);
const s32 diff = current_syncpoint_value - params.threshold; const s32 diff = current_syncpoint_value - params.threshold;
if (diff >= 0) { if (diff >= 0) {
event.writable->Signal();
params.value = current_syncpoint_value; params.value = current_syncpoint_value;
std::memcpy(output.data(), &params, sizeof(params)); std::memcpy(output.data(), &params, sizeof(params));
return NvResult::Success; return NvResult::Success;
@ -88,27 +98,6 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
return NvResult::Timeout; return NvResult::Timeout;
} }
u32 event_id;
if (is_async) {
event_id = params.value & 0x00FF;
if (event_id >= MaxNvEvents) {
std::memcpy(output.data(), &params, sizeof(params));
return NvResult::BadParameter;
}
} else {
if (ctrl.fresh_call) {
const auto result = events_interface.GetFreeEvent();
if (result) {
event_id = *result;
} else {
LOG_CRITICAL(Service_NVDRV, "No Free Events available!");
event_id = params.value & 0x00FF;
}
} else {
event_id = ctrl.event_id;
}
}
EventState status = events_interface.status[event_id]; EventState status = events_interface.status[event_id];
if (event_id < MaxNvEvents || status == EventState::Free || status == EventState::Registered) { if (event_id < MaxNvEvents || status == EventState::Free || status == EventState::Registered) {
events_interface.SetEventStatus(event_id, EventState::Waiting); events_interface.SetEventStatus(event_id, EventState::Waiting);
@ -120,7 +109,7 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000; params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000;
} }
params.value |= event_id; params.value |= event_id;
events_interface.events[event_id].writable->Clear(); event.writable->Clear();
gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value); gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value);
if (!is_async && ctrl.fresh_call) { if (!is_async && ctrl.fresh_call) {
ctrl.must_delay = true; ctrl.must_delay = true;

View file

@ -134,7 +134,9 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3, 1}; IPC::ResponseBuilder rb{ctx, 3, 1};
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
if (event_id < MaxNvEvents) { if (event_id < MaxNvEvents) {
rb.PushCopyObjects(nvdrv->GetEvent(event_id)); auto event = nvdrv->GetEvent(event_id);
event->Clear();
rb.PushCopyObjects(event);
rb.Push<u32>(NvResult::Success); rb.Push<u32>(NvResult::Success);
} else { } else {
rb.Push<u32>(0); rb.Push<u32>(0);

View file

@ -40,8 +40,8 @@ Module::Module(Core::System& system) {
auto& kernel = system.Kernel(); auto& kernel = system.Kernel();
for (u32 i = 0; i < MaxNvEvents; i++) { for (u32 i = 0; i < MaxNvEvents; i++) {
std::string event_label = fmt::format("NVDRV::NvEvent_{}", i); std::string event_label = fmt::format("NVDRV::NvEvent_{}", i);
events_interface.events[i] = Kernel::WritableEvent::CreateEventPair( events_interface.events[i] =
kernel, Kernel::ResetType::Automatic, event_label); Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, event_label);
events_interface.status[i] = EventState::Free; events_interface.status[i] = EventState::Free;
events_interface.registered[i] = false; events_interface.registered[i] = false;
} }

View file

@ -187,14 +187,18 @@ void NVFlinger::Compose() {
MicroProfileFlip(); MicroProfileFlip();
if (!buffer) { if (!buffer) {
// There was no queued buffer to draw, render previous frame
system.GetPerfStats().EndGameFrame();
system.GPU().SwapBuffers({});
continue; continue;
} }
const auto& igbp_buffer = buffer->get().igbp_buffer; const auto& igbp_buffer = buffer->get().igbp_buffer;
const auto& gpu = system.GPU();
const auto& multi_fence = buffer->get().multi_fence;
for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) {
const auto& fence = multi_fence.fences[fence_id];
gpu.WaitFence(fence.id, fence.value);
}
// Now send the buffer to the GPU for drawing. // Now send the buffer to the GPU for drawing.
// TODO(Subv): Support more than just disp0. The display device selection is probably based // TODO(Subv): Support more than just disp0. The display device selection is probably based
// on which display we're drawing (Default, Internal, External, etc) // on which display we're drawing (Default, Internal, External, etc)

View file

@ -3,6 +3,7 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "common/assert.h" #include "common/assert.h"
#include "common/microprofile.h"
#include "core/core.h" #include "core/core.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/memory.h" #include "core/memory.h"
@ -17,6 +18,8 @@
namespace Tegra { namespace Tegra {
MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192));
GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer, bool is_async) GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer, bool is_async)
: system{system}, renderer{renderer}, is_async{is_async} { : system{system}, renderer{renderer}, is_async{is_async} {
auto& rasterizer{renderer.Rasterizer()}; auto& rasterizer{renderer.Rasterizer()};
@ -63,6 +66,16 @@ const DmaPusher& GPU::DmaPusher() const {
return *dma_pusher; return *dma_pusher;
} }
void GPU::WaitFence(u32 syncpoint_id, u32 value) const {
// Synced GPU, is always in sync
if (!is_async) {
return;
}
MICROPROFILE_SCOPE(GPU_wait);
while (syncpoints[syncpoint_id].load(std::memory_order_relaxed) < value) {
}
}
void GPU::IncrementSyncPoint(const u32 syncpoint_id) { void GPU::IncrementSyncPoint(const u32 syncpoint_id) {
syncpoints[syncpoint_id]++; syncpoints[syncpoint_id]++;
std::lock_guard lock{sync_mutex}; std::lock_guard lock{sync_mutex};

View file

@ -177,6 +177,12 @@ public:
/// Returns a reference to the GPU DMA pusher. /// Returns a reference to the GPU DMA pusher.
Tegra::DmaPusher& DmaPusher(); Tegra::DmaPusher& DmaPusher();
// Waits for the GPU to finish working
virtual void WaitIdle() const = 0;
/// Allows the CPU/NvFlinger to wait on the GPU before presenting a frame.
void WaitFence(u32 syncpoint_id, u32 value) const;
void IncrementSyncPoint(u32 syncpoint_id); void IncrementSyncPoint(u32 syncpoint_id);
u32 GetSyncpointValue(u32 syncpoint_id) const; u32 GetSyncpointValue(u32 syncpoint_id) const;

View file

@ -44,4 +44,8 @@ void GPUAsynch::TriggerCpuInterrupt(const u32 syncpoint_id, const u32 value) con
interrupt_manager.GPUInterruptSyncpt(syncpoint_id, value); interrupt_manager.GPUInterruptSyncpt(syncpoint_id, value);
} }
void GPUAsynch::WaitIdle() const {
gpu_thread.WaitIdle();
}
} // namespace VideoCommon } // namespace VideoCommon

View file

@ -25,6 +25,7 @@ public:
void FlushRegion(CacheAddr addr, u64 size) override; void FlushRegion(CacheAddr addr, u64 size) override;
void InvalidateRegion(CacheAddr addr, u64 size) override; void InvalidateRegion(CacheAddr addr, u64 size) override;
void FlushAndInvalidateRegion(CacheAddr addr, u64 size) override; void FlushAndInvalidateRegion(CacheAddr addr, u64 size) override;
void WaitIdle() const override;
protected: protected:
void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const override; void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const override;

View file

@ -24,6 +24,7 @@ public:
void FlushRegion(CacheAddr addr, u64 size) override; void FlushRegion(CacheAddr addr, u64 size) override;
void InvalidateRegion(CacheAddr addr, u64 size) override; void InvalidateRegion(CacheAddr addr, u64 size) override;
void FlushAndInvalidateRegion(CacheAddr addr, u64 size) override; void FlushAndInvalidateRegion(CacheAddr addr, u64 size) override;
void WaitIdle() const override {}
protected: protected:
void TriggerCpuInterrupt([[maybe_unused]] u32 syncpoint_id, void TriggerCpuInterrupt([[maybe_unused]] u32 syncpoint_id,

View file

@ -5,8 +5,6 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/microprofile.h" #include "common/microprofile.h"
#include "core/core.h" #include "core/core.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/frontend/scope_acquire_window_context.h" #include "core/frontend/scope_acquire_window_context.h"
#include "video_core/dma_pusher.h" #include "video_core/dma_pusher.h"
#include "video_core/gpu.h" #include "video_core/gpu.h"
@ -68,14 +66,10 @@ ThreadManager::~ThreadManager() {
void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher) { void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher) {
thread = std::thread{RunThread, std::ref(renderer), std::ref(dma_pusher), std::ref(state)}; thread = std::thread{RunThread, std::ref(renderer), std::ref(dma_pusher), std::ref(state)};
synchronization_event = system.CoreTiming().RegisterEvent(
"GPUThreadSynch", [this](u64 fence, s64) { state.WaitForSynchronization(fence); });
} }
void ThreadManager::SubmitList(Tegra::CommandList&& entries) { void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
const u64 fence{PushCommand(SubmitListCommand(std::move(entries)))}; PushCommand(SubmitListCommand(std::move(entries)));
const s64 synchronization_ticks{Core::Timing::usToCycles(std::chrono::microseconds{9000})};
system.CoreTiming().ScheduleEvent(synchronization_ticks, synchronization_event, fence);
} }
void ThreadManager::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { void ThreadManager::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
@ -96,16 +90,15 @@ void ThreadManager::FlushAndInvalidateRegion(CacheAddr addr, u64 size) {
InvalidateRegion(addr, size); InvalidateRegion(addr, size);
} }
void ThreadManager::WaitIdle() const {
while (state.last_fence > state.signaled_fence.load(std::memory_order_relaxed)) {
}
}
u64 ThreadManager::PushCommand(CommandData&& command_data) { u64 ThreadManager::PushCommand(CommandData&& command_data) {
const u64 fence{++state.last_fence}; const u64 fence{++state.last_fence};
state.queue.Push(CommandDataContainer(std::move(command_data), fence)); state.queue.Push(CommandDataContainer(std::move(command_data), fence));
return fence; return fence;
} }
MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192));
void SynchState::WaitForSynchronization(u64 fence) {
while (signaled_fence.load() < fence)
;
}
} // namespace VideoCommon::GPUThread } // namespace VideoCommon::GPUThread

View file

@ -21,9 +21,6 @@ class DmaPusher;
namespace Core { namespace Core {
class System; class System;
namespace Timing {
struct EventType;
} // namespace Timing
} // namespace Core } // namespace Core
namespace VideoCommon::GPUThread { namespace VideoCommon::GPUThread {
@ -89,8 +86,6 @@ struct CommandDataContainer {
struct SynchState final { struct SynchState final {
std::atomic_bool is_running{true}; std::atomic_bool is_running{true};
void WaitForSynchronization(u64 fence);
using CommandQueue = Common::SPSCQueue<CommandDataContainer>; using CommandQueue = Common::SPSCQueue<CommandDataContainer>;
CommandQueue queue; CommandQueue queue;
u64 last_fence{}; u64 last_fence{};
@ -121,6 +116,9 @@ public:
/// Notify rasterizer that any caches of the specified region should be flushed and invalidated /// Notify rasterizer that any caches of the specified region should be flushed and invalidated
void FlushAndInvalidateRegion(CacheAddr addr, u64 size); void FlushAndInvalidateRegion(CacheAddr addr, u64 size);
// Wait until the gpu thread is idle.
void WaitIdle() const;
private: private:
/// Pushes a command to be executed by the GPU thread /// Pushes a command to be executed by the GPU thread
u64 PushCommand(CommandData&& command_data); u64 PushCommand(CommandData&& command_data);
@ -128,7 +126,6 @@ private:
private: private:
SynchState state; SynchState state;
Core::System& system; Core::System& system;
Core::Timing::EventType* synchronization_event{};
std::thread thread; std::thread thread;
std::thread::id thread_id; std::thread::id thread_id;
}; };

View file

@ -348,6 +348,7 @@ static constexpr auto RangeFromInterval(Map& map, const Interval& interval) {
} }
void RasterizerOpenGL::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) { void RasterizerOpenGL::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {
std::lock_guard lock{pages_mutex};
const u64 page_start{addr >> Memory::PAGE_BITS}; const u64 page_start{addr >> Memory::PAGE_BITS};
const u64 page_end{(addr + size + Memory::PAGE_SIZE - 1) >> Memory::PAGE_BITS}; const u64 page_end{(addr + size + Memory::PAGE_SIZE - 1) >> Memory::PAGE_BITS};

View file

@ -9,6 +9,7 @@
#include <cstddef> #include <cstddef>
#include <map> #include <map>
#include <memory> #include <memory>
#include <mutex>
#include <optional> #include <optional>
#include <tuple> #include <tuple>
#include <utility> #include <utility>
@ -230,6 +231,8 @@ private:
using CachedPageMap = boost::icl::interval_map<u64, int>; using CachedPageMap = boost::icl::interval_map<u64, int>;
CachedPageMap cached_pages; CachedPageMap cached_pages;
std::mutex pages_mutex;
}; };
} // namespace OpenGL } // namespace OpenGL

View file

@ -102,8 +102,6 @@ RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::Syst
RendererOpenGL::~RendererOpenGL() = default; RendererOpenGL::~RendererOpenGL() = default;
void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
system.GetPerfStats().EndSystemFrame();
// Maintain the rasterizer's state as a priority // Maintain the rasterizer's state as a priority
OpenGLState prev_state = OpenGLState::GetCurState(); OpenGLState prev_state = OpenGLState::GetCurState();
state.AllDirty(); state.AllDirty();
@ -135,9 +133,6 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
render_window.PollEvents(); render_window.PollEvents();
system.FrameLimiter().DoFrameLimiting(system.CoreTiming().GetGlobalTimeUs());
system.GetPerfStats().BeginSystemFrame();
// Restore the rasterizer state // Restore the rasterizer state
prev_state.AllDirty(); prev_state.AllDirty();
prev_state.Apply(); prev_state.Apply();