#include "Common/Event.h" #include "Core/ConfigManager.h" #include "VideoCommon/AsyncRequests.h" #include "VideoCommon/BoundingBox.h" #include "VideoCommon/BPStructs.h" #include "VideoCommon/CommandProcessor.h" #include "VideoCommon/Fifo.h" #include "VideoCommon/FramebufferManagerBase.h" #include "VideoCommon/MainBase.h" #include "VideoCommon/OnScreenDisplay.h" #include "VideoCommon/PixelEngine.h" #include "VideoCommon/RenderBase.h" #include "VideoCommon/TextureCacheBase.h" #include "VideoCommon/VertexLoaderManager.h" #include "VideoCommon/VideoBackendBase.h" #include "VideoCommon/VideoConfig.h" #include "VideoCommon/VideoState.h" bool s_BackendInitialized = false; Common::Flag s_swapRequested; static Common::Flag s_FifoShuttingDown; 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 { u32 xfbAddr; u32 fbWidth; u32 fbStride; u32 fbHeight; } s_beginFieldArgs; void VideoBackendHardware::EmuStateChange(EMUSTATE_CHANGE newState) { EmulatorState((newState == EMUSTATE_CHANGE_PLAY) ? true : false); } // Enter and exit the video loop void VideoBackendHardware::Video_EnterLoop() { RunGpuLoop(); } void VideoBackendHardware::Video_ExitLoop() { ExitGpuLoop(); s_FifoShuttingDown.Set(); s_perfQueryReadyEvent.Set(); } 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; s_beginFieldArgs.fbHeight = fbHeight; } } // Run from the CPU thread (from VideoInterface.cpp) void VideoBackendHardware::Video_EndField() { if (s_BackendInitialized) { 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(); } } void VideoBackendHardware::Video_AddMessage(const std::string& msg, u32 milliseconds) { OSD::AddMessage(msg, milliseconds); } void VideoBackendHardware::Video_ClearMessages() { OSD::ClearMessages(); } // Screenshot bool VideoBackendHardware::Video_Screenshot(const std::string& filename) { Renderer::SetScreenshot(filename.c_str()); return true; } u32 VideoBackendHardware::Video_AccessEFB(EFBAccessType type, u32 x, u32 y, u32 InputData) { if (!g_ActiveConfig.bEFBAccessEnable) { return 0; } if (type == POKE_COLOR || type == POKE_Z) { 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, 0); 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, 1); return result; } } static void VideoFifo_CheckPerfQueryRequest() { if (s_perfQueryRequested.IsSet()) { g_perf_query->FlushResults(); s_perfQueryRequested.Clear(); s_perfQueryReadyEvent.Set(); } } u32 VideoBackendHardware::Video_GetQueryResult(PerfQueryType type) { if (!g_perf_query->ShouldEmulate()) { return 0; } SyncGPU(SYNC_GPU_PERFQUERY); // TODO: Is this check sane? 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(); } return g_perf_query->GetQueryResult(type); } u16 VideoBackendHardware::Video_GetBoundingBox(int index) { if (!g_ActiveConfig.backend_info.bSupportsBBox) return BoundingBox::coords[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); } } static void VideoFifo_CheckBBoxRequest() { if (s_BBoxRequested.IsSet()) { s_BBoxResult = g_renderer->BBoxRead(s_BBoxIndex); s_BBoxRequested.Clear(); s_BBoxReadyEvent.Set(); } } void VideoBackendHardware::InitializeShared() { VideoCommon_Init(); s_swapRequested.Clear(); s_perfQueryRequested.Clear(); s_FifoShuttingDown.Clear(); memset((void*)&s_beginFieldArgs, 0, sizeof(s_beginFieldArgs)); m_invalid = false; } // Run from the CPU thread void VideoBackendHardware::DoState(PointerWrap& p) { bool software = false; p.Do(software); if (p.GetMode() == PointerWrap::MODE_READ && software == true) { // change mode to abort load of incompatible save state. p.SetMode(PointerWrap::MODE_VERIFY); } VideoCommon_DoState(p); p.DoMarker("VideoCommon"); p.Do(s_swapRequested); p.Do(s_beginFieldArgs); p.DoMarker("VideoBackendHardware"); // Refresh state. if (p.GetMode() == PointerWrap::MODE_READ) { m_invalid = true; RecomputeCachedArraybases(); // Clear all caches that touch RAM // (? these don't appear to touch any emulation state that gets saved. moved to on load only.) VertexLoaderManager::MarkAllDirty(); } } void VideoBackendHardware::CheckInvalidState() { if (m_invalid) { m_invalid = false; BPReload(); TextureCache::Invalidate(); } } 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_CheckPerfQueryRequest(); VideoFifo_CheckBBoxRequest(); } void VideoBackendHardware::Video_GatherPipeBursted() { CommandProcessor::GatherPipeBursted(); } bool VideoBackendHardware::Video_IsPossibleWaitingSetDrawDone() { return CommandProcessor::isPossibleWaitingSetDrawDone; } void VideoBackendHardware::RegisterCPMMIO(MMIO::Mapping* mmio, u32 base) { CommandProcessor::RegisterMMIO(mmio, base); } void VideoBackendHardware::UpdateWantDeterminism(bool want) { Fifo_UpdateWantDeterminism(want); }