diff --git a/Source/Core/VideoCommon/AsyncRequests.cpp b/Source/Core/VideoCommon/AsyncRequests.cpp new file mode 100644 index 0000000000..a6cc5a4b9c --- /dev/null +++ b/Source/Core/VideoCommon/AsyncRequests.cpp @@ -0,0 +1,114 @@ +#include "VideoCommon/AsyncRequests.h" +#include "VideoCommon/RenderBase.h" + +AsyncRequests AsyncRequests::s_singleton; + +AsyncRequests::AsyncRequests() +: m_enable(false), m_passthrough(true) +{ +} + +void AsyncRequests::PullEventsInternal() +{ + std::unique_lock lock(m_mutex); + m_empty.store(true); + + while (!m_queue.empty()) + { + const Event& e = m_queue.front(); + + lock.unlock(); + HandleEvent(e); + lock.lock(); + + m_queue.pop(); + } + + if (m_wake_me_up_again) + { + m_wake_me_up_again = false; + m_cond.notify_all(); + } +} + +void AsyncRequests::PushEvent(const AsyncRequests::Event& event, bool blocking) +{ + std::unique_lock lock(m_mutex); + + if (m_passthrough) + { + HandleEvent(event); + return; + } + + m_empty.store(false); + m_wake_me_up_again |= blocking; + + if (!m_enable) + return; + + m_queue.push(event); + + if (blocking) + { + m_cond.wait(lock, [this]{return m_queue.empty();}); + } +} + +void AsyncRequests::SetEnable(bool enable) +{ + std::unique_lock lock(m_mutex); + m_enable = enable; + + if (!enable) + { + // flush the queue on disabling + while (!m_queue.empty()) + m_queue.pop(); + if (m_wake_me_up_again) + m_cond.notify_all(); + } +} + +void AsyncRequests::HandleEvent(const AsyncRequests::Event& e) +{ + EFBRectangle rc; + switch (e.type) + { + case Event::EFB_POKE_COLOR: + g_renderer->AccessEFB(POKE_COLOR, e.efb_poke.x, e.efb_poke.y, e.efb_poke.data); + break; + + case Event::EFB_POKE_Z: + g_renderer->AccessEFB(POKE_Z, e.efb_poke.x, e.efb_poke.y, e.efb_poke.data); + break; + + case Event::EFB_PEEK_COLOR: + *e.efb_peek.data = g_renderer->AccessEFB(PEEK_COLOR, e.efb_peek.x, e.efb_peek.y, 0); + break; + + case Event::EFB_PEEK_Z: + *e.efb_peek.data = g_renderer->AccessEFB(PEEK_Z, e.efb_peek.x, e.efb_peek.y, 0); + break; + + case Event::SWAP_EVENT: + Renderer::Swap(e.swap_event.xfbAddr, e.swap_event.fbWidth, e.swap_event.fbStride, e.swap_event.fbHeight, rc); + break; + + case Event::BBOX_READ: + *e.bbox.data = g_renderer->BBoxRead(e.bbox.index); + break; + + case Event::PERF_QUERY: + g_perf_query->FlushResults(); + break; + + } +} + +void AsyncRequests::SetPassthrough(bool enable) +{ + std::unique_lock lock(m_mutex); + m_passthrough = enable; +} + diff --git a/Source/Core/VideoCommon/AsyncRequests.h b/Source/Core/VideoCommon/AsyncRequests.h new file mode 100644 index 0000000000..68ef08cf67 --- /dev/null +++ b/Source/Core/VideoCommon/AsyncRequests.h @@ -0,0 +1,94 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include + +#include "Common/CommonTypes.h" + +class AsyncRequests +{ +public: + struct Event + { + enum Type + { + EFB_POKE_COLOR, + EFB_POKE_Z, + EFB_PEEK_COLOR, + EFB_PEEK_Z, + SWAP_EVENT, + BBOX_READ, + PERF_QUERY, + } type; + u64 time; + + union + { + struct + { + u16 x; + u16 y; + u32 data; + } efb_poke; + + struct + { + u16 x; + u16 y; + u32* data; + } efb_peek; + + struct + { + u32 xfbAddr; + u32 fbWidth; + u32 fbStride; + u32 fbHeight; + } swap_event; + + struct + { + int index; + u16* data; + } bbox; + + struct + { + } perf_query; + }; + }; + + AsyncRequests(); + + void PullEvents() + { + if (!m_empty.load()) + PullEventsInternal(); + } + void PushEvent(const Event& event, bool blocking = false); + void SetEnable(bool enable); + void SetPassthrough(bool enable); + + static AsyncRequests* GetInstance() { return &s_singleton; } + +private: + void PullEventsInternal(); + void HandleEvent(const Event& e); + + static AsyncRequests s_singleton; + + std::atomic m_empty; + std::queue m_queue; + std::mutex m_mutex; + std::condition_variable m_cond; + + bool m_wake_me_up_again; + bool m_enable; + bool m_passthrough; +}; diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt index 4e6dd8a6b6..f3ef98a741 100644 --- a/Source/Core/VideoCommon/CMakeLists.txt +++ b/Source/Core/VideoCommon/CMakeLists.txt @@ -1,4 +1,5 @@ -set(SRCS BoundingBox.cpp +set(SRCS AsyncRequests.cpp + BoundingBox.cpp BPFunctions.cpp BPMemory.cpp BPStructs.cpp diff --git a/Source/Core/VideoCommon/Fifo.cpp b/Source/Core/VideoCommon/Fifo.cpp index 9da3583a9d..4a85679210 100644 --- a/Source/Core/VideoCommon/Fifo.cpp +++ b/Source/Core/VideoCommon/Fifo.cpp @@ -15,6 +15,7 @@ #include "Core/NetPlayProto.h" #include "Core/HW/Memmap.h" +#include "VideoCommon/AsyncRequests.h" #include "VideoCommon/CommandProcessor.h" #include "VideoCommon/CPMemory.h" #include "VideoCommon/DataReader.h" @@ -282,11 +283,14 @@ void RunGpuLoop() // This allows a system that we are maxing out in dual core mode to do other things bool yield_cpu = cpu_info.num_cores <= 2; + AsyncRequests::GetInstance()->SetEnable(true); + AsyncRequests::GetInstance()->SetPassthrough(false); + while (GpuRunningState) { g_video_backend->PeekMessages(); - VideoFifo_CheckAsyncRequest(); + AsyncRequests::GetInstance()->PullEvents(); if (g_use_deterministic_gpu_thread) { // All the fifo/CP stuff is on the CPU. We just need to run the opcode decoder. @@ -348,7 +352,7 @@ void RunGpuLoop() // This call is pretty important in DualCore mode and must be called in the FIFO Loop. // If we don't, s_swapRequested or s_efbAccessRequested won't be set to false // leading the CPU thread to wait in Video_BeginField or Video_AccessEFB thus slowing things down. - VideoFifo_CheckAsyncRequest(); + AsyncRequests::GetInstance()->PullEvents(); CommandProcessor::isPossibleWaitingSetDrawDone = false; } @@ -377,6 +381,8 @@ void RunGpuLoop() } // wake up SyncGPU if we were interrupted s_video_buffer_cond.notify_all(); + AsyncRequests::GetInstance()->SetEnable(false); + AsyncRequests::GetInstance()->SetPassthrough(true); } diff --git a/Source/Core/VideoCommon/Fifo.h b/Source/Core/VideoCommon/Fifo.h index d0b3c07015..56212e7d17 100644 --- a/Source/Core/VideoCommon/Fifo.h +++ b/Source/Core/VideoCommon/Fifo.h @@ -53,7 +53,3 @@ void EmulatorState(bool running); bool AtBreakpoint(); void ResetVideoBuffer(); void Fifo_SetRendering(bool bEnabled); - - -// Implemented by the Video Backend -void VideoFifo_CheckAsyncRequest(); diff --git a/Source/Core/VideoCommon/MainBase.cpp b/Source/Core/VideoCommon/MainBase.cpp index b5c6cdebf9..a8af71b31e 100644 --- a/Source/Core/VideoCommon/MainBase.cpp +++ b/Source/Core/VideoCommon/MainBase.cpp @@ -1,6 +1,7 @@ #include "Common/Event.h" #include "Core/ConfigManager.h" +#include "VideoCommon/AsyncRequests.h" #include "VideoCommon/BoundingBox.h" #include "VideoCommon/BPStructs.h" #include "VideoCommon/CommandProcessor.h" @@ -18,18 +19,7 @@ bool s_BackendInitialized = false; -Common::Flag s_swapRequested; static Common::Flag s_FifoShuttingDown; -static Common::Flag s_efbAccessRequested; -static Common::Event s_efbAccessReadyEvent; - -static Common::Flag s_perfQueryRequested; -static Common::Event s_perfQueryReadyEvent; - -static Common::Flag s_BBoxRequested; -static Common::Event s_BBoxReadyEvent; -static int s_BBoxIndex; -static u16 s_BBoxResult; static volatile struct { @@ -39,16 +29,6 @@ static volatile struct u32 fbHeight; } s_beginFieldArgs; -static struct -{ - EFBAccessType type; - u32 x; - u32 y; - u32 Data; -} s_accessEFBArgs; - -static u32 s_AccessEFBResult = 0; - void VideoBackendHardware::EmuStateChange(EMUSTATE_CHANGE newState) { EmulatorState((newState == EMUSTATE_CHANGE_PLAY) ? true : false); @@ -64,8 +44,6 @@ void VideoBackendHardware::Video_ExitLoop() { ExitGpuLoop(); s_FifoShuttingDown.Set(); - s_efbAccessReadyEvent.Set(); - s_perfQueryReadyEvent.Set(); } void VideoBackendHardware::Video_SetRendering(bool bEnabled) @@ -73,45 +51,11 @@ void VideoBackendHardware::Video_SetRendering(bool bEnabled) Fifo_SetRendering(bEnabled); } -// Run from the graphics thread (from Fifo.cpp) -static void VideoFifo_CheckSwapRequest() -{ - if (g_ActiveConfig.bUseXFB) - { - if (s_swapRequested.IsSet()) - { - EFBRectangle rc; - Renderer::Swap(s_beginFieldArgs.xfbAddr, s_beginFieldArgs.fbWidth, s_beginFieldArgs.fbStride, s_beginFieldArgs.fbHeight, rc); - s_swapRequested.Clear(); - } - } -} - -// Run from the graphics thread (from Fifo.cpp) -void VideoFifo_CheckSwapRequestAt(u32 xfbAddr, u32 fbWidth, u32 fbHeight) -{ - if (g_ActiveConfig.bUseXFB) - { - if (s_swapRequested.IsSet()) - { - u32 aLower = xfbAddr; - u32 aUpper = xfbAddr + 2 * fbWidth * fbHeight; - u32 bLower = s_beginFieldArgs.xfbAddr; - u32 bUpper = s_beginFieldArgs.xfbAddr + 2 * s_beginFieldArgs.fbStride * s_beginFieldArgs.fbHeight; - - if (AddressRangesOverlap(aLower, aUpper, bLower, bUpper)) - VideoFifo_CheckSwapRequest(); - } - } -} - // Run from the CPU thread (from VideoInterface.cpp) void VideoBackendHardware::Video_BeginField(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight) { if (s_BackendInitialized && g_ActiveConfig.bUseXFB) { - if (!SConfig::GetInstance().m_LocalCoreStartupParameter.bCPUThread) - VideoFifo_CheckSwapRequest(); s_beginFieldArgs.xfbAddr = xfbAddr; s_beginFieldArgs.fbWidth = fbWidth; s_beginFieldArgs.fbStride = fbStride; @@ -122,16 +66,19 @@ void VideoBackendHardware::Video_BeginField(u32 xfbAddr, u32 fbWidth, u32 fbStri // Run from the CPU thread (from VideoInterface.cpp) void VideoBackendHardware::Video_EndField() { - if (s_BackendInitialized) + if (s_BackendInitialized && g_ActiveConfig.bUseXFB && g_renderer) { SyncGPU(SYNC_GPU_SWAP); - // Wait until the GPU thread has swapped. Prevents FIFO overflows. - while (g_ActiveConfig.bUseXFB && SConfig::GetInstance().m_LocalCoreStartupParameter.bCPUThread && s_swapRequested.IsSet()) - { - Common::YieldCPU(); - } - s_swapRequested.Set(); + AsyncRequests::Event e; + e.time = 0; + e.type = AsyncRequests::Event::SWAP_EVENT; + + e.swap_event.xfbAddr = s_beginFieldArgs.xfbAddr; + e.swap_event.fbWidth = s_beginFieldArgs.fbWidth; + e.swap_event.fbStride = s_beginFieldArgs.fbStride; + e.swap_event.fbHeight = s_beginFieldArgs.fbHeight; + AsyncRequests::GetInstance()->PushEvent(e, false); } } @@ -152,53 +99,35 @@ bool VideoBackendHardware::Video_Screenshot(const std::string& filename) return true; } -void VideoFifo_CheckEFBAccess() -{ - if (s_efbAccessRequested.IsSet()) - { - s_AccessEFBResult = g_renderer->AccessEFB(s_accessEFBArgs.type, s_accessEFBArgs.x, s_accessEFBArgs.y, s_accessEFBArgs.Data); - s_efbAccessRequested.Clear(); - s_efbAccessReadyEvent.Set(); - } -} - u32 VideoBackendHardware::Video_AccessEFB(EFBAccessType type, u32 x, u32 y, u32 InputData) { - if (s_BackendInitialized && g_ActiveConfig.bEFBAccessEnable) + if (!g_ActiveConfig.bEFBAccessEnable) { - SyncGPU(SYNC_GPU_EFB_POKE); - - s_accessEFBArgs.type = type; - s_accessEFBArgs.x = x; - s_accessEFBArgs.y = y; - s_accessEFBArgs.Data = InputData; - - s_efbAccessRequested.Set(); - - if (SConfig::GetInstance().m_LocalCoreStartupParameter.bCPUThread) - { - s_efbAccessReadyEvent.Reset(); - if (s_FifoShuttingDown.IsSet()) - return 0; - s_efbAccessRequested.Set(); - s_efbAccessReadyEvent.Wait(); - } - else - VideoFifo_CheckEFBAccess(); - - return s_AccessEFBResult; + return 0; } - return 0; -} - -static void VideoFifo_CheckPerfQueryRequest() -{ - if (s_perfQueryRequested.IsSet()) + if (type == POKE_COLOR || type == POKE_Z) { - g_perf_query->FlushResults(); - s_perfQueryRequested.Clear(); - s_perfQueryReadyEvent.Set(); + AsyncRequests::Event e; + e.type = type == POKE_COLOR ? AsyncRequests::Event::EFB_POKE_COLOR : AsyncRequests::Event::EFB_POKE_Z; + e.time = 0; + e.efb_poke.data = InputData; + e.efb_poke.x = x; + e.efb_poke.y = y; + AsyncRequests::GetInstance()->PushEvent(e, false); + return 0; + } + else + { + AsyncRequests::Event e; + u32 result; + e.type = type == PEEK_COLOR ? AsyncRequests::Event::EFB_PEEK_COLOR : AsyncRequests::Event::EFB_PEEK_Z; + e.time = 0; + e.efb_peek.x = x; + e.efb_peek.y = y; + e.efb_peek.data = &result; + AsyncRequests::GetInstance()->PushEvent(e, true); + return result; } } @@ -211,20 +140,12 @@ u32 VideoBackendHardware::Video_GetQueryResult(PerfQueryType type) SyncGPU(SYNC_GPU_PERFQUERY); - // TODO: Is this check sane? + AsyncRequests::Event e; + e.time = 0; + e.type = AsyncRequests::Event::PERF_QUERY; + if (!g_perf_query->IsFlushed()) - { - if (SConfig::GetInstance().m_LocalCoreStartupParameter.bCPUThread) - { - s_perfQueryReadyEvent.Reset(); - if (s_FifoShuttingDown.IsSet()) - return 0; - s_perfQueryRequested.Set(); - s_perfQueryReadyEvent.Wait(); - } - else - g_perf_query->FlushResults(); - } + AsyncRequests::GetInstance()->PushEvent(e, true); return g_perf_query->GetQueryResult(type); } @@ -236,43 +157,23 @@ u16 VideoBackendHardware::Video_GetBoundingBox(int index) SyncGPU(SYNC_GPU_BBOX); - if (SConfig::GetInstance().m_LocalCoreStartupParameter.bCPUThread) - { - s_BBoxReadyEvent.Reset(); - if (s_FifoShuttingDown.IsSet()) - return 0; - s_BBoxIndex = index; - s_BBoxRequested.Set(); - s_BBoxReadyEvent.Wait(); - return s_BBoxResult; - } - else - { - return g_renderer->BBoxRead(index); - } -} + AsyncRequests::Event e; + u16 result; + e.time = 0; + e.type = AsyncRequests::Event::BBOX_READ; + e.bbox.index = index; + e.bbox.data = &result; + AsyncRequests::GetInstance()->PushEvent(e, true); -static void VideoFifo_CheckBBoxRequest() -{ - if (s_BBoxRequested.IsSet()) - { - s_BBoxResult = g_renderer->BBoxRead(s_BBoxIndex); - s_BBoxRequested.Clear(); - s_BBoxReadyEvent.Set(); - } + return result; } void VideoBackendHardware::InitializeShared() { VideoCommon_Init(); - s_swapRequested.Clear(); - s_efbAccessRequested.Clear(); - s_perfQueryRequested.Clear(); s_FifoShuttingDown.Clear(); memset((void*)&s_beginFieldArgs, 0, sizeof(s_beginFieldArgs)); - memset(&s_accessEFBArgs, 0, sizeof(s_accessEFBArgs)); - s_AccessEFBResult = 0; m_invalid = false; } @@ -291,11 +192,7 @@ void VideoBackendHardware::DoState(PointerWrap& p) VideoCommon_DoState(p); p.DoMarker("VideoCommon"); - p.Do(s_swapRequested); - p.Do(s_efbAccessRequested); p.Do(s_beginFieldArgs); - p.Do(s_accessEFBArgs); - p.Do(s_AccessEFBResult); p.DoMarker("VideoBackendHardware"); // Refresh state. @@ -326,20 +223,11 @@ void VideoBackendHardware::PauseAndLock(bool doLock, bool unpauseOnUnlock) Fifo_PauseAndLock(doLock, unpauseOnUnlock); } - void VideoBackendHardware::RunLoop(bool enable) { VideoCommon_RunLoop(enable); } -void VideoFifo_CheckAsyncRequest() -{ - VideoFifo_CheckSwapRequest(); - VideoFifo_CheckEFBAccess(); - VideoFifo_CheckPerfQueryRequest(); - VideoFifo_CheckBBoxRequest(); -} - void VideoBackendHardware::Video_GatherPipeBursted() { CommandProcessor::GatherPipeBursted(); diff --git a/Source/Core/VideoCommon/MainBase.h b/Source/Core/VideoCommon/MainBase.h index bcef8024ec..c9d059cf7a 100644 --- a/Source/Core/VideoCommon/MainBase.h +++ b/Source/Core/VideoCommon/MainBase.h @@ -4,7 +4,3 @@ #include "Common/Flag.h" extern bool s_BackendInitialized; -extern Common::Flag s_swapRequested; - -void VideoFifo_CheckEFBAccess(); -void VideoFifo_CheckSwapRequestAt(u32 xfbAddr, u32 fbWidth, u32 fbHeight); diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 3e95eca5cc..ccceb954dd 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -116,8 +116,6 @@ void Renderer::RenderToXFB(u32 xfbAddr, const EFBRectangle& sourceRc, u32 fbWidt if (!fbWidth || !fbHeight) return; - VideoFifo_CheckEFBAccess(); - VideoFifo_CheckSwapRequestAt(xfbAddr, fbWidth, fbHeight); XFBWrited = true; if (g_ActiveConfig.bUseXFB) @@ -127,7 +125,6 @@ void Renderer::RenderToXFB(u32 xfbAddr, const EFBRectangle& sourceRc, u32 fbWidt else { Swap(xfbAddr, fbWidth, fbWidth, fbHeight, sourceRc, Gamma); - s_swapRequested.Clear(); } } diff --git a/Source/Core/VideoCommon/VertexManagerBase.cpp b/Source/Core/VideoCommon/VertexManagerBase.cpp index bf7800cc9a..520654448f 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.cpp +++ b/Source/Core/VideoCommon/VertexManagerBase.cpp @@ -166,8 +166,6 @@ void VertexManager::Flush() // loading a state will invalidate BP, so check for it g_video_backend->CheckInvalidState(); - VideoFifo_CheckEFBAccess(); - #if defined(_DEBUG) || defined(DEBUGFAST) PRIM_LOG("frame%d:\n texgen=%d, numchan=%d, dualtex=%d, ztex=%d, cole=%d, alpe=%d, ze=%d", g_ActiveConfig.iSaveTargetId, xfmem.numTexGen.numTexGens, xfmem.numChan.numColorChans, xfmem.dualTexTrans.enabled, bpmem.ztex2.op, diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj b/Source/Core/VideoCommon/VideoCommon.vcxproj index 649cd5095e..ff56a97703 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcxproj +++ b/Source/Core/VideoCommon/VideoCommon.vcxproj @@ -35,6 +35,7 @@ + @@ -84,6 +85,7 @@ + diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters index eca9287439..1998fce1cc 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters +++ b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters @@ -146,6 +146,9 @@ Decoding + + Util + Util @@ -290,6 +293,9 @@ Vertex Loading + + Util + Util