diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index e4d2af12ab..8a2d3185c9 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -91,6 +91,7 @@ #include "VideoCommon/Present.h" #include "VideoCommon/RenderBase.h" #include "VideoCommon/VideoBackendBase.h" +#include "VideoCommon/VideoEvents.h" #ifdef ANDROID #include "jni/AndroidCommon/IDCache.h" @@ -132,6 +133,13 @@ static thread_local bool tls_is_gpu_thread = false; static void EmuThread(std::unique_ptr boot, WindowSystemInfo wsi); +static EventHook s_frame_presented = AfterPresentEvent::Register([](auto& present_info) { + const double last_speed_denominator = g_perf_metrics.GetLastSpeedDenominator(); + // The denominator should always be > 0 but if it's not, just return 1 + const double last_speed = last_speed_denominator > 0.0 ? (1.0 / last_speed_denominator) : 1.0; + Core::Callback_FramePresented(last_speed); +}, "Core Frame Presented"); + bool GetIsThrottlerTempDisabled() { return s_is_throttler_temp_disabled; diff --git a/Source/Core/Core/FifoPlayer/FifoRecorder.cpp b/Source/Core/Core/FifoPlayer/FifoRecorder.cpp index 0d12481420..0ec2232843 100644 --- a/Source/Core/Core/FifoPlayer/FifoRecorder.cpp +++ b/Source/Core/Core/FifoPlayer/FifoRecorder.cpp @@ -14,8 +14,13 @@ #include "Core/HW/Memmap.h" #include "Core/System.h" +#include "VideoCommon/BPMemory.h" +#include "VideoCommon/CommandProcessor.h" #include "VideoCommon/OpcodeDecoding.h" +#include "VideoCommon/TextureDecoder.h" #include "VideoCommon/XFStructs.h" +#include "VideoCommon/XFMemory.h" +#include "VideoCommon/VideoEvents.h" class FifoRecorder::FifoRecordAnalyzer : public OpcodeDecoder::Callback { @@ -249,6 +254,40 @@ void FifoRecorder::StartRecording(s32 numFrames, CallbackFunc finishedCb) m_RequestedRecordingEnd = false; m_FinishedCb = finishedCb; + + m_end_of_frame_event = AfterFrameEvent::Register([this] { + const bool was_recording = OpcodeDecoder::g_record_fifo_data; + OpcodeDecoder::g_record_fifo_data = IsRecording(); + + if (!OpcodeDecoder::g_record_fifo_data) + return; + + if (!was_recording) + { + RecordInitialVideoMemory(); + } + + auto& system = Core::System::GetInstance(); + auto& command_processor = system.GetCommandProcessor(); + const auto& fifo = command_processor.GetFifo(); + EndFrame(fifo.CPBase.load(std::memory_order_relaxed), + fifo.CPEnd.load(std::memory_order_relaxed)); + }, "FifoRecorder::EndFrame"); +} + +void FifoRecorder::RecordInitialVideoMemory() +{ + const u32* bpmem_ptr = reinterpret_cast(&bpmem); + u32 cpmem[256] = {}; + // The FIFO recording format splits XF memory into xfmem and xfregs; follow + // that split here. + const u32* xfmem_ptr = reinterpret_cast(&xfmem); + const u32* xfregs_ptr = reinterpret_cast(&xfmem) + FifoDataFile::XF_MEM_SIZE; + u32 xfregs_size = sizeof(XFMemory) / 4 - FifoDataFile::XF_MEM_SIZE; + + g_main_cp_state.FillCPMemoryArray(cpmem); + + SetVideoMemory(bpmem_ptr, cpmem, xfmem_ptr, xfregs_ptr, xfregs_size, texMem); } void FifoRecorder::StopRecording() @@ -391,11 +430,14 @@ void FifoRecorder::EndFrame(u32 fifoStart, u32 fifoEnd) m_SkipFutureData = true; // Signal video backend that it should not call this function when the next frame ends m_IsRecording = false; + + // Remove our frame end callback + m_end_of_frame_event.reset(); } } void FifoRecorder::SetVideoMemory(const u32* bpMem, const u32* cpMem, const u32* xfMem, - const u32* xfRegs, u32 xfRegsSize, const u8* texMem) + const u32* xfRegs, u32 xfRegsSize, const u8* texMem_ptr) { std::lock_guard lk(m_mutex); @@ -408,7 +450,7 @@ void FifoRecorder::SetVideoMemory(const u32* bpMem, const u32* cpMem, const u32* u32 xfRegsCopySize = std::min((u32)FifoDataFile::XF_REGS_SIZE, xfRegsSize); memcpy(m_File->GetXFRegs(), xfRegs, xfRegsCopySize * 4); - memcpy(m_File->GetTexMem(), texMem, FifoDataFile::TEX_MEM_SIZE); + memcpy(m_File->GetTexMem(), texMem_ptr, FifoDataFile::TEX_MEM_SIZE); } m_record_analyzer = std::make_unique(this, cpMem); diff --git a/Source/Core/Core/FifoPlayer/FifoRecorder.h b/Source/Core/Core/FifoPlayer/FifoRecorder.h index 3a28d05bce..6c26ba5934 100644 --- a/Source/Core/Core/FifoPlayer/FifoRecorder.h +++ b/Source/Core/Core/FifoPlayer/FifoRecorder.h @@ -9,6 +9,7 @@ #include #include "Common/Assert.h" +#include "Common/EventHook.h" #include "Core/FifoPlayer/FifoDataFile.h" class FifoRecorder @@ -50,6 +51,8 @@ public: private: class FifoRecordAnalyzer; + void RecordInitialVideoMemory(); + // Accessed from both GUI and video threads std::recursive_mutex m_mutex; @@ -72,4 +75,6 @@ private: std::vector m_FifoData; std::vector m_Ram; std::vector m_ExRam; + + EventHook m_end_of_frame_event; }; diff --git a/Source/Core/InputCommon/DynamicInputTextureManager.cpp b/Source/Core/InputCommon/DynamicInputTextureManager.cpp index 7b7811801c..8a325f8f7e 100644 --- a/Source/Core/InputCommon/DynamicInputTextureManager.cpp +++ b/Source/Core/InputCommon/DynamicInputTextureManager.cpp @@ -13,7 +13,7 @@ #include "InputCommon/DynamicInputTextures/DITConfiguration.h" #include "VideoCommon/HiresTextures.h" -#include "VideoCommon/RenderBase.h" +#include "VideoCommon/TextureCacheBase.h" namespace InputCommon { @@ -48,7 +48,7 @@ void DynamicInputTextureManager::GenerateTextures(const IniFile& file, any_dirty |= configuration.GenerateTextures(file, controller_names); } - if (any_dirty && g_renderer && Core::GetState() != Core::State::Starting) - g_renderer->ForceReloadTextures(); + if (any_dirty && g_texture_cache && Core::GetState() != Core::State::Starting) + g_texture_cache->ForceReloadTextures(); } } // namespace InputCommon diff --git a/Source/Core/VideoCommon/FrameDumper.cpp b/Source/Core/VideoCommon/FrameDumper.cpp index 35431a4abf..bb0cb2c378 100644 --- a/Source/Core/VideoCommon/FrameDumper.cpp +++ b/Source/Core/VideoCommon/FrameDumper.cpp @@ -28,6 +28,7 @@ static bool DumpFrameToPNG(const FrameData& frame, const std::string& file_name) FrameDumper::FrameDumper() { + m_frame_end_handle = AfterFrameEvent::Register([this] { FlushFrameDump(); }, "FrameDumper"); } FrameDumper::~FrameDumper() diff --git a/Source/Core/VideoCommon/FrameDumper.h b/Source/Core/VideoCommon/FrameDumper.h index f2c53a28b9..8006739671 100644 --- a/Source/Core/VideoCommon/FrameDumper.h +++ b/Source/Core/VideoCommon/FrameDumper.h @@ -10,6 +10,7 @@ #include "Common/Thread.h" #include "VideoCommon/FrameDumpFFMpeg.h" +#include "VideoCommon/VideoEvents.h" class AbstractStagingTexture; class AbstractTexture; @@ -116,6 +117,8 @@ private: Common::Event m_screenshot_completed; std::mutex m_screenshot_lock; std::string m_screenshot_name; + + EventHook m_frame_end_handle; }; extern std::unique_ptr g_frame_dumper; diff --git a/Source/Core/VideoCommon/FramebufferManager.cpp b/Source/Core/VideoCommon/FramebufferManager.cpp index 861536813e..3032de574f 100644 --- a/Source/Core/VideoCommon/FramebufferManager.cpp +++ b/Source/Core/VideoCommon/FramebufferManager.cpp @@ -79,6 +79,8 @@ bool FramebufferManager::Initialize() return false; } + m_end_of_frame_event = AfterFrameEvent::Register([this] { EndOfFrame(); }, "FramebufferManager"); + return true; } diff --git a/Source/Core/VideoCommon/FramebufferManager.h b/Source/Core/VideoCommon/FramebufferManager.h index dd073dda12..405ef1fc3a 100644 --- a/Source/Core/VideoCommon/FramebufferManager.h +++ b/Source/Core/VideoCommon/FramebufferManager.h @@ -16,6 +16,7 @@ #include "VideoCommon/AbstractTexture.h" #include "VideoCommon/RenderState.h" #include "VideoCommon/TextureConfig.h" +#include "VideoCommon/VideoEvents.h" class NativeVertexFormat; class PointerWrap; @@ -213,6 +214,8 @@ protected: std::unique_ptr m_depth_poke_pipeline; std::vector m_color_poke_vertices; std::vector m_depth_poke_vertices; + + EventHook m_end_of_frame_event; }; extern std::unique_ptr g_framebuffer_manager; diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.cpp b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.cpp index 4b5f5dbd84..368e7adba4 100644 --- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.cpp +++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.cpp @@ -85,6 +85,8 @@ bool GraphicsModManager::Initialize() g_ActiveConfig.graphics_mod_config->Load(); g_ActiveConfig.graphics_mod_config->SetChangeCount(old_game_mod_changes); g_graphics_mod_manager->Load(*g_ActiveConfig.graphics_mod_config); + + m_end_of_frame_event = AfterFrameEvent::Register([this] { EndOfFrame(); }, "ModManager"); } return true; diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h index 035ee48a36..aeb55f17d1 100644 --- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h +++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h @@ -14,6 +14,7 @@ #include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h" #include "VideoCommon/TextureInfo.h" #include "VideoCommon/XFMemory.h" +#include "VideoCommon/VideoEvents.h" class GraphicsModGroupConfig; class GraphicsModManager @@ -34,9 +35,8 @@ public: void Load(const GraphicsModGroupConfig& config); - void EndOfFrame(); - private: + void EndOfFrame(); void Reset(); class DecoratedAction; @@ -53,6 +53,8 @@ private: std::unordered_map, FBInfoHasher> m_xfb_target_to_actions; std::unordered_set m_groups; + + EventHook m_end_of_frame_event; }; extern std::unique_ptr g_graphics_mod_manager; diff --git a/Source/Core/VideoCommon/Present.cpp b/Source/Core/VideoCommon/Present.cpp index 69deb32e2c..73532367f9 100644 --- a/Source/Core/VideoCommon/Present.cpp +++ b/Source/Core/VideoCommon/Present.cpp @@ -100,6 +100,8 @@ void Presenter::ViSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, { Present(); ProcessFrameDumping(ticks); + + AfterPresentEvent::Trigger(present_info); } else { @@ -117,8 +119,12 @@ void Presenter::ImmediateSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_ present_info.reason = PresentInfo::PresentReason::Immediate; present_info.present_count = m_present_count++; + BeforePresentEvent::Trigger(present_info); + Present(); ProcessFrameDumping(ticks); + + AfterPresentEvent::Trigger(present_info); } void Presenter::ProcessFrameDumping(u64 ticks) const diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 706e0ac58c..69555f9919 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -64,6 +64,10 @@ Renderer::Renderer() { UpdateActiveConfig(); CalculateTargetSize(); + UpdateWidescreen(); + // VertexManager doesn't maintain statistics in Wii mode. + if (!SConfig::GetInstance().bWii) + m_update_widescreen_handle = AfterFrameEvent::Register([this] { UpdateWidescreenHeuristic(); }, "WideScreen Heuristic"); } Renderer::~Renderer() = default; @@ -202,15 +206,6 @@ void Renderer::PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num } } -void Renderer::RenderToXFB(u32 xfbAddr, const MathUtil::Rectangle& sourceRc, u32 fbStride, - u32 fbHeight, float Gamma) -{ - CheckFifoRecording(); - - if (!fbStride || !fbHeight) - return; -} - unsigned int Renderer::GetEFBScale() const { return m_efb_scale; @@ -288,60 +283,39 @@ MathUtil::Rectangle Renderer::ConvertEFBRectangle(const MathUtil::Rectangle return result; } -void Renderer::CheckFifoRecording() +void Renderer::UpdateWidescreen() { - const bool was_recording = OpcodeDecoder::g_record_fifo_data; - OpcodeDecoder::g_record_fifo_data = FifoRecorder::GetInstance().IsRecording(); + if (SConfig::GetInstance().bWii) + m_is_game_widescreen = Config::Get(Config::SYSCONF_WIDESCREEN); - if (!OpcodeDecoder::g_record_fifo_data) - return; + // suggested_aspect_mode overrides SYSCONF_WIDESCREEN + if (g_ActiveConfig.suggested_aspect_mode == AspectMode::Analog) + m_is_game_widescreen = false; + else if (g_ActiveConfig.suggested_aspect_mode == AspectMode::AnalogWide) + m_is_game_widescreen = true; - if (!was_recording) + // If widescreen hack is disabled override game's AR if UI is set to 4:3 or 16:9. + if (!g_ActiveConfig.bWidescreenHack) { - RecordVideoMemory(); + const auto aspect_mode = g_ActiveConfig.aspect_mode; + if (aspect_mode == AspectMode::Analog) + m_is_game_widescreen = false; + else if (aspect_mode == AspectMode::AnalogWide) + m_is_game_widescreen = true; } - - auto& system = Core::System::GetInstance(); - auto& command_processor = system.GetCommandProcessor(); - const auto& fifo = command_processor.GetFifo(); - FifoRecorder::GetInstance().EndFrame(fifo.CPBase.load(std::memory_order_relaxed), - fifo.CPEnd.load(std::memory_order_relaxed)); -} - -void Renderer::RecordVideoMemory() -{ - const u32* bpmem_ptr = reinterpret_cast(&bpmem); - u32 cpmem[256] = {}; - // The FIFO recording format splits XF memory into xfmem and xfregs; follow - // that split here. - const u32* xfmem_ptr = reinterpret_cast(&xfmem); - const u32* xfregs_ptr = reinterpret_cast(&xfmem) + FifoDataFile::XF_MEM_SIZE; - u32 xfregs_size = sizeof(XFMemory) / 4 - FifoDataFile::XF_MEM_SIZE; - - g_main_cp_state.FillCPMemoryArray(cpmem); - - FifoRecorder::GetInstance().SetVideoMemory(bpmem_ptr, cpmem, xfmem_ptr, xfregs_ptr, xfregs_size, - texMem); -} - -void Renderer::ForceReloadTextures() -{ - m_force_reload_textures.Set(); } // Heuristic to detect if a GameCube game is in 16:9 anamorphic widescreen mode. void Renderer::UpdateWidescreenHeuristic() { - // VertexManager maintains no statistics in Wii mode. - if (SConfig::GetInstance().bWii) - return; - const auto flush_statistics = g_vertex_manager->ResetFlushAspectRatioCount(); // If suggested_aspect_mode (GameINI) is configured don't use heuristic. if (g_ActiveConfig.suggested_aspect_mode != AspectMode::Auto) return; + UpdateWidescreen(); + // If widescreen hack isn't active and aspect_mode (UI) is 4:3 or 16:9 don't use heuristic. if (!g_ActiveConfig.bWidescreenHack && (g_ActiveConfig.aspect_mode == AspectMode::Analog || g_ActiveConfig.aspect_mode == AspectMode::AnalogWide)) @@ -382,109 +356,16 @@ void Renderer::UpdateWidescreenHeuristic() m_was_orthographically_anamorphic = ortho_looks_anamorphic; } +void Renderer::OnConfigChanged(u32 bits) +{ + if (bits & CONFIG_CHANGE_BIT_ASPECT_RATIO) + UpdateWidescreen(); +} + void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks) { - if (SConfig::GetInstance().bWii) - m_is_game_widescreen = Config::Get(Config::SYSCONF_WIDESCREEN); - - // suggested_aspect_mode overrides SYSCONF_WIDESCREEN - if (g_ActiveConfig.suggested_aspect_mode == AspectMode::Analog) - m_is_game_widescreen = false; - else if (g_ActiveConfig.suggested_aspect_mode == AspectMode::AnalogWide) - m_is_game_widescreen = true; - - // If widescreen hack is disabled override game's AR if UI is set to 4:3 or 16:9. - if (!g_ActiveConfig.bWidescreenHack) - { - const auto aspect_mode = g_ActiveConfig.aspect_mode; - if (aspect_mode == AspectMode::Analog) - m_is_game_widescreen = false; - else if (aspect_mode == AspectMode::AnalogWide) - m_is_game_widescreen = true; - } - UpdateWidescreenHeuristic(); - - // Ensure the last frame was written to the dump. - // This is required even if frame dumping has stopped, since the frame dump is one frame - // behind the renderer. - g_frame_dumper->FlushFrameDump(); - - if (g_ActiveConfig.bGraphicMods) - { - g_graphics_mod_manager->EndOfFrame(); - } - - g_framebuffer_manager->EndOfFrame(); - if (xfb_addr && fb_width && fb_stride && fb_height) { - // Get the current XFB from texture cache - - g_presenter->ReleaseXFBContentLock(); - - MathUtil::Rectangle xfb_rect; - RcTcacheEntry xfb_entry = - g_texture_cache->GetXFBTexture(xfb_addr, fb_width, fb_height, fb_stride, &xfb_rect); - - bool is_duplicate_frame = - g_presenter->SubmitXFB(std::move(xfb_entry), xfb_rect, ticks, m_frame_count); - - if (!g_ActiveConfig.bSkipPresentingDuplicateXFBs || !is_duplicate_frame) - { - if (!is_duplicate_frame) - { - DolphinAnalytics::PerformanceSample perf_sample; - perf_sample.speed_ratio = SystemTimers::GetEstimatedEmulationPerformance(); - perf_sample.num_prims = g_stats.this_frame.num_prims + g_stats.this_frame.num_dl_prims; - perf_sample.num_draw_calls = g_stats.this_frame.num_draw_calls; - DolphinAnalytics::Instance().ReportPerformanceInfo(std::move(perf_sample)); - - // Begin new frame - m_frame_count++; - g_stats.ResetFrame(); - } - - g_shader_cache->RetrieveAsyncShaders(); - g_vertex_manager->OnEndFrame(); - - // We invalidate the pipeline object at the start of the frame. - // This is for the rare case where only a single pipeline configuration is used, - // and hybrid ubershaders have compiled the specialized shader, but without any - // state changes the specialized shader will not take over. - g_vertex_manager->InvalidatePipelineObject(); - - if (m_force_reload_textures.TestAndClear()) - { - g_texture_cache->ForceReload(); - } - else - { - // Flush any outstanding EFB copies to RAM, in case the game is running at an uncapped frame - // rate and not waiting for vblank. Otherwise, we'd end up with a huge list of pending - // copies. - g_texture_cache->FlushEFBCopies(); - } - - if (!is_duplicate_frame) - { - // Remove stale EFB/XFB copies. - g_texture_cache->Cleanup(m_frame_count); - const double last_speed_denominator = g_perf_metrics.GetLastSpeedDenominator(); - // The denominator should always be > 0 but if it's not, just return 1 - const double last_speed = - last_speed_denominator > 0.0 ? (1.0 / last_speed_denominator) : 1.0; - Core::Callback_FramePresented(last_speed); - } - - // Handle any config changes, this gets propagated to the backend. - CheckForConfigChanges(); - g_Config.iSaveTargetId = 0; - } - else - { - g_gfx->Flush(); - } - // Update our last xfb values m_last_xfb_addr = xfb_addr; m_last_xfb_ticks = ticks; @@ -492,10 +373,6 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6 m_last_xfb_stride = fb_stride; m_last_xfb_height = fb_height; } - else - { - g_gfx->Flush(); - } } bool Renderer::UseVertexDepthRange() const diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index ea38b71b98..520acdb8e4 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -26,6 +26,7 @@ #include "Common/Flag.h" #include "Common/MathUtil.h" #include "VideoCommon/RenderState.h" +#include "VideoCommon/VideoEvents.h" class AbstractFramebuffer; class AbstractPipeline; @@ -86,8 +87,6 @@ public: void ClearScreen(const MathUtil::Rectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z); virtual void ReinterpretPixelData(EFBReinterpretType convtype); - void RenderToXFB(u32 xfbAddr, const MathUtil::Rectangle& sourceRc, u32 fbStride, - u32 fbHeight, float Gamma = 1.0f); virtual u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data); virtual void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points); @@ -95,7 +94,6 @@ public: // Finish up the current frame, print some stats void Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks); - void UpdateWidescreenHeuristic(); bool IsGameWidescreen() const { return m_is_game_widescreen; } PixelFormat GetPrevPixelFormat() const { return m_prev_efb_format; } @@ -104,16 +102,17 @@ public: bool UseVertexDepthRange() const; void DoState(PointerWrap& p); - // Will forcibly reload all textures on the next swap - void ForceReloadTextures(); - bool CalculateTargetSize(); -protected: - std::tuple CalculateTargetScale(int x, int y) const; + int m_frame_count = 0; - void CheckFifoRecording(); - void RecordVideoMemory(); + void OnConfigChanged(u32 bits); + +protected: + void UpdateWidescreen(); + void UpdateWidescreenHeuristic(); + + std::tuple CalculateTargetScale(int x, int y) const; bool m_is_game_widescreen = false; bool m_was_orthographically_anamorphic = false; @@ -122,8 +121,6 @@ protected: int m_target_width = 1; int m_target_height = 1; - int m_frame_count = 0; - private: PixelFormat m_prev_efb_format; unsigned int m_efb_scale = 1; @@ -134,7 +131,8 @@ private: u32 m_last_xfb_stride = 0; u32 m_last_xfb_height = 0; - Common::Flag m_force_reload_textures; + EventHook m_update_widescreen_handle; + EventHook m_config_changed_handle; }; extern std::unique_ptr g_renderer; diff --git a/Source/Core/VideoCommon/ShaderCache.cpp b/Source/Core/VideoCommon/ShaderCache.cpp index 1e46f3527b..cc6e9fb86a 100644 --- a/Source/Core/VideoCommon/ShaderCache.cpp +++ b/Source/Core/VideoCommon/ShaderCache.cpp @@ -46,6 +46,7 @@ bool ShaderCache::Initialize() return false; m_async_shader_compiler = g_gfx->CreateAsyncShaderCompiler(); + m_frame_end_handler = AfterFrameEvent::Register([this] { RetrieveAsyncShaders(); }, "RetreiveAsyncShaders"); return true; } diff --git a/Source/Core/VideoCommon/ShaderCache.h b/Source/Core/VideoCommon/ShaderCache.h index 2721cd7319..f0982b3104 100644 --- a/Source/Core/VideoCommon/ShaderCache.h +++ b/Source/Core/VideoCommon/ShaderCache.h @@ -30,6 +30,7 @@ #include "VideoCommon/UberShaderPixel.h" #include "VideoCommon/UberShaderVertex.h" #include "VideoCommon/VertexShaderGen.h" +#include "VideoCommon/VideoEvents.h" class NativeVertexFormat; enum class AbstractTextureFormat : u32; @@ -250,6 +251,8 @@ private: // Texture decoding shaders std::map, std::unique_ptr> m_texture_decoding_shaders; + + EventHook m_frame_end_handler; }; } // namespace VideoCommon diff --git a/Source/Core/VideoCommon/Statistics.cpp b/Source/Core/VideoCommon/Statistics.cpp index a6b7eb8167..a1ab6122ab 100644 --- a/Source/Core/VideoCommon/Statistics.cpp +++ b/Source/Core/VideoCommon/Statistics.cpp @@ -8,11 +8,28 @@ #include +#include "Core/DolphinAnalytics.h" +#include "Core/HW/SystemTimers.h" + #include "VideoCommon/BPFunctions.h" #include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoConfig.h" +#include "VideoCommon/VideoEvents.h" Statistics g_stats; + +static EventHook s_before_frame_event = BeforeFrameEvent::Register([] { + g_stats.ResetFrame(); +}, "Statistics::ResetFrame"); + +static EventHook s_after_frame_event = AfterFrameEvent::Register([] { + DolphinAnalytics::PerformanceSample perf_sample; + perf_sample.speed_ratio = SystemTimers::GetEstimatedEmulationPerformance(); + perf_sample.num_prims = g_stats.this_frame.num_prims + g_stats.this_frame.num_dl_prims; + perf_sample.num_draw_calls = g_stats.this_frame.num_draw_calls; + DolphinAnalytics::Instance().ReportPerformanceInfo(std::move(perf_sample)); +}, "Statistics::PerformanceSample"); + static bool clear_scissors; void Statistics::ResetFrame() diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index 21b58d931e..6029f09c5c 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -769,6 +769,23 @@ void TextureCacheBase::DoLoadState(PointerWrap& p) } } +void TextureCacheBase::OnFrameEnd() +{ + if (m_force_reload_textures.TestAndClear()) + { + ForceReload(); + } + else + { + // Flush any outstanding EFB copies to RAM, in case the game is running at an uncapped frame + // rate and not waiting for vblank. Otherwise, we'd end up with a huge list of pending + // copies. + g_texture_cache->FlushEFBCopies(); + } + + g_texture_cache->Cleanup(g_renderer->m_frame_count); +} + void TCacheEntry::DoState(PointerWrap& p) { p.Do(addr); diff --git a/Source/Core/VideoCommon/TextureCacheBase.h b/Source/Core/VideoCommon/TextureCacheBase.h index 8b9010a50e..c38f2578b9 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.h +++ b/Source/Core/VideoCommon/TextureCacheBase.h @@ -18,12 +18,15 @@ #include "Common/BitSet.h" #include "Common/CommonTypes.h" +#include "Common/Flag.h" #include "Common/MathUtil.h" + #include "VideoCommon/AbstractTexture.h" #include "VideoCommon/BPMemory.h" #include "VideoCommon/TextureConfig.h" #include "VideoCommon/TextureDecoder.h" #include "VideoCommon/TextureInfo.h" +#include "VideoCommon/VideoEvents.h" class AbstractFramebuffer; class AbstractStagingTexture; @@ -288,6 +291,9 @@ public: static bool AllCopyFilterCoefsNeeded(const std::array& coefficients); static bool CopyFilterCanOverflow(const std::array& coefficients); + // Will forcibly reload all textures when the frame next ends + void ForceReloadTextures() { m_force_reload_textures.Set(); } + protected: // Decodes the specified data to the GPU texture specified by entry. // Returns false if the configuration is not supported. @@ -428,6 +434,11 @@ private: // We store this in the class so that the same staging texture can be used for multiple // readbacks, saving the overhead of allocating a new buffer every time. std::unique_ptr m_readback_texture; + + void OnFrameEnd(); + + Common::Flag m_force_reload_textures; + EventHook m_frame_event = AfterFrameEvent::Register([this] { OnFrameEnd(); }, "TextureCache"); }; extern std::unique_ptr g_texture_cache; diff --git a/Source/Core/VideoCommon/VertexManagerBase.cpp b/Source/Core/VideoCommon/VertexManagerBase.cpp index 34fe374d25..895ddb1372 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.cpp +++ b/Source/Core/VideoCommon/VertexManagerBase.cpp @@ -105,6 +105,7 @@ VertexManagerBase::~VertexManagerBase() = default; bool VertexManagerBase::Initialize() { + m_frame_end_event = AfterFrameEvent::Register([this] { OnEndFrame();}, "VertexManagerBase"); m_index_generator.Init(); m_cpu_cull.Init(); return true; @@ -996,4 +997,10 @@ void VertexManagerBase::OnEndFrame() #endif m_cpu_accesses_this_frame.clear(); + + // We invalidate the pipeline object at the start of the frame. + // This is for the rare case where only a single pipeline configuration is used, + // and hybrid ubershaders have compiled the specialized shader, but without any + // state changes the specialized shader will not take over. + InvalidatePipelineObject(); } diff --git a/Source/Core/VideoCommon/VertexManagerBase.h b/Source/Core/VideoCommon/VertexManagerBase.h index b2cb8b54bd..18e0f1d402 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.h +++ b/Source/Core/VideoCommon/VertexManagerBase.h @@ -229,6 +229,8 @@ private: std::vector m_cpu_accesses_this_frame; std::vector m_scheduled_command_buffer_kicks; bool m_allow_background_execution = true; + + EventHook m_frame_end_event; }; extern std::unique_ptr g_vertex_manager; diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp index 8b8a73e6cf..86cfc97ade 100644 --- a/Source/Core/VideoCommon/VideoConfig.cpp +++ b/Source/Core/VideoCommon/VideoConfig.cpp @@ -341,3 +341,8 @@ void CheckForConfigChanges() BPFunctions::SetScissorAndViewport(); } } + +static EventHook s_check_config_event = AfterFrameEvent::Register([] { + CheckForConfigChanges(); + +}, "CheckForConfigChanges");