Refactor more GX2 code to use LatteReg.h

This commit is contained in:
Exzap 2023-09-08 02:28:51 +02:00
parent 96800c6f97
commit 524188bb7a
10 changed files with 536 additions and 262 deletions

View file

@ -396,12 +396,7 @@ void gx2_load()
osLib_addFunction("gx2", "GX2GetCurrentScanBuffer", gx2Export_GX2GetCurrentScanBuffer);
// shader stuff
osLib_addFunction("gx2", "GX2GetVertexShaderGPRs", gx2Export_GX2GetVertexShaderGPRs);
osLib_addFunction("gx2", "GX2GetVertexShaderStackEntries", gx2Export_GX2GetVertexShaderStackEntries);
osLib_addFunction("gx2", "GX2GetPixelShaderGPRs", gx2Export_GX2GetPixelShaderGPRs);
osLib_addFunction("gx2", "GX2GetPixelShaderStackEntries", gx2Export_GX2GetPixelShaderStackEntries);
osLib_addFunction("gx2", "GX2SetFetchShader", gx2Export_GX2SetFetchShader);
osLib_addFunction("gx2", "GX2SetVertexShader", gx2Export_GX2SetVertexShader);
//osLib_addFunction("gx2", "GX2SetVertexShader", gx2Export_GX2SetVertexShader);
osLib_addFunction("gx2", "GX2SetPixelShader", gx2Export_GX2SetPixelShader);
osLib_addFunction("gx2", "GX2SetGeometryShader", gx2Export_GX2SetGeometryShader);
osLib_addFunction("gx2", "GX2SetComputeShader", gx2Export_GX2SetComputeShader);

View file

@ -20,12 +20,6 @@ void gx2_load();
// shader
void gx2Export_GX2SetFetchShader(PPCInterpreter_t* hCPU);
void gx2Export_GX2GetVertexShaderGPRs(PPCInterpreter_t* hCPU);
void gx2Export_GX2GetVertexShaderStackEntries(PPCInterpreter_t* hCPU);
void gx2Export_GX2GetPixelShaderGPRs(PPCInterpreter_t* hCPU);
void gx2Export_GX2GetPixelShaderStackEntries(PPCInterpreter_t* hCPU);
void gx2Export_GX2SetVertexShader(PPCInterpreter_t* hCPU);
void gx2Export_GX2SetPixelShader(PPCInterpreter_t* hCPU);
void gx2Export_GX2SetGeometryShader(PPCInterpreter_t* hCPU);
void gx2Export_GX2SetComputeShader(PPCInterpreter_t* hCPU);

View file

@ -263,7 +263,7 @@ namespace GX2
if (patchType == GX2_PATCH_TYPE::VERTEX_SHADER)
{
GX2VertexShader_t* vertexShader = (GX2VertexShader_t*)obj;
GX2VertexShader* vertexShader = (GX2VertexShader*)obj;
displayData[patchOffset / 4 + 2] = memory_virtualToPhysical(vertexShader->GetProgramAddr()) >> 8;
}
else if (patchType == GX2_PATCH_TYPE::PIXEL_SHADER)
@ -273,7 +273,7 @@ namespace GX2
}
else if (patchType == GX2_PATCH_TYPE::FETCH_SHADER)
{
GX2FetchShader_t* fetchShader = (GX2FetchShader_t*)obj;
GX2FetchShader* fetchShader = (GX2FetchShader*)obj;
displayData[patchOffset / 4 + 2] = memory_virtualToPhysical(fetchShader->GetProgramAddr()) >> 8;
}
else if (patchType == GX2_PATCH_TYPE::GEOMETRY_COPY_SHADER)

View file

