mirror of
https://github.com/izzy2lost/WeeU.git
synced 2026-07-06 00:19:59 -07:00
Revert "GX2+TCL: Reimplement command buffer submission"
This reverts commit 28ea70b6d8.
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,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);
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
@@ -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]);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user