Split out everying remaining from Swap

This commit is contained in:
Scott Mansell 2023-01-31 00:46:10 +13:00
parent 2a18b34a73
commit 0da69055d9
21 changed files with 182 additions and 170 deletions

View File

@ -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<BootParameters> 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;

View File

@ -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<const u32*>(&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<const u32*>(&xfmem);
const u32* xfregs_ptr = reinterpret_cast<const u32*>(&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<FifoRecordAnalyzer>(this, cpMem);

View File

@ -9,6 +9,7 @@
#include <vector>
#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<u8> m_FifoData;
std::vector<u8> m_Ram;
std::vector<u8> m_ExRam;
EventHook m_end_of_frame_event;
};

View File

@ -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

View File

@ -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()

View File

@ -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<FrameDumper> g_frame_dumper;

View File

@ -79,6 +79,8 @@ bool FramebufferManager::Initialize()
return false;
}
m_end_of_frame_event = AfterFrameEvent::Register([this] { EndOfFrame(); }, "FramebufferManager");
return true;
}

View File

@ -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<AbstractPipeline> m_depth_poke_pipeline;
std::vector<EFBPokeVertex> m_color_poke_vertices;
std::vector<EFBPokeVertex> m_depth_poke_vertices;
EventHook m_end_of_frame_event;
};
extern std::unique_ptr<FramebufferManager> g_framebuffer_manager;

View File

@ -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;

View File

@ -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<FBInfo, std::vector<GraphicsModAction*>, FBInfoHasher> m_xfb_target_to_actions;
std::unordered_set<std::string> m_groups;
EventHook m_end_of_frame_event;
};
extern std::unique_ptr<GraphicsModManager> g_graphics_mod_manager;

View File

@ -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

View File

@ -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<int>& 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<int> 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<const u32*>(&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<const u32*>(&xfmem);
const u32* xfregs_ptr = reinterpret_cast<const u32*>(&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<int> 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

View File

@ -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<int>& rc, bool colorEnable, bool alphaEnable,
bool zEnable, u32 color, u32 z);
virtual void ReinterpretPixelData(EFBReinterpretType convtype);
void RenderToXFB(u32 xfbAddr, const MathUtil::Rectangle<int>& 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<int, int> 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<int, int> 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<Renderer> g_renderer;

View File

@ -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;
}

View File

@ -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::pair<u32, u32>, std::unique_ptr<AbstractShader>> m_texture_decoding_shaders;
EventHook m_frame_end_handler;
};
} // namespace VideoCommon

View File

@ -8,11 +8,28 @@
#include <imgui.h>
#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()

View File

@ -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);

View File

@ -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<u32, 3>& coefficients);
static bool CopyFilterCanOverflow(const std::array<u32, 3>& 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<AbstractStagingTexture> m_readback_texture;
void OnFrameEnd();
Common::Flag m_force_reload_textures;
EventHook m_frame_event = AfterFrameEvent::Register([this] { OnFrameEnd(); }, "TextureCache");
};
extern std::unique_ptr<TextureCacheBase> g_texture_cache;

View File

@ -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();
}

View File

@ -229,6 +229,8 @@ private:
std::vector<u32> m_cpu_accesses_this_frame;
std::vector<u32> m_scheduled_command_buffer_kicks;
bool m_allow_background_execution = true;
EventHook m_frame_end_event;
};
extern std::unique_ptr<VertexManagerBase> g_vertex_manager;

View File

@ -341,3 +341,8 @@ void CheckForConfigChanges()
BPFunctions::SetScissorAndViewport();
}
}
static EventHook s_check_config_event = AfterFrameEvent::Register([] {
CheckForConfigChanges();
}, "CheckForConfigChanges");