@ -3,6 +3,7 @@
#include "GX2_Shader.h"
#include "Cafe/HW/Latte/Core/LatteConst.h"
#include "Cafe/HW/Latte/Core/LattePM4.h"
#include "Cafe/HW/Latte/ISA/LatteReg.h"
#include "Cafe/HW/Latte/ISA/LatteInstructions.h"
uint32 memory_getVirtualOffsetFromPointer(void* ptr); // remove once we updated everything to MEMPTR
@ -70,9 +71,9 @@ namespace GX2
static_assert(sizeof(betype<LatteConst::VertexFetchEndianMode>) == 0x4);
// calculate size of CF program subpart, includes alignment padding for clause instructions
size_t _calcFetchShaderCFCodeSize(uint32 attributeCount, GX2FetchShader_t::FetchShaderType fetchShaderType, uint32 tessellationMode)
size_t _calcFetchShaderCFCodeSize(uint32 attributeCount, GX2FetchShader::FetchShaderType fetchShaderType, uint32 tessellationMode)
{
cemu_assert_debug(fetchShaderType == GX2FetchShader_t::FetchShaderType::NO_TESSELATION);
cemu_assert_debug(fetchShaderType == GX2FetchShader::FetchShaderType::NO_TESSELATION);
cemu_assert_debug(tessellationMode == 0);
uint32 numCFInstructions = ((attributeCount + 15) / 16) + 1; // one VTX clause can have up to 16 instructions + final CF instruction is RETURN
size_t cfSize = numCFInstructions * 8;
@ -80,16 +81,16 @@ namespace GX2
return cfSize;
}
size_t _calcFetchShaderClauseCodeSize(uint32 attributeCount, GX2FetchShader_t::FetchShaderType fetchShaderType, uint32 tessellationMode)
size_t _calcFetchShaderClauseCodeSize(uint32 attributeCount, GX2FetchShader::FetchShaderType fetchShaderType, uint32 tessellationMode)
{
cemu_assert_debug(fetchShaderType == GX2FetchShader_t::FetchShaderType::NO_TESSELATION);
cemu_assert_debug(fetchShaderType == GX2FetchShader::FetchShaderType::NO_TESSELATION);
cemu_assert_debug(tessellationMode == 0);
uint32 numClauseInstructions = attributeCount;
size_t clauseSize = numClauseInstructions * 16;
return clauseSize;
}
void _writeFetchShaderCFCode(void* programBufferOut, uint32 attributeCount, GX2FetchShader_t::FetchShaderType fetchShaderType, uint32 tessellationMode)
void _writeFetchShaderCFCode(void* programBufferOut, uint32 attributeCount, GX2FetchShader::FetchShaderType fetchShaderType, uint32 tessellationMode)
{
LatteCFInstruction* cfInstructionWriter = (LatteCFInstruction*)programBufferOut;
uint32 attributeIndex = 0;
@ -111,7 +112,7 @@ namespace GX2
memcpy(cfInstructionWriter, &returnInstr, sizeof(LatteCFInstruction));
}
void _writeFetchShaderVTXCode(GX2FetchShader_t* fetchShader, void* programOut, uint32 attributeCount, GX2AttribDescription* attributeDescription, GX2FetchShader_t::FetchShaderType fetchShaderType, uint32 tessellationMode)
void _writeFetchShaderVTXCode(GX2FetchShader* fetchShader, void* programOut, uint32 attributeCount, GX2AttribDescription* attributeDescription, GX2FetchShader::FetchShaderType fetchShaderType, uint32 tessellationMode)
{
uint8* writePtr = (uint8*)programOut;
// one instruction per attribute (hardcoded into _writeFetchShaderCFCode)
@ -151,7 +152,7 @@ namespace GX2
bool divisorFound = false;
for (uint32 i = 0; i < numDivisors; i++)
{
if (_swapEndianU32(fetchShader->divisors[i]) == attrAluDivisor)
if (fetchShader->divisors[i] == attrAluDivisor)
{
srcSelX = i != 0 ? 2 : 1;
divisorFound = true;
@ -168,7 +169,7 @@ namespace GX2
else
{
srcSelX = numDivisors != 0 ? 2 : 1;
fetchShader->divisors[numDivisors] = _swapEndianU32(attrAluDivisor);
fetchShader->divisors[numDivisors] = attrAluDivisor;
numDivisors++;
fetchShader->divisorCount = _swapEndianU32(numDivisors);
}
@ -213,9 +214,9 @@ namespace GX2
}
}
uint32 GX2CalcFetchShaderSizeEx(uint32 attributeCount, GX2FetchShader_t::FetchShaderType fetchShaderType, uint32 tessellationMode)
uint32 GX2CalcFetchShaderSizeEx(uint32 attributeCount, GX2FetchShader::FetchShaderType fetchShaderType, uint32 tessellationMode)
{
cemu_assert_debug(fetchShaderType == GX2FetchShader_t::FetchShaderType::NO_TESSELATION); // other types are todo
cemu_assert_debug(fetchShaderType == GX2FetchShader::FetchShaderType::NO_TESSELATION); // other types are todo
cemu_assert_debug(tessellationMode == 0); // other modes are todo
uint32 finalSize =
@ -225,9 +226,9 @@ namespace GX2
return finalSize;
}
void GX2InitFetchShaderEx(GX2FetchShader_t* fetchShader, void* programBufferOut, uint32 attributeCount, GX2AttribDescription* attributeDescription, GX2FetchShader_t::FetchShaderType fetchShaderType, uint32 tessellationMode)
void GX2InitFetchShaderEx(GX2FetchShader* fetchShader, void* programBufferOut, uint32 attributeCount, GX2AttribDescription* attributeDescription, GX2FetchShader::FetchShaderType fetchShaderType, uint32 tessellationMode)
{
cemu_assert_debug(fetchShaderType == GX2FetchShader_t::FetchShaderType::NO_TESSELATION);
cemu_assert_debug(fetchShaderType == GX2FetchShader::FetchShaderType::NO_TESSELATION);
cemu_assert_debug(tessellationMode == 0);
/*
@ -238,7 +239,7 @@ namespace GX2
[CLAUSES]
*/
memset(fetchShader, 0x00, sizeof(GX2FetchShader_t));
memset(fetchShader, 0x00, sizeof(GX2FetchShader));
fetchShader->attribCount = _swapEndianU32(attributeCount);
fetchShader->shaderPtr = (MPTR)_swapEndianU32(memory_getVirtualOffsetFromPointer(programBufferOut));
@ -251,14 +252,181 @@ namespace GX2
shaderOutput += _calcFetchShaderClauseCodeSize(attributeCount, fetchShaderType, tessellationMode);
uint32 shaderSize = (uint32)(shaderOutput - shaderStart);
cemu_assert_debug(shaderSize == GX2CalcFetchShaderSizeEx(attributeCount, GX2FetchShader_t::FetchShaderType::NO_TESSELATION, tessellationMode));
cemu_assert_debug(shaderSize == GX2CalcFetchShaderSizeEx(attributeCount, GX2FetchShader::FetchShaderType::NO_TESSELATION, tessellationMode));
fetchShader->shaderSize = _swapEndianU32((uint32)(shaderOutput - shaderStart));
fetchShader->reg_SQ_PGM_RESOURCES_FS = Latte::LATTE_SQ_PGM_RESOURCES_FS().set_NUM_GPRS(2); // todo - affected by tesselation params?
}
uint32 GX2GetVertexShaderGPRs(GX2VertexShader* vertexShader)
{
return vertexShader->regs.SQ_PGM_RESOURCES_VS.value().get_NUM_GPRS();
}
uint32 GX2GetVertexShaderStackEntries(GX2VertexShader* vertexShader)
{
return vertexShader->regs.SQ_PGM_RESOURCES_VS.value().get_NUM_STACK_ENTRIES();
}
uint32 GX2GetPixelShaderGPRs(GX2PixelShader_t* pixelShader)
{
return _swapEndianU32(pixelShader->regs[0])&0xFF;
}
uint32 GX2GetPixelShaderStackEntries(GX2PixelShader_t* pixelShader)
{
return (_swapEndianU32(pixelShader->regs[0]>>8))&0xFF;
}
void GX2SetFetchShader(GX2FetchShader* fetchShaderPtr)
{
GX2ReserveCmdSpace(11);
cemu_assert_debug((_swapEndianU32(fetchShaderPtr->shaderPtr) & 0xFF) == 0);
gx2WriteGather_submit(
// setup fetch shader
pm4HeaderType3(IT_SET_CONTEXT_REG, 1+5),
Latte::REGADDR::SQ_PGM_START_FS-0xA000,
_swapEndianU32(fetchShaderPtr->shaderPtr)>>8,
_swapEndianU32(fetchShaderPtr->shaderSize)>>3,
0x10000, // ukn (ring buffer size?)
0x10000, // ukn (ring buffer size?)
fetchShaderPtr->reg_SQ_PGM_RESOURCES_FS,
// write instance step
pm4HeaderType3(IT_SET_CONTEXT_REG, 1+2),
Latte::REGADDR::VGT_INSTANCE_STEP_RATE_0-0xA000,
fetchShaderPtr->divisors[0],
fetchShaderPtr->divisors[1]);
}
void GX2SetVertexShader(GX2VertexShader* vertexShader)
{
GX2ReserveCmdSpace(100);
MPTR shaderProgramAddr;
uint32 shaderProgramSize;
if (vertexShader->shaderPtr)
{
// without R API
shaderProgramAddr = vertexShader->shaderPtr.GetMPTR();
shaderProgramSize = vertexShader->shaderSize;
}
else
{
shaderProgramAddr = vertexShader->rBuffer.GetVirtualAddr();
shaderProgramSize = vertexShader->rBuffer.GetSize();
}
cemu_assert_debug(shaderProgramAddr != 0);
cemu_assert_debug(shaderProgramSize != 0);
if (vertexShader->shaderMode == GX2_SHADER_MODE::GEOMETRY_SHADER)
{
// in geometry shader mode the vertex shader is written to _ES register and almost all vs control registers are set by GX2SetGeometryShader
gx2WriteGather_submit(
pm4HeaderType3(IT_SET_CONTEXT_REG, 6),
Latte::REGADDR::SQ_PGM_START_ES-0xA000,
memory_virtualToPhysical(shaderProgramAddr)>>8,
shaderProgramSize>>3,
0x100000,
0x100000,
vertexShader->regs.SQ_PGM_RESOURCES_VS); // SQ_PGM_RESOURCES_VS/SQ_PGM_RESOURCES_ES
}
else
{
gx2WriteGather_submit(
/* vertex shader program */
pm4HeaderType3(IT_SET_CONTEXT_REG, 6),
Latte::REGADDR::SQ_PGM_START_VS-0xA000,
memory_virtualToPhysical(shaderProgramAddr)>>8, // physical address
shaderProgramSize>>3,
0x100000,
0x100000,
vertexShader->regs.SQ_PGM_RESOURCES_VS, // SQ_PGM_RESOURCES_VS/ES
/* primitive id enable */
pm4HeaderType3(IT_SET_CONTEXT_REG, 2),
Latte::REGADDR::VGT_PRIMITIVEID_EN-0xA000,
vertexShader->regs.VGT_PRIMITIVEID_EN,
/* output config */
pm4HeaderType3(IT_SET_CONTEXT_REG, 2),
Latte::REGADDR::SPI_VS_OUT_CONFIG-0xA000,
vertexShader->regs.SPI_VS_OUT_CONFIG,
/* PA_CL_VS_OUT_CNTL */
pm4HeaderType3(IT_SET_CONTEXT_REG, 2),
Latte::REGADDR::PA_CL_VS_OUT_CNTL-0xA000,
vertexShader->regs.PA_CL_VS_OUT_CNTL
);
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++)
gx2WriteGather_submitU32AsBE(vertexShader->regs.LATTE_SPI_VS_OUT_ID_N[i].value().getRawValue());
// todo: SQ_PGM_CF_OFFSET_VS
// todo: VGT_STRMOUT_BUFFER_EN
// stream out
if (vertexShader->usesStreamOut != 0)
{
// stride 0
gx2WriteGather_submit(pm4HeaderType3(IT_SET_CONTEXT_REG, 2),
Latte::REGADDR::VGT_STRMOUT_VTX_STRIDE_0-0xA000,
vertexShader->streamOutVertexStride[0]>>2,
// stride 1
pm4HeaderType3(IT_SET_CONTEXT_REG, 2),
Latte::REGADDR::VGT_STRMOUT_VTX_STRIDE_1-0xA000,
vertexShader->streamOutVertexStride[1]>>2,
// stride 2
pm4HeaderType3(IT_SET_CONTEXT_REG, 2),
Latte::REGADDR::VGT_STRMOUT_VTX_STRIDE_2-0xA000,
vertexShader->streamOutVertexStride[2]>>2,
// stride 3
pm4HeaderType3(IT_SET_CONTEXT_REG, 2),
Latte::REGADDR::VGT_STRMOUT_VTX_STRIDE_3-0xA000,
vertexShader->streamOutVertexStride[3]>>2);
}
}
// update semantic table
uint32 vsSemanticTableSize = vertexShader->regs.semanticTableSize;
if (vsSemanticTableSize > 0)
{
gx2WriteGather_submit(
pm4HeaderType3(IT_SET_CONTEXT_REG, 1+1),
Latte::REGADDR::SQ_VTX_SEMANTIC_CLEAR-0xA000,
0xFFFFFFFF);
if (vsSemanticTableSize == 0)
{
gx2WriteGather_submit(
pm4HeaderType3(IT_SET_CONTEXT_REG, 1+1),
Latte::REGADDR::SQ_VTX_SEMANTIC_0-0xA000,
0xFFFFFFFF);
}
else
{
uint32* vsSemanticTable = (uint32*)vertexShader->regs.SQ_VTX_SEMANTIC_N;
vsSemanticTableSize = std::min<uint32>(vsSemanticTableSize, 32);
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 1+vsSemanticTableSize));
gx2WriteGather_submitU32AsBE(Latte::REGADDR::SQ_VTX_SEMANTIC_0-0xA000);
gx2WriteGather_submitU32AsLEArray(vsSemanticTable, vsSemanticTableSize);
}
}
}
void GX2ShaderInit()
{
cafeExportRegister("gx2", GX2CalcFetchShaderSizeEx, LogType::GX2);
cafeExportRegister("gx2", GX2InitFetchShaderEx, LogType::GX2);
cafeExportRegister("gx2", GX2GetVertexShaderGPRs, LogType::GX2);
cafeExportRegister("gx2", GX2GetVertexShaderStackEntries, LogType::GX2);
cafeExportRegister("gx2", GX2GetPixelShaderGPRs, LogType::GX2);
cafeExportRegister("gx2", GX2GetPixelShaderStackEntries, LogType::GX2);
cafeExportRegister("gx2", GX2SetFetchShader, LogType::GX2);
cafeExportRegister("gx2", GX2SetVertexShader, LogType::GX2);
}
}

