Resolve merge conflicts

This commit is contained in:
Chris Spegal 2023-08-19 22:26:23 -04:00
commit 739b33f733
124 changed files with 3751 additions and 1277 deletions

View file

@ -1,5 +1,6 @@
#include "gui/guiWrapper.h"
#include "Debugger.h"
#include "Cafe/OS/RPL/rpl_structs.h"
#include "Cemu/PPCAssembler/ppcAssembler.h"
#include "Cafe/HW/Espresso/Recompiler/PPCRecompiler.h"
#include "Cemu/ExpressionParser/ExpressionParser.h"
@ -74,7 +75,7 @@ uint32 debugger_getAddressOriginalOpcode(uint32 address)
auto bpItr = debugger_getFirstBP(address);
while (bpItr)
{
if (bpItr->bpType == DEBUGGER_BP_T_NORMAL || bpItr->bpType == DEBUGGER_BP_T_ONE_SHOT)
if (bpItr->isExecuteBP())
return bpItr->originalOpcodeValue;
bpItr = bpItr->next;
}
@ -121,32 +122,23 @@ void debugger_updateExecutionBreakpoint(uint32 address, bool forceRestore)
}
}
void debugger_createExecuteBreakpoint(uint32 address)
void debugger_createCodeBreakpoint(uint32 address, uint8 bpType)
{
// check if breakpoint already exists
auto existingBP = debugger_getFirstBP(address);
if (existingBP && debuggerBPChain_hasType(existingBP, DEBUGGER_BP_T_NORMAL))
if (existingBP && debuggerBPChain_hasType(existingBP, bpType))
return; // breakpoint already exists
// get original opcode at address
uint32 originalOpcode = debugger_getAddressOriginalOpcode(address);
// init breakpoint object
DebuggerBreakpoint* bp = new DebuggerBreakpoint(address, originalOpcode, DEBUGGER_BP_T_NORMAL, true);
DebuggerBreakpoint* bp = new DebuggerBreakpoint(address, originalOpcode, bpType, true);
debuggerBPChain_add(address, bp);
debugger_updateExecutionBreakpoint(address);
}
void debugger_createSingleShotExecuteBreakpoint(uint32 address)
void debugger_createExecuteBreakpoint(uint32 address)
{
// check if breakpoint already exists
auto existingBP = debugger_getFirstBP(address);
if (existingBP && debuggerBPChain_hasType(existingBP, DEBUGGER_BP_T_ONE_SHOT))
return; // breakpoint already exists
// get original opcode at address
uint32 originalOpcode = debugger_getAddressOriginalOpcode(address);
// init breakpoint object
DebuggerBreakpoint* bp = new DebuggerBreakpoint(address, originalOpcode, DEBUGGER_BP_T_ONE_SHOT, true);
debuggerBPChain_add(address, bp);
debugger_updateExecutionBreakpoint(address);
debugger_createCodeBreakpoint(address, DEBUGGER_BP_T_NORMAL);
}
namespace coreinit
@ -218,7 +210,7 @@ void debugger_handleSingleStepException(uint64 dr6)
}
if (catchBP)
{
debugger_createSingleShotExecuteBreakpoint(ppcInterpreterCurrentInstance->instructionPointer + 4);
debugger_createCodeBreakpoint(ppcInterpreterCurrentInstance->instructionPointer + 4, DEBUGGER_BP_T_ONE_SHOT);
}
}
@ -250,7 +242,7 @@ void debugger_handleEntryBreakpoint(uint32 address)
if (!debuggerState.breakOnEntry)
return;
debugger_createExecuteBreakpoint(address);
debugger_createCodeBreakpoint(address, DEBUGGER_BP_T_NORMAL);
}
void debugger_deleteBreakpoint(DebuggerBreakpoint* bp)
@ -298,10 +290,12 @@ void debugger_toggleExecuteBreakpoint(uint32 address)
{
// delete existing breakpoint
debugger_deleteBreakpoint(existingBP);
return;
}
// create new
debugger_createExecuteBreakpoint(address);
else
{
// create new breakpoint
debugger_createExecuteBreakpoint(address);
}
}
void debugger_forceBreak()
@ -327,7 +321,7 @@ void debugger_toggleBreakpoint(uint32 address, bool state, DebuggerBreakpoint* b
{
if (bpItr == bp)
{
if (bpItr->bpType == DEBUGGER_BP_T_NORMAL)
if (bpItr->bpType == DEBUGGER_BP_T_NORMAL || bpItr->bpType == DEBUGGER_BP_T_LOGGING)
{
bp->enabled = state;
debugger_updateExecutionBreakpoint(address);
@ -486,7 +480,7 @@ bool debugger_stepOver(PPCInterpreter_t* hCPU)
return false;
}
// create one-shot breakpoint at next instruction
debugger_createSingleShotExecuteBreakpoint(initialIP +4);
debugger_createCodeBreakpoint(initialIP + 4, DEBUGGER_BP_T_ONE_SHOT);
// step over current instruction (to avoid breakpoint)
debugger_stepInto(hCPU);
debuggerWindow_moveIP();
@ -506,8 +500,39 @@ void debugger_createPPCStateSnapshot(PPCInterpreter_t* hCPU)
debuggerState.debugSession.ppcSnapshot.cr[i] = hCPU->cr[i];
}
void DebugLogStackTrace(OSThread_t* thread, MPTR sp);
void debugger_enterTW(PPCInterpreter_t* hCPU)
{
// handle logging points
DebuggerBreakpoint* bp = debugger_getFirstBP(hCPU->instructionPointer);
bool shouldBreak = debuggerBPChain_hasType(bp, DEBUGGER_BP_T_NORMAL) || debuggerBPChain_hasType(bp, DEBUGGER_BP_T_ONE_SHOT);
while (bp)
{
if (bp->bpType == DEBUGGER_BP_T_LOGGING && bp->enabled)
{
std::wstring logName = !bp->comment.empty() ? L"Breakpoint '"+bp->comment+L"'" : fmt::format(L"Breakpoint at 0x{:08X} (no comment)", bp->address);
std::wstring logContext = fmt::format(L"Thread: {:08x} LR: 0x{:08x}", coreinitThread_getCurrentThreadMPTRDepr(hCPU), hCPU->spr.LR, cemuLog_advancedPPCLoggingEnabled() ? L" Stack Trace:" : L"");
cemuLog_log(LogType::Force, L"[Debugger] {} was executed! {}", logName, logContext);
if (cemuLog_advancedPPCLoggingEnabled())
DebugLogStackTrace(coreinitThread_getCurrentThreadDepr(hCPU), hCPU->gpr[1]);
break;
}
bp = bp->next;
}
// return early if it's only a non-pausing logging breakpoint to prevent a modified debugger state and GUI updates
if (!shouldBreak)
{
uint32 backupIP = debuggerState.debugSession.instructionPointer;
debuggerState.debugSession.instructionPointer = hCPU->instructionPointer;
debugger_stepInto(hCPU, false);
PPCInterpreterSlim_executeInstruction(hCPU);
debuggerState.debugSession.instructionPointer = backupIP;
return;
}
// handle breakpoints
debuggerState.debugSession.isTrapped = true;
debuggerState.debugSession.debuggedThreadMPTR = coreinitThread_getCurrentThreadMPTRDepr(hCPU);
debuggerState.debugSession.instructionPointer = hCPU->instructionPointer;
@ -579,6 +604,20 @@ void debugger_shouldBreak(PPCInterpreter_t* hCPU)
void debugger_addParserSymbols(class ExpressionParser& ep)
{
const auto module_count = RPLLoader_GetModuleCount();
const auto module_list = RPLLoader_GetModuleList();
std::vector<double> module_tmp(module_count);
for (int i = 0; i < module_count; i++)
{
const auto module = module_list[i];
if (module)
{
module_tmp[i] = (double)module->regionMappingBase_text.GetMPTR();
ep.AddConstant(module->moduleName2, module_tmp[i]);
}
}
for (sint32 i = 0; i < 32; i++)
ep.AddConstant(fmt::format("r{}", i), debuggerState.debugSession.ppcSnapshot.gpr[i]);
}

View file

@ -7,6 +7,7 @@
#define DEBUGGER_BP_T_ONE_SHOT 1 // normal breakpoint, deletes itself after trigger (used for stepping)
#define DEBUGGER_BP_T_MEMORY_READ 2 // memory breakpoint
#define DEBUGGER_BP_T_MEMORY_WRITE 3 // memory breakpoint
#define DEBUGGER_BP_T_LOGGING 4 // logging breakpoint, prints the breakpoint comment and stack trace whenever hit
#define DEBUGGER_BP_T_GDBSTUB 1 // breakpoint created by GDBStub
#define DEBUGGER_BP_T_DEBUGGER 2 // breakpoint created by Cemu's debugger
@ -42,7 +43,7 @@ struct DebuggerBreakpoint
bool isExecuteBP() const
{
return bpType == DEBUGGER_BP_T_NORMAL || bpType == DEBUGGER_BP_T_ONE_SHOT;
return bpType == DEBUGGER_BP_T_NORMAL || bpType == DEBUGGER_BP_T_LOGGING || bpType == DEBUGGER_BP_T_ONE_SHOT;
}
bool isMemBP() const
@ -98,8 +99,9 @@ extern debuggerState_t debuggerState;
// new API
DebuggerBreakpoint* debugger_getFirstBP(uint32 address);
void debugger_toggleExecuteBreakpoint(uint32 address); // create/remove execute breakpoint
void debugger_createCodeBreakpoint(uint32 address, uint8 bpType);
void debugger_createExecuteBreakpoint(uint32 address);
void debugger_toggleExecuteBreakpoint(uint32 address); // create/remove execute breakpoint
void debugger_toggleBreakpoint(uint32 address, bool state, DebuggerBreakpoint* bp);
void debugger_createMemoryBreakpoint(uint32 address, bool onRead, bool onWrite);

View file

@ -100,11 +100,6 @@ PPCInterpreter_t* PPCCore_executeCallbackInternal(uint32 functionMPTR)
return hCPU;
}
void PPCCore_deleteAllThreads()
{
assert_dbg();
}
void PPCCore_init()
{
}

View file

@ -236,7 +236,6 @@ HLECALL PPCInterpreter_getHLECall(HLEIDX funcIndex);
// HLE scheduler
void PPCCore_deleteAllThreads();
void PPCInterpreter_relinquishTimeslice();
void PPCCore_boostQuantum(sint32 numCycles);

View file

@ -289,11 +289,16 @@ void PPCRecompiler_recompileAtAddress(uint32 address)
bool r = PPCRecompiler_makeRecompiledFunctionActive(address, range, func, functionEntryPoints);
}
std::thread s_threadRecompiler;
std::atomic_bool s_recompilerThreadStopSignal{false};
void PPCRecompiler_thread()
{
SetThreadName("PPCRecompiler_thread");
while (true)
{
if(s_recompilerThreadStopSignal)
return;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
// asynchronous recompilation:
// 1) take address from queue
@ -326,7 +331,12 @@ void PPCRecompiler_thread()
#define PPC_REC_ALLOC_BLOCK_SIZE (4*1024*1024) // 4MB
std::bitset<(MEMORY_CODEAREA_ADDR + MEMORY_CODEAREA_SIZE) / PPC_REC_ALLOC_BLOCK_SIZE> ppcRecompiler_reservedBlockMask;
constexpr uint32 PPCRecompiler_GetNumAddressSpaceBlocks()
{
return (MEMORY_CODEAREA_ADDR + MEMORY_CODEAREA_SIZE + PPC_REC_ALLOC_BLOCK_SIZE - 1) / PPC_REC_ALLOC_BLOCK_SIZE;
}
std::bitset<PPCRecompiler_GetNumAddressSpaceBlocks()> ppcRecompiler_reservedBlockMask;
void PPCRecompiler_reserveLookupTableBlock(uint32 offset)
{
@ -496,16 +506,9 @@ void PPCRecompiler_init()
MemMapper::AllocateMemory(&(ppcRecompilerInstanceData->_x64XMM_xorNegateMaskBottom), sizeof(PPCRecompilerInstanceData_t) - offsetof(PPCRecompilerInstanceData_t, _x64XMM_xorNegateMaskBottom), MemMapper::PAGE_PERMISSION::P_RW, true);
PPCRecompilerX64Gen_generateRecompilerInterfaceFunctions();
uint32 codeRegionEnd = RPLLoader_GetMaxCodeOffset();
codeRegionEnd = (codeRegionEnd + PPC_REC_ALLOC_BLOCK_SIZE - 1) & ~(PPC_REC_ALLOC_BLOCK_SIZE - 1);
uint32 codeRegionSize = codeRegionEnd - PPC_REC_CODE_AREA_START;
cemuLog_logDebug(LogType::Force, "Allocating recompiler tables for range 0x{:08x}-0x{:08x}", PPC_REC_CODE_AREA_START, codeRegionEnd);
for (uint32 i = 0; i < codeRegionSize; i += PPC_REC_ALLOC_BLOCK_SIZE)
{
PPCRecompiler_reserveLookupTableBlock(i);
}
PPCRecompiler_allocateRange(0, 0x1000); // the first entry is used for fallback to interpreter
PPCRecompiler_allocateRange(mmuRange_TRAMPOLINE_AREA.getBase(), mmuRange_TRAMPOLINE_AREA.getSize());
PPCRecompiler_allocateRange(mmuRange_CODECAVE.getBase(), mmuRange_CODECAVE.getSize());
// init x64 recompiler instance data
ppcRecompilerInstanceData->_x64XMM_xorNegateMaskBottom[0] = 1ULL << 63ULL;
@ -589,6 +592,33 @@ void PPCRecompiler_init()
ppcRecompilerEnabled = true;
// launch recompilation thread
std::thread t_recompiler(PPCRecompiler_thread);
t_recompiler.detach();
s_recompilerThreadStopSignal = false;
s_threadRecompiler = std::thread(PPCRecompiler_thread);
}
void PPCRecompiler_Shutdown()
{
// shut down recompiler thread
s_recompilerThreadStopSignal = true;
if(s_threadRecompiler.joinable())
s_threadRecompiler.join();
// clean up queues
while(!PPCRecompilerState.targetQueue.empty())
PPCRecompilerState.targetQueue.pop();
PPCRecompilerState.invalidationRanges.clear();
// clean range store
rangeStore_ppcRanges.clear();
// clean up memory
uint32 numBlocks = PPCRecompiler_GetNumAddressSpaceBlocks();
for(uint32 i=0; i<numBlocks; i++)
{
if(!ppcRecompiler_reservedBlockMask[i])
continue;
// deallocate
uint64 offset = i * PPC_REC_ALLOC_BLOCK_SIZE;
MemMapper::FreeMemory(&(ppcRecompilerInstanceData->ppcRecompilerFuncTable[offset/4]), (PPC_REC_ALLOC_BLOCK_SIZE/4)*sizeof(void*), true);
MemMapper::FreeMemory(&(ppcRecompilerInstanceData->ppcRecompilerDirectJumpTable[offset/4]), (PPC_REC_ALLOC_BLOCK_SIZE/4)*sizeof(void*), true);
// mark as unmapped
ppcRecompiler_reservedBlockMask[i] = false;
}
}

View file

@ -373,6 +373,7 @@ extern PPCRecompilerInstanceData_t* ppcRecompilerInstanceData;
extern bool ppcRecompilerEnabled;
void PPCRecompiler_init();
void PPCRecompiler_Shutdown();
void PPCRecompiler_allocateRange(uint32 startAddress, uint32 size);

View file

@ -84,6 +84,10 @@ extern uint8* gxRingBufferReadPtr; // currently active read pointer (gx2 ring bu
void LatteTextureLoader_estimateAccessedDataRange(LatteTexture* texture, sint32 sliceIndex, sint32 mipIndex, uint32& addrStart, uint32& addrEnd);
// render target
#define RENDER_TARGET_TV (1 << 0)
#define RENDER_TARGET_DRC (1 << 2)
void LatteRenderTarget_updateScissorBox();
void LatteRenderTarget_trackUpdates();
@ -173,4 +177,5 @@ void LatteRenderTarget_updateViewport();
// Latte emulation control
void Latte_Start();
void Latte_Stop();
bool Latte_IsActive();
bool Latte_GetStopSignal(); // returns true if stop was requested or if in stopped state
void LatteThread_Exit();

View file

@ -96,7 +96,7 @@ void LatteAsyncCommands_waitUntilAllProcessed()
void LatteAsyncCommands_checkAndExecute()
{
// quick check if queue is empty (requires no lock)
if (!Latte_IsActive())
if (Latte_GetStopSignal())
LatteThread_Exit();
if (LatteAsyncCommandQueue.empty())
return;

View file

@ -257,6 +257,11 @@ public:
}
}
bool empty() const
{
return m_map.empty();
}
const std::map<InternalRange, TNodeObject*>& getAll() const { return m_map; };
};
@ -455,48 +460,6 @@ public:
}
if(m_invalidationRangeEnd <= m_invalidationRangeBegin)
m_hasInvalidation = false;
//if (resRangeBegin <= m_invalidationRangeBegin)
//{
// // shrink/replace invalidation range from the bottom
// uint32 uploadBegin = m_invalidationRangeBegin;//std::max(m_invalidationRangeBegin, resRangeBegin);
// uint32 uploadEnd = std::min(resRangeEnd, m_invalidationRangeEnd);
// cemu_assert_debug(uploadEnd >= uploadBegin);
// if (uploadBegin != uploadEnd)
// checkAndSyncModifications(uploadBegin, uploadEnd, true);
// m_invalidationRangeBegin = uploadEnd;
// cemu_assert_debug(m_invalidationRangeBegin <= m_invalidationRangeEnd);
// if (m_invalidationRangeBegin >= m_invalidationRangeEnd)
// m_hasInvalidation = false;
//}
//else if (resRangeEnd >= m_invalidationRangeEnd)
//{
// // shrink/replace invalidation range from the top
// uint32 uploadBegin = std::max(m_invalidationRangeBegin, resRangeBegin);
// uint32 uploadEnd = m_invalidationRangeEnd;// std::min(resRangeEnd, m_invalidationRangeEnd);
// cemu_assert_debug(uploadEnd >= uploadBegin);
// if (uploadBegin != uploadEnd)
// checkAndSyncModifications(uploadBegin, uploadEnd, true);
// m_invalidationRangeEnd = uploadBegin;
// cemu_assert_debug(m_invalidationRangeBegin <= m_invalidationRangeEnd);
// if (m_invalidationRangeBegin >= m_invalidationRangeEnd)
// m_hasInvalidation = false;
//}
//else
//{
// // since we cant cut holes into the range upload it in it's entirety
// cemu_assert_debug(m_invalidationRangeEnd <= m_rangeEnd);
// cemu_assert_debug(m_invalidationRangeBegin >= m_rangeBegin);
// cemu_assert_debug(m_invalidationRangeBegin < m_invalidationRangeEnd);
// checkAndSyncModifications(m_invalidationRangeBegin, m_invalidationRangeEnd, true);
// m_hasInvalidation = false;
//}
// todo - dont re-upload the whole range immediately
// under ideal circumstances we would only upload the data range requested for the current draw call
// but this is a hot path so we can't check
}
}
@ -827,6 +790,21 @@ private:
static std::vector<uint32> g_deallocateQueue;
public:
static void UnloadAll()
{
size_t i = 0;
while (i < s_allCacheNodes.size())
{
BufferCacheNode* node = s_allCacheNodes[i];
node->ReleaseCacheMemoryImmediately();
LatteBufferCache_removeSingleNodeFromTree(node);
delete node;
}
for(auto& it : s_allCacheNodes)
delete it;
s_allCacheNodes.clear();
g_deallocateQueue.clear();
}
static void ProcessDeallocations()
{
@ -931,7 +909,6 @@ public:
};
std::vector<uint32> BufferCacheNode::g_deallocateQueue;
IntervalTree2<MPTR, BufferCacheNode> g_gpuBufferCache;
void LatteBufferCache_removeSingleNodeFromTree(BufferCacheNode* node)
@ -1009,10 +986,16 @@ void LatteBufferCache_processDeallocations()
void LatteBufferCache_init(size_t bufferSize)
{
cemu_assert_debug(g_gpuBufferCache.empty());
g_gpuBufferHeap.reset(new VHeap(nullptr, (uint32)bufferSize));
g_renderer->bufferCache_init((uint32)bufferSize);
}
void LatteBufferCache_UnloadAll()
{
BufferCacheNode::UnloadAll();
}
void LatteBufferCache_getStats(uint32& heapSize, uint32& allocationSize, uint32& allocNum)
{
g_gpuBufferHeap->getStats(heapSize, allocationSize, allocNum);

View file

@ -1,6 +1,7 @@
#pragma once
void LatteBufferCache_init(size_t bufferSize);
void LatteBufferCache_UnloadAll();
uint32 LatteBufferCache_retrieveDataInCache(MPTR physAddress, uint32 size);
void LatteBufferCache_copyStreamoutDataToCache(MPTR physAddress, uint32 size, uint32 streamoutBufferOffset);

View file

@ -125,7 +125,7 @@ uint32 LatteCP_readU32Deprc()
readDistance = (sint32)(gxRingBufferWritePtr - gxRingBufferReadPtr);
if (readDistance != 0)
break;
if (!Latte_IsActive())
if (Latte_GetStopSignal())
LatteThread_Exit();
// still no command data available, do some other tasks
@ -172,7 +172,7 @@ void LatteCP_waitForNWords(uint32 numWords)
if (readDistance >= waitDistance)
break;
if (!Latte_IsActive())
if (Latte_GetStopSignal())
LatteThread_Exit();
// still no command data available, do some other tasks

View file

@ -373,6 +373,7 @@ uint8 LatteMRT::GetActiveColorBufferMask(const LatteDecompilerShader* pixelShade
if ((colorBufferWidth < (sint32)scissorAccessWidth) ||
(colorBufferHeight < (sint32)scissorAccessHeight))
{
// log this?
colorBufferMask &= ~(1<<i);
}
@ -1047,9 +1048,9 @@ void LatteRenderTarget_itHLECopyColorBufferToScanBuffer(MPTR colorBufferPtr, uin
}
}
if (renderTarget == 4 && g_renderer->IsPadWindowActive())
if ((renderTarget & RENDER_TARGET_DRC) && g_renderer->IsPadWindowActive())
LatteRenderTarget_copyToBackbuffer(texView, true);
if ((renderTarget == 1 && !showDRC) || (renderTarget == 4 && showDRC))
if (((renderTarget & RENDER_TARGET_TV) && !showDRC) || ((renderTarget & RENDER_TARGET_DRC) && showDRC))
LatteRenderTarget_copyToBackbuffer(texView, false);
}

