GS: Make renderer a global

This commit is contained in:
Connor McLaughlin 2022-04-23 14:08:26 +10:00 committed by refractionpcsx2
parent 568ebc6199
commit 6e84613a14
12 changed files with 190 additions and 197 deletions

View File

@ -73,7 +73,6 @@ static HRESULT s_hr = E_FAIL;
Pcsx2Config::GSOptions GSConfig; Pcsx2Config::GSOptions GSConfig;
static std::unique_ptr<GSRenderer> s_gs;
static HostDisplay::RenderAPI s_render_api; static HostDisplay::RenderAPI s_render_api;
int GSinit() int GSinit()
@ -118,10 +117,10 @@ void GSinitConfig()
void GSshutdown() void GSshutdown()
{ {
if (s_gs) if (g_gs_renderer)
{ {
s_gs->Destroy(); g_gs_renderer->Destroy();
s_gs.reset(); g_gs_renderer.reset();
} }
if (g_gs_device) if (g_gs_device)
{ {
@ -143,10 +142,10 @@ void GSshutdown()
void GSclose() void GSclose()
{ {
if (s_gs) if (g_gs_renderer)
{ {
s_gs->Destroy(); g_gs_renderer->Destroy();
s_gs.reset(); g_gs_renderer.reset();
} }
if (g_gs_device) if (g_gs_device)
{ {
@ -257,28 +256,28 @@ static bool DoGSOpen(GSRendererType renderer, u8* basemem)
if (renderer == GSRendererType::Null) if (renderer == GSRendererType::Null)
{ {
s_gs = std::make_unique<GSRendererNull>(); g_gs_renderer = std::make_unique<GSRendererNull>();
} }
else if (renderer != GSRendererType::SW) else if (renderer != GSRendererType::SW)
{ {
s_gs = std::make_unique<GSRendererNew>(); g_gs_renderer = std::make_unique<GSRendererNew>();
} }
else else
{ {
const int threads = theApp.GetConfigI("extrathreads"); const int threads = theApp.GetConfigI("extrathreads");
s_gs = std::make_unique<GSRendererSW>(threads); g_gs_renderer = std::make_unique<GSRendererSW>(threads);
} }
} }
catch (std::exception& ex) catch (std::exception& ex)
{ {
Host::ReportFormattedErrorAsync("GS", "GS error: Exception caught in GSopen: %s", ex.what()); Host::ReportFormattedErrorAsync("GS", "GS error: Exception caught in GSopen: %s", ex.what());
s_gs.reset(); g_gs_renderer.reset();
g_gs_device->Destroy(); g_gs_device->Destroy();
g_gs_device.reset(); g_gs_device.reset();
return false; return false;
} }
s_gs->SetRegsMem(basemem); g_gs_renderer->SetRegsMem(basemem);
display->SetVSync(EmuConfig.GetEffectiveVsyncMode()); display->SetVSync(EmuConfig.GetEffectiveVsyncMode());
display->SetGPUTimingEnabled(GSConfig.OsdShowGPU); display->SetGPUTimingEnabled(GSConfig.OsdShowGPU);
@ -289,10 +288,10 @@ bool GSreopen(bool recreate_display)
{ {
Console.WriteLn("Reopening GS with %s display", recreate_display ? "new" : "existing"); Console.WriteLn("Reopening GS with %s display", recreate_display ? "new" : "existing");
s_gs->Flush(); g_gs_renderer->Flush();
freezeData fd = {}; freezeData fd = {};
if (s_gs->Freeze(&fd, true) != 0) if (g_gs_renderer->Freeze(&fd, true) != 0)
{ {
Console.Error("(GSreopen) Failed to get GS freeze size"); Console.Error("(GSreopen) Failed to get GS freeze size");
return false; return false;
@ -300,7 +299,7 @@ bool GSreopen(bool recreate_display)
std::unique_ptr<u8[]> fd_data = std::make_unique<u8[]>(fd.size); std::unique_ptr<u8[]> fd_data = std::make_unique<u8[]>(fd.size);
fd.data = fd_data.get(); fd.data = fd_data.get();
if (s_gs->Freeze(&fd, false) != 0) if (g_gs_renderer->Freeze(&fd, false) != 0)
{ {
Console.Error("(GSreopen) Failed to freeze GS"); Console.Error("(GSreopen) Failed to freeze GS");
return false; return false;
@ -313,11 +312,11 @@ bool GSreopen(bool recreate_display)
Host::EndPresentFrame(); Host::EndPresentFrame();
} }
u8* basemem = s_gs->GetRegsMem(); u8* basemem = g_gs_renderer->GetRegsMem();
const u32 gamecrc = s_gs->GetGameCRC(); const u32 gamecrc = g_gs_renderer->GetGameCRC();
const int gamecrc_options = s_gs->GetGameCRCOptions(); const int gamecrc_options = g_gs_renderer->GetGameCRCOptions();
s_gs->Destroy(); g_gs_renderer->Destroy();
s_gs.reset(); g_gs_renderer.reset();
g_gs_device->Destroy(); g_gs_device->Destroy();
g_gs_device.reset(); g_gs_device.reset();
@ -337,13 +336,13 @@ bool GSreopen(bool recreate_display)
return false; return false;
} }
if (s_gs->Defrost(&fd) != 0) if (g_gs_renderer->Defrost(&fd) != 0)
{ {
pxFailRel("(GSreopen) Failed to defrost"); pxFailRel("(GSreopen) Failed to defrost");
return false; return false;
} }
s_gs->SetGameCRC(gamecrc, gamecrc_options); g_gs_renderer->SetGameCRC(gamecrc, gamecrc_options);
return true; return true;
} }
@ -374,7 +373,7 @@ void GSreset()
{ {
try try
{ {
s_gs->Reset(); g_gs_renderer->Reset();
} }
catch (GSRecoverableError) catch (GSRecoverableError)
{ {
@ -385,7 +384,7 @@ void GSgifSoftReset(u32 mask)
{ {
try try
{ {
s_gs->SoftReset(mask); g_gs_renderer->SoftReset(mask);
} }
catch (GSRecoverableError) catch (GSRecoverableError)
{ {
@ -396,7 +395,7 @@ void GSwriteCSR(u32 csr)
{ {
try try
{ {
s_gs->WriteCSR(csr); g_gs_renderer->WriteCSR(csr);
} }
catch (GSRecoverableError) catch (GSRecoverableError)
{ {
@ -408,8 +407,8 @@ void GSInitAndReadFIFO(u8* mem, u32 size)
GL_PERF("Init and read FIFO %u qwc", size); GL_PERF("Init and read FIFO %u qwc", size);
try try
{ {
s_gs->InitReadFIFO(mem, size); g_gs_renderer->InitReadFIFO(mem, size);
s_gs->ReadFIFO(mem, size); g_gs_renderer->ReadFIFO(mem, size);
} }
catch (GSRecoverableError) catch (GSRecoverableError)
{ {
@ -424,7 +423,7 @@ void GSgifTransfer(const u8* mem, u32 size)
{ {
try try
{ {
s_gs->Transfer<3>(mem, size); g_gs_renderer->Transfer<3>(mem, size);
} }
catch (GSRecoverableError) catch (GSRecoverableError)
{ {
@ -435,7 +434,7 @@ void GSgifTransfer1(u8* mem, u32 addr)
{ {
try try
{ {
s_gs->Transfer<0>(const_cast<u8*>(mem) + addr, (0x4000 - addr) / 16); g_gs_renderer->Transfer<0>(const_cast<u8*>(mem) + addr, (0x4000 - addr) / 16);
} }
catch (GSRecoverableError) catch (GSRecoverableError)
{ {
@ -446,7 +445,7 @@ void GSgifTransfer2(u8* mem, u32 size)
{ {
try try
{ {
s_gs->Transfer<1>(const_cast<u8*>(mem), size); g_gs_renderer->Transfer<1>(const_cast<u8*>(mem), size);
} }
catch (GSRecoverableError) catch (GSRecoverableError)
{ {
@ -457,7 +456,7 @@ void GSgifTransfer3(u8* mem, u32 size)
{ {
try try
{ {
s_gs->Transfer<2>(const_cast<u8*>(mem), size); g_gs_renderer->Transfer<2>(const_cast<u8*>(mem), size);
} }
catch (GSRecoverableError) catch (GSRecoverableError)
{ {
@ -468,7 +467,7 @@ void GSvsync(u32 field, bool registers_written)
{ {
try try
{ {
s_gs->VSync(field, registers_written); g_gs_renderer->VSync(field, registers_written);
} }
catch (GSRecoverableError) catch (GSRecoverableError)
{ {
@ -495,12 +494,12 @@ u32 GSmakeSnapshot(char* path)
std::transform(extension.begin(), extension.end(), extension.begin(), tolower); std::transform(extension.begin(), extension.end(), extension.begin(), tolower);
#endif #endif
if (extension == ".png") if (extension == ".png")
return s_gs->MakeSnapshot(s); return g_gs_renderer->MakeSnapshot(s);
else if (s[s.length() - 1] != DIRECTORY_SEPARATOR) else if (s[s.length() - 1] != DIRECTORY_SEPARATOR)
s = s + DIRECTORY_SEPARATOR; s = s + DIRECTORY_SEPARATOR;
} }
return s_gs->MakeSnapshot(s + "gs"); return g_gs_renderer->MakeSnapshot(s + "gs");
} }
catch (GSRecoverableError) catch (GSRecoverableError)
{ {
@ -514,15 +513,15 @@ int GSfreeze(FreezeAction mode, freezeData* data)
{ {
if (mode == FreezeAction::Save) if (mode == FreezeAction::Save)
{ {
return s_gs->Freeze(data, false); return g_gs_renderer->Freeze(data, false);
} }
else if (mode == FreezeAction::Size) else if (mode == FreezeAction::Size)
{ {
return s_gs->Freeze(data, true); return g_gs_renderer->Freeze(data, true);
} }
else if (mode == FreezeAction::Load) else if (mode == FreezeAction::Load)
{ {
return s_gs->Defrost(data); return g_gs_renderer->Defrost(data);
} }
} }
catch (GSRecoverableError) catch (GSRecoverableError)
@ -538,8 +537,8 @@ void GSkeyEvent(const HostKeyEvent& e)
{ {
try try
{ {
if (s_gs) if (g_gs_renderer)
s_gs->KeyEvent(e); g_gs_renderer->KeyEvent(e);
} }
catch (GSRecoverableError) catch (GSRecoverableError)
{ {
@ -591,7 +590,7 @@ void pt(const char* str)
bool GSsetupRecording(std::string& filename) bool GSsetupRecording(std::string& filename)
{ {
if (s_gs == NULL) if (g_gs_renderer == NULL)
{ {
printf("GS: no s_gs for recording\n"); printf("GS: no s_gs for recording\n");
return false; return false;
@ -604,7 +603,7 @@ bool GSsetupRecording(std::string& filename)
} }
#endif #endif
printf("GS: Recording start command\n"); printf("GS: Recording start command\n");
if (s_gs->BeginCapture(filename)) if (g_gs_renderer->BeginCapture(filename))
{ {
pt(" - Capture started\n"); pt(" - Capture started\n");
return true; return true;
@ -619,30 +618,30 @@ bool GSsetupRecording(std::string& filename)
void GSendRecording() void GSendRecording()
{ {
printf("GS: Recording end command\n"); printf("GS: Recording end command\n");
s_gs->EndCapture(); g_gs_renderer->EndCapture();
pt(" - Capture ended\n"); pt(" - Capture ended\n");
} }
void GSsetGameCRC(u32 crc, int options) void GSsetGameCRC(u32 crc, int options)
{ {
s_gs->SetGameCRC(crc, options); g_gs_renderer->SetGameCRC(crc, options);
} }
void GSsetFrameSkip(int frameskip) void GSsetFrameSkip(int frameskip)
{ {
s_gs->SetFrameSkip(frameskip); g_gs_renderer->SetFrameSkip(frameskip);
} }
GSVideoMode GSgetDisplayMode() GSVideoMode GSgetDisplayMode()
{ {
GSRenderer* gs = s_gs.get(); GSRenderer* gs = g_gs_renderer.get();
return gs->GetVideoMode(); return gs->GetVideoMode();
} }
void GSgetInternalResolution(int* width, int* height) void GSgetInternalResolution(int* width, int* height)
{ {
GSRenderer* gs = s_gs.get(); GSRenderer* gs = g_gs_renderer.get();
if (!gs) if (!gs)
{ {
*width = 0; *width = 0;
@ -684,7 +683,7 @@ void GSgetStats(std::string& info)
{ {
info = format("%s HW | HC: %d MB | %d P | %d D | %d DC | %d B | %d RB | %d TC | %d TU", info = format("%s HW | HC: %d MB | %d P | %d D | %d DC | %d B | %d RB | %d TC | %d TU",
api_name, api_name,
(int)std::ceil(static_cast<GSRendererHW*>(s_gs.get())->GetTextureCache()->GetHashCacheMemoryUsage() / 1048576.0f), (int)std::ceil(GSRendererHW::GetInstance()->GetTextureCache()->GetHashCacheMemoryUsage() / 1048576.0f),
(int)pm.Get(GSPerfMon::Prim), (int)pm.Get(GSPerfMon::Prim),
(int)pm.Get(GSPerfMon::Draw), (int)pm.Get(GSPerfMon::Draw),
(int)std::ceil(pm.Get(GSPerfMon::DrawCalls)), (int)std::ceil(pm.Get(GSPerfMon::DrawCalls)),
@ -731,7 +730,7 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
Pcsx2Config::GSOptions old_config(std::move(GSConfig)); Pcsx2Config::GSOptions old_config(std::move(GSConfig));
GSConfig = new_config; GSConfig = new_config;
GSConfig.Renderer = (GSConfig.Renderer == GSRendererType::Auto) ? GSUtil::GetPreferredRenderer() : GSConfig.Renderer; GSConfig.Renderer = (GSConfig.Renderer == GSRendererType::Auto) ? GSUtil::GetPreferredRenderer() : GSConfig.Renderer;
if (!s_gs) if (!g_gs_renderer)
return; return;
HostDisplay* display = Host::GetHostDisplay(); HostDisplay* display = Host::GetHostDisplay();
@ -793,11 +792,11 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
GSConfig.PointListPalette != old_config.PointListPalette) GSConfig.PointListPalette != old_config.PointListPalette)
{ {
// for automatic mipmaps, we need to reload the crc // for automatic mipmaps, we need to reload the crc
s_gs->SetGameCRC(s_gs->GetGameCRC(), s_gs->GetGameCRCOptions()); g_gs_renderer->SetGameCRC(g_gs_renderer->GetGameCRC(), g_gs_renderer->GetGameCRCOptions());
} }
// renderer-specific options (e.g. auto flush, TC offset) // renderer-specific options (e.g. auto flush, TC offset)
s_gs->UpdateSettings(old_config); g_gs_renderer->UpdateSettings(old_config);
// reload texture cache when trilinear filtering or TC options change // reload texture cache when trilinear filtering or TC options change
if ( if (
@ -813,8 +812,8 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
GSConfig.UserHacks_DisablePartialInvalidation != old_config.UserHacks_DisablePartialInvalidation || GSConfig.UserHacks_DisablePartialInvalidation != old_config.UserHacks_DisablePartialInvalidation ||
GSConfig.UserHacks_TextureInsideRt != old_config.UserHacks_TextureInsideRt) GSConfig.UserHacks_TextureInsideRt != old_config.UserHacks_TextureInsideRt)
{ {
s_gs->PurgeTextureCache(); g_gs_renderer->PurgeTextureCache();
s_gs->PurgePool(); g_gs_renderer->PurgePool();
} }
// clear out the sampler cache when AF options change, since the anisotropy gets baked into them // clear out the sampler cache when AF options change, since the anisotropy gets baked into them
@ -829,7 +828,7 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
if (GSConfig.LoadTextureReplacements != old_config.LoadTextureReplacements || if (GSConfig.LoadTextureReplacements != old_config.LoadTextureReplacements ||
GSConfig.DumpReplaceableTextures != old_config.DumpReplaceableTextures) GSConfig.DumpReplaceableTextures != old_config.DumpReplaceableTextures)
{ {
s_gs->PurgeTextureCache(); g_gs_renderer->PurgeTextureCache();
} }
if (GSConfig.OsdShowGPU != old_config.OsdShowGPU) if (GSConfig.OsdShowGPU != old_config.OsdShowGPU)
@ -844,7 +843,7 @@ void GSSwitchRenderer(GSRendererType new_renderer)
if (new_renderer == GSRendererType::Auto) if (new_renderer == GSRendererType::Auto)
new_renderer = GSUtil::GetPreferredRenderer(); new_renderer = GSUtil::GetPreferredRenderer();
if (!s_gs || GSConfig.Renderer == new_renderer) if (!g_gs_renderer || GSConfig.Renderer == new_renderer)
return; return;
HostDisplay::RenderAPI existing_api = Host::GetHostDisplay()->GetRenderAPI(); HostDisplay::RenderAPI existing_api = Host::GetHostDisplay()->GetRenderAPI();
@ -874,10 +873,10 @@ void GSRestoreAPIState()
bool GSSaveSnapshotToMemory(u32 width, u32 height, std::vector<u32>* pixels) bool GSSaveSnapshotToMemory(u32 width, u32 height, std::vector<u32>* pixels)
{ {
if (!s_gs) if (!g_gs_renderer)
return false; return false;
return s_gs->SaveSnapshotToMemory(width, height, pixels); return g_gs_renderer->SaveSnapshotToMemory(width, height, pixels);
} }
std::string format(const char* fmt, ...) std::string format(const char* fmt, ...)
@ -1613,8 +1612,8 @@ BEGIN_HOTKEY_LIST(g_gs_hotkeys){
GetMTGS().RunOnGSThread([new_level]() { GetMTGS().RunOnGSThread([new_level]() {
GSConfig.HWMipmap = new_level; GSConfig.HWMipmap = new_level;
s_gs->PurgeTextureCache(); g_gs_renderer->PurgeTextureCache();
s_gs->PurgePool(); g_gs_renderer->PurgePool();
}); });
}}, }},
{"CycleInterlaceMode", "Graphics", "Cycle Deinterlace Mode", [](bool pressed) { {"CycleInterlaceMode", "Graphics", "Cycle Deinterlace Mode", [](bool pressed) {

View File

@ -52,6 +52,8 @@ static std::string GetDumpSerial()
} }
#endif #endif
std::unique_ptr<GSRenderer> g_gs_renderer;
GSRenderer::GSRenderer() GSRenderer::GSRenderer()
: m_shift_key(false) : m_shift_key(false)
, m_control_key(false) , m_control_key(false)

View File

@ -60,3 +60,5 @@ public:
bool SaveSnapshotToMemory(u32 width, u32 height, std::vector<u32>* pixels); bool SaveSnapshotToMemory(u32 width, u32 height, std::vector<u32>* pixels);
}; };
extern std::unique_ptr<GSRenderer> g_gs_renderer;

View File

@ -23,7 +23,7 @@ GSRendererHW::GSRendererHW()
: GSRenderer() : GSRenderer()
, m_width(default_rt_size.x) , m_width(default_rt_size.x)
, m_height(default_rt_size.y) , m_height(default_rt_size.y)
, m_tc(new GSTextureCache(this)) , m_tc(new GSTextureCache())
, m_src(nullptr) , m_src(nullptr)
, m_userhacks_tcoffset(false) , m_userhacks_tcoffset(false)
, m_userhacks_tcoffset_x(0) , m_userhacks_tcoffset_x(0)

View File

@ -154,6 +154,7 @@ public:
GSRendererHW(); GSRendererHW();
virtual ~GSRendererHW() override; virtual ~GSRendererHW() override;
__fi static GSRendererHW* GetInstance() { return static_cast<GSRendererHW*>(g_gs_renderer.get()); }
__fi GSTextureCache* GetTextureCache() const { return m_tc; } __fi GSTextureCache* GetTextureCache() const { return m_tc; }
void Destroy() override; void Destroy() override;

View File

@ -29,9 +29,7 @@
u8* GSTextureCache::m_temp; u8* GSTextureCache::m_temp;
GSTextureCache::GSTextureCache(GSRenderer* r) GSTextureCache::GSTextureCache()
: m_renderer(r)
, m_palette_map(r)
{ {
// In theory 4MB is enough but 9MB is safer for overflow (8MB // In theory 4MB is enough but 9MB is safer for overflow (8MB
// isn't enough in custom resolution) // isn't enough in custom resolution)
@ -143,7 +141,7 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0
TEX0.TBP0, psm_str(psm)); TEX0.TBP0, psm_str(psm));
// Create a shared texture source // Create a shared texture source
src = new Source(m_renderer, TEX0, TEXA, true); src = new Source(TEX0, TEXA, true);
src->m_texture = dst->m_texture; src->m_texture = dst->m_texture;
src->m_shared_texture = true; src->m_shared_texture = true;
src->m_target = true; // So renderer can check if a conversion is required src->m_target = true; // So renderer can check if a conversion is required
@ -164,7 +162,7 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0
m_src.m_surfaces.insert(src); m_src.m_surfaces.insert(src);
} }
else if (m_renderer->m_game.title == CRC::SVCChaos) else if (g_gs_renderer->m_game.title == CRC::SVCChaos)
{ {
// SVCChaos black screen on main menu, regardless of depth enabled or disabled. // SVCChaos black screen on main menu, regardless of depth enabled or disabled.
return LookupSource(TEX0, TEXA, r, nullptr); return LookupSource(TEX0, TEXA, r, nullptr);
@ -199,9 +197,9 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
// Until DX is fixed // Until DX is fixed
if (psm_s.pal > 0) if (psm_s.pal > 0)
m_renderer->m_mem.m_clut.Read32(TEX0, TEXA); g_gs_renderer->m_mem.m_clut.Read32(TEX0, TEXA);
const u32* clut = m_renderer->m_mem.m_clut; const u32* clut = g_gs_renderer->m_mem.m_clut;
Source* src = NULL; Source* src = NULL;
@ -417,7 +415,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, int type, bool used, u32 fbmask, const bool is_frame, const int real_h) GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, int type, bool used, u32 fbmask, const bool is_frame, const int real_h)
{ {
const GSLocalMemory::psm_t& psm_s = GSLocalMemory::m_psm[TEX0.PSM]; const GSLocalMemory::psm_t& psm_s = GSLocalMemory::m_psm[TEX0.PSM];
const GSVector2& new_s = m_renderer->GetTextureScaleFactor(); const GSVector2& new_s = g_gs_renderer->GetTextureScaleFactor();
const u32 bp = TEX0.TBP0; const u32 bp = TEX0.TBP0;
GSVector2 res_size{ 0, 0 }; GSVector2 res_size{ 0, 0 };
GSVector2i new_size{ 0, 0 }; GSVector2i new_size{ 0, 0 };
@ -685,7 +683,7 @@ void GSTextureCache::InvalidateVideoMem(const GSOffset& off, const GSVector4i& r
// Haunting ground write frame buffer 0x3000 and expect to write data to 0x3380 // Haunting ground write frame buffer 0x3000 and expect to write data to 0x3380
// Note: the game only does a 0 direct write. If some games expect some real data // Note: the game only does a 0 direct write. If some games expect some real data
// we are screwed. // we are screwed.
if (m_renderer->m_game.title == CRC::HauntingGround) if (g_gs_renderer->m_game.title == CRC::HauntingGround)
{ {
u32 end_block = GSLocalMemory::m_psm[psm].info.bn(rect.z - 1, rect.w - 1, bp, bw); // Valid only for color formats u32 end_block = GSLocalMemory::m_psm[psm].info.bn(rect.z - 1, rect.w - 1, bp, bw); // Valid only for color formats
auto type = RenderTarget; auto type = RenderTarget;
@ -1168,7 +1166,7 @@ void GSTextureCache::IncAge()
GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, Target* dst, bool half_right, int x_offset, int y_offset, const GSVector2i* lod, const GSVector4i* src_range) GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, Target* dst, bool half_right, int x_offset, int y_offset, const GSVector2i* lod, const GSVector4i* src_range)
{ {
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[TEX0.PSM]; const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[TEX0.PSM];
Source* src = new Source(m_renderer, TEX0, TEXA, false); Source* src = new Source(TEX0, TEXA, false);
int tw = 1 << TEX0.TW; int tw = 1 << TEX0.TW;
int th = 1 << TEX0.TH; int th = 1 << TEX0.TH;
@ -1204,7 +1202,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
AttachPaletteToSource(src, psm.pal, true); AttachPaletteToSource(src, psm.pal, true);
} }
} }
else if (dst && static_cast<GSRendererHW*>(m_renderer)->IsDummyTexture()) else if (dst && GSRendererHW::GetInstance()->IsDummyTexture())
{ {
// This shortcut is a temporary solution. It isn't a good solution // This shortcut is a temporary solution. It isn't a good solution
// as it won't work with Channel Shuffle/Texture Shuffle pattern // as it won't work with Channel Shuffle/Texture Shuffle pattern
@ -1366,12 +1364,12 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
else if (src_range && dst->m_TEX0.TBW == TEX0.TBW && !is_8bits) else if (src_range && dst->m_TEX0.TBW == TEX0.TBW && !is_8bits)
{ {
// optimization for TBP == FRAME // optimization for TBP == FRAME
const GSDrawingContext* const context = m_renderer->m_context; const GSDrawingContext* const context = g_gs_renderer->m_context;
if (context->FRAME.Block() == TEX0.TBP0 || context->ZBUF.Block() == TEX0.TBP0) if (context->FRAME.Block() == TEX0.TBP0 || context->ZBUF.Block() == TEX0.TBP0)
{ {
// if it looks like a texture shuffle, we might read up to +/- 8 pixels on either side. // if it looks like a texture shuffle, we might read up to +/- 8 pixels on either side.
GSVector4 adjusted_src_range(*src_range); GSVector4 adjusted_src_range(*src_range);
if (static_cast<GSRendererHW*>(m_renderer)->IsPossibleTextureShuffle(src)) if (GSRendererHW::GetInstance()->IsPossibleTextureShuffle(src))
adjusted_src_range += GSVector4(-8.0f, 0.0f, 8.0f, 0.0f); adjusted_src_range += GSVector4(-8.0f, 0.0f, 8.0f, 0.0f);
// don't forget to scale the copy range // don't forget to scale the copy range
@ -1427,7 +1425,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
if (GSConfig.UserHacks_HalfPixelOffset == 1 && hack) if (GSConfig.UserHacks_HalfPixelOffset == 1 && hack)
{ {
switch(m_renderer->GetUpscaleMultiplier()) switch(g_gs_renderer->GetUpscaleMultiplier())
{ {
case 2: modx = 2.2f; mody = 2.2f; dst->m_texture->LikelyOffset = true; break; case 2: modx = 2.2f; mody = 2.2f; dst->m_texture->LikelyOffset = true; break;
case 3: modx = 3.1f; mody = 3.1f; dst->m_texture->LikelyOffset = true; break; case 3: modx = 3.1f; mody = 3.1f; dst->m_texture->LikelyOffset = true; break;
@ -1447,7 +1445,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
{ {
// maintain the clut even when paltex is on for the dump/replacement texture lookup // maintain the clut even when paltex is on for the dump/replacement texture lookup
bool paltex = (GSConfig.GPUPaletteConversion && psm.pal > 0); bool paltex = (GSConfig.GPUPaletteConversion && psm.pal > 0);
const u32* clut = (psm.pal > 0) ? static_cast<const u32*>(m_renderer->m_mem.m_clut) : nullptr; const u32* clut = (psm.pal > 0) ? static_cast<const u32*>(g_gs_renderer->m_mem.m_clut) : nullptr;
// try the hash cache // try the hash cache
if ((src->m_from_hash_cache = LookupHashCache(TEX0, TEXA, paltex, clut, lod)) != nullptr) if ((src->m_from_hash_cache = LookupHashCache(TEX0, TEXA, paltex, clut, lod)) != nullptr)
@ -1473,7 +1471,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
ASSERT(src->m_texture); ASSERT(src->m_texture);
m_src.Add(src, TEX0, m_renderer->m_context->offset.tex); m_src.Add(src, TEX0, g_gs_renderer->m_context->offset.tex);
return src; return src;
} }
@ -1492,13 +1490,13 @@ GSTextureCache::HashCacheEntry* GSTextureCache::LookupHashCache(const GIFRegTEX0
// need the hash either for replacing, dumping or caching. // need the hash either for replacing, dumping or caching.
// if dumping/replacing is on, we compute the clut hash regardless, since replacements aren't indexed // if dumping/replacing is on, we compute the clut hash regardless, since replacements aren't indexed
HashCacheKey key{HashCacheKey::Create(TEX0, TEXA, m_renderer, (dump || replace || !paltex) ? clut : nullptr, lod)}; HashCacheKey key{HashCacheKey::Create(TEX0, TEXA, (dump || replace || !paltex) ? clut : nullptr, lod)};
// handle dumping first, this is mostly isolated. // handle dumping first, this is mostly isolated.
if (dump) if (dump)
{ {
// dump base level // dump base level
GSTextureReplacements::DumpTexture(key, TEX0, TEXA, m_renderer->m_mem, 0); GSTextureReplacements::DumpTexture(key, TEX0, TEXA, g_gs_renderer->m_mem, 0);
// and the mips // and the mips
if (lod && GSConfig.DumpReplaceableMipmaps) if (lod && GSConfig.DumpReplaceableMipmaps)
@ -1507,8 +1505,8 @@ GSTextureCache::HashCacheEntry* GSTextureCache::LookupHashCache(const GIFRegTEX0
const int nmips = lod->y - lod->x + 1; const int nmips = lod->y - lod->x + 1;
for (int mip = 1; mip < nmips; mip++) for (int mip = 1; mip < nmips; mip++)
{ {
const GIFRegTEX0 MIP_TEX0{m_renderer->GetTex0Layer(basemip + mip)}; const GIFRegTEX0 MIP_TEX0{g_gs_renderer->GetTex0Layer(basemip + mip)};
GSTextureReplacements::DumpTexture(key, MIP_TEX0, TEXA, m_renderer->m_mem, mip); GSTextureReplacements::DumpTexture(key, MIP_TEX0, TEXA, g_gs_renderer->m_mem, mip);
} }
} }
} }
@ -1567,7 +1565,7 @@ GSTextureCache::HashCacheEntry* GSTextureCache::LookupHashCache(const GIFRegTEX0
} }
// upload base level // upload base level
PreloadTexture(TEX0, TEXA, m_renderer->m_mem, paltex, tex, 0); PreloadTexture(TEX0, TEXA, g_gs_renderer->m_mem, paltex, tex, 0);
// upload mips if present // upload mips if present
if (lod) if (lod)
@ -1576,8 +1574,8 @@ GSTextureCache::HashCacheEntry* GSTextureCache::LookupHashCache(const GIFRegTEX0
const int nmips = lod->y - lod->x + 1; const int nmips = lod->y - lod->x + 1;
for (int mip = 1; mip < nmips; mip++) for (int mip = 1; mip < nmips; mip++)
{ {
const GIFRegTEX0 MIP_TEX0{m_renderer->GetTex0Layer(basemip + mip)}; const GIFRegTEX0 MIP_TEX0{g_gs_renderer->GetTex0Layer(basemip + mip)};
PreloadTexture(MIP_TEX0, TEXA, m_renderer->m_mem, paltex, tex, mip); PreloadTexture(MIP_TEX0, TEXA, g_gs_renderer->m_mem, paltex, tex, mip);
} }
} }
@ -1595,7 +1593,7 @@ GSTextureCache::Target* GSTextureCache::CreateTarget(const GIFRegTEX0& TEX0, int
{ {
ASSERT(type == RenderTarget || type == DepthStencil); ASSERT(type == RenderTarget || type == DepthStencil);
Target* t = new Target(m_renderer, TEX0, !GSConfig.UserHacks_DisableDepthSupport, type); Target* t = new Target(TEX0, !GSConfig.UserHacks_DisableDepthSupport, type);
// FIXME: initial data should be unswizzled from local mem in Update() if dirty // FIXME: initial data should be unswizzled from local mem in Update() if dirty
@ -1610,7 +1608,7 @@ GSTextureCache::Target* GSTextureCache::CreateTarget(const GIFRegTEX0& TEX0, int
t->m_texture = g_gs_device->CreateSparseDepthStencil(w, h, GSTexture::Format::DepthStencil, clear); t->m_texture = g_gs_device->CreateSparseDepthStencil(w, h, GSTexture::Format::DepthStencil, clear);
} }
t->m_texture->SetScale(m_renderer->GetTextureScaleFactor()); t->m_texture->SetScale(g_gs_renderer->GetTextureScaleFactor());
m_dst[type].push_front(t); m_dst[type].push_front(t);
@ -1674,23 +1672,23 @@ void GSTextureCache::Read(Target* t, const GSVector4i& r)
if (res) if (res)
{ {
const GSOffset off = m_renderer->m_mem.GetOffset(TEX0.TBP0, TEX0.TBW, TEX0.PSM); const GSOffset off = g_gs_renderer->m_mem.GetOffset(TEX0.TBP0, TEX0.TBW, TEX0.PSM);
switch (TEX0.PSM) switch (TEX0.PSM)
{ {
case PSM_PSMCT32: case PSM_PSMCT32:
case PSM_PSMZ32: case PSM_PSMZ32:
m_renderer->m_mem.WritePixel32(m.bits, m.pitch, off, r); g_gs_renderer->m_mem.WritePixel32(m.bits, m.pitch, off, r);
break; break;
case PSM_PSMCT24: case PSM_PSMCT24:
case PSM_PSMZ24: case PSM_PSMZ24:
m_renderer->m_mem.WritePixel24(m.bits, m.pitch, off, r); g_gs_renderer->m_mem.WritePixel24(m.bits, m.pitch, off, r);
break; break;
case PSM_PSMCT16: case PSM_PSMCT16:
case PSM_PSMCT16S: case PSM_PSMCT16S:
case PSM_PSMZ16: case PSM_PSMZ16:
case PSM_PSMZ16S: case PSM_PSMZ16S:
m_renderer->m_mem.WritePixel16(m.bits, m.pitch, off, r); g_gs_renderer->m_mem.WritePixel16(m.bits, m.pitch, off, r);
break; break;
default: default:
@ -1708,8 +1706,8 @@ void GSTextureCache::Read(Source* t, const GSVector4i& r)
GSTexture::GSMap m; GSTexture::GSMap m;
if (g_gs_device->DownloadTexture(t->m_texture, r, m)) if (g_gs_device->DownloadTexture(t->m_texture, r, m))
{ {
GSOffset off = m_renderer->m_mem.GetOffset(TEX0.TBP0, TEX0.TBW, TEX0.PSM); GSOffset off = g_gs_renderer->m_mem.GetOffset(TEX0.TBP0, TEX0.TBW, TEX0.PSM);
m_renderer->m_mem.WritePixel32(m.bits, m.pitch, off, r); g_gs_renderer->m_mem.WritePixel32(m.bits, m.pitch, off, r);
g_gs_device->DownloadTextureComplete(); g_gs_device->DownloadTextureComplete();
} }
} }
@ -1748,9 +1746,8 @@ void GSTextureCache::PrintMemoryUsage()
// GSTextureCache::Surface // GSTextureCache::Surface
GSTextureCache::Surface::Surface(GSRenderer* r) GSTextureCache::Surface::Surface()
: m_renderer(r) : m_texture(NULL)
, m_texture(NULL)
, m_from_hash_cache(NULL) , m_from_hash_cache(NULL)
, m_age(0) , m_age(0)
, m_32_bits_fmt(false) , m_32_bits_fmt(false)
@ -1790,9 +1787,8 @@ bool GSTextureCache::Surface::Overlaps(u32 bp, u32 bw, u32 psm, const GSVector4i
// GSTextureCache::Source // GSTextureCache::Source
GSTextureCache::Source::Source(GSRenderer* r, const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, bool dummy_container) GSTextureCache::Source::Source(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, bool dummy_container)
: Surface(r) : m_palette_obj(nullptr)
, m_palette_obj(nullptr)
, m_palette(nullptr) , m_palette(nullptr)
, m_valid_rect(0, 0) , m_valid_rect(0, 0)
, m_target(false) , m_target(false)
@ -1824,10 +1820,10 @@ GSTextureCache::Source::Source(GSRenderer* r, const GIFRegTEX0& TEX0, const GIFR
if (m_repeating && !CanPreload()) if (m_repeating && !CanPreload())
{ {
m_p2t = r->m_mem.GetPage2TileMap(m_TEX0); m_p2t = g_gs_renderer->m_mem.GetPage2TileMap(m_TEX0);
} }
m_pages = m_renderer->m_context->offset.tex.pageLooperForRect(GSVector4i(0, 0, 1 << TEX0.TW, 1 << TEX0.TH)); m_pages = g_gs_renderer->m_context->offset.tex.pageLooperForRect(GSVector4i(0, 0, 1 << TEX0.TW, 1 << TEX0.TH));
} }
} }
@ -1857,7 +1853,7 @@ void GSTextureCache::Source::Update(const GSVector4i& rect, int level)
if (r.eq(GSVector4i(0, 0, tw, th))) if (r.eq(GSVector4i(0, 0, tw, th)))
m_complete_layers |= (1u << level); m_complete_layers |= (1u << level);
const GSOffset& off = m_renderer->m_context->offset.tex; const GSOffset& off = g_gs_renderer->m_context->offset.tex;
GSOffset::BNHelper bn = off.bnMulti(r.left, r.top); GSOffset::BNHelper bn = off.bnMulti(r.left, r.top);
u32 blocks = 0; u32 blocks = 0;
@ -1998,9 +1994,9 @@ void GSTextureCache::Source::Flush(u32 count, int layer)
int pitch = std::max(tw, psm.bs.x) * sizeof(u32); int pitch = std::max(tw, psm.bs.x) * sizeof(u32);
GSLocalMemory& mem = m_renderer->m_mem; GSLocalMemory& mem = g_gs_renderer->m_mem;
const GSOffset& off = m_renderer->m_context->offset.tex; const GSOffset& off = g_gs_renderer->m_context->offset.tex;
GSLocalMemory::readTexture rtx = psm.rtx; GSLocalMemory::readTexture rtx = psm.rtx;
@ -2053,7 +2049,7 @@ void GSTextureCache::Source::Flush(u32 count, int layer)
void GSTextureCache::Source::PreloadLevel(int level) void GSTextureCache::Source::PreloadLevel(int level)
{ {
// m_TEX0 is adjusted for mips (messy, should be changed). // m_TEX0 is adjusted for mips (messy, should be changed).
const HashType hash = HashTexture(m_renderer, m_TEX0, m_TEXA); const HashType hash = HashTexture(m_TEX0, m_TEXA);
// Layer is complete again, regardless of whether the hash matches or not (and we reupload). // Layer is complete again, regardless of whether the hash matches or not (and we reupload).
const u8 layer_bit = static_cast<u8>(1) << level; const u8 layer_bit = static_cast<u8>(1) << level;
@ -2067,7 +2063,7 @@ void GSTextureCache::Source::PreloadLevel(int level)
m_layer_hash[level] = hash; m_layer_hash[level] = hash;
// And upload the texture. // And upload the texture.
PreloadTexture(m_TEX0, m_TEXA, m_renderer->m_mem, m_palette != nullptr, m_texture, level); PreloadTexture(m_TEX0, m_TEXA, g_gs_renderer->m_mem, m_palette != nullptr, m_texture, level);
} }
bool GSTextureCache::Source::ClutMatch(const PaletteKey& palette_key) bool GSTextureCache::Source::ClutMatch(const PaletteKey& palette_key)
@ -2077,9 +2073,8 @@ bool GSTextureCache::Source::ClutMatch(const PaletteKey& palette_key)
// GSTextureCache::Target // GSTextureCache::Target
GSTextureCache::Target::Target(GSRenderer* r, const GIFRegTEX0& TEX0, const bool depth_supported, const int type) GSTextureCache::Target::Target(const GIFRegTEX0& TEX0, const bool depth_supported, const int type)
: Surface(r) : m_type(type)
, m_type(type)
, m_used(false) , m_used(false)
, m_depth_supported(depth_supported) , m_depth_supported(depth_supported)
{ {
@ -2115,7 +2110,7 @@ void GSTextureCache::Target::Update()
return; return;
} }
else if (m_type == DepthStencil && m_renderer->m_game.title == CRC::FFX2) else if (m_type == DepthStencil && g_gs_renderer->m_game.title == CRC::FFX2)
{ {
GL_INS("ERROR: bad invalidation detected, depth buffer will be cleared"); GL_INS("ERROR: bad invalidation detected, depth buffer will be cleared");
// FFX2 menu. Invalidation of the depth is wrongly done and only the first // FFX2 menu. Invalidation of the depth is wrongly done and only the first
@ -2141,13 +2136,13 @@ void GSTextureCache::Target::Update()
GSTexture* t = g_gs_device->CreateTexture(w, h, false, GSTexture::Format::Color); GSTexture* t = g_gs_device->CreateTexture(w, h, false, GSTexture::Format::Color);
GSOffset off = m_renderer->m_mem.GetOffset(m_TEX0.TBP0, m_TEX0.TBW, m_TEX0.PSM); GSOffset off = g_gs_renderer->m_mem.GetOffset(m_TEX0.TBP0, m_TEX0.TBW, m_TEX0.PSM);
GSTexture::GSMap m; GSTexture::GSMap m;
if (t->Map(m)) if (t->Map(m))
{ {
m_renderer->m_mem.ReadTexture(off, r, m.bits, m.pitch, TEXA); g_gs_renderer->m_mem.ReadTexture(off, r, m.bits, m.pitch, TEXA);
t->Unmap(); t->Unmap();
} }
@ -2155,7 +2150,7 @@ void GSTextureCache::Target::Update()
{ {
int pitch = ((w + 3) & ~3) * 4; int pitch = ((w + 3) & ~3) * 4;
m_renderer->m_mem.ReadTexture(off, r, m_temp, pitch, TEXA); g_gs_renderer->m_mem.ReadTexture(off, r, m_temp, pitch, TEXA);
t->Update(r.rsize(), m_temp, pitch); t->Update(r.rsize(), m_temp, pitch);
} }
@ -2498,14 +2493,13 @@ void GSTextureCache::InjectHashCacheTexture(const HashCacheKey& key, GSTexture*
// GSTextureCache::Palette // GSTextureCache::Palette
GSTextureCache::Palette::Palette(const GSRenderer* renderer, u16 pal, bool need_gs_texture) GSTextureCache::Palette::Palette(u16 pal, bool need_gs_texture)
: m_pal(pal) : m_pal(pal)
, m_tex_palette(nullptr) , m_tex_palette(nullptr)
, m_renderer(renderer)
{ {
u16 palette_size = pal * sizeof(u32); u16 palette_size = pal * sizeof(u32);
m_clut = (u32*)_aligned_malloc(palette_size, 64); m_clut = (u32*)_aligned_malloc(palette_size, 64);
memcpy(m_clut, (const u32*)m_renderer->m_mem.m_clut, palette_size); memcpy(m_clut, (const u32*)g_gs_renderer->m_mem.m_clut, palette_size);
if (need_gs_texture) if (need_gs_texture)
{ {
InitializeTexture(); InitializeTexture();
@ -2566,8 +2560,7 @@ bool GSTextureCache::PaletteKeyEqual::operator()(const PaletteKey& lhs, const Pa
// GSTextureCache::PaletteMap // GSTextureCache::PaletteMap
GSTextureCache::PaletteMap::PaletteMap(const GSRenderer* renderer) GSTextureCache::PaletteMap::PaletteMap()
: m_renderer(renderer)
{ {
for (auto& map : m_maps) for (auto& map : m_maps)
{ {
@ -2584,7 +2577,7 @@ std::shared_ptr<GSTextureCache::Palette> GSTextureCache::PaletteMap::LookupPalet
// pal == 256 : index 1 // pal == 256 : index 1
auto& map = m_maps[pal == 16 ? 0 : 1]; auto& map = m_maps[pal == 16 ? 0 : 1];
const u32* clut = (const u32*)m_renderer->m_mem.m_clut; const u32* clut = (const u32*)g_gs_renderer->m_mem.m_clut;
// Create PaletteKey for searching into map (clut is actually not copied, so do not store this key into the map) // Create PaletteKey for searching into map (clut is actually not copied, so do not store this key into the map)
const PaletteKey palette_key = {clut, pal}; const PaletteKey palette_key = {clut, pal};
@ -2640,7 +2633,7 @@ std::shared_ptr<GSTextureCache::Palette> GSTextureCache::PaletteMap::LookupPalet
} }
} }
std::shared_ptr<Palette> palette = std::make_shared<Palette>(m_renderer, pal, need_gs_texture); std::shared_ptr<Palette> palette = std::make_shared<Palette>(pal, need_gs_texture);
map.emplace(palette->GetPaletteKey(), palette); map.emplace(palette->GetPaletteKey(), palette);
@ -2714,7 +2707,7 @@ __fi static GSTextureCache::HashType FinishBlockHash(BlockHashState& st)
return XXH3_64bits_digest(&st); return XXH3_64bits_digest(&st);
} }
static void HashTextureLevel(GSRenderer* renderer, const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, BlockHashState& hash_st, u8* temp) static void HashTextureLevel(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, BlockHashState& hash_st, u8* temp)
{ {
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[TEX0.PSM]; const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[TEX0.PSM];
const GSVector2i& bs = psm.bs; const GSVector2i& bs = psm.bs;
@ -2725,7 +2718,7 @@ static void HashTextureLevel(GSRenderer* renderer, const GIFRegTEX0& TEX0, const
// We want to hash the exact same blocks here. // We want to hash the exact same blocks here.
const GSVector4i rect(0, 0, tw, th); const GSVector4i rect(0, 0, tw, th);
const GSVector4i block_rect(rect.ralign<Align_Outside>(bs)); const GSVector4i block_rect(rect.ralign<Align_Outside>(bs));
GSLocalMemory& mem = renderer->m_mem; GSLocalMemory& mem = g_gs_renderer->m_mem;
GSOffset off = mem.GetOffset(TEX0.TBP0, TEX0.TBW, TEX0.PSM); GSOffset off = mem.GetOffset(TEX0.TBP0, TEX0.TBW, TEX0.PSM);
// For textures which are smaller than the block size, we expand and then hash. // For textures which are smaller than the block size, we expand and then hash.
@ -2738,7 +2731,7 @@ static void HashTextureLevel(GSRenderer* renderer, const GIFRegTEX0& TEX0, const
const GSLocalMemory::readTexture rtx = psm.rtxP; const GSLocalMemory::readTexture rtx = psm.rtxP;
// Use temp buffer for expanding, since we may not need to update. // Use temp buffer for expanding, since we may not need to update.
(renderer->m_mem.*rtx)(off, block_rect, temp, pitch, TEXA); (mem.*rtx)(off, block_rect, temp, pitch, TEXA);
// Hash the expanded texture. // Hash the expanded texture.
u8* ptr = temp; u8* ptr = temp;
@ -2771,11 +2764,11 @@ static void HashTextureLevel(GSRenderer* renderer, const GIFRegTEX0& TEX0, const
} }
} }
GSTextureCache::HashType GSTextureCache::HashTexture(GSRenderer* renderer, const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA) GSTextureCache::HashType GSTextureCache::HashTexture(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA)
{ {
BlockHashState hash_st; BlockHashState hash_st;
BlockHashReset(hash_st); BlockHashReset(hash_st);
HashTextureLevel(renderer, TEX0, TEXA, hash_st, m_temp); HashTextureLevel(TEX0, TEXA, hash_st, m_temp);
return FinishBlockHash(hash_st); return FinishBlockHash(hash_st);
} }
@ -2828,7 +2821,7 @@ GSTextureCache::HashCacheKey::HashCacheKey()
TEXA.U64 = 0; TEXA.U64 = 0;
} }
GSTextureCache::HashCacheKey GSTextureCache::HashCacheKey::Create(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, GSRenderer* renderer, const u32* clut, const GSVector2i* lod) GSTextureCache::HashCacheKey GSTextureCache::HashCacheKey::Create(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const u32* clut, const GSVector2i* lod)
{ {
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[TEX0.PSM]; const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[TEX0.PSM];
@ -2841,7 +2834,7 @@ GSTextureCache::HashCacheKey GSTextureCache::HashCacheKey::Create(const GIFRegTE
BlockHashReset(hash_st); BlockHashReset(hash_st);
// base level is always hashed // base level is always hashed
HashTextureLevel(renderer, TEX0, TEXA, hash_st, m_temp); HashTextureLevel(TEX0, TEXA, hash_st, m_temp);
if (lod) if (lod)
{ {
@ -2850,8 +2843,8 @@ GSTextureCache::HashCacheKey GSTextureCache::HashCacheKey::Create(const GIFRegTE
const int nmips = lod->y - lod->x + 1; const int nmips = lod->y - lod->x + 1;
for (int i = 1; i < nmips; i++) for (int i = 1; i < nmips; i++)
{ {
const GIFRegTEX0 MIP_TEX0{renderer->GetTex0Layer(basemip + i)}; const GIFRegTEX0 MIP_TEX0{g_gs_renderer->GetTex0Layer(basemip + i)};
HashTextureLevel(renderer, MIP_TEX0, TEXA, hash_st, m_temp); HashTextureLevel(MIP_TEX0, TEXA, hash_st, m_temp);
} }
} }

View File

@ -50,8 +50,7 @@ public:
HashCacheKey(); HashCacheKey();
static HashCacheKey Create(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, GSRenderer* renderer, const u32* clut, static HashCacheKey Create(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const u32* clut, const GSVector2i* lod);
const GSVector2i* lod);
HashCacheKey WithRemovedCLUTHash() const; HashCacheKey WithRemovedCLUTHash() const;
void RemoveCLUTHash(); void RemoveCLUTHash();
@ -76,9 +75,6 @@ public:
class Surface : public GSAlignedClass<32> class Surface : public GSAlignedClass<32>
{ {
protected:
GSRenderer* m_renderer;
public: public:
GSTexture* m_texture; GSTexture* m_texture;
HashCacheEntry* m_from_hash_cache; HashCacheEntry* m_from_hash_cache;
@ -90,7 +86,7 @@ public:
u32 m_end_block; // Hint of the surface area. u32 m_end_block; // Hint of the surface area.
public: public:
Surface(GSRenderer* r); Surface();
virtual ~Surface(); virtual ~Surface();
void UpdateAge(); void UpdateAge();
@ -110,10 +106,9 @@ public:
u32* m_clut; u32* m_clut;
u16 m_pal; u16 m_pal;
GSTexture* m_tex_palette; GSTexture* m_tex_palette;
const GSRenderer* m_renderer;
public: public:
Palette(const GSRenderer* renderer, u16 pal, bool need_gs_texture); Palette(u16 pal, bool need_gs_texture);
~Palette(); ~Palette();
// Disable copy constructor and copy operator // Disable copy constructor and copy operator
@ -178,7 +173,7 @@ public:
GSOffset::PageLooper m_pages; GSOffset::PageLooper m_pages;
public: public:
Source(GSRenderer* r, const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, bool dummy_container = false); Source(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, bool dummy_container = false);
virtual ~Source(); virtual ~Source();
__fi bool CanPreload() const { return CanPreloadTextureSize(m_TEX0.TW, m_TEX0.TH); } __fi bool CanPreload() const { return CanPreloadTextureSize(m_TEX0.TW, m_TEX0.TH); }
@ -200,7 +195,7 @@ public:
bool m_dirty_alpha; bool m_dirty_alpha;
public: public:
Target(GSRenderer* r, const GIFRegTEX0& TEX0, const bool depth_supported, const int type); Target(const GIFRegTEX0& TEX0, const bool depth_supported, const int type);
void UpdateValidity(const GSVector4i& rect); void UpdateValidity(const GSVector4i& rect);
@ -211,7 +206,6 @@ public:
{ {
private: private:
static const u16 MAX_SIZE = 65535; // Max size of each map. static const u16 MAX_SIZE = 65535; // Max size of each map.
const GSRenderer* m_renderer;
// Array of 2 maps, the first for 64B palettes and the second for 1024B palettes. // Array of 2 maps, the first for 64B palettes and the second for 1024B palettes.
// Each map stores the key PaletteKey (clut copy, pal value) pointing to the relevant shared pointer to Palette object. // Each map stores the key PaletteKey (clut copy, pal value) pointing to the relevant shared pointer to Palette object.
@ -219,7 +213,7 @@ public:
std::array<std::unordered_map<PaletteKey, std::shared_ptr<Palette>, PaletteKeyHash, PaletteKeyEqual>, 2> m_maps; std::array<std::unordered_map<PaletteKey, std::shared_ptr<Palette>, PaletteKeyHash, PaletteKeyEqual>, 2> m_maps;
public: public:
PaletteMap(const GSRenderer* renderer); PaletteMap();
// Retrieves a shared pointer to a valid Palette from m_maps or creates a new one adding it to the data structure // Retrieves a shared pointer to a valid Palette from m_maps or creates a new one adding it to the data structure
std::shared_ptr<Palette> LookupPalette(u16 pal, bool need_gs_texture); std::shared_ptr<Palette> LookupPalette(u16 pal, bool need_gs_texture);
@ -276,7 +270,6 @@ public:
}; };
protected: protected:
GSRenderer* m_renderer;
PaletteMap m_palette_map; PaletteMap m_palette_map;
SourceMap m_src; SourceMap m_src;
std::unordered_map<HashCacheKey, HashCacheEntry, HashCacheKeyHash> m_hash_cache; std::unordered_map<HashCacheKey, HashCacheEntry, HashCacheKeyHash> m_hash_cache;
@ -293,13 +286,13 @@ protected:
HashCacheEntry* LookupHashCache(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, bool& paltex, const u32* clut, const GSVector2i* lod); HashCacheEntry* LookupHashCache(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, bool& paltex, const u32* clut, const GSVector2i* lod);
static void PreloadTexture(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, GSLocalMemory& mem, bool paltex, GSTexture* tex, u32 level); static void PreloadTexture(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, GSLocalMemory& mem, bool paltex, GSTexture* tex, u32 level);
static HashType HashTexture(GSRenderer* renderer, const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA); static HashType HashTexture(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA);
// TODO: virtual void Write(Source* s, const GSVector4i& r) = 0; // TODO: virtual void Write(Source* s, const GSVector4i& r) = 0;
// TODO: virtual void Write(Target* t, const GSVector4i& r) = 0; // TODO: virtual void Write(Target* t, const GSVector4i& r) = 0;
public: public:
GSTextureCache(GSRenderer* r); GSTextureCache();
~GSTextureCache(); ~GSTextureCache();
__fi u64 GetHashCacheMemoryUsage() const { return m_hash_cache_memory_usage; } __fi u64 GetHashCacheMemoryUsage() const { return m_hash_cache_memory_usage; }

View File

@ -202,25 +202,25 @@ public:
virtual ~GSRasterizerList(); virtual ~GSRasterizerList();
template <class DS> template <class DS>
static IRasterizer* Create(int threads) static std::unique_ptr<IRasterizer> Create(int threads)
{ {
threads = std::max<int>(threads, 0); threads = std::max<int>(threads, 0);
if (threads == 0) if (threads == 0)
{ {
return new GSRasterizer(new DS(), 0, 1); return std::make_unique<GSRasterizer>(new DS(), 0, 1);
} }
GSRasterizerList* rl = new GSRasterizerList(threads); std::unique_ptr<GSRasterizerList> rl(new GSRasterizerList(threads));
for (int i = 0; i < threads; i++) for (int i = 0; i < threads; i++)
{ {
rl->m_r.push_back(std::unique_ptr<GSRasterizer>(new GSRasterizer(new DS(), i, threads))); rl->m_r.push_back(std::unique_ptr<GSRasterizer>(new GSRasterizer(new DS(), i, threads)));
auto& r = *rl->m_r[i]; auto& r = *rl->m_r[i];
rl->m_workers.push_back(std::unique_ptr<GSWorker>(new GSWorker( rl->m_workers.push_back(std::unique_ptr<GSWorker>(new GSWorker(
[rl, i]() { rl->OnWorkerStartup(i); }, [i]() { GSRasterizerList::OnWorkerStartup(i); },
[&r](GSRingHeap::SharedPtr<GSRasterizerData>& item) { r.Draw(item.get()); }, [&r](GSRingHeap::SharedPtr<GSRasterizerData>& item) { r.Draw(item.get()); },
[rl, i]() { rl->OnWorkerShutdown(i); }))); [i]() { GSRasterizerList::OnWorkerShutdown(i); })));
} }
return rl; return rl;

View File

@ -31,10 +31,7 @@ GSRendererSW::GSRendererSW(int threads)
{ {
m_nativeres = true; // ignore ini, sw is always native m_nativeres = true; // ignore ini, sw is always native
m_tc = new GSTextureCacheSW(this); m_tc = std::make_unique<GSTextureCacheSW>();
memset(m_texture, 0, sizeof(m_texture));
m_rl = GSRasterizerList::Create<GSDrawScanline>(threads); m_rl = GSRasterizerList::Create<GSDrawScanline>(threads);
m_output = (u8*)_aligned_malloc(1024 * 1024 * sizeof(u32), 32); m_output = (u8*)_aligned_malloc(1024 * 1024 * sizeof(u32), 32);
@ -62,16 +59,10 @@ GSRendererSW::GSRendererSW(int threads)
GSRendererSW::~GSRendererSW() GSRendererSW::~GSRendererSW()
{ {
// Need to destroy worker queue first to stop any pending thread work // strictly speaking we should always be destroyed when the destructor runs..
delete m_rl; // except if an exception gets thrown during construction. this will go once
delete m_tc; // we get rid of exceptions...
GSRendererSW::Destroy();
for (GSTexture* tex : m_texture)
{
delete tex;
}
_aligned_free(m_output);
} }
void GSRendererSW::Reset() void GSRendererSW::Reset()
@ -83,6 +74,22 @@ void GSRendererSW::Reset()
GSRenderer::Reset(); GSRenderer::Reset();
} }
void GSRendererSW::Destroy()
{
// Need to destroy worker queue first to stop any pending thread work
m_rl.reset();
m_tc.reset();
for (GSTexture*& tex : m_texture)
{
delete tex;
tex = nullptr;
}
_aligned_free(m_output);
m_output = nullptr;
}
void GSRendererSW::VSync(u32 field, bool registers_written) void GSRendererSW::VSync(u32 field, bool registers_written)
{ {
Sync(0); // IncAge might delete a cached texture in use Sync(0); // IncAge might delete a cached texture in use
@ -335,7 +342,7 @@ void GSRendererSW::Draw()
} }
} }
auto data = m_vertex_heap.make_shared<SharedData>(this).cast<GSRasterizerData>(); auto data = m_vertex_heap.make_shared<SharedData>().cast<GSRasterizerData>();
SharedData* sd = static_cast<SharedData*>(data.get()); SharedData* sd = static_cast<SharedData*>(data.get());
sd->primclass = m_vt.m_primclass; sd->primclass = m_vt.m_primclass;
@ -1419,9 +1426,8 @@ bool GSRendererSW::GetScanlineGlobalData(SharedData* data)
return true; return true;
} }
GSRendererSW::SharedData::SharedData(GSRendererSW* parent) GSRendererSW::SharedData::SharedData()
: m_parent(parent) : m_fpsm(0)
, m_fpsm(0)
, m_zpsm(0) , m_zpsm(0)
, m_using_pages(false) , m_using_pages(false)
, m_syncpoint(SyncNone) , m_syncpoint(SyncNone)
@ -1466,17 +1472,17 @@ void GSRendererSW::SharedData::UsePages(const GSOffset::PageLooper* fb_pages, in
if (global.sel.fb) if (global.sel.fb)
{ {
m_parent->UsePages(*fb_pages, 0); GSRendererSW::GetInstance()->UsePages(*fb_pages, 0);
} }
if (global.sel.zb) if (global.sel.zb)
{ {
m_parent->UsePages(*zb_pages, 1); GSRendererSW::GetInstance()->UsePages(*zb_pages, 1);
} }
for (size_t i = 0; m_tex[i].t != NULL; i++) for (size_t i = 0; m_tex[i].t != NULL; i++)
{ {
m_parent->UsePages(m_tex[i].t->m_pages, 2); GSRendererSW::GetInstance()->UsePages(m_tex[i].t->m_pages, 2);
} }
} }
@ -1500,17 +1506,17 @@ void GSRendererSW::SharedData::ReleasePages()
if (global.sel.fb) if (global.sel.fb)
{ {
m_parent->ReleasePages(m_fb_pages, 0); GSRendererSW::GetInstance()->ReleasePages(m_fb_pages, 0);
} }
if (global.sel.zb) if (global.sel.zb)
{ {
m_parent->ReleasePages(m_zb_pages, 1); GSRendererSW::GetInstance()->ReleasePages(m_zb_pages, 1);
} }
for (size_t i = 0; m_tex[i].t != NULL; i++) for (size_t i = 0; m_tex[i].t != NULL; i++)
{ {
m_parent->ReleasePages(m_tex[i].t->m_pages, 2); GSRendererSW::GetInstance()->ReleasePages(m_tex[i].t->m_pages, 2);
} }
} }
@ -1545,19 +1551,19 @@ void GSRendererSW::SharedData::UpdateSource()
// TODO // TODO
if (m_parent->s_dump) if (g_gs_renderer->s_dump)
{ {
u64 frame = g_perfmon.GetFrame(); u64 frame = g_perfmon.GetFrame();
std::string s; std::string s;
if (m_parent->s_savet && m_parent->s_n >= m_parent->s_saven) if (g_gs_renderer->s_savet && g_gs_renderer->s_n >= g_gs_renderer->s_saven)
{ {
for (size_t i = 0; m_tex[i].t != NULL; i++) for (size_t i = 0; m_tex[i].t != NULL; i++)
{ {
const GIFRegTEX0& TEX0 = m_parent->GetTex0Layer(i); const GIFRegTEX0& TEX0 = g_gs_renderer->GetTex0Layer(i);
s = format("%05d_f%lld_itex%d_%05x_%s.bmp", m_parent->s_n, frame, i, TEX0.TBP0, psm_str(TEX0.PSM)); s = format("%05d_f%lld_itex%d_%05x_%s.bmp", g_gs_renderer->s_n, frame, i, TEX0.TBP0, psm_str(TEX0.PSM));
m_tex[i].t->Save(root_sw + s); m_tex[i].t->Save(root_sw + s);
} }
@ -1568,7 +1574,7 @@ void GSRendererSW::SharedData::UpdateSource()
t->Update(GSVector4i(0, 0, 256, 1), global.clut, sizeof(u32) * 256); t->Update(GSVector4i(0, 0, 256, 1), global.clut, sizeof(u32) * 256);
s = format("%05d_f%lld_itexp_%05x_%s.bmp", m_parent->s_n, frame, (int)m_parent->m_context->TEX0.CBP, psm_str(m_parent->m_context->TEX0.CPSM)); s = format("%05d_f%lld_itexp_%05x_%s.bmp", g_gs_renderer->s_n, frame, (int)g_gs_renderer->m_context->TEX0.CBP, psm_str(g_gs_renderer->m_context->TEX0.CPSM));
t->Save(root_sw + s); t->Save(root_sw + s);

View File

@ -19,7 +19,7 @@
#include "GSDrawScanline.h" #include "GSDrawScanline.h"
#include "GS/GSRingHeap.h" #include "GS/GSRingHeap.h"
class GSRendererSW : public GSRenderer class GSRendererSW final : public GSRenderer
{ {
static const GSVector4 m_pos_scale; static const GSVector4 m_pos_scale;
#if _M_SSE >= 0x501 #if _M_SSE >= 0x501
@ -35,7 +35,6 @@ class GSRendererSW : public GSRenderer
}; };
public: public:
GSRendererSW* m_parent;
GSOffset::PageLooper m_fb_pages; GSOffset::PageLooper m_fb_pages;
GSOffset::PageLooper m_zb_pages; GSOffset::PageLooper m_zb_pages;
int m_fpsm; int m_fpsm;
@ -50,7 +49,7 @@ class GSRendererSW : public GSRenderer
} m_syncpoint; } m_syncpoint;
public: public:
SharedData(GSRendererSW* parent); SharedData();
virtual ~SharedData(); virtual ~SharedData();
void UsePages(const GSOffset::PageLooper* fb_pages, int fpsm, const GSOffset::PageLooper* zb_pages, int zpsm); void UsePages(const GSOffset::PageLooper* fb_pages, int fpsm, const GSOffset::PageLooper* zb_pages, int zpsm);
@ -68,10 +67,10 @@ class GSRendererSW : public GSRenderer
void ConvertVertexBuffer(GSVertexSW* RESTRICT dst, const GSVertex* RESTRICT src, size_t count); void ConvertVertexBuffer(GSVertexSW* RESTRICT dst, const GSVertex* RESTRICT src, size_t count);
protected: protected:
IRasterizer* m_rl; std::unique_ptr<IRasterizer> m_rl;
std::unique_ptr<GSTextureCacheSW> m_tc;
GSRingHeap m_vertex_heap; GSRingHeap m_vertex_heap;
GSTextureCacheSW* m_tc; std::array<GSTexture*, 3> m_texture = {};
GSTexture* m_texture[3];
u8* m_output; u8* m_output;
GSPixelOffset4* m_fzb; GSPixelOffset4* m_fzb;
GSVector4i m_fzb_bbox; GSVector4i m_fzb_bbox;
@ -101,4 +100,8 @@ protected:
public: public:
GSRendererSW(int threads); GSRendererSW(int threads);
~GSRendererSW() override; ~GSRendererSW() override;
__fi static GSRendererSW* GetInstance() { return static_cast<GSRendererSW*>(g_gs_renderer.get()); }
void Destroy() override;
}; };

View File

@ -16,10 +16,7 @@
#include "PrecompiledHeader.h" #include "PrecompiledHeader.h"
#include "GSTextureCacheSW.h" #include "GSTextureCacheSW.h"
GSTextureCacheSW::GSTextureCacheSW(GSState* state) GSTextureCacheSW::GSTextureCacheSW() = default;
: m_state(state)
{
}
GSTextureCacheSW::~GSTextureCacheSW() GSTextureCacheSW::~GSTextureCacheSW()
{ {
@ -58,7 +55,7 @@ GSTextureCacheSW::Texture* GSTextureCacheSW::Lookup(const GIFRegTEX0& TEX0, cons
} }
// Lookup miss // Lookup miss
Texture* t = new Texture(m_state, tw0, TEX0, TEXA); Texture* t = new Texture(tw0, TEX0, TEXA);
m_textures.insert(t); m_textures.insert(t);
@ -137,9 +134,8 @@ void GSTextureCacheSW::IncAge()
// //
GSTextureCacheSW::Texture::Texture(GSState* state, u32 tw0, const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA) GSTextureCacheSW::Texture::Texture(u32 tw0, const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA)
: m_state(state) : m_buff(NULL)
, m_buff(NULL)
, m_tw(tw0) , m_tw(tw0)
, m_age(0) , m_age(0)
, m_complete(false) , m_complete(false)
@ -157,14 +153,14 @@ GSTextureCacheSW::Texture::Texture(GSState* state, u32 tw0, const GIFRegTEX0& TE
m_sharedbits = GSUtil::HasSharedBitsPtr(m_TEX0.PSM); m_sharedbits = GSUtil::HasSharedBitsPtr(m_TEX0.PSM);
m_offset = m_state->m_mem.GetOffset(TEX0.TBP0, TEX0.TBW, TEX0.PSM); m_offset = g_gs_renderer->m_mem.GetOffset(TEX0.TBP0, TEX0.TBW, TEX0.PSM);
m_pages = m_offset.pageLooperForRect(GSVector4i(0, 0, 1 << TEX0.TW, 1 << TEX0.TH)); m_pages = m_offset.pageLooperForRect(GSVector4i(0, 0, 1 << TEX0.TW, 1 << TEX0.TH));
m_repeating = m_TEX0.IsRepeating(); // repeating mode always works, it is just slightly slower m_repeating = m_TEX0.IsRepeating(); // repeating mode always works, it is just slightly slower
if (m_repeating) if (m_repeating)
{ {
m_p2t = m_state->m_mem.GetPage2TileMap(m_TEX0); m_p2t = g_gs_renderer->m_mem.GetPage2TileMap(m_TEX0);
} }
} }
@ -213,7 +209,7 @@ bool GSTextureCacheSW::Texture::Update(const GSVector4i& rect)
} }
} }
GSLocalMemory& mem = m_state->m_mem; GSLocalMemory& mem = g_gs_renderer->m_mem;
GSOffset off = m_offset; GSOffset off = m_offset;
@ -291,7 +287,7 @@ bool GSTextureCacheSW::Texture::Update(const GSVector4i& rect)
bool GSTextureCacheSW::Texture::Save(const std::string& fn, bool dds) const bool GSTextureCacheSW::Texture::Save(const std::string& fn, bool dds) const
{ {
const u32* RESTRICT clut = m_state->m_mem.m_clut; const u32* RESTRICT clut = g_gs_renderer->m_mem.m_clut;
int w = 1 << m_TEX0.TW; int w = 1 << m_TEX0.TW;
int h = 1 << m_TEX0.TH; int h = 1 << m_TEX0.TH;

View File

@ -25,7 +25,6 @@ public:
class Texture class Texture
{ {
public: public:
GSState* m_state;
GSOffset m_offset; GSOffset m_offset;
GSOffset::PageLooper m_pages; GSOffset::PageLooper m_pages;
GIFRegTEX0 m_TEX0; GIFRegTEX0 m_TEX0;
@ -44,7 +43,7 @@ public:
// fast mode: each u32 bits map to the 32 blocks of that page // fast mode: each u32 bits map to the 32 blocks of that page
// repeating mode: 1 bpp image of the texture tiles (8x8), also having 512 elements is just a coincidence (worst case: (1024*1024)/(8*8)/(sizeof(u32)*8)) // repeating mode: 1 bpp image of the texture tiles (8x8), also having 512 elements is just a coincidence (worst case: (1024*1024)/(8*8)/(sizeof(u32)*8))
Texture(GSState* state, u32 tw0, const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA); Texture(u32 tw0, const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA);
virtual ~Texture(); virtual ~Texture();
bool Update(const GSVector4i& r); bool Update(const GSVector4i& r);
@ -52,12 +51,11 @@ public:
}; };
protected: protected:
GSState* m_state;
std::unordered_set<Texture*> m_textures; std::unordered_set<Texture*> m_textures;
std::array<FastList<Texture*>, MAX_PAGES> m_map; std::array<FastList<Texture*>, MAX_PAGES> m_map;
public: public:
GSTextureCacheSW(GSState* state); GSTextureCacheSW();
virtual ~GSTextureCacheSW(); virtual ~GSTextureCacheSW();
Texture* Lookup(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, u32 tw0 = 0); Texture* Lookup(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, u32 tw0 = 0);