From 821cf81a91822742b3dbf3fdfaa73a99382b8a8d Mon Sep 17 00:00:00 2001 From: bo3b Date: Mon, 12 Jan 2015 23:15:16 -0800 Subject: [PATCH] Fix bugs in resinfo opcode parsing. Numerous problems with the previous version, this rewrites it completely using the BinaryCompiler flags as much as possible. This uses temporary registers defined at the top, to hold the return values, because this instruction GetDimension does not have a return value, only parameters. Those parameters are then assigned to what the output was defined to be. Should handle the swizzle properly. Not 100% sure, it's fairly gnarly. Definitely handles both float and uint variants, and should generate a non-compiling error for cases that don't work, instead of generating bad code. Also added some code to the BinaryCompiler, copied from the newest version on Git, to handle the resinfo flags that were not previously decoded. --- BinaryDecompiler/decode.cpp | 16 ++- BinaryDecompiler/internal_includes/structs.h | 2 + BinaryDecompiler/internal_includes/tokens.h | 15 +++ HLSLDecompiler/DecompileHLSL.cpp | 119 +++++++++++++++++-- 4 files changed, 142 insertions(+), 10 deletions(-) diff --git a/BinaryDecompiler/decode.cpp b/BinaryDecompiler/decode.cpp index bc3f76929..734a5033d 100644 --- a/BinaryDecompiler/decode.cpp +++ b/BinaryDecompiler/decode.cpp @@ -935,7 +935,6 @@ const uint32_t* DeocdeInstruction(const uint32_t* pui32Token, Instruction* psIns case OPCODE_DLT: case OPCODE_DNE: case OPCODE_DDIV: - case OPCODE_RESINFO: case OPCODE_XOR: case OPCODE_SAMPLE_POS: // bo3b: added for WatchDogs { @@ -1075,7 +1074,20 @@ const uint32_t* DeocdeInstruction(const uint32_t* pui32Token, Instruction* psIns ui32OperandOffset += DecodeOperand(pui32Token+ui32OperandOffset, &psInst->asOperands[3]); break; } - case OPCODE_MSAD: + // Added to decode ResInfo variants. + // TODO: update to newest version of CrossCompiler. + case OPCODE_RESINFO: + { + psInst->ui32NumOperands = 3; + + psInst->eResInfoReturnType = DecodeResInfoReturnType(pui32Token[0]); + + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[1]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[2]); + break; + } + case OPCODE_MSAD: default: { ASSERT(0); diff --git a/BinaryDecompiler/internal_includes/structs.h b/BinaryDecompiler/internal_includes/structs.h index 20e7b1801..aca9cdd20 100644 --- a/BinaryDecompiler/internal_includes/structs.h +++ b/BinaryDecompiler/internal_includes/structs.h @@ -63,6 +63,8 @@ struct Instruction uint32_t bSaturate; uint32_t ui32FuncIndexWithinInterface; + RESINFO_RETURN_TYPE eResInfoReturnType; // added for ResInfo parse + int bAddressOffset; int iUAddrOffset; int iVAddrOffset; diff --git a/BinaryDecompiler/internal_includes/tokens.h b/BinaryDecompiler/internal_includes/tokens.h index 867f1d847..73acb1f11 100644 --- a/BinaryDecompiler/internal_includes/tokens.h +++ b/BinaryDecompiler/internal_includes/tokens.h @@ -746,6 +746,21 @@ static uint32_t DecodeAccessCoherencyFlags(uint32_t ui32Token) return ui32Token & 0x00010000; } + +// From newer version of James-Jones CrossCompiler- needed to decode ResInfo. + +typedef enum RESINFO_RETURN_TYPE +{ + RESINFO_INSTRUCTION_RETURN_FLOAT = 0, + RESINFO_INSTRUCTION_RETURN_RCPFLOAT = 1, + RESINFO_INSTRUCTION_RETURN_UINT = 2 +} RESINFO_RETURN_TYPE; + +static RESINFO_RETURN_TYPE DecodeResInfoReturnType(uint32_t ui32Token) +{ + return (RESINFO_RETURN_TYPE)((ui32Token & 0x00001800) >> 11); +} + #include "tokensDX9.h" #endif diff --git a/HLSLDecompiler/DecompileHLSL.cpp b/HLSLDecompiler/DecompileHLSL.cpp index b58215d5a..796bd92b1 100644 --- a/HLSLDecompiler/DecompileHLSL.cpp +++ b/HLSLDecompiler/DecompileHLSL.cpp @@ -2761,7 +2761,7 @@ class Decompiler mOutput.pop_back(); mOutput.push_back(';'); mOutput.push_back('\n'); - const char *helperDecl = " uint4 bitmask, src0, src1, dst0;\n"; + const char *helperDecl = " uint4 bitmask, src0, src1, uiDest;\n float4 fDest;\n\n"; mOutput.insert(mOutput.end(), helperDecl, helperDecl + strlen(helperDecl)); } else if (!strncmp(statement, "dcl_", 4)) @@ -4058,16 +4058,119 @@ class Decompiler appendOutput(buffer); break; + // The GetDimensions can also see a 4 parameter version in the immediate case. + // resinfo[_uint|_rcpFloat] dest[.mask], srcMipLevel.select_component, srcResource[.swizzle] + // In different variants based on input texture, becomes: + // void Object.GetDimensions(UINT MipLevel, typeX Width, typeX Height, typeX Elements, typeX Depth, typeX NumberOfLevels, typeX NumberOfSamples); + // + // We only see the immediate version l(0) in use, like: + // + // resinfo_indexable(texture2d)(uint, uint, uint, uint)_uint r2.zw, l(0), t5.zwxy + // Becomes 2 param version: SectorAtlasTexture_UINT_TextureObject.GetDimensions(r2.z, r2.w); + // + // resinfo_indexable(texture2dms)(float, float, float, float)_uint r0.xy, l(0), t0.xyzw + // Becomes 3 param version: DepthVPSampler_TextureObject.GetDimensions(r0.x, r0.y, bitmask.x); + // + // resinfo_indexable(texture2darray)(float, float, float, float) r0.xy, l(0), t0.xyzw + // + // So, we'll only handle that immediate for now, and generate syntax errors if we see any other variant. + // We don't want to knowingly generate code that compiles, but has errors. Includes _rcpFloat as unknown. + // + // This also added new ResInfo parsing that was not in our older BinaryCompiler. case OPCODE_RESINFO: { remapTarget(op1); - applySwizzle(".x", fixImm(op2, instr->asOperands[1]), true); - if (instr->asOperands[1].eType == OPERAND_TYPE_IMMEDIATE32) - strcpy(op2, "bitmask.x"); - int textureId; - sscanf_s(op3, "t%d.", &textureId); - sprintf(buffer, " %s.GetDimensions(%s, %s, %s);\n", mTextureNames[textureId].c_str(), ci(GetSuffix(op1, 0)).c_str(), ci(GetSuffix(op1, 1)).c_str(), ci(op2).c_str()); - appendOutput(buffer); + + bool unknownVariant = true; + Operand output = instr->asOperands[0]; + Operand constZero = instr->asOperands[1]; + Operand texture = instr->asOperands[2]; + RESINFO_RETURN_TYPE returnType = instr->eResInfoReturnType; + int texReg = texture.ui32RegisterNumber; + ResourceBinding *bindInfo; + + // We only presently handle the float and _uint return types, and the const 0 mode. + // And the texture2d and textures2dms types. That's all we've seen so far. + if ((constZero.eType == OPERAND_TYPE_IMMEDIATE32) && (constZero.afImmediates[0] == 0) + && (returnType == RESINFO_INSTRUCTION_RETURN_UINT || returnType == RESINFO_INSTRUCTION_RETURN_FLOAT) + && GetResourceFromBindingPoint(RTYPE_TEXTURE, texReg, shader->sInfo, &bindInfo) + && texture.eType == OPERAND_TYPE_RESOURCE) + { + //string out1, out2, out3; + + // return results into uint variables forces compiler to generate _uint variant. + //if (returnType == RESINFO_INSTRUCTION_RETURN_UINT) + //{ + // out1 = "dst0.x"; + // out2 = "dst0.y"; + // out3 = "dst0.w"; + //} + //else + //{ + // out1 = ci(GetSuffix(op1, 0)); + // out2 = ci(GetSuffix(op1, 1)); + // out3 = "fDst0.x"; + //} + + if (bindInfo->eDimension == REFLECT_RESOURCE_DIMENSION_TEXTURE2D) + { + if (returnType == RESINFO_INSTRUCTION_RETURN_UINT) + sprintf(buffer, " %s.GetDimensions(0, uiDest.x, uiDest.y, uiDest.z);\n", bindInfo->Name.c_str()); + else + sprintf(buffer, " %s.GetDimensions(0, fDest.x, fDest.y, fDest.z);\n", bindInfo->Name.c_str()); + appendOutput(buffer); + unknownVariant = false; + } + else if (bindInfo->eDimension == REFLECT_RESOURCE_DIMENSION_TEXTURE2DMS) + { + if (returnType == RESINFO_INSTRUCTION_RETURN_UINT) + sprintf(buffer, " %s.GetDimensions(uiDest.x, uiDest.y, uiDest.z);\n", bindInfo->Name.c_str()); + else + sprintf(buffer, " %s.GetDimensions(fDest.x, fDest.y, fDest.z);\n", bindInfo->Name.c_str()); + appendOutput(buffer); + unknownVariant = false; + } + else if (bindInfo->eDimension == REFLECT_RESOURCE_DIMENSION_TEXTURE2DARRAY) + { + if (returnType == RESINFO_INSTRUCTION_RETURN_UINT) + sprintf(buffer, " %s.GetDimensions(0, uiDest.x, uiDest.y, uiDest.z, uiDest.w);\n", bindInfo->Name.c_str()); + else + sprintf(buffer, " %s.GetDimensions(0, fDest.x, fDest.y, fDest.z, fDest.w);\n", bindInfo->Name.c_str()); + appendOutput(buffer); + unknownVariant = false; + } + + // For the output, we saw a r3.xyzw which makes no sense for this instruction. + // Not sure this is fully correct, but the goal here is to apply the swizzle from the op3, which is the texture + // register, as the pieces that are valid to copy to the op1 output. + // The op3 texture swizzle can determine which components to use, and what order. + // The op1 output determines which ones are valid for output. + if (returnType == RESINFO_INSTRUCTION_RETURN_UINT) + { + char dest[opcodeSize] = "uiDest.xyzw"; + applySwizzle(op3, dest); + applySwizzle(op1, dest); + sprintf(buffer, " %s = %s;\n", op1, dest); + appendOutput(buffer); + } + else + { + char dest[opcodeSize] = "fDest.xyzw"; + applySwizzle(op3, dest); + applySwizzle(op1, dest); + sprintf(buffer, " %s = %s;\n", op1, dest); + appendOutput(buffer); + } + } + if (unknownVariant) + { + string line = string(c + pos); + line = line.substr(0, line.find('\n')); + sprintf(buffer, " Unknown use of GetDimensions for _resinfo: %s\n", line.c_str()); + appendOutput(buffer); + + logDecompileError("Unknown _resinfo variant: " + line); + } break; }