Latte: Add support for LOOP_START_NO_AL shader instruction

This instruction is used by Injustice: Gods Among Us and Project Zero

Also improved robustness of rendering to be less prone to crashing when a game tries to draw with broken shaders
This commit is contained in:
Exzap 2024-03-27 16:01:44 +01:00
parent 60adc38205
commit fa8bab2f39
6 changed files with 16 additions and 10 deletions

View File

@ -340,7 +340,7 @@ uint8 LatteMRT::GetActiveColorBufferMask(const LatteDecompilerShader* pixelShade
return 0;
cemu_assert_debug(colorControlReg.get_DEGAMMA_ENABLE() == false); // not supported
// combine color buffer mask with pixel output mask from pixel shader
colorBufferMask &= pixelShader->pixelColorOutputMask;
colorBufferMask &= (pixelShader ? pixelShader->pixelColorOutputMask : 0);
// combine color buffer mask with color channel mask from mmCB_TARGET_MASK (disable render buffer if all colors are blocked)
uint32 channelTargetMask = lcr.CB_TARGET_MASK.get_MASK();
for (uint32 i = 0; i < 8; i++)

View File

@ -12,7 +12,7 @@
#define GPU7_CF_INST_VTX (0x02) // used only in GS copy program?
#define GPU7_CF_INST_LOOP_END (0x05)
#define GPU7_CF_INST_LOOP_START_DX10 (0x06)
#define GPU7_CF_INST_LOOP_START_NO_AL (0x07) // (Seen in Project Zero)
#define GPU7_CF_INST_LOOP_START_NO_AL (0x07) // (Seen in Project Zero, Injustice: Gods Among Us)
#define GPU7_CF_INST_LOOP_BREAK (0x09)
#define GPU7_CF_INST_JUMP (0x0A)

View File

@ -101,7 +101,8 @@ bool LatteDecompiler_ParseCFInstruction(LatteDecompilerShaderContext* shaderCont
// ignored (we use ALU/IF/ELSE/PUSH/POP clauses to determine code flow)
return true;
}
else if (cf_inst23_7 == GPU7_CF_INST_LOOP_START_DX10 || cf_inst23_7 == GPU7_CF_INST_LOOP_END)
else if (cf_inst23_7 == GPU7_CF_INST_LOOP_START_DX10 || cf_inst23_7 == GPU7_CF_INST_LOOP_END ||
cf_inst23_7 == GPU7_CF_INST_LOOP_START_NO_AL)
{
LatteDecompilerCFInstruction& cfInstruction = instructionList.emplace_back();
// set type and address
@ -966,7 +967,8 @@ void LatteDecompiler_ParseClauses(LatteDecompilerShaderContext* decompilerContex
{
// no sub-instructions
}
else if (cfInstruction.type == GPU7_CF_INST_LOOP_START_DX10 || cfInstruction.type == GPU7_CF_INST_LOOP_END)
else if (cfInstruction.type == GPU7_CF_INST_LOOP_START_DX10 || cfInstruction.type == GPU7_CF_INST_LOOP_END ||
cfInstruction.type == GPU7_CF_INST_LOOP_START_NO_AL)
{
// no sub-instructions
}

View File

@ -441,7 +441,8 @@ void LatteDecompiler_analyzeSubroutine(LatteDecompilerShaderContext* shaderConte
{
shaderContext->analyzer.modifiesPixelActiveState = true;
}
else if (cfInstruction.type == GPU7_CF_INST_LOOP_START_DX10 || cfInstruction.type == GPU7_CF_INST_LOOP_END)
else if (cfInstruction.type == GPU7_CF_INST_LOOP_START_DX10 || cfInstruction.type == GPU7_CF_INST_LOOP_END ||
cfInstruction.type == GPU7_CF_INST_LOOP_START_NO_AL)
{
shaderContext->analyzer.modifiesPixelActiveState = true;
}
@ -685,7 +686,8 @@ void LatteDecompiler_analyze(LatteDecompilerShaderContext* shaderContext, LatteD
{
shaderContext->analyzer.modifiesPixelActiveState = true;
}
else if (cfInstruction.type == GPU7_CF_INST_LOOP_START_DX10 || cfInstruction.type == GPU7_CF_INST_LOOP_END)
else if (cfInstruction.type == GPU7_CF_INST_LOOP_START_DX10 || cfInstruction.type == GPU7_CF_INST_LOOP_END ||
cfInstruction.type == GPU7_CF_INST_LOOP_START_NO_AL)
{
shaderContext->analyzer.modifiesPixelActiveState = true;
shaderContext->analyzer.hasLoops = true;
@ -929,7 +931,8 @@ void LatteDecompiler_analyze(LatteDecompilerShaderContext* shaderContext, LatteD
if (cfCurrentStackDepth < 0)
debugBreakpoint();
}
else if (cfInstruction.type == GPU7_CF_INST_LOOP_START_DX10 || cfInstruction.type == GPU7_CF_INST_LOOP_END)
else if (cfInstruction.type == GPU7_CF_INST_LOOP_START_DX10 || cfInstruction.type == GPU7_CF_INST_LOOP_END ||
cfInstruction.type == GPU7_CF_INST_LOOP_START_NO_AL)
{
// no effect on stack depth
cfInstruction.activeStackDepth = cfCurrentStackDepth;

View File

@ -3662,7 +3662,8 @@ void LatteDecompiler_emitClauseCode(LatteDecompilerShaderContext* shaderContext,
{
src->addFmt("{} = {} == true && {} == true;" _CRLF, _getActiveMaskCVarName(shaderContext, cfInstruction->activeStackDepth + 1 - cfInstruction->popCount), _getActiveMaskVarName(shaderContext, cfInstruction->activeStackDepth - cfInstruction->popCount), _getActiveMaskCVarName(shaderContext, cfInstruction->activeStackDepth - cfInstruction->popCount));
}
else if( cfInstruction->type == GPU7_CF_INST_LOOP_START_DX10 )
else if( cfInstruction->type == GPU7_CF_INST_LOOP_START_DX10 ||
cfInstruction->type == GPU7_CF_INST_LOOP_START_NO_AL)
{
// start of loop
// if pixel is disabled, then skip loop

View File

@ -1285,9 +1285,9 @@ void VulkanRenderer::draw_beginSequence()
// update shader state
LatteSHRC_UpdateActiveShaders();
if (m_state.drawSequenceSkip)
if (LatteGPUState.activeShaderHasError)
{
debug_printf("Skipping drawcalls due to shader error\n");
cemuLog_logDebugOnce(LogType::Force, "Skipping drawcalls due to shader error");
m_state.drawSequenceSkip = true;
cemu_assert_debug(false);
return;