Cemu/src/Cafe/OS/libs/gx2/GX2_Command.h
Exzap 28ea70b6d8 GX2+TCL: Reimplement command buffer submission
- GX2 utilizes TCL(.rpl) API for command submission instead of directly writing to an internal GPU fifo
- Submission & retire timestamps are correctly implemented as incremental counters
- Command buffering behaviour matches console
- Fixes race conditions on aarch64
2025-05-17 21:35:42 +02:00

108 lines
No EOL
3.5 KiB
C++

#pragma once
#include "Cafe/HW/Latte/ISA/LatteReg.h"
#include "Cafe/HW/Espresso/Const.h"
namespace GX2
{
struct GX2PerCoreCBState
{
uint32be* bufferPtr;
uint32 bufferSizeInU32s;
uint32be* currentWritePtr;
bool isDisplayList;
};
extern GX2PerCoreCBState s_perCoreCBState[Espresso::CORE_COUNT];
};
void gx2WriteGather_submitU32AsBE(uint32 v);
void gx2WriteGather_submitU32AsLE(uint32 v);
void gx2WriteGather_submitU32AsLEArray(uint32* v, uint32 numValues);
uint32 PPCInterpreter_getCurrentCoreIndex();
// gx2WriteGather_submit functions
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));
}
template <typename T, typename ...Targs>
inline void gx2WriteGather_submit_(uint32 coreIndex, uint32be* writePtr, const betype<T>& arg, Targs... args)
{
static_assert(sizeof(betype<T>) == sizeof(uint32be));
*(betype<T>*)writePtr = arg;
writePtr++;
gx2WriteGather_submit_(coreIndex, writePtr, args...);
}
template <typename T, typename ...Targs>
inline
typename std::enable_if< std::is_floating_point<T>::value, void>::type
gx2WriteGather_submit_(uint32 coreIndex, uint32be* writePtr, const T& arg, Targs... args)
{
static_assert(sizeof(T) == sizeof(uint32));
*writePtr = *(uint32*)&arg;
writePtr++;
gx2WriteGather_submit_(coreIndex, writePtr, args...);
}
template <typename T, typename ...Targs>
inline
typename std::enable_if< std::is_base_of<Latte::LATTEREG, T>::value, void>::type
gx2WriteGather_submit_(uint32 coreIndex, uint32be* writePtr, const T& arg, Targs... args)
{
static_assert(sizeof(Latte::LATTEREG) == sizeof(uint32be));
*writePtr = arg.getRawValue();
writePtr++;
gx2WriteGather_submit_(coreIndex, writePtr, args...);
}
template <typename T, typename ...Targs>
inline
typename std::enable_if< !std::is_base_of<Latte::LATTEREG, T>::value && !std::is_floating_point<T>::value, void>::type
gx2WriteGather_submit_(uint32 coreIndex, uint32be* writePtr, const T& arg, Targs... args)
{
*writePtr = arg;
writePtr++;
gx2WriteGather_submit_(coreIndex, writePtr, args...);
}
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?
return;
}
uint32be* writePtr = GX2::s_perCoreCBState[coreIndex].currentWritePtr;
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);
void GX2BeginDisplayList(MEMPTR<void> displayListAddr, uint32 size);
void GX2BeginDisplayListEx(MEMPTR<void> displayListAddr, uint32 size, bool profiling);
uint32 GX2EndDisplayList(MEMPTR<void> displayListAddr);
void GX2CallDisplayList(MPTR addr, uint32 size);
void GX2DirectCallDisplayList(void* addr, uint32 size);
bool GX2GetDisplayListWriteStatus();
void GX2CommandInit();
void GX2Init_commandBufferPool(void* bufferBase, uint32 bufferSize);
void GX2Shutdown_commandBufferPool();
void GX2CommandResetToDefaultState();
}