View file

@ -144,7 +144,7 @@ LatteShaderPSInputTable* LatteSHRC_GetPSInputTable()
return &_activePSImportTable;
}
bool LatteSHRC_RemoveFromCache(LatteDecompilerShader* shader)
void LatteSHRC_RemoveFromCache(LatteDecompilerShader* shader)
{
bool removed = false;
auto& cache = LatteSHRC_GetCacheByType(shader->shaderType);
@ -156,10 +156,14 @@ bool LatteSHRC_RemoveFromCache(LatteDecompilerShader* shader)
}
else if (baseIt->second == shader)
{
if (baseIt->second->next)
cache.emplace(shader->baseHash, baseIt->second->next);
else
cache.erase(baseIt);
cemu_assert_debug(baseIt->second == shader);
cache.erase(baseIt);
if (shader->next)
{
cemu_assert_debug(shader->baseHash == shader->next->baseHash);
cache.emplace(shader->baseHash, shader->next);
}
shader->next = 0;
removed = true;
}
else
@ -176,7 +180,7 @@ bool LatteSHRC_RemoveFromCache(LatteDecompilerShader* shader)
}
}
}
return removed;
cemu_assert(removed);
}
void LatteSHRC_RemoveFromCacheByHash(uint64 shader_base_hash, uint64 shader_aux_hash, LatteConst::ShaderType type)
@ -1009,3 +1013,16 @@ void LatteSHRC_Init()
cemu_assert_debug(sGeometryShaders.empty());
cemu_assert_debug(sPixelShaders.empty());
}
void LatteSHRC_UnloadAll()
{
while(!sVertexShaders.empty())
LatteShader_free(sVertexShaders.begin()->second);
cemu_assert_debug(sVertexShaders.empty());
while(!sGeometryShaders.empty())
LatteShader_free(sGeometryShaders.begin()->second);
cemu_assert_debug(sGeometryShaders.empty());
while(!sPixelShaders.empty())
LatteShader_free(sPixelShaders.begin()->second);
cemu_assert_debug(sPixelShaders.empty());
}