View file

@ -2,7 +2,7 @@
#include "Cafe/HW/Latte/ISA/LatteReg.h"
#include "GX2_Streamout.h"
struct GX2FetchShader_t
struct GX2FetchShader
{
enum class FetchShaderType : uint32
{
@ -10,12 +10,12 @@ struct GX2FetchShader_t
};
/* +0x00 */ betype<FetchShaderType> fetchShaderType;
/* +0x04 */ uint32 _regs[1];
/* +0x04 */ betype<Latte::LATTE_SQ_PGM_RESOURCES_FS> reg_SQ_PGM_RESOURCES_FS;
/* +0x08 */ uint32 shaderSize;
/* +0x0C */ MPTR shaderPtr;
/* +0x10 */ uint32 attribCount;
/* +0x14 */ uint32 divisorCount;
/* +0x18 */ uint32 divisors[2];
/* +0x18 */ uint32be divisors[2];
MPTR GetProgramAddr() const
{
@ -23,8 +23,8 @@ struct GX2FetchShader_t
}
};
static_assert(sizeof(GX2FetchShader_t) == 0x20);
static_assert(sizeof(betype<GX2FetchShader_t::FetchShaderType>) == 4);
static_assert(sizeof(GX2FetchShader) == 0x20);
static_assert(sizeof(betype<GX2FetchShader::FetchShaderType>) == 4);
namespace GX2
{
@ -32,19 +32,43 @@ namespace GX2
void GX2ShaderInit();
}
// code below still needs to be modernized (use betype, enum classes)
// code below still needs to be modernized (use betype, enum classes, move to namespace)
// deprecated, use GX2_SHADER_MODE enum class instead
#define GX2_SHADER_MODE_UNIFORM_REGISTER 0
#define GX2_SHADER_MODE_UNIFORM_BLOCK 1
#define GX2_SHADER_MODE_GEOMETRY_SHADER 2
#define GX2_SHADER_MODE_COMPUTE_SHADER 3
struct GX2VertexShader_t
enum class GX2_SHADER_MODE : uint32
{
/* +0x000 */ uint32 regs[52];
/* +0x0D0 */ uint32 shaderSize;
/* +0x0D4 */ MPTR shaderPtr;
/* +0x0D8 */ uint32 shaderMode; // GX2_SHADER_MODE_*
UNIFORM_REGISTER = 0,
UNIFORM_BLOCK = 1,
GEOMETRY_SHADER = 2,
COMPUTE_SHADER = 3,
};
struct GX2VertexShader
{
/* +0x000 */
struct
{
/* +0x00 */ betype<Latte::LATTE_SQ_PGM_RESOURCES_VS> SQ_PGM_RESOURCES_VS; // compatible with SQ_PGM_RESOURCES_ES
/* +0x04 */ betype<Latte::LATTE_VGT_PRIMITIVEID_EN> VGT_PRIMITIVEID_EN;
/* +0x08 */ betype<Latte::LATTE_SPI_VS_OUT_CONFIG> SPI_VS_OUT_CONFIG;
/* +0x0C */ uint32be vsOutIdTableSize;
/* +0x10 */ betype<Latte::LATTE_SPI_VS_OUT_ID_N> LATTE_SPI_VS_OUT_ID_N[10];
/* +0x38 */ betype<Latte::LATTE_PA_CL_VS_OUT_CNTL> PA_CL_VS_OUT_CNTL;
/* +0x3C */ uint32be uknReg15; // ?
/* +0x40 */ uint32be semanticTableSize;
/* +0x44 */ betype<Latte::LATTE_SQ_VTX_SEMANTIC_X> SQ_VTX_SEMANTIC_N[32];
/* +0xC4 */ uint32be uknReg49; // ?
/* +0xC8 */ uint32be uknReg50; // vgt_vertex_reuse_block_cntl
/* +0xCC */ uint32be uknReg51; // vgt_hos_reuse_depth
}regs;
/* +0x0D0 */ uint32be shaderSize;
/* +0x0D4 */ MEMPTR<void> shaderPtr;
/* +0x0D8 */ betype<GX2_SHADER_MODE> shaderMode;
/* +0x0DC */ uint32 uniformBlockCount;
/* +0x0E0 */ MPTR uniformBlockInfo;
/* +0x0E4 */ uint32 uniformVarCount;
@ -57,20 +81,20 @@ struct GX2VertexShader_t
/* +0x100 */ MPTR samplerInfo;
/* +0x104 */ uint32 attribCount;
/* +0x108 */ MPTR attribInfo;
/* +0x10C */ uint32 ringItemsize; // for GS
/* +0x110 */ uint32 usesStreamOut;
/* +0x114 */ uint32 streamOutVertexStride[GX2_MAX_STREAMOUT_BUFFERS];
/* +0x10C */ uint32be ringItemsize; // for GS
/* +0x110 */ uint32be usesStreamOut;
/* +0x114 */ uint32be streamOutVertexStride[GX2_MAX_STREAMOUT_BUFFERS];
/* +0x124 */ GX2RBuffer rBuffer;
MPTR GetProgramAddr() const
{
if (_swapEndianU32(this->shaderPtr) != MPTR_NULL)
return _swapEndianU32(this->shaderPtr);
if (this->shaderPtr)
return this->shaderPtr.GetMPTR();
return this->rBuffer.GetVirtualAddr();
}
};
static_assert(sizeof(GX2VertexShader_t) == 0x134);
static_assert(sizeof(GX2VertexShader) == 0x134);
typedef struct _GX2PixelShader
{

View file

@ -8,204 +8,6 @@
#include "GX2.h"
#include "GX2_Shader.h"
void gx2Export_GX2SetFetchShader(PPCInterpreter_t* hCPU)
{
cemuLog_log(LogType::GX2, "GX2SetFetchShader(0x{:08x})", hCPU->gpr[3]);
GX2ReserveCmdSpace(11);
GX2FetchShader_t* fetchShaderPtr = (GX2FetchShader_t*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
cemu_assert_debug((_swapEndianU32(fetchShaderPtr->shaderPtr) & 0xFF) == 0);
gx2WriteGather_submit(
// setup fetch shader
pm4HeaderType3(IT_SET_CONTEXT_REG, 1+5),
mmSQ_PGM_START_FS-0xA000,
_swapEndianU32(fetchShaderPtr->shaderPtr)>>8, // pointer divided by 256
_swapEndianU32(fetchShaderPtr->shaderSize)>>3, // size divided by 8
0x10000, // ukn (ring buffer size?)
0x10000, // ukn (ring buffer size?)
*(uint32be*)&(fetchShaderPtr->_regs[0]),
// write instance step
pm4HeaderType3(IT_SET_CONTEXT_REG, 1+2),
mmVGT_INSTANCE_STEP_RATE_0-0xA000,
*(uint32be*)&(fetchShaderPtr->divisors[0]),
*(uint32be*)&(fetchShaderPtr->divisors[1]));
osLib_returnFromFunction(hCPU, 0);
}
void gx2Export_GX2GetVertexShaderGPRs(PPCInterpreter_t* hCPU)
{
cemuLog_log(LogType::GX2, "GX2GetVertexShaderGPRs(0x{:08x})", hCPU->gpr[3]);
GX2VertexShader_t* vertexShader = (GX2VertexShader_t*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
uint8 numGPRs = _swapEndianU32(vertexShader->regs[0])&0xFF;
osLib_returnFromFunction(hCPU, numGPRs);
}
void gx2Export_GX2GetVertexShaderStackEntries(PPCInterpreter_t* hCPU)
{
cemuLog_log(LogType::GX2, "GX2GetVertexShaderStackEntries(0x{:08x})", hCPU->gpr[3]);
GX2VertexShader_t* vertexShader = (GX2VertexShader_t*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
uint8 stackEntries = (_swapEndianU32(vertexShader->regs[0])>>8)&0xFF;
osLib_returnFromFunction(hCPU, stackEntries);
}
void gx2Export_GX2GetPixelShaderGPRs(PPCInterpreter_t* hCPU)
{
cemuLog_log(LogType::GX2, "GX2GetPixelShaderGPRs(0x{:08x})", hCPU->gpr[3]);
GX2PixelShader_t* pixelShader = (GX2PixelShader_t*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
uint8 stackEntries = (_swapEndianU32(pixelShader->regs[0]))&0xFF;
osLib_returnFromFunction(hCPU, stackEntries);
}
void gx2Export_GX2GetPixelShaderStackEntries(PPCInterpreter_t* hCPU)
{
cemuLog_log(LogType::GX2, "GX2GetPixelShaderStackEntries(0x{:08x})", hCPU->gpr[3]);
GX2PixelShader_t* pixelShader = (GX2PixelShader_t*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
uint8 numGPRs = (_swapEndianU32(pixelShader->regs[0]>>8))&0xFF;
osLib_returnFromFunction(hCPU, numGPRs);
}
void gx2Export_GX2SetVertexShader(PPCInterpreter_t* hCPU)
{
cemuLog_log(LogType::GX2, "GX2SetVertexShader(0x{:08x})", hCPU->gpr[3]);
GX2ReserveCmdSpace(100);
GX2VertexShader_t* vertexShader = (GX2VertexShader_t*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
MPTR shaderProgramAddr;
uint32 shaderProgramSize;
if( _swapEndianU32(vertexShader->shaderPtr) != MPTR_NULL )
{
// without R API
shaderProgramAddr = _swapEndianU32(vertexShader->shaderPtr);
shaderProgramSize = _swapEndianU32(vertexShader->shaderSize);
}
else
{
shaderProgramAddr = vertexShader->rBuffer.GetVirtualAddr();
shaderProgramSize = vertexShader->rBuffer.GetSize();
}
cemu_assert_debug(shaderProgramAddr != 0);
cemu_assert_debug(shaderProgramSize != 0);
if( _swapEndianU32(vertexShader->shaderMode) == GX2_SHADER_MODE_GEOMETRY_SHADER )
{
// in geometry shader mode the vertex shader is written to _ES register and almost all vs control registers are set by GX2SetGeometryShader
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 6));
gx2WriteGather_submitU32AsBE(mmSQ_PGM_START_ES-0xA000);
gx2WriteGather_submitU32AsBE(memory_virtualToPhysical(shaderProgramAddr)>>8);
gx2WriteGather_submitU32AsBE(shaderProgramSize>>3);
gx2WriteGather_submitU32AsBE(0x100000);
gx2WriteGather_submitU32AsBE(0x100000);
gx2WriteGather_submitU32AsBE(_swapEndianU32(vertexShader->regs[0])); // unknown
}
else
{
gx2WriteGather_submit(
/* vertex shader program */
pm4HeaderType3(IT_SET_CONTEXT_REG, 6),
mmSQ_PGM_START_VS-0xA000,
memory_virtualToPhysical(shaderProgramAddr)>>8, // physical address
shaderProgramSize>>3, // size
0x100000,
0x100000,
_swapEndianU32(vertexShader->regs[0]), // unknown
/* primitive id enable */
pm4HeaderType3(IT_SET_CONTEXT_REG, 2),
mmVGT_PRIMITIVEID_EN-0xA000,
_swapEndianU32(vertexShader->regs[1]),
/* output config */
pm4HeaderType3(IT_SET_CONTEXT_REG, 2),
mmSPI_VS_OUT_CONFIG-0xA000,
_swapEndianU32(vertexShader->regs[2]));
if( (_swapEndianU32(vertexShader->regs[2]) & 1) != 0 )
debugBreakpoint(); // per-component flag?
// ukn
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 2));
gx2WriteGather_submitU32AsBE(mmPA_CL_VS_OUT_CNTL-0xA000);
gx2WriteGather_submitU32AsBE(_swapEndianU32(vertexShader->regs[14]));
uint32 numOutputIds = _swapEndianU32(vertexShader->regs[3]);
numOutputIds = std::min<uint32>(numOutputIds, 0xA);
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 1+numOutputIds));
gx2WriteGather_submitU32AsBE(mmSPI_VS_OUT_ID_0-0xA000);
for(uint32 i=0; i<numOutputIds; i++)
{
gx2WriteGather_submitU32AsBE(_swapEndianU32(vertexShader->regs[4+i]));
}
/*
VS _regs[]:
0 ?
1 mmVGT_PRIMITIVEID_EN (?)
2 mmSPI_VS_OUT_CONFIG
3 Number of used SPI_VS_OUT_ID_* entries
4 - 13 SPI_VS_OUT_ID_0 - SPI_VS_OUT_ID_9
14 pa_cl_vs_out_cntl
...
17 - ?? semantic table entry (input)
...
50 vgt_vertex_reuse_block_cntl
51 vgt_hos_reuse_depth
*/
// todo: mmSQ_PGM_CF_OFFSET_VS
// todo: mmVGT_STRMOUT_BUFFER_EN
// stream out
if( _swapEndianU32(vertexShader->usesStreamOut) != 0 )
{
// stride 0
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 2));
gx2WriteGather_submitU32AsBE(mmVGT_STRMOUT_VTX_STRIDE_0-0xA000);
gx2WriteGather_submitU32AsBE(_swapEndianU32(vertexShader->streamOutVertexStride[0])>>2);
// stride 1
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 2));
gx2WriteGather_submitU32AsBE(mmVGT_STRMOUT_VTX_STRIDE_1-0xA000);
gx2WriteGather_submitU32AsBE(_swapEndianU32(vertexShader->streamOutVertexStride[1])>>2);
// stride 2
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 2));
gx2WriteGather_submitU32AsBE(mmVGT_STRMOUT_VTX_STRIDE_2-0xA000);
gx2WriteGather_submitU32AsBE(_swapEndianU32(vertexShader->streamOutVertexStride[2])>>2);
// stride 3
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 2));
gx2WriteGather_submitU32AsBE(mmVGT_STRMOUT_VTX_STRIDE_3-0xA000);
gx2WriteGather_submitU32AsBE(_swapEndianU32(vertexShader->streamOutVertexStride[3])>>2);
}
}
// update semantic table
uint32 vsSemanticTableSize = _swapEndianU32(vertexShader->regs[0x40/4]);
if( vsSemanticTableSize > 0 )
{
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 1+1));
gx2WriteGather_submitU32AsBE(mmSQ_VTX_SEMANTIC_CLEAR-0xA000);
gx2WriteGather_submitU32AsBE(0xFFFFFFFF);
if( vsSemanticTableSize == 0 )
{
// todo: Figure out how this is done on real SW/HW (some vertex shaders don't have a semantic table)
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 1+1));
gx2WriteGather_submitU32AsBE(mmSQ_VTX_SEMANTIC_0-0xA000);
gx2WriteGather_submitU32AsBE(0xFFFFFFFF);
}
else
{
uint32* vsSemanticTable = vertexShader->regs+(0x44/4);
vsSemanticTableSize = std::min<uint32>(vsSemanticTableSize, 0x20);
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 1+vsSemanticTableSize));
gx2WriteGather_submitU32AsBE(mmSQ_VTX_SEMANTIC_0-0xA000);
for(uint32 i=0; i<vsSemanticTableSize; i++)
gx2WriteGather_submitU32AsLE(vsSemanticTable[i]);
}
}
osLib_returnFromFunction(hCPU, 0);
}
void gx2Export_GX2SetPixelShader(PPCInterpreter_t* hCPU)
{
cemuLog_log(LogType::GX2, "GX2SetPixelShader(0x{:08x})", hCPU->gpr[3]);
@ -415,14 +217,14 @@ void gx2Export_GX2SetGeometryShader(PPCInterpreter_t* hCPU)
osLib_returnFromFunction(hCPU, 0);
}
struct GX2ComputeShader_t
struct GX2ComputeShader
{
/* +0x00 */ uint32be regs[12];
/* +0x30 */ uint32be programSize;
/* +0x34 */ uint32be programPtr;
/* +0x38 */ uint32 ukn38;
/* +0x3C */ uint32 ukn3C;
/* +0x40 */ uint32 ukn40[8];
/* +0x38 */ uint32be ukn38;
/* +0x3C */ uint32be ukn3C;
/* +0x40 */ uint32be ukn40[8];
/* +0x60 */ uint32be workgroupSizeX;
/* +0x64 */ uint32be workgroupSizeY;
/* +0x68 */ uint32be workgroupSizeZ;
@ -431,13 +233,13 @@ struct GX2ComputeShader_t
/* +0x74 */ GX2RBuffer rBuffer;
};
static_assert(offsetof(GX2ComputeShader_t, programSize) == 0x30);
static_assert(offsetof(GX2ComputeShader_t, workgroupSizeX) == 0x60);
static_assert(offsetof(GX2ComputeShader_t, rBuffer) == 0x74);
static_assert(offsetof(GX2ComputeShader, programSize) == 0x30);
static_assert(offsetof(GX2ComputeShader, workgroupSizeX) == 0x60);
static_assert(offsetof(GX2ComputeShader, rBuffer) == 0x74);
void gx2Export_GX2SetComputeShader(PPCInterpreter_t* hCPU)
{
ppcDefineParamTypePtr(computeShader, GX2ComputeShader_t, 0);
ppcDefineParamTypePtr(computeShader, GX2ComputeShader, 0);
cemuLog_log(LogType::GX2, "GX2SetComputeShader(0x{:08x})", hCPU->gpr[3]);
MPTR shaderPtr;