diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/video_core/engines/shader_bytecode.h | 16 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_decompiler.cpp | 141 |
2 files changed, 82 insertions, 75 deletions
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index d3095089c..bc61f953f 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h | |||
| @@ -214,7 +214,7 @@ enum class IMinMaxExchange : u64 { | |||
| 214 | XHi = 3, | 214 | XHi = 3, |
| 215 | }; | 215 | }; |
| 216 | 216 | ||
| 217 | enum class VmadType : u64 { | 217 | enum class VideoType : u64 { |
| 218 | Size16_Low = 0, | 218 | Size16_Low = 0, |
| 219 | Size16_High = 1, | 219 | Size16_High = 1, |
| 220 | Size32 = 2, | 220 | Size32 = 2, |
| @@ -783,6 +783,14 @@ union Instruction { | |||
| 783 | } psetp; | 783 | } psetp; |
| 784 | 784 | ||
| 785 | union { | 785 | union { |
| 786 | BitField<43, 4, PredCondition> cond; | ||
| 787 | BitField<45, 2, PredOperation> op; | ||
| 788 | BitField<3, 3, u64> pred3; | ||
| 789 | BitField<0, 3, u64> pred0; | ||
| 790 | BitField<39, 3, u64> pred39; | ||
| 791 | } vsetp; | ||
| 792 | |||
| 793 | union { | ||
| 786 | BitField<12, 3, u64> pred12; | 794 | BitField<12, 3, u64> pred12; |
| 787 | BitField<15, 1, u64> neg_pred12; | 795 | BitField<15, 1, u64> neg_pred12; |
| 788 | BitField<24, 2, PredOperation> cond; | 796 | BitField<24, 2, PredOperation> cond; |
| @@ -1154,15 +1162,17 @@ union Instruction { | |||
| 1154 | union { | 1162 | union { |
| 1155 | BitField<48, 1, u64> signed_a; | 1163 | BitField<48, 1, u64> signed_a; |
| 1156 | BitField<38, 1, u64> is_byte_chunk_a; | 1164 | BitField<38, 1, u64> is_byte_chunk_a; |
| 1157 | BitField<36, 2, VmadType> type_a; | 1165 | BitField<36, 2, VideoType> type_a; |
| 1158 | BitField<36, 2, u64> byte_height_a; | 1166 | BitField<36, 2, u64> byte_height_a; |
| 1159 | 1167 | ||
| 1160 | BitField<49, 1, u64> signed_b; | 1168 | BitField<49, 1, u64> signed_b; |
| 1161 | BitField<50, 1, u64> use_register_b; | 1169 | BitField<50, 1, u64> use_register_b; |
| 1162 | BitField<30, 1, u64> is_byte_chunk_b; | 1170 | BitField<30, 1, u64> is_byte_chunk_b; |
| 1163 | BitField<28, 2, VmadType> type_b; | 1171 | BitField<28, 2, VideoType> type_b; |
| 1164 | BitField<28, 2, u64> byte_height_b; | 1172 | BitField<28, 2, u64> byte_height_b; |
| 1173 | } video; | ||
| 1165 | 1174 | ||
| 1175 | union { | ||
| 1166 | BitField<51, 2, VmadShr> shr; | 1176 | BitField<51, 2, VmadShr> shr; |
| 1167 | BitField<55, 1, u64> saturate; // Saturates the result (a * b + c) | 1177 | BitField<55, 1, u64> saturate; // Saturates the result (a * b + c) |
| 1168 | BitField<47, 1, u64> cc; | 1178 | BitField<47, 1, u64> cc; |
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index a3daef014..ad4d5a72f 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -1291,6 +1291,63 @@ private: | |||
| 1291 | } | 1291 | } |
| 1292 | } | 1292 | } |
| 1293 | 1293 | ||
| 1294 | /// Unpacks a video instruction operand (e.g. VMAD). | ||
| 1295 | std::string GetVideoOperand(const std::string& op, bool is_chunk, bool is_signed, | ||
| 1296 | Tegra::Shader::VideoType type, u64 byte_height) { | ||
| 1297 | const std::string value = [&]() { | ||
| 1298 | if (!is_chunk) { | ||
| 1299 | const auto offset = static_cast<u32>(byte_height * 8); | ||
| 1300 | return "((" + op + " >> " + std::to_string(offset) + ") & 0xff)"; | ||
| 1301 | } | ||
| 1302 | const std::string zero = "0"; | ||
| 1303 | |||
| 1304 | switch (type) { | ||
| 1305 | case Tegra::Shader::VideoType::Size16_Low: | ||
| 1306 | return '(' + op + " & 0xffff)"; | ||
| 1307 | case Tegra::Shader::VideoType::Size16_High: | ||
| 1308 | return '(' + op + " >> 16)"; | ||
| 1309 | case Tegra::Shader::VideoType::Size32: | ||
| 1310 | // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when | ||
| 1311 | // this type is used (1 * 1 + 0 == 0x5b800000). Until a better | ||
| 1312 | // explanation is found: assert. | ||
| 1313 | UNIMPLEMENTED(); | ||
| 1314 | return zero; | ||
| 1315 | case Tegra::Shader::VideoType::Invalid: | ||
| 1316 | UNREACHABLE_MSG("Invalid instruction encoding"); | ||
| 1317 | return zero; | ||
| 1318 | default: | ||
| 1319 | UNREACHABLE(); | ||
| 1320 | return zero; | ||
| 1321 | } | ||
| 1322 | }(); | ||
| 1323 | |||
| 1324 | if (is_signed) { | ||
| 1325 | return "int(" + value + ')'; | ||
| 1326 | } | ||
| 1327 | return value; | ||
| 1328 | }; | ||
| 1329 | |||
| 1330 | /// Gets the A operand for a video instruction. | ||
| 1331 | std::string GetVideoOperandA(Instruction instr) { | ||
| 1332 | return GetVideoOperand(regs.GetRegisterAsInteger(instr.gpr8, 0, false), | ||
| 1333 | instr.video.is_byte_chunk_a != 0, instr.video.signed_a, | ||
| 1334 | instr.video.type_a, instr.video.byte_height_a); | ||
| 1335 | } | ||
| 1336 | |||
| 1337 | /// Gets the B operand for a video instruction. | ||
| 1338 | std::string GetVideoOperandB(Instruction instr) { | ||
| 1339 | if (instr.video.use_register_b) { | ||
| 1340 | return GetVideoOperand(regs.GetRegisterAsInteger(instr.gpr20, 0, false), | ||
| 1341 | instr.video.is_byte_chunk_b != 0, instr.video.signed_b, | ||
| 1342 | instr.video.type_b, instr.video.byte_height_b); | ||
| 1343 | } else { | ||
| 1344 | return '(' + | ||
| 1345 | std::to_string(instr.video.signed_b ? static_cast<s16>(instr.alu.GetImm20_16()) | ||
| 1346 | : instr.alu.GetImm20_16()) + | ||
| 1347 | ')'; | ||
| 1348 | } | ||
| 1349 | } | ||
| 1350 | |||
| 1294 | /** | 1351 | /** |
| 1295 | * Compiles a single instruction from Tegra to GLSL. | 1352 | * Compiles a single instruction from Tegra to GLSL. |
| 1296 | * @param offset the offset of the Tegra shader instruction. | 1353 | * @param offset the offset of the Tegra shader instruction. |
| @@ -3284,82 +3341,22 @@ private: | |||
| 3284 | break; | 3341 | break; |
| 3285 | } | 3342 | } |
| 3286 | case OpCode::Id::VMAD: { | 3343 | case OpCode::Id::VMAD: { |
| 3287 | const bool signed_a = instr.vmad.signed_a == 1; | 3344 | const bool result_signed = instr.video.signed_a == 1 || instr.video.signed_b == 1; |
| 3288 | const bool signed_b = instr.vmad.signed_b == 1; | 3345 | const std::string op_a = GetVideoOperandA(instr); |
| 3289 | const bool result_signed = signed_a || signed_b; | 3346 | const std::string op_b = GetVideoOperandB(instr); |
| 3290 | boost::optional<std::string> forced_result; | ||
| 3291 | |||
| 3292 | auto Unpack = [&](const std::string& op, bool is_chunk, bool is_signed, | ||
| 3293 | Tegra::Shader::VmadType type, u64 byte_height) { | ||
| 3294 | const std::string value = [&]() { | ||
| 3295 | if (!is_chunk) { | ||
| 3296 | const auto shift = static_cast<u32>(byte_height * 8); | ||
| 3297 | return "((" + op + " >> " + std::to_string(shift) + ") & 0xff)"; | ||
| 3298 | } | ||
| 3299 | const std::string zero = "0"; | ||
| 3300 | |||
| 3301 | switch (type) { | ||
| 3302 | case Tegra::Shader::VmadType::Size16_Low: | ||
| 3303 | return '(' + op + " & 0xffff)"; | ||
| 3304 | case Tegra::Shader::VmadType::Size16_High: | ||
| 3305 | return '(' + op + " >> 16)"; | ||
| 3306 | case Tegra::Shader::VmadType::Size32: | ||
| 3307 | // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when | ||
| 3308 | // this type is used (1 * 1 + 0 == 0x5b800000). Until a better | ||
| 3309 | // explanation is found: assert. | ||
| 3310 | UNREACHABLE_MSG("Unimplemented"); | ||
| 3311 | return zero; | ||
| 3312 | case Tegra::Shader::VmadType::Invalid: | ||
| 3313 | // Note(Rodrigo): This flag is invalid according to nvdisasm. From my | ||
| 3314 | // testing (even though it's invalid) this makes the whole instruction | ||
| 3315 | // assign zero to target register. | ||
| 3316 | forced_result = boost::make_optional(zero); | ||
| 3317 | return zero; | ||
| 3318 | default: | ||
| 3319 | UNREACHABLE(); | ||
| 3320 | return zero; | ||
| 3321 | } | ||
| 3322 | }(); | ||
| 3323 | |||
| 3324 | if (is_signed) { | ||
| 3325 | return "int(" + value + ')'; | ||
| 3326 | } | ||
| 3327 | return value; | ||
| 3328 | }; | ||
| 3329 | |||
| 3330 | const std::string op_a = Unpack(regs.GetRegisterAsInteger(instr.gpr8, 0, false), | ||
| 3331 | instr.vmad.is_byte_chunk_a != 0, signed_a, | ||
| 3332 | instr.vmad.type_a, instr.vmad.byte_height_a); | ||
| 3333 | |||
| 3334 | std::string op_b; | ||
| 3335 | if (instr.vmad.use_register_b) { | ||
| 3336 | op_b = Unpack(regs.GetRegisterAsInteger(instr.gpr20, 0, false), | ||
| 3337 | instr.vmad.is_byte_chunk_b != 0, signed_b, instr.vmad.type_b, | ||
| 3338 | instr.vmad.byte_height_b); | ||
| 3339 | } else { | ||
| 3340 | op_b = '(' + | ||
| 3341 | std::to_string(signed_b ? static_cast<s16>(instr.alu.GetImm20_16()) | ||
| 3342 | : instr.alu.GetImm20_16()) + | ||
| 3343 | ')'; | ||
| 3344 | } | ||
| 3345 | |||
| 3346 | const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39, 0, result_signed); | 3347 | const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39, 0, result_signed); |
| 3347 | 3348 | ||
| 3348 | std::string result; | 3349 | std::string result = '(' + op_a + " * " + op_b + " + " + op_c + ')'; |
| 3349 | if (forced_result) { | ||
| 3350 | result = *forced_result; | ||
| 3351 | } else { | ||
| 3352 | result = '(' + op_a + " * " + op_b + " + " + op_c + ')'; | ||
| 3353 | 3350 | ||
| 3354 | switch (instr.vmad.shr) { | 3351 | switch (instr.vmad.shr) { |
| 3355 | case Tegra::Shader::VmadShr::Shr7: | 3352 | case Tegra::Shader::VmadShr::Shr7: |
| 3356 | result = '(' + result + " >> 7)"; | 3353 | result = '(' + result + " >> 7)"; |
| 3357 | break; | 3354 | break; |
| 3358 | case Tegra::Shader::VmadShr::Shr15: | 3355 | case Tegra::Shader::VmadShr::Shr15: |
| 3359 | result = '(' + result + " >> 15)"; | 3356 | result = '(' + result + " >> 15)"; |
| 3360 | break; | 3357 | break; |
| 3361 | } | ||
| 3362 | } | 3358 | } |
| 3359 | |||
| 3363 | regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1, | 3360 | regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1, |
| 3364 | instr.vmad.saturate == 1, 0, Register::Size::Word, | 3361 | instr.vmad.saturate == 1, 0, Register::Size::Word, |
| 3365 | instr.vmad.cc); | 3362 | instr.vmad.cc); |