View file

@ -3,6 +3,7 @@
#include "Cafe/HW/Latte/ISA/RegDefines.h"
void LatteSHRC_Init();
void LatteSHRC_UnloadAll();
void LatteSHRC_ResetCachedShaderHash();
void LatteShaderSHRC_UpdateFetchShader();
@ -117,11 +118,12 @@ void LatteShader_DumpShader(uint64 baseHash, uint64 auxHash, LatteDecompilerShad
void LatteShader_DumpRawShader(uint64 baseHash, uint64 auxHash, uint32 type, uint8* programCode, uint32 programLen);
// shader cache file
void LatteShaderCache_load();
void LatteShaderCache_Load();
void LatteShaderCache_Close();
void LatteShaderCache_writeSeparableVertexShader(uint64 shaderBaseHash, uint64 shaderAuxHash, uint8* fetchShader, uint32 fetchShaderSize, uint8* vertexShader, uint32 vertexShaderSize, uint32* contextRegisters, bool usesGeometryShader);
void LatteShaderCache_writeSeparableGeometryShader(uint64 shaderBaseHash, uint64 shaderAuxHash, uint8* geometryShader, uint32 geometryShaderSize, uint8* gsCopyShader, uint32 gsCopyShaderSize, uint32* contextRegisters, uint32* hleSpecialState, uint32 vsRingParameterCount);
void LatteShaderCache_writeSeparablePixelShader(uint64 shaderBaseHash, uint64 shaderAuxHash, uint8* pixelShader, uint32 pixelShaderSize, uint32* contextRegisters, bool usesGeometryShader);
// todo - sort this
// todo - refactor this
sint32 LatteDecompiler_getTextureSamplerBaseIndex(LatteConst::ShaderType shaderType);

View file

@ -53,7 +53,7 @@ struct
sint32 pipelineFileCount;
}g_shaderCacheLoaderState;
FileCache* fc_shaderCacheGeneric = nullptr; // contains hardware and Cemu version independent shader information
FileCache* s_shaderCacheGeneric = nullptr; // contains hardware and version independent shader information
#define SHADER_CACHE_GENERIC_EXTRA_VERSION 2 // changing this constant will invalidate all hardware-independent cache files
@ -62,7 +62,7 @@ FileCache* fc_shaderCacheGeneric = nullptr; // contains hardware and Cemu versio
#define SHADER_CACHE_TYPE_PIXEL (2)
bool LatteShaderCache_readSeparableShader(uint8* shaderInfoData, sint32 shaderInfoSize);
void LatteShaderCache_loadVulkanPipelineCache(uint64 cacheTitleId);
void LatteShaderCache_LoadVulkanPipelineCache(uint64 cacheTitleId);
bool LatteShaderCache_updatePipelineLoadingProgress();
void LatteShaderCache_ShowProgress(const std::function <bool(void)>& loadUpdateFunc, bool isPipelines);
@ -216,7 +216,7 @@ void LatteShaderCache_drawBackgroundImage(ImTextureID texture, int width, int he
ImGui::PopStyleVar(2);
}
void LatteShaderCache_load()
void LatteShaderCache_Load()
{
shaderCacheScreenStats.compiledShaderCount = 0;
shaderCacheScreenStats.vertexShaderCount = 0;
@ -251,21 +251,21 @@ void LatteShaderCache_load()
LatteShaderCache_handleDeprecatedCacheFiles(pathGeneric, pathGenericPre1_25_0, pathGenericPre1_16_0);
// calculate extraVersion for transferable and precompiled shader cache
uint32 transferableExtraVersion = SHADER_CACHE_GENERIC_EXTRA_VERSION;
fc_shaderCacheGeneric = FileCache::Open(pathGeneric.generic_wstring(), false, transferableExtraVersion); // legacy extra version (1.25.0 - 1.25.1b)
if(!fc_shaderCacheGeneric)
fc_shaderCacheGeneric = FileCache::Open(pathGeneric.generic_wstring(), true, LatteShaderCache_getShaderCacheExtraVersion(cacheTitleId));
if(!fc_shaderCacheGeneric)
s_shaderCacheGeneric = FileCache::Open(pathGeneric, false, transferableExtraVersion); // legacy extra version (1.25.0 - 1.25.1b)
if(!s_shaderCacheGeneric)
s_shaderCacheGeneric = FileCache::Open(pathGeneric, true, LatteShaderCache_getShaderCacheExtraVersion(cacheTitleId));
if(!s_shaderCacheGeneric)
{
// no shader cache available yet
cemuLog_log(LogType::Force, "Unable to open or create shader cache file \"{}\"", _pathToUtf8(pathGeneric));
LatteShaderCache_finish();
return;
}
fc_shaderCacheGeneric->UseCompression(false);
s_shaderCacheGeneric->UseCompression(false);
// load/compile cached shaders
sint32 entryCount = fc_shaderCacheGeneric->GetMaximumFileIndex();
g_shaderCacheLoaderState.shaderFileCount = fc_shaderCacheGeneric->GetFileCount();
sint32 entryCount = s_shaderCacheGeneric->GetMaximumFileIndex();
g_shaderCacheLoaderState.shaderFileCount = s_shaderCacheGeneric->GetFileCount();
g_shaderCacheLoaderState.loadedShaderFiles = 0;
// get game background loading image
@ -304,13 +304,13 @@ void LatteShaderCache_load()
auto LoadShadersUpdate = [&]() -> bool
{
if (loadIndex >= (uint32)fc_shaderCacheGeneric->GetMaximumFileIndex())
if (loadIndex >= (uint32)s_shaderCacheGeneric->GetMaximumFileIndex())
return false;
LatteShaderCache_updateCompileQueue(SHADER_CACHE_COMPILE_QUEUE_SIZE - 2);
uint64 name1;
uint64 name2;
std::vector<uint8> fileData;
if (!fc_shaderCacheGeneric->GetFileByIndex(loadIndex, &name1, &name2, fileData))
if (!s_shaderCacheGeneric->GetFileByIndex(loadIndex, &name1, &name2, fileData))
{
loadIndex++;
return true;
@ -320,7 +320,7 @@ void LatteShaderCache_load()
{
// something is wrong with the stored shader, remove entry from shader cache files
cemuLog_log(LogType::Force, "Shader cache entry {} invalid, deleting...", loadIndex);
fc_shaderCacheGeneric->DeleteFile({ name1, name2 });
s_shaderCacheGeneric->DeleteFile({name1, name2 });
}
numLoadedShaders++;
loadIndex++;
@ -343,7 +343,7 @@ void LatteShaderCache_load()
LatteShaderCache_finish();
// if Vulkan then also load pipeline cache
if (g_renderer->GetType() == RendererAPI::Vulkan)
LatteShaderCache_loadVulkanPipelineCache(cacheTitleId);
LatteShaderCache_LoadVulkanPipelineCache(cacheTitleId);
g_renderer->BeginFrame(true);
@ -376,6 +376,8 @@ void LatteShaderCache_ShowProgress(const std::function <bool(void)>& loadUpdateF
while (true)
{
if (Latte_GetStopSignal())
break; // thread stop requested, cancel shader loading
bool r = loadUpdateFunc();
if (!r)
break;
@ -496,13 +498,15 @@ void LatteShaderCache_ShowProgress(const std::function <bool(void)>& loadUpdateF
}
}
void LatteShaderCache_loadVulkanPipelineCache(uint64 cacheTitleId)
void LatteShaderCache_LoadVulkanPipelineCache(uint64 cacheTitleId)
{
auto& pipelineCache = VulkanPipelineStableCache::GetInstance();
g_shaderCacheLoaderState.pipelineFileCount = pipelineCache.BeginLoading(cacheTitleId);
g_shaderCacheLoaderState.loadedPipelines = 0;
LatteShaderCache_ShowProgress(LatteShaderCache_updatePipelineLoadingProgress, true);
pipelineCache.EndLoading();
if(Latte_GetStopSignal())
LatteThread_Exit();
}
bool LatteShaderCache_updatePipelineLoadingProgress()
@ -520,7 +524,7 @@ uint64 LatteShaderCache_getShaderNameInTransferableCache(uint64 baseHash, uint32
void LatteShaderCache_writeSeparableVertexShader(uint64 shaderBaseHash, uint64 shaderAuxHash, uint8* fetchShader, uint32 fetchShaderSize, uint8* vertexShader, uint32 vertexShaderSize, uint32* contextRegisters, bool usesGeometryShader)
{
if (!fc_shaderCacheGeneric)
if (!s_shaderCacheGeneric)
return;
MemStreamWriter streamWriter(128 * 1024);
// header
@ -539,12 +543,12 @@ void LatteShaderCache_writeSeparableVertexShader(uint64 shaderBaseHash, uint64 s
// write to cache
uint64 shaderCacheName = LatteShaderCache_getShaderNameInTransferableCache(shaderBaseHash, SHADER_CACHE_TYPE_VERTEX);
std::span<uint8> dataBlob = streamWriter.getResult();
fc_shaderCacheGeneric->AddFileAsync({ shaderCacheName, shaderAuxHash }, dataBlob.data(), dataBlob.size());
s_shaderCacheGeneric->AddFileAsync({shaderCacheName, shaderAuxHash }, dataBlob.data(), dataBlob.size());
}
void LatteShaderCache_writeSeparableGeometryShader(uint64 shaderBaseHash, uint64 shaderAuxHash, uint8* geometryShader, uint32 geometryShaderSize, uint8* gsCopyShader, uint32 gsCopyShaderSize, uint32* contextRegisters, uint32* hleSpecialState, uint32 vsRingParameterCount)
{
if (!fc_shaderCacheGeneric)
if (!s_shaderCacheGeneric)
return;
MemStreamWriter streamWriter(128 * 1024);
// header
@ -564,12 +568,12 @@ void LatteShaderCache_writeSeparableGeometryShader(uint64 shaderBaseHash, uint64
// write to cache
uint64 shaderCacheName = LatteShaderCache_getShaderNameInTransferableCache(shaderBaseHash, SHADER_CACHE_TYPE_GEOMETRY);
std::span<uint8> dataBlob = streamWriter.getResult();
fc_shaderCacheGeneric->AddFileAsync({ shaderCacheName, shaderAuxHash }, dataBlob.data(), dataBlob.size());
s_shaderCacheGeneric->AddFileAsync({shaderCacheName, shaderAuxHash }, dataBlob.data(), dataBlob.size());
}
void LatteShaderCache_writeSeparablePixelShader(uint64 shaderBaseHash, uint64 shaderAuxHash, uint8* pixelShader, uint32 pixelShaderSize, uint32* contextRegisters, bool usesGeometryShader)
{
if (!fc_shaderCacheGeneric)
if (!s_shaderCacheGeneric)
return;
MemStreamWriter streamWriter(128 * 1024);
streamWriter.writeBE<uint8>(1 | (SHADER_CACHE_TYPE_PIXEL << 4)); // version and type (shared field)
@ -585,7 +589,7 @@ void LatteShaderCache_writeSeparablePixelShader(uint64 shaderBaseHash, uint64 sh
// write to cache
uint64 shaderCacheName = LatteShaderCache_getShaderNameInTransferableCache(shaderBaseHash, SHADER_CACHE_TYPE_PIXEL);
std::span<uint8> dataBlob = streamWriter.getResult();
fc_shaderCacheGeneric->AddFileAsync({ shaderCacheName, shaderAuxHash }, dataBlob.data(), dataBlob.size());
s_shaderCacheGeneric->AddFileAsync({shaderCacheName, shaderAuxHash }, dataBlob.data(), dataBlob.size());
}
void LatteShaderCache_loadOrCompileSeparableShader(LatteDecompilerShader* shader, uint64 shaderBaseHash, uint64 shaderAuxHash)
@ -759,6 +763,23 @@ bool LatteShaderCache_readSeparableShader(uint8* shaderInfoData, sint32 shaderIn
return false;
}
void LatteShaderCache_Close()
{
if(s_shaderCacheGeneric)
{
delete s_shaderCacheGeneric;
s_shaderCacheGeneric = nullptr;
}
if (g_renderer->GetType() == RendererAPI::Vulkan)
RendererShaderVk::ShaderCacheLoading_Close();
else if (g_renderer->GetType() == RendererAPI::OpenGL)
RendererShaderGL::ShaderCacheLoading_Close();
// if Vulkan then also close pipeline cache
if (g_renderer->GetType() == RendererAPI::Vulkan)
VulkanPipelineStableCache::GetInstance().Close();
}
#include <wx/msgdlg.h>
void LatteShaderCache_handleDeprecatedCacheFiles(fs::path pathGeneric, fs::path pathGenericPre1_25_0, fs::path pathGenericPre1_16_0)

View file

@ -183,9 +183,8 @@ int Latte_ThreadEntry()
// before doing anything with game specific shaders, we need to wait for graphic packs to finish loading
GraphicPack2::WaitUntilReady();
// load/init shader cache file
LatteShaderCache_load();
// load disk shader cache
LatteShaderCache_Load();
// init registers
Latte_LoadInitialRegisters();
// let CPU thread know the GPU is done initializing
@ -196,7 +195,7 @@ int Latte_ThreadEntry()
std::this_thread::yield();
std::this_thread::sleep_for(std::chrono::milliseconds(1));
LatteThread_HandleOSScreen();
if (!Latte_IsActive())
if (Latte_GetStopSignal())
LatteThread_Exit();
}
gxRingBufferReadPtr = gx2WriteGatherPipe.gxRingBuffer;
@ -232,20 +231,24 @@ void Latte_Stop()
sLatteThread.join();
}
bool Latte_IsActive()
bool Latte_GetStopSignal()
{
return sLatteThreadRunning;
return !sLatteThreadRunning;
}
void LatteThread_Exit()
{
if (g_renderer)
g_renderer->Shutdown();
// clean up vertex/uniform cache
LatteBufferCache_UnloadAll();
// clean up texture cache
LatteTC_UnloadAllTextures();
// clean up runtime shader cache
// todo
// destroy renderer but make sure that g_renderer remains valid until the destructor has finished
LatteSHRC_UnloadAll();
// close disk cache
LatteShaderCache_Close();
// destroy renderer but make sure that g_renderer remains valid until the destructor has finished
if (g_renderer)
{
Renderer* renderer = g_renderer.get();

View file

@ -363,9 +363,6 @@ void OpenGLRenderer::NotifyLatteCommandProcessorIdle()
glFlush();
}
bool IsRunningInWine();
void OpenGLRenderer::GetVendorInformation()
{
// example vendor strings:

View file

@ -13,7 +13,7 @@ bool s_isLoadingShaders{false};
bool RendererShaderGL::loadBinary()
{
if (!g_programBinaryCache)
if (!s_programBinaryCache)
return false;
if (m_isGameShader == false || m_isGfxPackShader)
return false; // only non-custom
@ -25,7 +25,7 @@ bool RendererShaderGL::loadBinary()
GenerateShaderPrecompiledCacheFilename(m_type, m_baseHash, m_auxHash, h1, h2);
sint32 fileSize = 0;
std::vector<uint8> cacheFileData;
if (!g_programBinaryCache->GetFile({ h1, h2 }, cacheFileData))
if (!s_programBinaryCache->GetFile({h1, h2 }, cacheFileData))
return false;
if (fileSize < sizeof(uint32))
{
@ -51,7 +51,7 @@ bool RendererShaderGL::loadBinary()
void RendererShaderGL::storeBinary()
{
if (!g_programBinaryCache)
if (!s_programBinaryCache)
return;
if (!glGetProgramBinary)
return;
@ -72,7 +72,7 @@ void RendererShaderGL::storeBinary()
glGetProgramBinary(m_program, binaryLength, NULL, &binaryFormat, storedBinary.data()+sizeof(uint32));
*(uint32*)(storedBinary.data() + 0) = binaryFormat;
// store
g_programBinaryCache->AddFileAsync({ h1, h2 }, storedBinary.data(), storedBinary.size());
s_programBinaryCache->AddFileAsync({h1, h2 }, storedBinary.data(), storedBinary.size());
}
}
@ -247,12 +247,7 @@ void RendererShaderGL::SetUniform4iv(sint32 location, void* data, sint32 count)
void RendererShaderGL::ShaderCacheLoading_begin(uint64 cacheTitleId)
{
if (g_programBinaryCache)
{
delete g_programBinaryCache;
g_programBinaryCache = nullptr;
}
cemu_assert_debug(!s_programBinaryCache); // should not be set, ShaderCacheLoading_Close() not called?
// determine if cache is enabled
bool usePrecompiled = false;
switch (ActiveSettings::GetPrecompiledShadersOption())
@ -279,9 +274,8 @@ void RendererShaderGL::ShaderCacheLoading_begin(uint64 cacheTitleId)
{
const uint32 cacheMagic = GeneratePrecompiledCacheId();
const std::string cacheFilename = fmt::format("{:016x}_gl.bin", cacheTitleId);
const std::wstring cachePath = ActiveSettings::GetCachePath("shaderCache/precompiled/{}", cacheFilename).generic_wstring();
g_programBinaryCache = FileCache::Open(cachePath, true, cacheMagic);
if (g_programBinaryCache == nullptr)
s_programBinaryCache = FileCache::Open(ActiveSettings::GetCachePath("shaderCache/precompiled/{}", cacheFilename), true, cacheMagic);
if (s_programBinaryCache == nullptr)
cemuLog_log(LogType::Force, "Unable to open OpenGL precompiled cache {}", cacheFilename);
}
s_isLoadingShaders = true;
@ -292,4 +286,15 @@ void RendererShaderGL::ShaderCacheLoading_end()
s_isLoadingShaders = false;
}
FileCache* RendererShaderGL::g_programBinaryCache{};
void RendererShaderGL::ShaderCacheLoading_Close()
{
if(s_programBinaryCache)
{
delete s_programBinaryCache;
s_programBinaryCache = nullptr;
}
g_compiled_shaders_total = 0;
g_compiled_shaders_async = 0;
}
FileCache* RendererShaderGL::s_programBinaryCache{};

View file

@ -24,6 +24,7 @@ public:
static void ShaderCacheLoading_begin(uint64 cacheTitleId);
static void ShaderCacheLoading_end();
static void ShaderCacheLoading_Close();
private:
GLuint m_program;
@ -37,6 +38,6 @@ private:
bool m_shader_attached{ false };
bool m_isCompiled{ false };
static class FileCache* g_programBinaryCache;
static class FileCache* s_programBinaryCache;
};

View file

@ -62,6 +62,7 @@ void Renderer::Shutdown()
// imgui
ImGui::DestroyContext(imguiTVContext);
ImGui::DestroyContext(imguiPadContext);
ImGui_ClearFonts();
delete imguiFontAtlas;
}

View file

@ -451,3 +451,9 @@ void RendererShaderVk::ShaderCacheLoading_end()
// keep g_spirvCache open since we will write to it while the game is running
s_isLoadingShadersVk = false;
}
void RendererShaderVk::ShaderCacheLoading_Close()
{
delete s_spirvCache;
s_spirvCache = nullptr;
}

View file

@ -22,7 +22,8 @@ class RendererShaderVk : public RendererShader
public:
static void ShaderCacheLoading_begin(uint64 cacheTitleId);
static void ShaderCacheLoading_end();
static void ShaderCacheLoading_end();
static void ShaderCacheLoading_Close();
RendererShaderVk(ShaderType type, uint64 baseHash, uint64 auxHash, bool isGameShader, bool isGfxPackShader, const std::string& glslCode);
virtual ~RendererShaderVk();

View file

@ -117,6 +117,15 @@ void VulkanPipelineStableCache::EndLoading()
// keep cache file open for writing of new pipelines
}
void VulkanPipelineStableCache::Close()
{
if(s_cache)
{
delete s_cache;
s_cache = nullptr;
}
}
struct CachedPipeline
{
struct ShaderHash

View file

@ -41,6 +41,7 @@ public:
bool UpdateLoading(uint32& pipelinesLoadedTotal, uint32& pipelinesMissingShaders);
void EndLoading();
void LoadPipelineFromCache(std::span<uint8> fileData);
void Close(); // called on title exit
bool HasPipelineCached(uint64 baseHash, uint64 pipelineStateHash);
void AddCurrentStateToCache(uint64 baseHash, uint64 pipelineStateHash);

View file

@ -179,7 +179,6 @@ std::vector<VulkanRenderer::DeviceInfo> VulkanRenderer::GetDevices()
}
bool IsRunningInWine();
void VulkanRenderer::DetermineVendor()
{
VkPhysicalDeviceProperties2 properties{};
@ -350,7 +349,15 @@ VulkanRenderer::VulkanRenderer()
create_info.ppEnabledLayerNames = m_layerNames.data();
create_info.enabledLayerCount = m_layerNames.size();
if ((err = vkCreateInstance(&create_info, nullptr, &m_instance)) != VK_SUCCESS)
err = vkCreateInstance(&create_info, nullptr, &m_instance);
if (err == VK_ERROR_LAYER_NOT_PRESENT) {
cemuLog_log(LogType::Force, "Failed to enable vulkan validation (VK_LAYER_KHRONOS_validation)");
create_info.enabledLayerCount = 0;
err = vkCreateInstance(&create_info, nullptr, &m_instance);
}
if (err != VK_SUCCESS)
throw std::runtime_error(fmt::format("Unable to create a Vulkan instance: {}", err));
if (!InitializeInstanceVulkan(m_instance))

View file

@ -159,7 +159,7 @@ void MMURange::mapMem()
void MMURange::unmapMem()
{
cemu_assert_debug(false);
MemMapper::FreeMemory(memory_base + baseAddress, size, true);
m_isMapped = false;
}
@ -252,6 +252,15 @@ void memory_mapForCurrentTitle()
}
}
void memory_unmapForCurrentTitle()
{
for (auto& itr : g_mmuRanges)
{
if (itr->isMapped() && !itr->isMappedEarly())
itr->unmapMem();
}
}
void memory_logModifiedMemoryRanges()
{
auto gfxPackMappings = GraphicPack2::GetActiveRAMMappings();

View file

@ -4,6 +4,7 @@
void memory_init();
void memory_mapForCurrentTitle();
void memory_unmapForCurrentTitle();
void memory_logModifiedMemoryRanges();
void memory_enableOverlayArena();