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; }