Latte: Instance count minimum is 1

Previously we skipped drawcalls when the instance count was set to zero. But the hardware register enforces a minimum of 1.

Fixes black screen in "Cubit The Hardcore Platformer Robot" which does all it's drawcalls with an hardcoded instance count of 0.
This commit is contained in:
Exzap 2025-11-28 10:34:52 +01:00
parent 5bf58c3d20
commit 47b8d911b9
5 changed files with 15 additions and 28 deletions

View file

@ -27,11 +27,6 @@ struct LatteGPUState_t
uint32 contextControl1;
// optional features
bool allowFramebufferSizeOptimization{false}; // allow using scissor box as size hint to determine non-padded rendertarget size
// draw context
struct
{
uint32 numInstances;
}drawContext;
// stats
uint32 frameCounter;
uint32 flipCounter; // increased by one everytime a vsync + flip happens

View file

@ -62,16 +62,7 @@ public:
{
uint32 baseVertex = LatteGPUState.contextRegister[mmSQ_VTX_BASE_VTX_LOC];
uint32 baseInstance = LatteGPUState.contextRegister[mmSQ_VTX_START_INST_LOC];
uint32 numInstances = LatteGPUState.drawContext.numInstances;
if (numInstances == 0)
return;
/*
if (GetAsyncKeyState('B'))
{
cemuLog_force("[executeDraw] {} Count {} BaseVertex {} BaseInstance {}", m_isFirstDraw?"Init":"Fast", count, baseVertex, baseInstance);
}
*/
uint32 numInstances = LatteGPUState.contextNew.VGT_DMA_NUM_INSTANCES.get_NUM_INSTANCES();
if (!isAutoIndex)
{
@ -391,7 +382,10 @@ LatteCMDPtr LatteCP_itIndexType(LatteCMDPtr cmd, uint32 nWords)
LatteCMDPtr LatteCP_itNumInstances(LatteCMDPtr cmd, uint32 nWords)
{
cemu_assert_debug(nWords == 1);
LatteGPUState.drawContext.numInstances = LatteReadCMD();
uint32 numInstances = LatteReadCMD();
if (numInstances == 0)
numInstances = 1;
LatteGPUState.contextNew.VGT_DMA_NUM_INSTANCES.set_NUM_INSTANCES(numInstances);
return cmd;
}
@ -690,9 +684,6 @@ LatteCMDPtr LatteCP_itDrawIndexAuto(LatteCMDPtr cmd, uint32 nWords, DrawPassCont
cemu_assert_debug(nWords == 2);
uint32 count = LatteReadCMD();
uint32 ukn = LatteReadCMD();
if (LatteGPUState.drawContext.numInstances == 0)
return cmd;
LatteGPUState.currentDrawCallTick = GetTickCount();
// todo - better way to identify compute drawcalls
if ((LatteGPUState.contextRegister[mmSQ_CONFIG] >> 24) == 0xE4)
@ -750,13 +741,7 @@ LatteCMDPtr LatteCP_itDrawImmediate(LatteCMDPtr cmd, uint32 nWords, DrawPassCont
cemuLog_log(LogType::Force, "itDrawImmediate - Unsupported index type");
return cmd;
}
// verify packet size
if (nWords != (2 + numIndexU32s))
debugBreakpoint();
uint32 baseVertex = LatteGPUState.contextRegister[mmSQ_VTX_BASE_VTX_LOC];
uint32 baseInstance = LatteGPUState.contextRegister[mmSQ_VTX_START_INST_LOC];
uint32 numInstances = LatteGPUState.drawContext.numInstances;
cemu_assert_debug(nWords == (2 + numIndexU32s)); // verify packet size
drawPassCtx.executeDraw(count, false, _tempIndexArrayMPTR);
return cmd;

View file

@ -30,6 +30,7 @@ void Latte_LoadInitialRegisters()
{
LatteGPUState.contextNew.CB_TARGET_MASK.set_MASK(0xFFFFFFFF);
LatteGPUState.contextNew.VGT_MULTI_PRIM_IB_RESET_INDX.set_RESTART_INDEX(0xFFFFFFFF);
LatteGPUState.contextNew.VGT_DMA_NUM_INSTANCES.set_NUM_INSTANCES(1);
LatteGPUState.contextRegister[Latte::REGADDR::PA_CL_CLIP_CNTL] = 0;
*(float*)&LatteGPUState.contextRegister[mmDB_DEPTH_CLEAR] = 1.0f;
}

View file

@ -445,6 +445,7 @@ namespace Latte
VGT_DMA_INDEX_TYPE = 0xA29F, // todo - verify offset
VGT_PRIMITIVEID_EN = 0xA2A1,
VGT_DMA_NUM_INSTANCES = 0xA2A2,
VGT_MULTI_PRIM_IB_RESET_EN = 0xA2A5,
@ -977,6 +978,11 @@ float get_##__regname() const \
LATTE_BITFIELD_BOOL(PRIMITIVEID_EN, 0);
};
struct LATTE_VGT_DMA_NUM_INSTANCES : LATTEREG // 0xA2A2
{
LATTE_BITFIELD_FULL_TYPED(NUM_INSTANCES, uint32);
};
struct LATTE_VGT_MULTI_PRIM_IB_RESET_EN : LATTEREG // 0xA2A5
{
LATTE_BITFIELD_BOOL(RESET_EN, 0);
@ -1541,7 +1547,7 @@ struct LatteContextRegister
/* +0x28A7C */ Latte::LATTE_VGT_DMA_INDEX_TYPE VGT_DMA_INDEX_TYPE;
/* +0x28A80 */ uint32 ukn28A80;
/* +0x28A84 */ Latte::LATTE_VGT_PRIMITIVEID_EN VGT_PRIMITIVEID_EN;
/* +0x28A88 */ uint32 ukn28A88;
/* +0x28A88 */ Latte::LATTE_VGT_DMA_NUM_INSTANCES VGT_DMA_NUM_INSTANCES;
/* +0x28A8C */ uint32 ukn28A8C;
/* +0x28A90 */ uint32 ukn28A90;
/* +0x28A94 */ Latte::LATTE_VGT_MULTI_PRIM_IB_RESET_EN VGT_MULTI_PRIM_IB_RESET_EN;
@ -1611,6 +1617,7 @@ static_assert(offsetof(LatteContextRegister, PA_SC_GENERIC_SCISSOR_TL) == Latte:
static_assert(offsetof(LatteContextRegister, PA_SC_GENERIC_SCISSOR_BR) == Latte::REGADDR::PA_SC_GENERIC_SCISSOR_BR * 4);
static_assert(offsetof(LatteContextRegister, VGT_MULTI_PRIM_IB_RESET_INDX) == Latte::REGADDR::VGT_MULTI_PRIM_IB_RESET_INDX * 4);
static_assert(offsetof(LatteContextRegister, VGT_PRIMITIVEID_EN) == Latte::REGADDR::VGT_PRIMITIVEID_EN * 4);
static_assert(offsetof(LatteContextRegister, VGT_DMA_NUM_INSTANCES) == Latte::REGADDR::VGT_DMA_NUM_INSTANCES * 4);
static_assert(offsetof(LatteContextRegister, VGT_MULTI_PRIM_IB_RESET_EN) == Latte::REGADDR::VGT_MULTI_PRIM_IB_RESET_EN * 4);
static_assert(offsetof(LatteContextRegister, VGT_INSTANCE_STEP_RATE_0) == Latte::REGADDR::VGT_INSTANCE_STEP_RATE_0 * 4);
static_assert(offsetof(LatteContextRegister, VGT_INSTANCE_STEP_RATE_1) == Latte::REGADDR::VGT_INSTANCE_STEP_RATE_1 * 4);

View file

@ -1331,7 +1331,6 @@ void wxGameList::AsyncWorkerThread()
else
{
cemuLog_log(LogType::Force, "Failed to load icon for title {:016x}", titleId);
cemu_assert_debug(false);
}
titleInfo.Unmount(tempMountPath);
// notify UI about loaded icon