Revert "GX2+TCL: Reimplement command buffer submission"

This reverts commit 28ea70b6d8.
This commit is contained in:
SSimco
2025-05-29 18:42:47 +03:00
parent be10f8859e
commit a0bf8dab7d
21 changed files with 472 additions and 761 deletions
+4
View File
@@ -47,6 +47,8 @@ struct LatteGPUState_t
gx2GPUSharedArea_t* sharedArea; // quick reference to shared area
MPTR sharedAreaAddr;
// other
// todo: Currently we have the command buffer logic implemented as a FIFO ringbuffer. On real HW it's handled as a series of command buffers that are pushed individually.
std::atomic<uint64> lastSubmittedCommandBufferTimestamp;
uint32 gx2InitCalled; // incremented every time GX2Init() is called
// OpenGL control
uint32 glVendor; // GLVENDOR_*
@@ -73,6 +75,8 @@ struct LatteGPUState_t
extern LatteGPUState_t LatteGPUState;
extern uint8* gxRingBufferReadPtr; // currently active read pointer (gx2 ring buffer or display list)
// texture
#include "Cafe/HW/Latte/Core/LatteTexture.h"
+134 -74
View File
@@ -13,7 +13,6 @@
#include "Cafe/HW/Latte/Core/LattePM4.h"
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
#include "Cafe/OS/libs/TCL/TCL.h" // TCL currently handles the GPU command ringbuffer
#include "Cafe/CafeSystem.h"
@@ -29,6 +28,11 @@ typedef uint32be* LatteCMDPtr;
#define LatteReadCMD() ((uint32)*(cmd++))
#define LatteSkipCMD(_nWords) cmd += (_nWords)
uint8* gxRingBufferReadPtr; // currently active read pointer (gx2 ring buffer or display list)
uint8* gx2CPParserDisplayListPtr;
uint8* gx2CPParserDisplayListStart; // used for debugging
uint8* gx2CPParserDisplayListEnd;
void LatteThread_HandleOSScreen();
void LatteThread_Exit();
@@ -151,12 +155,16 @@ void LatteCP_signalEnterWait()
*/
uint32 LatteCP_readU32Deprc()
{
uint32 v;
uint8* gxRingBufferWritePtr;
sint32 readDistance;
// no display list active
while (true)
{
uint32 cmdWord;
if ( TCL::TCLGPUReadRBWord(cmdWord) )
return cmdWord;
gxRingBufferWritePtr = gx2WriteGatherPipe.writeGatherPtrGxBuffer[GX2::sGX2MainCoreIndex];
readDistance = (sint32)(gxRingBufferWritePtr - gxRingBufferReadPtr);
if (readDistance != 0)
break;
g_renderer->NotifyLatteCommandProcessorIdle(); // let the renderer know in case it wants to flush any commands
performanceMonitor.gpuTime_idleTime.beginMeasuring();
@@ -167,8 +175,56 @@ uint32 LatteCP_readU32Deprc()
}
LatteThread_HandleOSScreen(); // check if new frame was presented via OSScreen API
if ( TCL::TCLGPUReadRBWord(cmdWord) )
return cmdWord;
readDistance = (sint32)(gxRingBufferWritePtr - gxRingBufferReadPtr);
if (readDistance != 0)
break;
if (Latte_GetStopSignal())
LatteThread_Exit();
// still no command data available, do some other tasks
LatteTiming_HandleTimedVsync();
LatteAsyncCommands_checkAndExecute();
std::this_thread::yield();
performanceMonitor.gpuTime_idleTime.endMeasuring();
}
v = *(uint32*)gxRingBufferReadPtr;
gxRingBufferReadPtr += 4;
#ifdef CEMU_DEBUG_ASSERT
if (v == 0xcdcdcdcd)
assert_dbg();
#endif
v = _swapEndianU32(v);
return v;
}
void LatteCP_waitForNWords(uint32 numWords)
{
uint8* gxRingBufferWritePtr;
sint32 readDistance;
bool isFlushed = false;
sint32 waitDistance = numWords * sizeof(uint32be);
// no display list active
while (true)
{
gxRingBufferWritePtr = gx2WriteGatherPipe.writeGatherPtrGxBuffer[GX2::sGX2MainCoreIndex];
readDistance = (sint32)(gxRingBufferWritePtr - gxRingBufferReadPtr);
if (readDistance < 0)
return; // wrap around means there is at least one full command queued after this
if (readDistance >= waitDistance)
break;
g_renderer->NotifyLatteCommandProcessorIdle(); // let the renderer know in case it wants to flush any commands
performanceMonitor.gpuTime_idleTime.beginMeasuring();
// no command data available, spin in a busy loop for a while then check again
for (sint32 busy = 0; busy < 80; busy++)
{
_mm_pause();
}
readDistance = (sint32)(gxRingBufferWritePtr - gxRingBufferReadPtr);
if (readDistance < 0)
return; // wrap around means there is at least one full command queued after this
if (readDistance >= waitDistance)
break;
if (Latte_GetStopSignal())
LatteThread_Exit();
@@ -178,7 +234,6 @@ uint32 LatteCP_readU32Deprc()
std::this_thread::yield();
performanceMonitor.gpuTime_idleTime.endMeasuring();
}
UNREACHABLE;
}
template<uint32 readU32()>
@@ -215,23 +270,21 @@ void LatteCP_itIndirectBufferDepr(LatteCMDPtr cmd, uint32 nWords)
cemu_assert_debug(nWords == 3);
uint32 physicalAddress = LatteReadCMD();
uint32 physicalAddressHigh = LatteReadCMD(); // unused
uint32 sizeInU32s = LatteReadCMD();
uint32 sizeInDWords = LatteReadCMD();
uint32 displayListSize = sizeInDWords * 4;
DrawPassContext drawPassCtx;
#ifdef LATTE_CP_LOGGING
if (GetAsyncKeyState('A'))
LatteCP_DebugPrintCmdBuffer(MEMPTR<uint32be>(physicalAddress), displayListSize);
#endif
if (sizeInU32s > 0)
{
DrawPassContext drawPassCtx;
uint32be* buf = MEMPTR<uint32be>(physicalAddress).GetPtr();
drawPassCtx.PushCurrentCommandQueuePos(buf, buf, buf + sizeInU32s);
uint32be* buf = MEMPTR<uint32be>(physicalAddress).GetPtr();
drawPassCtx.PushCurrentCommandQueuePos(buf, buf, buf + sizeInDWords);
LatteCP_processCommandBuffer(drawPassCtx);
if (drawPassCtx.isWithinDrawPass())
drawPassCtx.endDrawPass();
}
LatteCP_processCommandBuffer(drawPassCtx);
if (drawPassCtx.isWithinDrawPass())
drawPassCtx.endDrawPass();
}
// pushes the command buffer to the stack
@@ -241,12 +294,11 @@ void LatteCP_itIndirectBuffer(LatteCMDPtr cmd, uint32 nWords, DrawPassContext& d
uint32 physicalAddress = LatteReadCMD();
uint32 physicalAddressHigh = LatteReadCMD(); // unused
uint32 sizeInDWords = LatteReadCMD();
if (sizeInDWords > 0)
{
uint32 displayListSize = sizeInDWords * 4;
uint32be* buf = MEMPTR<uint32be>(physicalAddress).GetPtr();
drawPassCtx.PushCurrentCommandQueuePos(buf, buf, buf + sizeInDWords);
}
uint32 displayListSize = sizeInDWords * 4;
cemu_assert_debug(displayListSize >= 4);
uint32be* buf = MEMPTR<uint32be>(physicalAddress).GetPtr();
drawPassCtx.PushCurrentCommandQueuePos(buf, buf, buf + sizeInDWords);
}
LatteCMDPtr LatteCP_itStreamoutBufferUpdate(LatteCMDPtr cmd, uint32 nWords)
@@ -513,55 +565,26 @@ LatteCMDPtr LatteCP_itMemWrite(LatteCMDPtr cmd, uint32 nWords)
if (word1 == 0x40000)
{
// write U32
stdx::atomic_ref<uint32be> atomicRef(*memPtr);
atomicRef.store(word2);
*memPtr = word2;
}
else if (word1 == 0x00000)
{
// write U64
// note: The U32s are swapped here, but needs verification. Also, it seems like the two U32 halves are written independently and the U64 as a whole is not atomic -> investiagte
stdx::atomic_ref<uint64be> atomicRef(*(uint64be*)memPtr);
atomicRef.store(((uint64le)word2 << 32) | word3);
// write U64 (as two U32)
// note: The U32s are swapped
memPtr[0] = word2;
memPtr[1] = word3;
}
else if (word1 == 0x20000)
{
// write U64 (little endian)
stdx::atomic_ref<uint64le> atomicRef(*(uint64le*)memPtr);
atomicRef.store(((uint64le)word3 << 32) | word2);
memPtr[0] = _swapEndianU32(word2);
memPtr[1] = _swapEndianU32(word3);
}
else
cemu_assert_unimplemented();
return cmd;
}
LatteCMDPtr LatteCP_itEventWriteEOP(LatteCMDPtr cmd, uint32 nWords)
{
cemu_assert_debug(nWords == 5);
uint32 word0 = LatteReadCMD();
uint32 word1 = LatteReadCMD();
uint32 word2 = LatteReadCMD();
uint32 word3 = LatteReadCMD(); // value low bits
uint32 word4 = LatteReadCMD(); // value high bits
cemu_assert_debug(word2 == 0x40000000 || word2 == 0x42000000);
if (word0 == 0x504 && (word2&0x40000000)) // todo - figure out the flags
{
stdx::atomic_ref<uint64be> atomicRef(*(uint64be*)memory_getPointerFromPhysicalOffset(word1));
uint64 val = ((uint64)word4 << 32) | word3;
atomicRef.store(val);
}
else
{ cemu_assert_unimplemented();
}
bool triggerInterrupt = (word2 & 0x2000000) != 0;
if (triggerInterrupt)
{
// todo - timestamp interrupt
}
TCL::TCLGPUNotifyNewRetirementTimestamp();
return cmd;
}
LatteCMDPtr LatteCP_itMemSemaphore(LatteCMDPtr cmd, uint32 nWords)
{
@@ -760,6 +783,16 @@ LatteCMDPtr LatteCP_itDrawImmediate(LatteCMDPtr cmd, uint32 nWords, DrawPassCont
drawPassCtx.executeDraw(count, false, _tempIndexArrayMPTR);
return cmd;
}
LatteCMDPtr LatteCP_itHLEFifoWrapAround(LatteCMDPtr cmd, uint32 nWords)
{
cemu_assert_debug(nWords == 1);
uint32 unused = LatteReadCMD();
gxRingBufferReadPtr = gx2WriteGatherPipe.gxRingBuffer;
cmd = (LatteCMDPtr)gxRingBufferReadPtr;
return cmd;
}
LatteCMDPtr LatteCP_itHLESampleTimer(LatteCMDPtr cmd, uint32 nWords)
@@ -786,6 +819,16 @@ LatteCMDPtr LatteCP_itHLESpecialState(LatteCMDPtr cmd, uint32 nWords)
return cmd;
}
LatteCMDPtr LatteCP_itHLESetRetirementTimestamp(LatteCMDPtr cmd, uint32 nWords)
{
cemu_assert_debug(nWords == 2);
uint32 timestampHigh = (uint32)LatteReadCMD();
uint32 timestampLow = (uint32)LatteReadCMD();
uint64 timestamp = ((uint64)timestampHigh << 32ULL) | (uint64)timestampLow;
GX2::__GX2NotifyNewRetirementTimestamp(timestamp);
return cmd;
}
LatteCMDPtr LatteCP_itHLEBeginOcclusionQuery(LatteCMDPtr cmd, uint32 nWords)
{
cemu_assert_debug(nWords == 1);
@@ -1102,10 +1145,9 @@ void LatteCP_processCommandBuffer(DrawPassContext& drawPassCtx)
LatteCMDPtr cmd, cmdStart, cmdEnd;
if (!drawPassCtx.PopCurrentCommandQueuePos(cmd, cmdStart, cmdEnd))
break;
uint32 itHeader;
while (cmd < cmdEnd)
{
itHeader = LatteReadCMD();
uint32 itHeader = LatteReadCMD();
uint32 itHeaderType = (itHeader >> 30) & 3;
if (itHeaderType == 3)
{
@@ -1319,6 +1361,11 @@ void LatteCP_processCommandBuffer(DrawPassContext& drawPassCtx)
LatteCP_itHLEEndOcclusionQuery(cmdData, nWords);
break;
}
case IT_HLE_SET_CB_RETIREMENT_TIMESTAMP:
{
LatteCP_itHLESetRetirementTimestamp(cmdData, nWords);
break;
}
case IT_HLE_BOTTOM_OF_PIPE_CB:
{
LatteCP_itHLEBottomOfPipeCB(cmdData, nWords);
@@ -1374,7 +1421,6 @@ void LatteCP_processCommandBuffer(DrawPassContext& drawPassCtx)
void LatteCP_ProcessRingbuffer()
{
sint32 timerRecheck = 0; // estimates how much CP processing time has elapsed based on the executed commands, if the value exceeds CP_TIMER_RECHECK then _handleTimers() is called
uint32be tmpBuffer[128];
while (true)
{
uint32 itHeader = LatteCP_readU32Deprc();
@@ -1383,13 +1429,10 @@ void LatteCP_ProcessRingbuffer()
{
uint32 itCode = (itHeader >> 8) & 0xFF;
uint32 nWords = ((itHeader >> 16) & 0x3FFF) + 1;
cemu_assert(nWords < 128);
for (sint32 i=0; i<nWords; i++)
{
uint32 word = LatteCP_readU32Deprc();
tmpBuffer[i] = word;
}
LatteCMDPtr cmd = (LatteCMDPtr)tmpBuffer;
LatteCP_waitForNWords(nWords);
LatteCMDPtr cmd = (LatteCMDPtr)gxRingBufferReadPtr;
uint8* cmdEnd = gxRingBufferReadPtr + nWords * 4;
gxRingBufferReadPtr = cmdEnd;
switch (itCode)
{
case IT_SURFACE_SYNC:
@@ -1556,11 +1599,6 @@ void LatteCP_ProcessRingbuffer()
timerRecheck += CP_TIMER_RECHECK / 512;
break;
}
case IT_EVENT_WRITE_EOP:
{
LatteCP_itEventWriteEOP(cmd, nWords);
break;
}
case IT_HLE_COPY_COLORBUFFER_TO_SCANBUFFER:
{
LatteCP_itHLECopyColorBufferToScanBuffer(cmd, nWords);
@@ -1599,6 +1637,12 @@ void LatteCP_ProcessRingbuffer()
timerRecheck += CP_TIMER_RECHECK / 128;
break;
}
case IT_HLE_FIFO_WRAP_AROUND:
{
LatteCP_itHLEFifoWrapAround(cmd, nWords);
timerRecheck += CP_TIMER_RECHECK / 512;
break;
}
case IT_HLE_SAMPLE_TIMER:
{
LatteCP_itHLESampleTimer(cmd, nWords);
@@ -1623,6 +1667,12 @@ void LatteCP_ProcessRingbuffer()
timerRecheck += CP_TIMER_RECHECK / 512;
break;
}
case IT_HLE_SET_CB_RETIREMENT_TIMESTAMP:
{
LatteCP_itHLESetRetirementTimestamp(cmd, nWords);
timerRecheck += CP_TIMER_RECHECK / 512;
break;
}
case IT_HLE_BOTTOM_OF_PIPE_CB:
{
LatteCP_itHLEBottomOfPipeCB(cmd, nWords);
@@ -1883,6 +1933,11 @@ void LatteCP_DebugPrintCmdBuffer(uint32be* bufferPtr, uint32 size)
cemuLog_log(LogType::Force, "{} IT_HLE_COPY_SURFACE_NEW", strPrefix);
break;
}
case IT_HLE_FIFO_WRAP_AROUND:
{
cemuLog_log(LogType::Force, "{} IT_HLE_FIFO_WRAP_AROUND", strPrefix);
break;
}
case IT_HLE_SAMPLE_TIMER:
{
cemuLog_log(LogType::Force, "{} IT_HLE_SAMPLE_TIMER", strPrefix);
@@ -1903,6 +1958,11 @@ void LatteCP_DebugPrintCmdBuffer(uint32be* bufferPtr, uint32 size)
cemuLog_log(LogType::Force, "{} IT_HLE_END_OCCLUSION_QUERY", strPrefix);
break;
}
case IT_HLE_SET_CB_RETIREMENT_TIMESTAMP:
{
cemuLog_log(LogType::Force, "{} IT_HLE_SET_CB_RETIREMENT_TIMESTAMP", strPrefix);
break;
}
case IT_HLE_BOTTOM_OF_PIPE_CB:
{
cemuLog_log(LogType::Force, "{} IT_HLE_BOTTOM_OF_PIPE_CB", strPrefix);
+2 -1
View File
@@ -14,7 +14,6 @@
#define IT_MEM_WRITE 0x3D
#define IT_SURFACE_SYNC 0x43
#define IT_EVENT_WRITE 0x46
#define IT_EVENT_WRITE_EOP 0x47 // end of pipe
#define IT_LOAD_CONFIG_REG 0x60
#define IT_LOAD_CONTEXT_REG 0x61
@@ -48,12 +47,14 @@
#define IT_HLE_WAIT_FOR_FLIP 0xF1
#define IT_HLE_BOTTOM_OF_PIPE_CB 0xF2
#define IT_HLE_COPY_COLORBUFFER_TO_SCANBUFFER 0xF3
#define IT_HLE_FIFO_WRAP_AROUND 0xF4
#define IT_HLE_CLEAR_COLOR_DEPTH_STENCIL 0xF5
#define IT_HLE_SAMPLE_TIMER 0xF7
#define IT_HLE_TRIGGER_SCANBUFFER_SWAP 0xF8
#define IT_HLE_SPECIAL_STATE 0xF9
#define IT_HLE_BEGIN_OCCLUSION_QUERY 0xFA
#define IT_HLE_END_OCCLUSION_QUERY 0xFB
#define IT_HLE_SET_CB_RETIREMENT_TIMESTAMP 0xFD
#define pm4HeaderType3(__itCode, __dataDWordCount) (0xC0000000|((uint32)(__itCode)<<8)|((uint32)((__dataDWordCount)-1)<<16))
#define pm4HeaderType2Filler() (0x80000000)
+1
View File
@@ -206,6 +206,7 @@ int Latte_ThreadEntry()
if (Latte_GetStopSignal())
LatteThread_Exit();
}
gxRingBufferReadPtr = gx2WriteGatherPipe.gxRingBuffer;
LatteCP_ProcessRingbuffer();
cemu_assert_debug(false); // should never reach
return 0;
+8 -141
View File
@@ -1,161 +1,28 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/libs/TCL/TCL.h"
#include "HW/Latte/Core/LattePM4.h"
namespace TCL
{
SysAllocator<coreinit::OSEvent> s_updateRetirementEvent;
uint64 s_currentRetireMarker = 0;
struct TCLStatePPC // mapped into PPC space
enum class TCL_SUBMISSION_FLAG : uint32
{
uint64be gpuRetireMarker; // written by GPU
SURFACE_SYNC = 0x400000, // submit surface sync packet before cmd
TRIGGER_INTERRUPT = 0x200000, // probably
UKN_20000000 = 0x20000000,
};
SysAllocator<TCLStatePPC> s_tclStatePPC;
// called from GPU for timestamp EOP event
void TCLGPUNotifyNewRetirementTimestamp()
int TCLSubmitToRing(uint32be* cmd, uint32 cmdLen, uint32be* controlFlags, uint64* submissionTimestamp)
{
// gpuRetireMarker is updated via event eop command
__OSLockScheduler();
coreinit::OSSignalEventAllInternal(s_updateRetirementEvent.GetPtr());
__OSUnlockScheduler();
}
// todo - figure out all the bits of *controlFlags
// if submissionTimestamp != nullptr then set it to the timestamp of the submission. Note: We should make sure that uint64's are written atomically by the GPU command processor
int TCLTimestamp(TCLTimestampId id, uint64be* timestampOut)
{
if (id == TCLTimestampId::TIMESTAMP_LAST_BUFFER_RETIRED)
{
MEMPTR<uint32> b;
// this is the timestamp of the last buffer that was retired by the GPU
stdx::atomic_ref<uint64be> retireTimestamp(s_tclStatePPC->gpuRetireMarker);
*timestampOut = retireTimestamp.load();
return 0;
}
else
{
cemuLog_log(LogType::Force, "TCLTimestamp(): Unsupported timestamp ID {}", (uint32)id);
*timestampOut = 0;
return 0;
}
}
cemu_assert_debug(false);
int TCLWaitTimestamp(TCLTimestampId id, uint64 waitTs, uint64 timeout)
{
if (id == TCLTimestampId::TIMESTAMP_LAST_BUFFER_RETIRED)
{
while ( true )
{
stdx::atomic_ref<uint64be> retireTimestamp(s_tclStatePPC->gpuRetireMarker);
uint64 currentTimestamp = retireTimestamp.load();
if (currentTimestamp >= waitTs)
return 0;
coreinit::OSWaitEvent(s_updateRetirementEvent.GetPtr());
}
}
else
{
cemuLog_log(LogType::Force, "TCLWaitTimestamp(): Unsupported timestamp ID {}", (uint32)id);
}
return 0;
}
static constexpr uint32 TCL_RING_BUFFER_SIZE = 4096; // in U32s
std::atomic<uint32> tclRingBufferA[TCL_RING_BUFFER_SIZE];
std::atomic<uint32> tclRingBufferA_readIndex{0};
uint32 tclRingBufferA_writeIndex{0};
// GPU code calls this to grab the next command word
bool TCLGPUReadRBWord(uint32& cmdWord)
{
if (tclRingBufferA_readIndex == tclRingBufferA_writeIndex)
return false;
cmdWord = tclRingBufferA[tclRingBufferA_readIndex];
tclRingBufferA_readIndex = (tclRingBufferA_readIndex+1) % TCL_RING_BUFFER_SIZE;
return true;
}
void TCLWaitForRBSpace(uint32be numU32s)
{
while ( true )
{
uint32 distance = (tclRingBufferA_readIndex + TCL_RING_BUFFER_SIZE - tclRingBufferA_writeIndex) & (TCL_RING_BUFFER_SIZE - 1);
if (tclRingBufferA_writeIndex == tclRingBufferA_readIndex) // buffer completely empty
distance = TCL_RING_BUFFER_SIZE;
if (distance >= numU32s+1) // assume distance minus one, because we are never allowed to completely wrap around
break;
_mm_pause();
}
}
// this function assumes that TCLWaitForRBSpace was called and that there is enough space
void TCLWriteCmd(uint32be* cmd, uint32 cmdLen)
{
while (cmdLen > 0)
{
tclRingBufferA[tclRingBufferA_writeIndex] = *cmd;
tclRingBufferA_writeIndex++;
tclRingBufferA_writeIndex &= (TCL_RING_BUFFER_SIZE - 1);
cmd++;
cmdLen--;
}
}
#define EVENT_TYPE_TS 5
void TCLSubmitRetireMarker(bool triggerEventInterrupt)
{
s_currentRetireMarker++;
uint32be cmd[6];
cmd[0] = pm4HeaderType3(IT_EVENT_WRITE_EOP, 5);
cmd[1] = (4 | (EVENT_TYPE_TS << 8)); // event type (bits 8-15) and event index (bits 0-7).
cmd[2] = MEMPTR<void>(&s_tclStatePPC->gpuRetireMarker).GetMPTR(); // address lower 32bits + data sel bits
cmd[3] = 0x40000000; // select 64bit write, lower 16 bits are the upper bits of the address
if (triggerEventInterrupt)
cmd[3] |= 0x2000000; // trigger interrupt after value has been written
cmd[4] = (uint32)s_currentRetireMarker; // data lower 32 bits
cmd[5] = (uint32)(s_currentRetireMarker>>32); // data higher 32 bits
TCLWriteCmd(cmd, 6);
}
int TCLSubmitToRing(uint32be* cmd, uint32 cmdLen, betype<TCLSubmissionFlag>* controlFlags, uint64be* timestampValueOut)
{
TCLSubmissionFlag flags = *controlFlags;
cemu_assert_debug(timestampValueOut); // handle case where this is null
// make sure there is enough space to submit all commands at one
uint32 totalCommandLength = cmdLen;
totalCommandLength += 6; // space needed for TCLSubmitRetireMarker
TCLWaitForRBSpace(totalCommandLength);
// submit command buffer
TCLWriteCmd(cmd, cmdLen);
// create new marker timestamp and tell GPU to write it to our variable after its done processing the command
if ((HAS_FLAG(flags, TCLSubmissionFlag::USE_RETIRED_MARKER)))
{
TCLSubmitRetireMarker(!HAS_FLAG(flags, TCLSubmissionFlag::NO_MARKER_INTERRUPT));
*timestampValueOut = s_currentRetireMarker; // incremented before each submit
}
else
{
cemu_assert_unimplemented();
}
return 0;
}
void Initialize()
{
cafeExportRegister("TCL", TCLSubmitToRing, LogType::Placeholder);
cafeExportRegister("TCL", TCLTimestamp, LogType::Placeholder);
cafeExportRegister("TCL", TCLWaitTimestamp, LogType::Placeholder);
s_currentRetireMarker = 0;
s_tclStatePPC->gpuRetireMarker = 0;
coreinit::OSInitEvent(s_updateRetirementEvent.GetPtr(), coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_AUTO);
}
}
+1 -22
View File
@@ -1,25 +1,4 @@
namespace TCL
{
enum class TCLTimestampId
{
TIMESTAMP_LAST_BUFFER_RETIRED = 1,
};
enum class TCLSubmissionFlag : uint32
{
SURFACE_SYNC = 0x400000, // submit surface sync packet before cmd
NO_MARKER_INTERRUPT = 0x200000,
USE_RETIRED_MARKER = 0x20000000, // Controls whether the timer is updated before or after (retired) the cmd. Also controls which timestamp is returned for the submission. Before and after using separate counters
};
int TCLTimestamp(TCLTimestampId id, uint64be* timestampOut);
int TCLWaitTimestamp(TCLTimestampId id, uint64 waitTs, uint64 timeout);
int TCLSubmitToRing(uint32be* cmd, uint32 cmdLen, betype<TCLSubmissionFlag>* controlFlags, uint64be* timestampValueOut);
// called from Latte code
bool TCLGPUReadRBWord(uint32& cmdWord);
void TCLGPUNotifyNewRetirementTimestamp();
void Initialize();
}
ENABLE_BITMASK_OPERATORS(TCL::TCLSubmissionFlag);
}
+89 -2
View File
@@ -59,7 +59,7 @@ void gx2Export_GX2SwapScanBuffers(PPCInterpreter_t* hCPU)
if (isPokken)
GX2::GX2DrawDone();
GX2::GX2ReserveCmdSpace(5+2);
GX2ReserveCmdSpace(5+2);
uint64 tick64 = PPCInterpreter_getMainCoreCycleCounter() / 20ULL;
lastSwapTime = tick64;
@@ -86,16 +86,24 @@ void gx2Export_GX2SwapScanBuffers(PPCInterpreter_t* hCPU)
GX2::GX2WaitForFlip();
}
GX2::GX2WriteGather_checkAndInsertWrapAroundMark();
osLib_returnFromFunction(hCPU, 0);
}
void gx2Export_GX2CopyColorBufferToScanBuffer(PPCInterpreter_t* hCPU)
{
cemuLog_log(LogType::GX2, "GX2CopyColorBufferToScanBuffer(0x{:08x},{})", hCPU->gpr[3], hCPU->gpr[4]);
GX2::GX2ReserveCmdSpace(10);
GX2ReserveCmdSpace(5);
// todo: proper implementation
// hack: Avoid running to far ahead of GPU. Normally this would be guaranteed by the circular buffer model, which we currently dont fully emulate
if(GX2::GX2WriteGather_getReadWriteDistance() > 32*1024*1024 )
{
debug_printf("Waiting for GPU to catch up...\n");
PPCInterpreter_relinquishTimeslice(); // release current thread
return;
}
GX2ColorBuffer* colorBuffer = (GX2ColorBuffer*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_HLE_COPY_COLORBUFFER_TO_SCANBUFFER, 9));
@@ -301,6 +309,81 @@ void gx2Export_GX2SetSemaphore(PPCInterpreter_t* hCPU)
osLib_returnFromFunction(hCPU, 0);
}
void gx2Export_GX2Flush(PPCInterpreter_t* hCPU)
{
cemuLog_log(LogType::GX2, "GX2Flush()");
_GX2SubmitToTCL();
osLib_returnFromFunction(hCPU, 0);
}
uint8* _GX2LastFlushPtr[PPC_CORE_COUNT] = {NULL};
uint64 _prevReturnedGPUTime = 0;
uint64 Latte_GetTime()
{
uint64 gpuTime = coreinit::OSGetSystemTime();
gpuTime *= 20000ULL;
if (gpuTime <= _prevReturnedGPUTime)
gpuTime = _prevReturnedGPUTime + 1; // avoid ever returning identical timestamps
_prevReturnedGPUTime = gpuTime;
return gpuTime;
}
void _GX2SubmitToTCL()
{
uint32 coreIndex = PPCInterpreter_getCoreIndex(PPCInterpreter_getCurrentInstance());
// do nothing if called from non-main GX2 core
if (GX2::sGX2MainCoreIndex != coreIndex)
{
cemuLog_logDebug(LogType::Force, "_GX2SubmitToTCL() called on non-main GX2 core");
return;
}
if( gx2WriteGatherPipe.displayListStart[coreIndex] != MPTR_NULL )
return; // quit if in display list
_GX2LastFlushPtr[coreIndex] = (gx2WriteGatherPipe.writeGatherPtrGxBuffer[coreIndex]);
// update last submitted CB timestamp
uint64 commandBufferTimestamp = Latte_GetTime();
LatteGPUState.lastSubmittedCommandBufferTimestamp.store(commandBufferTimestamp);
cemuLog_log(LogType::GX2, "Submitting GX2 command buffer with timestamp {:016x}", commandBufferTimestamp);
// submit HLE packet to write retirement timestamp
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_HLE_SET_CB_RETIREMENT_TIMESTAMP, 2));
gx2WriteGather_submitU32AsBE((uint32)(commandBufferTimestamp>>32ULL));
gx2WriteGather_submitU32AsBE((uint32)(commandBufferTimestamp&0xFFFFFFFFULL));
}
uint32 _GX2GetUnflushedBytes(uint32 coreIndex)
{
uint32 unflushedBytes = 0;
if (_GX2LastFlushPtr[coreIndex] != NULL)
{
if (_GX2LastFlushPtr[coreIndex] > gx2WriteGatherPipe.writeGatherPtrGxBuffer[coreIndex])
unflushedBytes = (uint32)(gx2WriteGatherPipe.writeGatherPtrGxBuffer[coreIndex] - gx2WriteGatherPipe.gxRingBuffer + 4); // this isn't 100% correct since we ignore the bytes between the last flush address and the start of the wrap around
else
unflushedBytes = (uint32)(gx2WriteGatherPipe.writeGatherPtrGxBuffer[coreIndex] - _GX2LastFlushPtr[coreIndex]);
}
else
unflushedBytes = (uint32)(gx2WriteGatherPipe.writeGatherPtrGxBuffer[coreIndex] - gx2WriteGatherPipe.gxRingBuffer);
return unflushedBytes;
}
/*
* Guarantees that the requested amount of space is available on the current command buffer
* If the space is not available, the current command buffer is pushed to the GPU and a new one is allocated
*/
void GX2ReserveCmdSpace(uint32 reservedFreeSpaceInU32)
{
uint32 coreIndex = coreinit::OSGetCoreId();
// if we are in a display list then do nothing
if( gx2WriteGatherPipe.displayListStart[coreIndex] != MPTR_NULL )
return;
uint32 unflushedBytes = _GX2GetUnflushedBytes(coreIndex);
if( unflushedBytes >= 0x1000 )
{
_GX2SubmitToTCL();
}
}
void gx2_load()
{
osLib_addFunction("gx2", "GX2GetContextStateDisplayList", gx2Export_GX2GetContextStateDisplayList);
@@ -362,6 +445,10 @@ void gx2_load()
// semaphore
osLib_addFunction("gx2", "GX2SetSemaphore", gx2Export_GX2SetSemaphore);
// command buffer
osLib_addFunction("gx2", "GX2Flush", gx2Export_GX2Flush);
GX2::GX2Init_writeGather();
GX2::GX2MemInit();
GX2::GX2ResourceInit();
GX2::GX2CommandInit();
+7 -1
View File
@@ -67,4 +67,10 @@ void gx2Export_GX2MarkScanBufferCopied(PPCInterpreter_t* hCPU);
void gx2Export_GX2SetDefaultState(PPCInterpreter_t* hCPU);
void gx2Export_GX2SetupContextStateEx(PPCInterpreter_t* hCPU);
void gx2Export_GX2SetContextState(PPCInterpreter_t* hCPU);
void gx2Export_GX2SetContextState(PPCInterpreter_t* hCPU);
// command buffer
uint32 _GX2GetUnflushedBytes(uint32 coreIndex);
void _GX2SubmitToTCL();
void GX2ReserveCmdSpace(uint32 reservedFreeSpaceInU32);
+1
View File
@@ -132,6 +132,7 @@ namespace GX2
depthFirstSlice = _swapEndianU32(depthBuffer->viewFirstSlice);
depthNumSlices = _swapEndianU32(depthBuffer->viewNumSlices);
}
gx2WriteGather_submit(pm4HeaderType3(IT_HLE_CLEAR_COLOR_DEPTH_STENCIL, 23),
hleClearFlags,
colorPhysAddr,
File diff suppressed because it is too large Load Diff
+19 -25
View File
@@ -2,19 +2,21 @@
#include "Cafe/HW/Latte/ISA/LatteReg.h"
#include "Cafe/HW/Espresso/Const.h"
namespace GX2
struct GX2WriteGatherPipeState
{
struct GX2PerCoreCBState
{
uint32be* bufferPtr;
uint32 bufferSizeInU32s;
uint32be* currentWritePtr;
bool isDisplayList;
};
extern GX2PerCoreCBState s_perCoreCBState[Espresso::CORE_COUNT];
uint8* gxRingBuffer;
// each core has it's own write gatherer and display list state (writing)
uint8* writeGatherPtrGxBuffer[Espresso::CORE_COUNT];
uint8** writeGatherPtrWrite[Espresso::CORE_COUNT];
uint8* writeGatherPtrDisplayList[Espresso::CORE_COUNT];
MPTR displayListStart[Espresso::CORE_COUNT];
uint32 displayListMaxSize[Espresso::CORE_COUNT];
};
extern GX2WriteGatherPipeState gx2WriteGatherPipe;
void GX2ReserveCmdSpace(uint32 reservedFreeSpaceInU32); // move to GX2 namespace eventually
void gx2WriteGather_submitU32AsBE(uint32 v);
void gx2WriteGather_submitU32AsLE(uint32 v);
void gx2WriteGather_submitU32AsLEArray(uint32* v, uint32 numValues);
@@ -25,8 +27,7 @@ uint32 PPCInterpreter_getCurrentCoreIndex();
template <typename ...Targs>
inline void gx2WriteGather_submit_(uint32 coreIndex, uint32be* writePtr)
{
GX2::s_perCoreCBState[coreIndex].currentWritePtr = writePtr;
cemu_assert_debug(GX2::s_perCoreCBState[coreIndex].currentWritePtr <= (GX2::s_perCoreCBState[coreIndex].bufferPtr + GX2::s_perCoreCBState[coreIndex].bufferSizeInU32s));
(*gx2WriteGatherPipe.writeGatherPtrWrite[coreIndex]) = (uint8*)writePtr;
}
template <typename T, typename ...Targs>
@@ -74,23 +75,17 @@ template <typename ...Targs>
inline void gx2WriteGather_submit(Targs... args)
{
uint32 coreIndex = PPCInterpreter_getCurrentCoreIndex();
if (GX2::s_perCoreCBState[coreIndex].currentWritePtr == nullptr)
{
cemu_assert_suspicious(); // writing to command buffer without valid write pointer?
if (gx2WriteGatherPipe.writeGatherPtrWrite[coreIndex] == nullptr)
return;
}
uint32be* writePtr = GX2::s_perCoreCBState[coreIndex].currentWritePtr;
uint32be* writePtr = (uint32be*)(*gx2WriteGatherPipe.writeGatherPtrWrite[coreIndex]);
gx2WriteGather_submit_(coreIndex, writePtr, std::forward<Targs>(args)...);
}
namespace GX2
{
void GX2Command_Flush(uint32 numU32sForNextBuffer, bool triggerMarkerInterrupt = true);
void GX2ReserveCmdSpace(uint32 reservedFreeSpaceInU32);
uint64 GX2GetLastSubmittedTimeStamp();
uint64 GX2GetRetiredTimeStamp();
bool GX2WaitTimeStamp(uint64 tsWait);
uint32 GX2WriteGather_getReadWriteDistance();
void GX2WriteGather_checkAndInsertWrapAroundMark();
void GX2BeginDisplayList(MEMPTR<void> displayListAddr, uint32 size);
void GX2BeginDisplayListEx(MEMPTR<void> displayListAddr, uint32 size, bool profiling);
@@ -101,8 +96,7 @@ namespace GX2
bool GX2GetDisplayListWriteStatus();
void GX2Init_writeGather();
void GX2CommandInit();
void GX2Init_commandBufferPool(void* bufferBase, uint32 bufferSize);
void GX2Shutdown_commandBufferPool();
void GX2CommandResetToDefaultState();
}
+6 -4
View File
@@ -168,7 +168,7 @@ uint32 _GX2Context_CalcStateSize()
void _GX2Context_CreateLoadDL()
{
GX2::GX2ReserveCmdSpace(3);
GX2ReserveCmdSpace(3);
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_CONTEXT_CONTROL, 2));
gx2WriteGather_submitU32AsBE(0x80000077);
gx2WriteGather_submitU32AsBE(0x80000077);
@@ -176,7 +176,7 @@ void _GX2Context_CreateLoadDL()
void _GX2Context_WriteCmdDisableStateShadowing()
{
GX2::GX2ReserveCmdSpace(3);
GX2ReserveCmdSpace(3);
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_CONTEXT_CONTROL, 2));
gx2WriteGather_submitU32AsBE(0x80000000);
gx2WriteGather_submitU32AsBE(0x80000000);
@@ -184,7 +184,7 @@ void _GX2Context_WriteCmdDisableStateShadowing()
void _GX2Context_cmdLoad(void* gx2ukn, uint32 pm4Header, MPTR physAddrRegArea, uint32 waitForIdle, uint32 numRegOffsetEntries, GX2RegLoadPktEntry_t* regOffsetEntries)
{
GX2::GX2ReserveCmdSpace(3 + numRegOffsetEntries*2);
GX2ReserveCmdSpace(3 + numRegOffsetEntries*2);
gx2WriteGather_submitU32AsBE(pm4Header);
gx2WriteGather_submitU32AsBE(physAddrRegArea);
gx2WriteGather_submitU32AsBE(waitForIdle);
@@ -199,6 +199,7 @@ void _GX2Context_cmdLoad(void* gx2ukn, uint32 pm4Header, MPTR physAddrRegArea, u
void _GX2Context_WriteCmdRestoreState(GX2ContextState_t* gx2ContextState, uint32 ukn)
{
GX2::GX2WriteGather_checkAndInsertWrapAroundMark();
MPTR physAddrContextState = memory_virtualToPhysical(memory_getVirtualOffsetFromPointer(gx2ContextState));
_GX2Context_CreateLoadDL();
__cmdStateLoad(NULL, IT_LOAD_CONFIG_REG, gx2ContextState->hwContext.areaConfigReg, 0x80000000, configReg_loadPktEntries);
@@ -211,7 +212,7 @@ void _GX2Context_WriteCmdRestoreState(GX2ContextState_t* gx2ContextState, uint32
void GX2SetDefaultState()
{
GX2::GX2ReserveCmdSpace(0x100);
GX2ReserveCmdSpace(0x100);
Latte::LATTE_PA_CL_VTE_CNTL reg{};
reg.set_VPORT_X_OFFSET_ENA(true).set_VPORT_X_SCALE_ENA(true);
@@ -375,6 +376,7 @@ void gx2Export_GX2SetContextState(PPCInterpreter_t* hCPU)
osLib_returnFromFunction(hCPU, 0);
}
void gx2Export_GX2GetContextStateDisplayList(PPCInterpreter_t* hCPU)
{
cemuLog_log(LogType::GX2, "GX2GetContextStateDisplayList(0x{:08x}, 0x{:08x}, 0x{:08x})", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]);
+4
View File
@@ -52,6 +52,7 @@ namespace GX2
0,
count,
0);
GX2::GX2WriteGather_checkAndInsertWrapAroundMark();
}
void GX2DrawIndexedEx2(GX2PrimitiveMode2 primitiveMode, uint32 count, GX2IndexType indexType, void* indexData, uint32 baseVertex, uint32 numInstances, uint32 baseInstance)
@@ -84,6 +85,7 @@ namespace GX2
pm4HeaderType3(IT_SET_CTL_CONST, 2), 1,
0 // baseInstance
);
GX2::GX2WriteGather_checkAndInsertWrapAroundMark();
}
void GX2DrawEx(GX2PrimitiveMode2 primitiveMode, uint32 count, uint32 baseVertex, uint32 numInstances)
@@ -107,6 +109,7 @@ namespace GX2
count,
0 // DRAW_INITIATOR
);
GX2::GX2WriteGather_checkAndInsertWrapAroundMark();
}
void GX2DrawIndexedImmediateEx(GX2PrimitiveMode2 primitiveMode, uint32 count, GX2IndexType indexType, void* indexData, uint32 baseVertex, uint32 numInstances)
@@ -174,6 +177,7 @@ namespace GX2
}
}
GX2::GX2WriteGather_checkAndInsertWrapAroundMark();
}
struct GX2DispatchComputeParam
+45 -1
View File
@@ -16,6 +16,18 @@ namespace GX2
SysAllocator<coreinit::OSThreadQueue> g_vsyncThreadQueue;
SysAllocator<coreinit::OSThreadQueue> g_flipThreadQueue;
SysAllocator<coreinit::OSEvent> s_updateRetirementEvent;
std::atomic<uint64> s_lastRetirementTimestamp = 0;
// called from GPU code when a command buffer is retired
void __GX2NotifyNewRetirementTimestamp(uint64 tsRetire)
{
__OSLockScheduler();
s_lastRetirementTimestamp = tsRetire;
coreinit::OSSignalEventAllInternal(s_updateRetirementEvent.GetPtr());
__OSUnlockScheduler();
}
void GX2SetGPUFence(uint32be* fencePtr, uint32 mask, uint32 compareOp, uint32 compareValue)
{
GX2ReserveCmdSpace(7);
@@ -198,6 +210,16 @@ namespace GX2
osLib_returnFromFunction(hCPU, 0);
}
uint64 GX2GetLastSubmittedTimeStamp()
{
return LatteGPUState.lastSubmittedCommandBufferTimestamp.load();
}
uint64 GX2GetRetiredTimeStamp()
{
return s_lastRetirementTimestamp;
}
void GX2WaitForVsync()
{
__OSLockScheduler();
@@ -214,6 +236,19 @@ namespace GX2
__OSUnlockScheduler();
}
bool GX2WaitTimeStamp(uint64 tsWait)
{
__OSLockScheduler();
while (tsWait > s_lastRetirementTimestamp)
{
// GPU hasn't caught up yet
coreinit::OSWaitEventInternal(s_updateRetirementEvent.GetPtr());
}
__OSUnlockScheduler();
// return true to indicate no timeout
return true;
}
void GX2DrawDone()
{
// optional force full sync (texture readback and occlusion queries)
@@ -228,10 +263,13 @@ namespace GX2
gx2WriteGather_submitU32AsBE(0x00000000); // unused
}
// flush pipeline
GX2Command_Flush(0x100, true);
if (_GX2GetUnflushedBytes(coreinit::OSGetCoreId()) > 0)
_GX2SubmitToTCL();
uint64 ts = GX2GetLastSubmittedTimeStamp();
GX2WaitTimeStamp(ts);
GX2::GX2WriteGather_checkAndInsertWrapAroundMark();
}
void GX2Init_event()
@@ -256,19 +294,25 @@ namespace GX2
cafeExportRegister("gx2", GX2SetEventCallback, LogType::GX2);
cafeExportRegister("gx2", GX2GetEventCallback, LogType::GX2);
cafeExportRegister("gx2", GX2GetLastSubmittedTimeStamp, LogType::GX2);
cafeExportRegister("gx2", GX2GetRetiredTimeStamp, LogType::GX2);
cafeExportRegister("gx2", GX2WaitForVsync, LogType::GX2);
cafeExportRegister("gx2", GX2WaitForFlip, LogType::GX2);
cafeExportRegister("gx2", GX2WaitTimeStamp, LogType::GX2);
cafeExportRegister("gx2", GX2DrawDone, LogType::GX2);
coreinit::OSInitThreadQueue(g_vsyncThreadQueue.GetPtr());
coreinit::OSInitThreadQueue(g_flipThreadQueue.GetPtr());
coreinit::OSInitEvent(s_updateRetirementEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_AUTO);
coreinit::OSInitSemaphore(s_eventCbQueueSemaphore, 0);
}
void GX2EventResetToDefaultState()
{
s_callbackThreadLaunched = false;
s_lastRetirementTimestamp = 0;
for(auto& it : s_eventCallback)
{
it.callbackFuncPtr = nullptr;
+2 -67
View File
@@ -81,68 +81,19 @@ namespace GX2
void _test_AddrLib();
using GX2InitArg = uint32;
enum class GX2InitArgId : GX2InitArg
{
EndOfArgs = 0,
CommandPoolBase = 1,
CommandPoolSize = 2,
UknArg7 = 7,
UknArg8 = 8,
UknArg9 = 9,
UknArg11 = 11,
};
void GX2Init(betype<GX2InitArg>* initArgStream)
void GX2Init(void* initSettings)
{
if (LatteGPUState.gx2InitCalled)
{
cemuLog_logDebug(LogType::Force, "GX2Init() called while already initialized");
return;
}
// parse init params from the stream
MEMPTR<void> commandPoolBase = nullptr;
uint32 commandPoolSize = 0;
if (initArgStream)
{
while (true)
{
GX2InitArgId paramId = static_cast<GX2InitArgId>((GX2InitArg)*initArgStream);
initArgStream++;
if (paramId == GX2InitArgId::EndOfArgs)
{
break;
}
else if (paramId == GX2InitArgId::CommandPoolBase)
{
commandPoolBase = MEMPTR<void>(*initArgStream);
initArgStream++;
}
else if (paramId == GX2InitArgId::CommandPoolSize)
{
commandPoolSize = *initArgStream;
initArgStream++;
}
else if (paramId == GX2InitArgId::UknArg7 ||
paramId == GX2InitArgId::UknArg8 ||
paramId == GX2InitArgId::UknArg9 ||
paramId == GX2InitArgId::UknArg11)
{
initArgStream++;
}
else
{
cemuLog_log(LogType::Force, "GX2Init: Unsupported init arg {}", (uint32)paramId);
}
}
}
// init main core
uint32 coreIndex = coreinit::OSGetCoreId();
cemuLog_log(LogType::GX2, "GX2Init() on core {} by thread 0x{:08x}", coreIndex, MEMPTR<OSThread_t>(coreinit::OSGetCurrentThread()).GetMPTR());
sGX2MainCoreIndex = coreIndex;
// init submodules
GX2::GX2Init_event();
GX2::GX2Init_commandBufferPool(commandPoolBase, commandPoolSize);
GX2::GX2Init_writeGather();
// init shared area
if (LatteGPUState.sharedAreaAddr == MPTR_NULL)
{
@@ -161,21 +112,6 @@ namespace GX2
_test_AddrLib();
}
void GX2Shutdown()
{
if (!LatteGPUState.gx2InitCalled)
{
cemuLog_logDebug(LogType::Force, "GX2Shutdown() called while not initialized");
return;
}
LatteGPUState.gx2InitCalled--;
if (LatteGPUState.gx2InitCalled != 0)
return;
GX2DrawDone();
GX2Shutdown_commandBufferPool();
cemuLog_log(LogType::Force, "GX2 shutdown");
}
void _GX2DriverReset()
{
LatteGPUState.gx2InitCalled = 0;
@@ -301,7 +237,6 @@ namespace GX2
void GX2MiscInit()
{
cafeExportRegister("gx2", GX2Init, LogType::GX2);
cafeExportRegister("gx2", GX2Shutdown, LogType::GX2);
cafeExportRegister("gx2", GX2GetMainCoreId, LogType::GX2);
cafeExportRegister("gx2", GX2ResetGPU, LogType::GX2);
+7 -3
View File
@@ -135,7 +135,7 @@ void gx2Export_GX2InitDepthBufferRegs(PPCInterpreter_t* hCPU)
void gx2Export_GX2SetColorBuffer(PPCInterpreter_t* hCPU)
{
cemuLog_log(LogType::GX2, "GX2SetColorBuffer(0x{:08x}, {})", hCPU->gpr[3], hCPU->gpr[4]);
GX2::GX2ReserveCmdSpace(20);
GX2ReserveCmdSpace(20);
GX2ColorBuffer* colorBufferBE = (GX2ColorBuffer*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
@@ -198,13 +198,15 @@ void gx2Export_GX2SetColorBuffer(PPCInterpreter_t* hCPU)
mmCB_COLOR0_INFO - 0xA000 + hCPU->gpr[4],
colorBufferBE->reg_info);
GX2::GX2WriteGather_checkAndInsertWrapAroundMark();
osLib_returnFromFunction(hCPU, 0);
}
void gx2Export_GX2SetDepthBuffer(PPCInterpreter_t* hCPU)
{
cemuLog_log(LogType::GX2, "GX2SetDepthBuffer(0x{:08x})", hCPU->gpr[3]);
GX2::GX2ReserveCmdSpace(20);
GX2ReserveCmdSpace(20);
GX2DepthBuffer* depthBufferBE = (GX2DepthBuffer*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
@@ -262,6 +264,8 @@ void gx2Export_GX2SetDepthBuffer(PPCInterpreter_t* hCPU)
gx2WriteGather_submitU32AsBE(mmDB_DEPTH_VIEW - 0xA000);
gx2WriteGather_submitU32AsBE(db_view);
GX2::GX2WriteGather_checkAndInsertWrapAroundMark();
osLib_returnFromFunction(hCPU, 0);
}
@@ -277,7 +281,7 @@ void gx2Export_GX2MarkScanBufferCopied(PPCInterpreter_t* hCPU)
uint32 scanTarget = hCPU->gpr[3];
if( scanTarget == GX2_SCAN_TARGET_TV )
{
GX2::GX2ReserveCmdSpace(10);
GX2ReserveCmdSpace(10);
uint32 physAddr = (MEMORY_TILINGAPERTURE_AREA_ADDR+0x200000);
+4 -21
View File
@@ -303,27 +303,7 @@ namespace GX2
void GX2SetVertexShader(GX2VertexShader* vertexShader)
{
uint32 numOutputIds = vertexShader->regs.vsOutIdTableSize;
numOutputIds = std::min<uint32>(numOutputIds, 0xA);
uint32 vsSemanticTableSize = vertexShader->regs.semanticTableSize;
uint32 reserveSize = 31;
if (vertexShader->shaderMode == GX2_SHADER_MODE::GEOMETRY_SHADER)
{
reserveSize += 7;
}
else
{
reserveSize += 18;
reserveSize += numOutputIds;
if (vertexShader->usesStreamOut != 0)
reserveSize += 2+12;
}
if (vsSemanticTableSize > 0)
{
reserveSize += 5 + vsSemanticTableSize;
}
GX2ReserveCmdSpace(reserveSize);
GX2ReserveCmdSpace(100);
MPTR shaderProgramAddr;
uint32 shaderProgramSize;
@@ -381,6 +361,8 @@ namespace GX2
cemu_assert_debug(vertexShader->regs.SPI_VS_OUT_CONFIG.value().get_VS_PER_COMPONENT() == false); // not handled on the GPU side
uint32 numOutputIds = vertexShader->regs.vsOutIdTableSize;
numOutputIds = std::min<uint32>(numOutputIds, 0xA);
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 1+numOutputIds));
gx2WriteGather_submitU32AsBE(Latte::REGADDR::SPI_VS_OUT_ID_0-0xA000);
for(uint32 i=0; i<numOutputIds; i++)
@@ -410,6 +392,7 @@ namespace GX2
}
}
// update semantic table
uint32 vsSemanticTableSize = vertexShader->regs.semanticTableSize;
if (vsSemanticTableSize > 0)
{
gx2WriteGather_submit(
+1
View File
@@ -213,6 +213,7 @@ namespace GX2
void GX2SetViewportReg(GX2ViewportReg* viewportReg)
{
GX2::GX2WriteGather_checkAndInsertWrapAroundMark();
GX2ReserveCmdSpace(2 + 6);
gx2WriteGather_submit(pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 6),
+3 -3
View File
@@ -264,7 +264,7 @@ void gx2Surface_GX2CopySurface(GX2Surface* srcSurface, uint32 srcMip, uint32 src
// send copy command to GPU
if( srcHwTileMode > 0 && srcHwTileMode < 16 && dstHwTileMode > 0 && dstHwTileMode < 16 || requestGPURAMCopy )
{
GX2::GX2ReserveCmdSpace(1+13*2);
GX2ReserveCmdSpace(1+13*2);
gx2WriteGather_submit(pm4HeaderType3(IT_HLE_COPY_SURFACE_NEW, 13*2),
// src
@@ -540,7 +540,7 @@ void gx2Export_GX2ResolveAAColorBuffer(PPCInterpreter_t* hCPU)
uint32 dstDepth = std::max<uint32>(surfOutDst.depth, 1);
// send copy command to GPU
GX2::GX2ReserveCmdSpace(1 + 13 * 2);
GX2ReserveCmdSpace(1 + 13 * 2);
gx2WriteGather_submit(pm4HeaderType3(IT_HLE_COPY_SURFACE_NEW, 13 * 2),
// src
(uint32)srcSurface->imagePtr,
@@ -619,7 +619,7 @@ void gx2Export_GX2ConvertDepthBufferToTextureSurface(PPCInterpreter_t* hCPU)
sint32 srcMip = 0;
uint32 numSlices = std::max<uint32>(_swapEndianU32(depthBuffer->viewNumSlices), 1);
GX2::GX2ReserveCmdSpace((1 + 13 * 2) * numSlices);
GX2ReserveCmdSpace((1 + 13 * 2) * numSlices);
for (uint32 subSliceIndex = 0; subSliceIndex < numSlices; subSliceIndex++)
{
// send copy command to GPU
+13 -21
View File
@@ -11,14 +11,9 @@
void gx2Export_GX2SetPixelShader(PPCInterpreter_t* hCPU)
{
cemuLog_log(LogType::GX2, "GX2SetPixelShader(0x{:08x})", hCPU->gpr[3]);
GX2ReserveCmdSpace(100);
GX2PixelShader_t* pixelShader = (GX2PixelShader_t*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
uint32 numInputs = _swapEndianU32(pixelShader->regs[4]);
if( numInputs > 0x20 )
numInputs = 0x20;
GX2::GX2ReserveCmdSpace(26 + numInputs);
MPTR shaderProgramAddr;
uint32 shaderProgramSize;
@@ -49,6 +44,9 @@ void gx2Export_GX2SetPixelShader(PPCInterpreter_t* hCPU)
_swapEndianU32(pixelShader->regs[2]),
_swapEndianU32(pixelShader->regs[3]));
// setup pixel shader extended inputs control
uint32 numInputs = _swapEndianU32(pixelShader->regs[4]);
if( numInputs > 0x20 )
numInputs = 0x20;
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 1+numInputs));
gx2WriteGather_submitU32AsBE(mmSPI_PS_INPUT_CNTL_0-0xA000);
for(uint32 i=0; i<numInputs; i++)
@@ -81,17 +79,9 @@ void gx2Export_GX2SetPixelShader(PPCInterpreter_t* hCPU)
void gx2Export_GX2SetGeometryShader(PPCInterpreter_t* hCPU)
{
cemuLog_log(LogType::GX2, "GX2SetGeometryShader(0x{:08x})", hCPU->gpr[3]);
GX2ReserveCmdSpace(100);
GX2GeometryShader_t* geometryShader = (GX2GeometryShader_t*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
uint32 numOutputIds = _swapEndianU32(geometryShader->regs[7]);
numOutputIds = std::min<uint32>(numOutputIds, 0xA);
uint32 reserveSize = 38; // 38 fixed parameters
if (numOutputIds != 0)
reserveSize += 2 + numOutputIds;
if( _swapEndianU32(geometryShader->useStreamout) != 0 )
reserveSize += 2 + 12;
GX2::GX2ReserveCmdSpace(reserveSize);
MPTR shaderProgramAddr;
uint32 shaderProgramSize;
@@ -138,7 +128,6 @@ void gx2Export_GX2SetGeometryShader(PPCInterpreter_t* hCPU)
if( _swapEndianU32(geometryShader->useStreamout) != 0 )
{
// todo - IT_EVENT_WRITE packet here
// stride 0
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 2));
gx2WriteGather_submitU32AsBE(mmVGT_STRMOUT_VTX_STRIDE_0-0xA000);
@@ -191,6 +180,8 @@ void gx2Export_GX2SetGeometryShader(PPCInterpreter_t* hCPU)
gx2WriteGather_submitU32AsBE(_swapEndianU32(geometryShader->regs[3]));
// GS outputs
uint32 numOutputIds = _swapEndianU32(geometryShader->regs[7]);
numOutputIds = std::min<uint32>(numOutputIds, 0xA);
if( numOutputIds != 0 )
{
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 1+numOutputIds));
@@ -263,7 +254,8 @@ void gx2Export_GX2SetComputeShader(PPCInterpreter_t* hCPU)
shaderPtr = computeShader->rBuffer.GetVirtualAddr();
shaderSize = computeShader->rBuffer.GetSize();
}
GX2::GX2ReserveCmdSpace(0x11);
GX2ReserveCmdSpace(0x11);
gx2WriteGather_submit(pm4HeaderType3(IT_SET_CONTEXT_REG, 6),
mmSQ_PGM_START_ES-0xA000,
@@ -280,7 +272,7 @@ void gx2Export_GX2SetComputeShader(PPCInterpreter_t* hCPU)
void _GX2SubmitUniformBlock(uint32 registerBase, uint32 index, MPTR virtualAddress, uint32 size)
{
GX2::GX2ReserveCmdSpace(9);
GX2ReserveCmdSpace(9);
gx2WriteGather_submit(pm4HeaderType3(IT_SET_RESOURCE, 8),
registerBase + index * 7,
memory_virtualToPhysical(virtualAddress),
@@ -315,7 +307,7 @@ void gx2Export_GX2SetGeometryUniformBlock(PPCInterpreter_t* hCPU)
void gx2Export_GX2RSetVertexUniformBlock(PPCInterpreter_t* hCPU)
{
GX2::GX2ReserveCmdSpace(9);
GX2ReserveCmdSpace(9);
GX2RBuffer* bufferPtr = (GX2RBuffer*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
uint32 index = hCPU->gpr[4];
@@ -328,7 +320,7 @@ void gx2Export_GX2RSetVertexUniformBlock(PPCInterpreter_t* hCPU)
void gx2Export_GX2SetShaderModeEx(PPCInterpreter_t* hCPU)
{
GX2::GX2ReserveCmdSpace(8+4);
GX2ReserveCmdSpace(8+4);
uint32 mode = hCPU->gpr[3];
uint32 sqConfig = hCPU->gpr[3] == 0 ? 4 : 0;

Some files were not shown because too many files have changed in this diff Show More