mirror of
https://github.com/libretro/pcsx2.git
synced 2024-11-27 03:10:31 +00:00
GS: Rework texture pooling behavior
- Split into texture/target pools. - Keep textures around when they're used recently regardless of size (saves work in the backend/driver). - Don't boot textures out of the pool when it's an idle frame.
This commit is contained in:
parent
a4e99366fb
commit
0ab6eb6587
@ -516,7 +516,7 @@ void GSvsync(u32 field, bool registers_written)
|
||||
{
|
||||
try
|
||||
{
|
||||
g_gs_renderer->VSync(field, registers_written);
|
||||
g_gs_renderer->VSync(field, registers_written, g_gs_renderer->IsIdleFrame());
|
||||
}
|
||||
catch (GSRecoverableError)
|
||||
{
|
||||
|
@ -91,7 +91,7 @@ GSDevice::GSDevice() = default;
|
||||
GSDevice::~GSDevice()
|
||||
{
|
||||
// should've been cleaned up in Destroy()
|
||||
pxAssert(m_pool.empty() && !m_merge && !m_weavebob && !m_blend && !m_mad && !m_target_tmp && !m_cas);
|
||||
pxAssert(m_pool[0].empty() && m_pool[1].empty() && !m_merge && !m_weavebob && !m_blend && !m_mad && !m_target_tmp && !m_cas);
|
||||
}
|
||||
|
||||
const char* GSDevice::RenderAPIToString(RenderAPI api)
|
||||
@ -248,11 +248,12 @@ GSTexture* GSDevice::FetchSurface(GSTexture::Type type, int width, int height, i
|
||||
{
|
||||
const GSVector2i size(width, height);
|
||||
const bool prefer_new_texture = (m_features.prefer_new_textures && type == GSTexture::Type::Texture && !prefer_reuse);
|
||||
FastList<GSTexture*>& pool = m_pool[type != GSTexture::Type::Texture];
|
||||
|
||||
GSTexture* t = nullptr;
|
||||
auto fallback = m_pool.end();
|
||||
auto fallback = pool.end();
|
||||
|
||||
for (auto i = m_pool.begin(); i != m_pool.end(); ++i)
|
||||
for (auto i = pool.begin(); i != pool.end(); ++i)
|
||||
{
|
||||
t = *i;
|
||||
|
||||
@ -263,10 +264,10 @@ GSTexture* GSDevice::FetchSurface(GSTexture::Type type, int width, int height, i
|
||||
if (!prefer_new_texture || t->GetLastFrameUsed() != m_frame)
|
||||
{
|
||||
m_pool_memory_usage -= t->GetMemUsage();
|
||||
m_pool.erase(i);
|
||||
pool.erase(i);
|
||||
break;
|
||||
}
|
||||
else if (fallback == m_pool.end())
|
||||
else if (fallback == pool.end())
|
||||
{
|
||||
fallback = i;
|
||||
}
|
||||
@ -277,11 +278,12 @@ GSTexture* GSDevice::FetchSurface(GSTexture::Type type, int width, int height, i
|
||||
|
||||
if (!t)
|
||||
{
|
||||
if (m_pool.size() >= MAX_POOLED_TEXTURES && fallback != m_pool.end())
|
||||
if (pool.size() >= ((type == GSTexture::Type::Texture) ? MAX_POOLED_TEXTURES : MAX_POOLED_TARGETS) &&
|
||||
fallback != pool.end())
|
||||
{
|
||||
t = *fallback;
|
||||
m_pool_memory_usage -= t->GetMemUsage();
|
||||
m_pool.erase(fallback);
|
||||
pool.erase(fallback);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -323,17 +325,24 @@ void GSDevice::Recycle(GSTexture* t)
|
||||
|
||||
t->SetLastFrameUsed(m_frame);
|
||||
|
||||
m_pool.push_front(t);
|
||||
FastList<GSTexture*>& pool = m_pool[!t->IsTexture()];
|
||||
pool.push_front(t);
|
||||
m_pool_memory_usage += t->GetMemUsage();
|
||||
|
||||
//printf("%d\n",m_pool.size());
|
||||
|
||||
while (m_pool.size() > MAX_POOLED_TEXTURES)
|
||||
const u32 max_size = t->IsTexture() ? MAX_POOLED_TEXTURES : MAX_POOLED_TARGETS;
|
||||
const u32 max_age = t->IsTexture() ? MAX_TEXTURE_AGE : MAX_TARGET_AGE;
|
||||
while (pool.size() > max_size)
|
||||
{
|
||||
m_pool_memory_usage -= m_pool.back()->GetMemUsage();
|
||||
delete m_pool.back();
|
||||
// Don't toss when the texture was last used in this frame.
|
||||
// Because we're going to need to keep it alive anyway.
|
||||
GSTexture* back = pool.back();
|
||||
if ((m_frame - back->GetLastFrameUsed()) < max_age)
|
||||
break;
|
||||
|
||||
m_pool.pop_back();
|
||||
m_pool_memory_usage -= back->GetMemUsage();
|
||||
delete back;
|
||||
|
||||
pool.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
@ -347,20 +356,33 @@ void GSDevice::AgePool()
|
||||
{
|
||||
m_frame++;
|
||||
|
||||
while (m_pool.size() > 40 && m_frame - m_pool.back()->GetLastFrameUsed() > 10)
|
||||
// Toss out textures when they're not too-recently used.
|
||||
for (u32 pool_idx = 0; pool_idx < m_pool.size(); pool_idx++)
|
||||
{
|
||||
m_pool_memory_usage -= m_pool.back()->GetMemUsage();
|
||||
delete m_pool.back();
|
||||
const u32 max_age = (pool_idx == 0) ? MAX_TEXTURE_AGE : MAX_TARGET_AGE;
|
||||
FastList<GSTexture*>& pool = m_pool[pool_idx];
|
||||
while (!pool.empty())
|
||||
{
|
||||
GSTexture* back = pool.back();
|
||||
if ((m_frame - back->GetLastFrameUsed()) < max_age)
|
||||
break;
|
||||
|
||||
m_pool.pop_back();
|
||||
m_pool_memory_usage -= back->GetMemUsage();
|
||||
delete back;
|
||||
|
||||
pool.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GSDevice::PurgePool()
|
||||
{
|
||||
for (auto t : m_pool)
|
||||
delete t;
|
||||
m_pool.clear();
|
||||
for (FastList<GSTexture*>& pool : m_pool)
|
||||
{
|
||||
for (GSTexture* t : pool)
|
||||
delete t;
|
||||
pool.clear();
|
||||
}
|
||||
m_pool_memory_usage = 0;
|
||||
}
|
||||
|
||||
|
@ -738,18 +738,21 @@ public:
|
||||
// clang-format on
|
||||
|
||||
private:
|
||||
FastList<GSTexture*> m_pool;
|
||||
std::array<FastList<GSTexture*>, 2> m_pool; // [texture, target]
|
||||
u64 m_pool_memory_usage = 0;
|
||||
|
||||
static const std::array<HWBlend, 3*3*3*3> m_blendMap;
|
||||
static const std::array<u8, 16> m_replaceDualSrcBlendMap;
|
||||
|
||||
protected:
|
||||
static constexpr int NUM_INTERLACE_SHADERS = 5;
|
||||
static constexpr int NUM_INTERLACE_SHADERS = 5;
|
||||
static constexpr float MAD_SENSITIVITY = 0.08f;
|
||||
static constexpr u32 MAX_POOLED_TEXTURES = 300;
|
||||
static constexpr u32 NUM_CAS_CONSTANTS = 12; // 8 plus src offset x/y, 16 byte alignment
|
||||
static constexpr u32 EXPAND_BUFFER_SIZE = sizeof(u16) * 65532 * 6;
|
||||
static constexpr u32 MAX_POOLED_TARGETS = 300;
|
||||
static constexpr u32 MAX_TARGET_AGE = 20;
|
||||
static constexpr u32 MAX_POOLED_TEXTURES = 300;
|
||||
static constexpr u32 MAX_TEXTURE_AGE = 10;
|
||||
static constexpr u32 NUM_CAS_CONSTANTS = 12; // 8 plus src offset x/y, 16 byte alignment
|
||||
static constexpr u32 EXPAND_BUFFER_SIZE = sizeof(u16) * 65532 * 6;
|
||||
|
||||
WindowInfo m_window_info;
|
||||
VsyncMode m_vsync_mode = VsyncMode::Off;
|
||||
@ -806,7 +809,6 @@ public:
|
||||
/// Generates a fixed index buffer for expanding points and sprites. Buffer is assumed to be at least EXPAND_BUFFER_SIZE in size.
|
||||
static void GenerateExpansionIndexBuffer(void* buffer);
|
||||
|
||||
__fi unsigned int GetFrameNumber() const { return m_frame; }
|
||||
__fi u64 GetPoolMemoryUsage() const { return m_pool_memory_usage; }
|
||||
|
||||
__fi FeatureSupport Features() const { return m_features; }
|
||||
|
@ -527,7 +527,7 @@ void GSRenderer::EndPresentFrame()
|
||||
ImGuiManager::NewFrame();
|
||||
}
|
||||
|
||||
void GSRenderer::VSync(u32 field, bool registers_written)
|
||||
void GSRenderer::VSync(u32 field, bool registers_written, bool idle_frame)
|
||||
{
|
||||
Flush(GSFlushReason::VSYNC);
|
||||
|
||||
@ -569,6 +569,9 @@ void GSRenderer::VSync(u32 field, bool registers_written)
|
||||
|
||||
const bool blank_frame = !Merge(field);
|
||||
|
||||
m_last_draw_n = s_n;
|
||||
m_last_transfer_n = s_transfer_n;
|
||||
|
||||
if (skip_frame)
|
||||
{
|
||||
if (BeginPresentFrame(true))
|
||||
@ -578,7 +581,8 @@ void GSRenderer::VSync(u32 field, bool registers_written)
|
||||
return;
|
||||
}
|
||||
|
||||
g_gs_device->AgePool();
|
||||
if (!idle_frame)
|
||||
g_gs_device->AgePool();
|
||||
|
||||
g_perfmon.EndFrame();
|
||||
if ((g_perfmon.GetFrame() & 0x1f) == 0)
|
||||
@ -898,6 +902,11 @@ GSTexture* GSRenderer::LookupPaletteSource(u32 CBP, u32 CPSM, u32 CBW, GSVector2
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool GSRenderer::IsIdleFrame() const
|
||||
{
|
||||
return (m_last_draw_n == s_n && m_last_transfer_n == s_transfer_n);
|
||||
}
|
||||
|
||||
bool GSRenderer::SaveSnapshotToMemory(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders,
|
||||
u32* width, u32* height, std::vector<u32>* pixels)
|
||||
{
|
||||
|
@ -32,6 +32,10 @@ private:
|
||||
u32 m_dump_frames = 0;
|
||||
u32 m_skipped_duplicate_frames = 0;
|
||||
|
||||
// Tracking draw counters for idle frame detection.
|
||||
int m_last_draw_n = 0;
|
||||
int m_last_transfer_n = 0;
|
||||
|
||||
protected:
|
||||
GSVector2i m_real_size{0, 0};
|
||||
bool m_texture_shuffle = false;
|
||||
@ -48,7 +52,7 @@ public:
|
||||
|
||||
virtual void Destroy();
|
||||
|
||||
virtual void VSync(u32 field, bool registers_written);
|
||||
virtual void VSync(u32 field, bool registers_written, bool idle_frame);
|
||||
virtual bool CanUpscale() { return false; }
|
||||
virtual float GetUpscaleMultiplier() { return 1.0f; }
|
||||
virtual float GetTextureScaleFactor() { return 1.0f; }
|
||||
@ -57,6 +61,8 @@ public:
|
||||
|
||||
virtual GSTexture* LookupPaletteSource(u32 CBP, u32 CPSM, u32 CBW, GSVector2i& offset, float* scale, const GSVector2i& size);
|
||||
|
||||
bool IsIdleFrame() const;
|
||||
|
||||
bool SaveSnapshotToMemory(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders,
|
||||
u32* width, u32* height, std::vector<u32>* pixels);
|
||||
|
||||
|
@ -126,6 +126,10 @@ public:
|
||||
{
|
||||
return (m_type == Type::DepthStencil);
|
||||
}
|
||||
__fi bool IsTexture() const
|
||||
{
|
||||
return (m_type == Type::Texture);
|
||||
}
|
||||
|
||||
__fi State GetState() const { return m_state; }
|
||||
__fi void SetState(State state) { m_state = state; }
|
||||
|
@ -112,7 +112,7 @@ void GSRendererHW::UpdateSettings(const Pcsx2Config::GSOptions& old_config)
|
||||
SetTCOffset();
|
||||
}
|
||||
|
||||
void GSRendererHW::VSync(u32 field, bool registers_written)
|
||||
void GSRendererHW::VSync(u32 field, bool registers_written, bool idle_frame)
|
||||
{
|
||||
if (m_force_preload > 0)
|
||||
{
|
||||
@ -138,7 +138,7 @@ void GSRendererHW::VSync(u32 field, bool registers_written)
|
||||
|
||||
// Don't age the texture cache when no draws or EE writes have occurred.
|
||||
// Xenosaga needs its targets kept around while it's loading, because it uses them for a fade transition.
|
||||
if (m_last_draw_n == s_n && m_last_transfer_n == s_transfer_n)
|
||||
if (idle_frame)
|
||||
{
|
||||
GL_INS("No draws or transfers, not aging TC");
|
||||
}
|
||||
@ -147,10 +147,7 @@ void GSRendererHW::VSync(u32 field, bool registers_written)
|
||||
g_texture_cache->IncAge();
|
||||
}
|
||||
|
||||
m_last_draw_n = s_n + 1; // +1 for vsync
|
||||
m_last_transfer_n = s_transfer_n;
|
||||
|
||||
GSRenderer::VSync(field, registers_written);
|
||||
GSRenderer::VSync(field, registers_written, idle_frame);
|
||||
|
||||
if (g_texture_cache->GetHashCacheMemoryUsage() > 1024 * 1024 * 1024)
|
||||
{
|
||||
|
@ -149,10 +149,6 @@ private:
|
||||
std::unique_ptr<GSTextureCacheSW::Texture> m_sw_texture[7 + 1];
|
||||
std::unique_ptr<GSVirtualAlignedClass<32>> m_sw_rasterizer;
|
||||
|
||||
// Tracking draw counters for idle frame detection.
|
||||
int m_last_draw_n = 0;
|
||||
int m_last_transfer_n = 0;
|
||||
|
||||
public:
|
||||
GSRendererHW();
|
||||
virtual ~GSRendererHW() override;
|
||||
@ -178,7 +174,7 @@ public:
|
||||
|
||||
void Reset(bool hardware_reset) override;
|
||||
void UpdateSettings(const Pcsx2Config::GSOptions& old_config) override;
|
||||
void VSync(u32 field, bool registers_written) override;
|
||||
void VSync(u32 field, bool registers_written, bool idle_frame) override;
|
||||
|
||||
GSTexture* GetOutput(int i, float& scale, int& y_offset) override;
|
||||
GSTexture* GetFeedbackOutput(float& scale) override;
|
||||
|
@ -78,7 +78,7 @@ void GSRendererSW::Destroy()
|
||||
m_output = nullptr;
|
||||
}
|
||||
|
||||
void GSRendererSW::VSync(u32 field, bool registers_written)
|
||||
void GSRendererSW::VSync(u32 field, bool registers_written, bool idle_frame)
|
||||
{
|
||||
Sync(0); // IncAge might delete a cached texture in use
|
||||
|
||||
@ -99,7 +99,7 @@ void GSRendererSW::VSync(u32 field, bool registers_written)
|
||||
//
|
||||
*/
|
||||
|
||||
GSRenderer::VSync(field, registers_written);
|
||||
GSRenderer::VSync(field, registers_written, idle_frame);
|
||||
|
||||
m_tc->IncAge();
|
||||
|
||||
|
@ -73,7 +73,7 @@ protected:
|
||||
GSVector4i m_dimx[8] = {};
|
||||
|
||||
void Reset(bool hardware_reset) override;
|
||||
void VSync(u32 field, bool registers_written) override;
|
||||
void VSync(u32 field, bool registers_written, bool idle_frame) override;
|
||||
GSTexture* GetOutput(int i, float& scale, int& y_offset) override;
|
||||
GSTexture* GetFeedbackOutput(float& scale) override;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user