diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/common/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | src/video_core/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_decompiler.cpp | 272 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_gen.cpp | 16 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_shader_decompiler.cpp | 359 | ||||
| -rw-r--r-- | src/video_core/shader/ast.cpp | 766 | ||||
| -rw-r--r-- | src/video_core/shader/ast.h | 391 | ||||
| -rw-r--r-- | src/video_core/shader/compiler_settings.cpp | 26 | ||||
| -rw-r--r-- | src/video_core/shader/compiler_settings.h | 26 | ||||
| -rw-r--r-- | src/video_core/shader/control_flow.cpp | 154 | ||||
| -rw-r--r-- | src/video_core/shader/control_flow.h | 14 | ||||
| -rw-r--r-- | src/video_core/shader/decode.cpp | 170 | ||||
| -rw-r--r-- | src/video_core/shader/expr.cpp | 82 | ||||
| -rw-r--r-- | src/video_core/shader/expr.h | 120 | ||||
| -rw-r--r-- | src/video_core/shader/shader_ir.cpp | 12 | ||||
| -rw-r--r-- | src/video_core/shader/shader_ir.h | 33 |
16 files changed, 2293 insertions, 160 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index dfed8b51d..0ed96c0d4 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -60,9 +60,15 @@ add_custom_command(OUTPUT scm_rev.cpp | |||
| 60 | "${VIDEO_CORE}/shader/decode/video.cpp" | 60 | "${VIDEO_CORE}/shader/decode/video.cpp" |
| 61 | "${VIDEO_CORE}/shader/decode/warp.cpp" | 61 | "${VIDEO_CORE}/shader/decode/warp.cpp" |
| 62 | "${VIDEO_CORE}/shader/decode/xmad.cpp" | 62 | "${VIDEO_CORE}/shader/decode/xmad.cpp" |
| 63 | "${VIDEO_CORE}/shader/ast.cpp" | ||
| 64 | "${VIDEO_CORE}/shader/ast.h" | ||
| 63 | "${VIDEO_CORE}/shader/control_flow.cpp" | 65 | "${VIDEO_CORE}/shader/control_flow.cpp" |
| 64 | "${VIDEO_CORE}/shader/control_flow.h" | 66 | "${VIDEO_CORE}/shader/control_flow.h" |
| 67 | "${VIDEO_CORE}/shader/compiler_settings.cpp" | ||
| 68 | "${VIDEO_CORE}/shader/compiler_settings.h" | ||
| 65 | "${VIDEO_CORE}/shader/decode.cpp" | 69 | "${VIDEO_CORE}/shader/decode.cpp" |
| 70 | "${VIDEO_CORE}/shader/expr.cpp" | ||
| 71 | "${VIDEO_CORE}/shader/expr.h" | ||
| 66 | "${VIDEO_CORE}/shader/node.h" | 72 | "${VIDEO_CORE}/shader/node.h" |
| 67 | "${VIDEO_CORE}/shader/node_helper.cpp" | 73 | "${VIDEO_CORE}/shader/node_helper.cpp" |
| 68 | "${VIDEO_CORE}/shader/node_helper.h" | 74 | "${VIDEO_CORE}/shader/node_helper.h" |
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index e2f85c5f1..eaa694ff8 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -105,9 +105,15 @@ add_library(video_core STATIC | |||
| 105 | shader/decode/warp.cpp | 105 | shader/decode/warp.cpp |
| 106 | shader/decode/xmad.cpp | 106 | shader/decode/xmad.cpp |
| 107 | shader/decode/other.cpp | 107 | shader/decode/other.cpp |
| 108 | shader/ast.cpp | ||
| 109 | shader/ast.h | ||
| 108 | shader/control_flow.cpp | 110 | shader/control_flow.cpp |
| 109 | shader/control_flow.h | 111 | shader/control_flow.h |
| 112 | shader/compiler_settings.cpp | ||
| 113 | shader/compiler_settings.h | ||
| 110 | shader/decode.cpp | 114 | shader/decode.cpp |
| 115 | shader/expr.cpp | ||
| 116 | shader/expr.h | ||
| 111 | shader/node_helper.cpp | 117 | shader/node_helper.cpp |
| 112 | shader/node_helper.h | 118 | shader/node_helper.h |
| 113 | shader/node.h | 119 | shader/node.h |
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 8fa9e6534..6a610a3bc 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -19,6 +19,7 @@ | |||
| 19 | #include "video_core/renderer_opengl/gl_device.h" | 19 | #include "video_core/renderer_opengl/gl_device.h" |
| 20 | #include "video_core/renderer_opengl/gl_rasterizer.h" | 20 | #include "video_core/renderer_opengl/gl_rasterizer.h" |
| 21 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" | 21 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" |
| 22 | #include "video_core/shader/ast.h" | ||
| 22 | #include "video_core/shader/node.h" | 23 | #include "video_core/shader/node.h" |
| 23 | #include "video_core/shader/shader_ir.h" | 24 | #include "video_core/shader/shader_ir.h" |
| 24 | 25 | ||
| @@ -334,39 +335,24 @@ constexpr bool IsVertexShader(ProgramType stage) { | |||
| 334 | return stage == ProgramType::VertexA || stage == ProgramType::VertexB; | 335 | return stage == ProgramType::VertexA || stage == ProgramType::VertexB; |
| 335 | } | 336 | } |
| 336 | 337 | ||
| 338 | class ASTDecompiler; | ||
| 339 | class ExprDecompiler; | ||
| 340 | |||
| 337 | class GLSLDecompiler final { | 341 | class GLSLDecompiler final { |
| 338 | public: | 342 | public: |
| 339 | explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, ProgramType stage, | 343 | explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, ProgramType stage, |
| 340 | std::string suffix) | 344 | std::string suffix) |
| 341 | : device{device}, ir{ir}, stage{stage}, suffix{suffix}, header{ir.GetHeader()} {} | 345 | : device{device}, ir{ir}, stage{stage}, suffix{suffix}, header{ir.GetHeader()} {} |
| 342 | 346 | ||
| 343 | void Decompile() { | 347 | void DecompileBranchMode() { |
| 344 | DeclareVertex(); | ||
| 345 | DeclareGeometry(); | ||
| 346 | DeclareRegisters(); | ||
| 347 | DeclarePredicates(); | ||
| 348 | DeclareLocalMemory(); | ||
| 349 | DeclareSharedMemory(); | ||
| 350 | DeclareInternalFlags(); | ||
| 351 | DeclareInputAttributes(); | ||
| 352 | DeclareOutputAttributes(); | ||
| 353 | DeclareConstantBuffers(); | ||
| 354 | DeclareGlobalMemory(); | ||
| 355 | DeclareSamplers(); | ||
| 356 | DeclarePhysicalAttributeReader(); | ||
| 357 | DeclareImages(); | ||
| 358 | |||
| 359 | code.AddLine("void execute_{}() {{", suffix); | ||
| 360 | ++code.scope; | ||
| 361 | |||
| 362 | // VM's program counter | 348 | // VM's program counter |
| 363 | const auto first_address = ir.GetBasicBlocks().begin()->first; | 349 | const auto first_address = ir.GetBasicBlocks().begin()->first; |
| 364 | code.AddLine("uint jmp_to = {}U;", first_address); | 350 | code.AddLine("uint jmp_to = {}U;", first_address); |
| 365 | 351 | ||
| 366 | // TODO(Subv): Figure out the actual depth of the flow stack, for now it seems | 352 | // TODO(Subv): Figure out the actual depth of the flow stack, for now it seems |
| 367 | // unlikely that shaders will use 20 nested SSYs and PBKs. | 353 | // unlikely that shaders will use 20 nested SSYs and PBKs. |
| 354 | constexpr u32 FLOW_STACK_SIZE = 20; | ||
| 368 | if (!ir.IsFlowStackDisabled()) { | 355 | if (!ir.IsFlowStackDisabled()) { |
| 369 | constexpr u32 FLOW_STACK_SIZE = 20; | ||
| 370 | for (const auto stack : std::array{MetaStackClass::Ssy, MetaStackClass::Pbk}) { | 356 | for (const auto stack : std::array{MetaStackClass::Ssy, MetaStackClass::Pbk}) { |
| 371 | code.AddLine("uint {}[{}];", FlowStackName(stack), FLOW_STACK_SIZE); | 357 | code.AddLine("uint {}[{}];", FlowStackName(stack), FLOW_STACK_SIZE); |
| 372 | code.AddLine("uint {} = 0U;", FlowStackTopName(stack)); | 358 | code.AddLine("uint {} = 0U;", FlowStackTopName(stack)); |
| @@ -392,10 +378,37 @@ public: | |||
| 392 | code.AddLine("default: return;"); | 378 | code.AddLine("default: return;"); |
| 393 | code.AddLine("}}"); | 379 | code.AddLine("}}"); |
| 394 | 380 | ||
| 395 | for (std::size_t i = 0; i < 2; ++i) { | 381 | --code.scope; |
| 396 | --code.scope; | 382 | code.AddLine("}}"); |
| 397 | code.AddLine("}}"); | 383 | } |
| 384 | |||
| 385 | void DecompileAST(); | ||
| 386 | |||
| 387 | void Decompile() { | ||
| 388 | DeclareVertex(); | ||
| 389 | DeclareGeometry(); | ||
| 390 | DeclareRegisters(); | ||
| 391 | DeclarePredicates(); | ||
| 392 | DeclareLocalMemory(); | ||
| 393 | DeclareInternalFlags(); | ||
| 394 | DeclareInputAttributes(); | ||
| 395 | DeclareOutputAttributes(); | ||
| 396 | DeclareConstantBuffers(); | ||
| 397 | DeclareGlobalMemory(); | ||
| 398 | DeclareSamplers(); | ||
| 399 | DeclarePhysicalAttributeReader(); | ||
| 400 | |||
| 401 | code.AddLine("void execute_{}() {{", suffix); | ||
| 402 | ++code.scope; | ||
| 403 | |||
| 404 | if (ir.IsDecompiled()) { | ||
| 405 | DecompileAST(); | ||
| 406 | } else { | ||
| 407 | DecompileBranchMode(); | ||
| 398 | } | 408 | } |
| 409 | |||
| 410 | --code.scope; | ||
| 411 | code.AddLine("}}"); | ||
| 399 | } | 412 | } |
| 400 | 413 | ||
| 401 | std::string GetResult() { | 414 | std::string GetResult() { |
| @@ -424,6 +437,9 @@ public: | |||
| 424 | } | 437 | } |
| 425 | 438 | ||
| 426 | private: | 439 | private: |
| 440 | friend class ASTDecompiler; | ||
| 441 | friend class ExprDecompiler; | ||
| 442 | |||
| 427 | void DeclareVertex() { | 443 | void DeclareVertex() { |
| 428 | if (!IsVertexShader(stage)) | 444 | if (!IsVertexShader(stage)) |
| 429 | return; | 445 | return; |
| @@ -1821,10 +1837,9 @@ private: | |||
| 1821 | return {}; | 1837 | return {}; |
| 1822 | } | 1838 | } |
| 1823 | 1839 | ||
| 1824 | Expression Exit(Operation operation) { | 1840 | void PreExit() { |
| 1825 | if (stage != ProgramType::Fragment) { | 1841 | if (stage != ProgramType::Fragment) { |
| 1826 | code.AddLine("return;"); | 1842 | return; |
| 1827 | return {}; | ||
| 1828 | } | 1843 | } |
| 1829 | const auto& used_registers = ir.GetRegisters(); | 1844 | const auto& used_registers = ir.GetRegisters(); |
| 1830 | const auto SafeGetRegister = [&](u32 reg) -> Expression { | 1845 | const auto SafeGetRegister = [&](u32 reg) -> Expression { |
| @@ -1856,7 +1871,10 @@ private: | |||
| 1856 | // already contains one past the last color register. | 1871 | // already contains one past the last color register. |
| 1857 | code.AddLine("gl_FragDepth = {};", SafeGetRegister(current_reg + 1).AsFloat()); | 1872 | code.AddLine("gl_FragDepth = {};", SafeGetRegister(current_reg + 1).AsFloat()); |
| 1858 | } | 1873 | } |
| 1874 | } | ||
| 1859 | 1875 | ||
| 1876 | Expression Exit(Operation operation) { | ||
| 1877 | PreExit(); | ||
| 1860 | code.AddLine("return;"); | 1878 | code.AddLine("return;"); |
| 1861 | return {}; | 1879 | return {}; |
| 1862 | } | 1880 | } |
| @@ -2253,6 +2271,208 @@ private: | |||
| 2253 | ShaderWriter code; | 2271 | ShaderWriter code; |
| 2254 | }; | 2272 | }; |
| 2255 | 2273 | ||
| 2274 | static constexpr std::string_view flow_var = "flow_var_"; | ||
| 2275 | |||
| 2276 | std::string GetFlowVariable(u32 i) { | ||
| 2277 | return fmt::format("{}{}", flow_var, i); | ||
| 2278 | } | ||
| 2279 | |||
| 2280 | class ExprDecompiler { | ||
| 2281 | public: | ||
| 2282 | explicit ExprDecompiler(GLSLDecompiler& decomp) : decomp{decomp} {} | ||
| 2283 | |||
| 2284 | void operator()(VideoCommon::Shader::ExprAnd& expr) { | ||
| 2285 | inner += "( "; | ||
| 2286 | std::visit(*this, *expr.operand1); | ||
| 2287 | inner += " && "; | ||
| 2288 | std::visit(*this, *expr.operand2); | ||
| 2289 | inner += ')'; | ||
| 2290 | } | ||
| 2291 | |||
| 2292 | void operator()(VideoCommon::Shader::ExprOr& expr) { | ||
| 2293 | inner += "( "; | ||
| 2294 | std::visit(*this, *expr.operand1); | ||
| 2295 | inner += " || "; | ||
| 2296 | std::visit(*this, *expr.operand2); | ||
| 2297 | inner += ')'; | ||
| 2298 | } | ||
| 2299 | |||
| 2300 | void operator()(VideoCommon::Shader::ExprNot& expr) { | ||
| 2301 | inner += '!'; | ||
| 2302 | std::visit(*this, *expr.operand1); | ||
| 2303 | } | ||
| 2304 | |||
| 2305 | void operator()(VideoCommon::Shader::ExprPredicate& expr) { | ||
| 2306 | const auto pred = static_cast<Tegra::Shader::Pred>(expr.predicate); | ||
| 2307 | inner += decomp.GetPredicate(pred); | ||
| 2308 | } | ||
| 2309 | |||
| 2310 | void operator()(VideoCommon::Shader::ExprCondCode& expr) { | ||
| 2311 | const Node cc = decomp.ir.GetConditionCode(expr.cc); | ||
| 2312 | std::string target; | ||
| 2313 | |||
| 2314 | if (const auto pred = std::get_if<PredicateNode>(&*cc)) { | ||
| 2315 | const auto index = pred->GetIndex(); | ||
| 2316 | switch (index) { | ||
| 2317 | case Tegra::Shader::Pred::NeverExecute: | ||
| 2318 | target = "false"; | ||
| 2319 | case Tegra::Shader::Pred::UnusedIndex: | ||
| 2320 | target = "true"; | ||
| 2321 | default: | ||
| 2322 | target = decomp.GetPredicate(index); | ||
| 2323 | } | ||
| 2324 | } else if (const auto flag = std::get_if<InternalFlagNode>(&*cc)) { | ||
| 2325 | target = decomp.GetInternalFlag(flag->GetFlag()); | ||
| 2326 | } else { | ||
| 2327 | UNREACHABLE(); | ||
| 2328 | } | ||
| 2329 | inner += target; | ||
| 2330 | } | ||
| 2331 | |||
| 2332 | void operator()(VideoCommon::Shader::ExprVar& expr) { | ||
| 2333 | inner += GetFlowVariable(expr.var_index); | ||
| 2334 | } | ||
| 2335 | |||
| 2336 | void operator()(VideoCommon::Shader::ExprBoolean& expr) { | ||
| 2337 | inner += expr.value ? "true" : "false"; | ||
| 2338 | } | ||
| 2339 | |||
| 2340 | std::string& GetResult() { | ||
| 2341 | return inner; | ||
| 2342 | } | ||
| 2343 | |||
| 2344 | private: | ||
| 2345 | std::string inner; | ||
| 2346 | GLSLDecompiler& decomp; | ||
| 2347 | }; | ||
| 2348 | |||
| 2349 | class ASTDecompiler { | ||
| 2350 | public: | ||
| 2351 | explicit ASTDecompiler(GLSLDecompiler& decomp) : decomp{decomp} {} | ||
| 2352 | |||
| 2353 | void operator()(VideoCommon::Shader::ASTProgram& ast) { | ||
| 2354 | ASTNode current = ast.nodes.GetFirst(); | ||
| 2355 | while (current) { | ||
| 2356 | Visit(current); | ||
| 2357 | current = current->GetNext(); | ||
| 2358 | } | ||
| 2359 | } | ||
| 2360 | |||
| 2361 | void operator()(VideoCommon::Shader::ASTIfThen& ast) { | ||
| 2362 | ExprDecompiler expr_parser{decomp}; | ||
| 2363 | std::visit(expr_parser, *ast.condition); | ||
| 2364 | decomp.code.AddLine("if ({}) {{", expr_parser.GetResult()); | ||
| 2365 | decomp.code.scope++; | ||
| 2366 | ASTNode current = ast.nodes.GetFirst(); | ||
| 2367 | while (current) { | ||
| 2368 | Visit(current); | ||
| 2369 | current = current->GetNext(); | ||
| 2370 | } | ||
| 2371 | decomp.code.scope--; | ||
| 2372 | decomp.code.AddLine("}}"); | ||
| 2373 | } | ||
| 2374 | |||
| 2375 | void operator()(VideoCommon::Shader::ASTIfElse& ast) { | ||
| 2376 | decomp.code.AddLine("else {{"); | ||
| 2377 | decomp.code.scope++; | ||
| 2378 | ASTNode current = ast.nodes.GetFirst(); | ||
| 2379 | while (current) { | ||
| 2380 | Visit(current); | ||
| 2381 | current = current->GetNext(); | ||
| 2382 | } | ||
| 2383 | decomp.code.scope--; | ||
| 2384 | decomp.code.AddLine("}}"); | ||
| 2385 | } | ||
| 2386 | |||
| 2387 | void operator()(VideoCommon::Shader::ASTBlockEncoded& ast) { | ||
| 2388 | UNREACHABLE(); | ||
| 2389 | } | ||
| 2390 | |||
| 2391 | void operator()(VideoCommon::Shader::ASTBlockDecoded& ast) { | ||
| 2392 | decomp.VisitBlock(ast.nodes); | ||
| 2393 | } | ||
| 2394 | |||
| 2395 | void operator()(VideoCommon::Shader::ASTVarSet& ast) { | ||
| 2396 | ExprDecompiler expr_parser{decomp}; | ||
| 2397 | std::visit(expr_parser, *ast.condition); | ||
| 2398 | decomp.code.AddLine("{} = {};", GetFlowVariable(ast.index), expr_parser.GetResult()); | ||
| 2399 | } | ||
| 2400 | |||
| 2401 | void operator()(VideoCommon::Shader::ASTLabel& ast) { | ||
| 2402 | decomp.code.AddLine("// Label_{}:", ast.index); | ||
| 2403 | } | ||
| 2404 | |||
| 2405 | void operator()(VideoCommon::Shader::ASTGoto& ast) { | ||
| 2406 | UNREACHABLE(); | ||
| 2407 | } | ||
| 2408 | |||
| 2409 | void operator()(VideoCommon::Shader::ASTDoWhile& ast) { | ||
| 2410 | ExprDecompiler expr_parser{decomp}; | ||
| 2411 | std::visit(expr_parser, *ast.condition); | ||
| 2412 | decomp.code.AddLine("do {{"); | ||
| 2413 | decomp.code.scope++; | ||
| 2414 | ASTNode current = ast.nodes.GetFirst(); | ||
| 2415 | while (current) { | ||
| 2416 | Visit(current); | ||
| 2417 | current = current->GetNext(); | ||
| 2418 | } | ||
| 2419 | decomp.code.scope--; | ||
| 2420 | decomp.code.AddLine("}} while({});", expr_parser.GetResult()); | ||
| 2421 | } | ||
| 2422 | |||
| 2423 | void operator()(VideoCommon::Shader::ASTReturn& ast) { | ||
| 2424 | const bool is_true = VideoCommon::Shader::ExprIsTrue(ast.condition); | ||
| 2425 | if (!is_true) { | ||
| 2426 | ExprDecompiler expr_parser{decomp}; | ||
| 2427 | std::visit(expr_parser, *ast.condition); | ||
| 2428 | decomp.code.AddLine("if ({}) {{", expr_parser.GetResult()); | ||
| 2429 | decomp.code.scope++; | ||
| 2430 | } | ||
| 2431 | if (ast.kills) { | ||
| 2432 | decomp.code.AddLine("discard;"); | ||
| 2433 | } else { | ||
| 2434 | decomp.PreExit(); | ||
| 2435 | decomp.code.AddLine("return;"); | ||
| 2436 | } | ||
| 2437 | if (!is_true) { | ||
| 2438 | decomp.code.scope--; | ||
| 2439 | decomp.code.AddLine("}}"); | ||
| 2440 | } | ||
| 2441 | } | ||
| 2442 | |||
| 2443 | void operator()(VideoCommon::Shader::ASTBreak& ast) { | ||
| 2444 | const bool is_true = VideoCommon::Shader::ExprIsTrue(ast.condition); | ||
| 2445 | if (!is_true) { | ||
| 2446 | ExprDecompiler expr_parser{decomp}; | ||
| 2447 | std::visit(expr_parser, *ast.condition); | ||
| 2448 | decomp.code.AddLine("if ({}) {{", expr_parser.GetResult()); | ||
| 2449 | decomp.code.scope++; | ||
| 2450 | } | ||
| 2451 | decomp.code.AddLine("break;"); | ||
| 2452 | if (!is_true) { | ||
| 2453 | decomp.code.scope--; | ||
| 2454 | decomp.code.AddLine("}}"); | ||
| 2455 | } | ||
| 2456 | } | ||
| 2457 | |||
| 2458 | void Visit(VideoCommon::Shader::ASTNode& node) { | ||
| 2459 | std::visit(*this, *node->GetInnerData()); | ||
| 2460 | } | ||
| 2461 | |||
| 2462 | private: | ||
| 2463 | GLSLDecompiler& decomp; | ||
| 2464 | }; | ||
| 2465 | |||
| 2466 | void GLSLDecompiler::DecompileAST() { | ||
| 2467 | const u32 num_flow_variables = ir.GetASTNumVariables(); | ||
| 2468 | for (u32 i = 0; i < num_flow_variables; i++) { | ||
| 2469 | code.AddLine("bool {} = false;", GetFlowVariable(i)); | ||
| 2470 | } | ||
| 2471 | ASTDecompiler decompiler{*this}; | ||
| 2472 | VideoCommon::Shader::ASTNode program = ir.GetASTProgram(); | ||
| 2473 | decompiler.Visit(program); | ||
| 2474 | } | ||
| 2475 | |||
| 2256 | } // Anonymous namespace | 2476 | } // Anonymous namespace |
| 2257 | 2477 | ||
| 2258 | std::string GetCommonDeclarations() { | 2478 | std::string GetCommonDeclarations() { |
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 3a8d9e1da..b5a43e79e 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp | |||
| @@ -11,12 +11,16 @@ | |||
| 11 | namespace OpenGL::GLShader { | 11 | namespace OpenGL::GLShader { |
| 12 | 12 | ||
| 13 | using Tegra::Engines::Maxwell3D; | 13 | using Tegra::Engines::Maxwell3D; |
| 14 | using VideoCommon::Shader::CompileDepth; | ||
| 15 | using VideoCommon::Shader::CompilerSettings; | ||
| 14 | using VideoCommon::Shader::ProgramCode; | 16 | using VideoCommon::Shader::ProgramCode; |
| 15 | using VideoCommon::Shader::ShaderIR; | 17 | using VideoCommon::Shader::ShaderIR; |
| 16 | 18 | ||
| 17 | static constexpr u32 PROGRAM_OFFSET = 10; | 19 | static constexpr u32 PROGRAM_OFFSET = 10; |
| 18 | static constexpr u32 COMPUTE_OFFSET = 0; | 20 | static constexpr u32 COMPUTE_OFFSET = 0; |
| 19 | 21 | ||
| 22 | static constexpr CompilerSettings settings{CompileDepth::NoFlowStack, true}; | ||
| 23 | |||
| 20 | ProgramResult GenerateVertexShader(const Device& device, const ShaderSetup& setup) { | 24 | ProgramResult GenerateVertexShader(const Device& device, const ShaderSetup& setup) { |
| 21 | const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); | 25 | const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); |
| 22 | 26 | ||
| @@ -31,13 +35,14 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform vs_config { | |||
| 31 | 35 | ||
| 32 | )"; | 36 | )"; |
| 33 | 37 | ||
| 34 | const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a); | 38 | const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a, settings); |
| 35 | const auto stage = setup.IsDualProgram() ? ProgramType::VertexA : ProgramType::VertexB; | 39 | const auto stage = setup.IsDualProgram() ? ProgramType::VertexA : ProgramType::VertexB; |
| 36 | ProgramResult program = Decompile(device, program_ir, stage, "vertex"); | 40 | ProgramResult program = Decompile(device, program_ir, stage, "vertex"); |
| 37 | out += program.first; | 41 | out += program.first; |
| 38 | 42 | ||
| 39 | if (setup.IsDualProgram()) { | 43 | if (setup.IsDualProgram()) { |
| 40 | const ShaderIR program_ir_b(setup.program.code_b, PROGRAM_OFFSET, setup.program.size_b); | 44 | const ShaderIR program_ir_b(setup.program.code_b, PROGRAM_OFFSET, setup.program.size_b, |
| 45 | settings); | ||
| 41 | ProgramResult program_b = Decompile(device, program_ir_b, ProgramType::VertexB, "vertex_b"); | 46 | ProgramResult program_b = Decompile(device, program_ir_b, ProgramType::VertexB, "vertex_b"); |
| 42 | out += program_b.first; | 47 | out += program_b.first; |
| 43 | } | 48 | } |
| @@ -80,7 +85,7 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform gs_config { | |||
| 80 | 85 | ||
| 81 | )"; | 86 | )"; |
| 82 | 87 | ||
| 83 | const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a); | 88 | const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a, settings); |
| 84 | ProgramResult program = Decompile(device, program_ir, ProgramType::Geometry, "geometry"); | 89 | ProgramResult program = Decompile(device, program_ir, ProgramType::Geometry, "geometry"); |
| 85 | out += program.first; | 90 | out += program.first; |
| 86 | 91 | ||
| @@ -114,7 +119,8 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform fs_config { | |||
| 114 | }; | 119 | }; |
| 115 | 120 | ||
| 116 | )"; | 121 | )"; |
| 117 | const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a); | 122 | |
| 123 | const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a, settings); | ||
| 118 | ProgramResult program = Decompile(device, program_ir, ProgramType::Fragment, "fragment"); | 124 | ProgramResult program = Decompile(device, program_ir, ProgramType::Fragment, "fragment"); |
| 119 | out += program.first; | 125 | out += program.first; |
| 120 | 126 | ||
| @@ -133,7 +139,7 @@ ProgramResult GenerateComputeShader(const Device& device, const ShaderSetup& set | |||
| 133 | std::string out = "// Shader Unique Id: CS" + id + "\n\n"; | 139 | std::string out = "// Shader Unique Id: CS" + id + "\n\n"; |
| 134 | out += GetCommonDeclarations(); | 140 | out += GetCommonDeclarations(); |
| 135 | 141 | ||
| 136 | const ShaderIR program_ir(setup.program.code, COMPUTE_OFFSET, setup.program.size_a); | 142 | const ShaderIR program_ir(setup.program.code, COMPUTE_OFFSET, setup.program.size_a, settings); |
| 137 | ProgramResult program = Decompile(device, program_ir, ProgramType::Compute, "compute"); | 143 | ProgramResult program = Decompile(device, program_ir, ProgramType::Compute, "compute"); |
| 138 | out += program.first; | 144 | out += program.first; |
| 139 | 145 | ||
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index 77fc58f25..8bcd04221 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp | |||
| @@ -88,6 +88,9 @@ bool IsPrecise(Operation operand) { | |||
| 88 | 88 | ||
| 89 | } // namespace | 89 | } // namespace |
| 90 | 90 | ||
| 91 | class ASTDecompiler; | ||
| 92 | class ExprDecompiler; | ||
| 93 | |||
| 91 | class SPIRVDecompiler : public Sirit::Module { | 94 | class SPIRVDecompiler : public Sirit::Module { |
| 92 | public: | 95 | public: |
| 93 | explicit SPIRVDecompiler(const VKDevice& device, const ShaderIR& ir, ShaderStage stage) | 96 | explicit SPIRVDecompiler(const VKDevice& device, const ShaderIR& ir, ShaderStage stage) |
| @@ -97,27 +100,7 @@ public: | |||
| 97 | AddExtension("SPV_KHR_variable_pointers"); | 100 | AddExtension("SPV_KHR_variable_pointers"); |
| 98 | } | 101 | } |
| 99 | 102 | ||
| 100 | void Decompile() { | 103 | void DecompileBranchMode() { |
| 101 | AllocateBindings(); | ||
| 102 | AllocateLabels(); | ||
| 103 | |||
| 104 | DeclareVertex(); | ||
| 105 | DeclareGeometry(); | ||
| 106 | DeclareFragment(); | ||
| 107 | DeclareRegisters(); | ||
| 108 | DeclarePredicates(); | ||
| 109 | DeclareLocalMemory(); | ||
| 110 | DeclareInternalFlags(); | ||
| 111 | DeclareInputAttributes(); | ||
| 112 | DeclareOutputAttributes(); | ||
| 113 | DeclareConstantBuffers(); | ||
| 114 | DeclareGlobalBuffers(); | ||
| 115 | DeclareSamplers(); | ||
| 116 | |||
| 117 | execute_function = | ||
| 118 | Emit(OpFunction(t_void, spv::FunctionControlMask::Inline, TypeFunction(t_void))); | ||
| 119 | Emit(OpLabel()); | ||
| 120 | |||
| 121 | const u32 first_address = ir.GetBasicBlocks().begin()->first; | 104 | const u32 first_address = ir.GetBasicBlocks().begin()->first; |
| 122 | const Id loop_label = OpLabel("loop"); | 105 | const Id loop_label = OpLabel("loop"); |
| 123 | const Id merge_label = OpLabel("merge"); | 106 | const Id merge_label = OpLabel("merge"); |
| @@ -174,6 +157,43 @@ public: | |||
| 174 | Emit(continue_label); | 157 | Emit(continue_label); |
| 175 | Emit(OpBranch(loop_label)); | 158 | Emit(OpBranch(loop_label)); |
| 176 | Emit(merge_label); | 159 | Emit(merge_label); |
| 160 | } | ||
| 161 | |||
| 162 | void DecompileAST(); | ||
| 163 | |||
| 164 | void Decompile() { | ||
| 165 | const bool is_fully_decompiled = ir.IsDecompiled(); | ||
| 166 | AllocateBindings(); | ||
| 167 | if (!is_fully_decompiled) { | ||
| 168 | AllocateLabels(); | ||
| 169 | } | ||
| 170 | |||
| 171 | DeclareVertex(); | ||
| 172 | DeclareGeometry(); | ||
| 173 | DeclareFragment(); | ||
| 174 | DeclareRegisters(); | ||
| 175 | DeclarePredicates(); | ||
| 176 | if (is_fully_decompiled) { | ||
| 177 | DeclareFlowVariables(); | ||
| 178 | } | ||
| 179 | DeclareLocalMemory(); | ||
| 180 | DeclareInternalFlags(); | ||
| 181 | DeclareInputAttributes(); | ||
| 182 | DeclareOutputAttributes(); | ||
| 183 | DeclareConstantBuffers(); | ||
| 184 | DeclareGlobalBuffers(); | ||
| 185 | DeclareSamplers(); | ||
| 186 | |||
| 187 | execute_function = | ||
| 188 | Emit(OpFunction(t_void, spv::FunctionControlMask::Inline, TypeFunction(t_void))); | ||
| 189 | Emit(OpLabel()); | ||
| 190 | |||
| 191 | if (is_fully_decompiled) { | ||
| 192 | DecompileAST(); | ||
| 193 | } else { | ||
| 194 | DecompileBranchMode(); | ||
| 195 | } | ||
| 196 | |||
| 177 | Emit(OpReturn()); | 197 | Emit(OpReturn()); |
| 178 | Emit(OpFunctionEnd()); | 198 | Emit(OpFunctionEnd()); |
| 179 | } | 199 | } |
| @@ -206,6 +226,9 @@ public: | |||
| 206 | } | 226 | } |
| 207 | 227 | ||
| 208 | private: | 228 | private: |
| 229 | friend class ASTDecompiler; | ||
| 230 | friend class ExprDecompiler; | ||
| 231 | |||
| 209 | static constexpr auto INTERNAL_FLAGS_COUNT = static_cast<std::size_t>(InternalFlag::Amount); | 232 | static constexpr auto INTERNAL_FLAGS_COUNT = static_cast<std::size_t>(InternalFlag::Amount); |
| 210 | 233 | ||
| 211 | void AllocateBindings() { | 234 | void AllocateBindings() { |
| @@ -294,6 +317,14 @@ private: | |||
| 294 | } | 317 | } |
| 295 | } | 318 | } |
| 296 | 319 | ||
| 320 | void DeclareFlowVariables() { | ||
| 321 | for (u32 i = 0; i < ir.GetASTNumVariables(); i++) { | ||
| 322 | const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false); | ||
| 323 | Name(id, fmt::format("flow_var_{}", static_cast<u32>(i))); | ||
| 324 | flow_variables.emplace(i, AddGlobalVariable(id)); | ||
| 325 | } | ||
| 326 | } | ||
| 327 | |||
| 297 | void DeclareLocalMemory() { | 328 | void DeclareLocalMemory() { |
| 298 | if (const u64 local_memory_size = header.GetLocalMemorySize(); local_memory_size > 0) { | 329 | if (const u64 local_memory_size = header.GetLocalMemorySize(); local_memory_size > 0) { |
| 299 | const auto element_count = static_cast<u32>(Common::AlignUp(local_memory_size, 4) / 4); | 330 | const auto element_count = static_cast<u32>(Common::AlignUp(local_memory_size, 4) / 4); |
| @@ -615,9 +646,15 @@ private: | |||
| 615 | Emit(OpBranchConditional(condition, true_label, skip_label)); | 646 | Emit(OpBranchConditional(condition, true_label, skip_label)); |
| 616 | Emit(true_label); | 647 | Emit(true_label); |
| 617 | 648 | ||
| 649 | ++conditional_nest_count; | ||
| 618 | VisitBasicBlock(conditional->GetCode()); | 650 | VisitBasicBlock(conditional->GetCode()); |
| 651 | --conditional_nest_count; | ||
| 619 | 652 | ||
| 620 | Emit(OpBranch(skip_label)); | 653 | if (inside_branch == 0) { |
| 654 | Emit(OpBranch(skip_label)); | ||
| 655 | } else { | ||
| 656 | inside_branch--; | ||
| 657 | } | ||
| 621 | Emit(skip_label); | 658 | Emit(skip_label); |
| 622 | return {}; | 659 | return {}; |
| 623 | 660 | ||
| @@ -980,7 +1017,11 @@ private: | |||
| 980 | UNIMPLEMENTED_IF(!target); | 1017 | UNIMPLEMENTED_IF(!target); |
| 981 | 1018 | ||
| 982 | Emit(OpStore(jmp_to, Constant(t_uint, target->GetValue()))); | 1019 | Emit(OpStore(jmp_to, Constant(t_uint, target->GetValue()))); |
| 983 | BranchingOp([&]() { Emit(OpBranch(continue_label)); }); | 1020 | Emit(OpBranch(continue_label)); |
| 1021 | inside_branch = conditional_nest_count; | ||
| 1022 | if (conditional_nest_count == 0) { | ||
| 1023 | Emit(OpLabel()); | ||
| 1024 | } | ||
| 984 | return {}; | 1025 | return {}; |
| 985 | } | 1026 | } |
| 986 | 1027 | ||
| @@ -988,7 +1029,11 @@ private: | |||
| 988 | const Id op_a = VisitOperand<Type::Uint>(operation, 0); | 1029 | const Id op_a = VisitOperand<Type::Uint>(operation, 0); |
| 989 | 1030 | ||
| 990 | Emit(OpStore(jmp_to, op_a)); | 1031 | Emit(OpStore(jmp_to, op_a)); |
| 991 | BranchingOp([&]() { Emit(OpBranch(continue_label)); }); | 1032 | Emit(OpBranch(continue_label)); |
| 1033 | inside_branch = conditional_nest_count; | ||
| 1034 | if (conditional_nest_count == 0) { | ||
| 1035 | Emit(OpLabel()); | ||
| 1036 | } | ||
| 992 | return {}; | 1037 | return {}; |
| 993 | } | 1038 | } |
| 994 | 1039 | ||
| @@ -1015,11 +1060,15 @@ private: | |||
| 1015 | 1060 | ||
| 1016 | Emit(OpStore(flow_stack_top, previous)); | 1061 | Emit(OpStore(flow_stack_top, previous)); |
| 1017 | Emit(OpStore(jmp_to, target)); | 1062 | Emit(OpStore(jmp_to, target)); |
| 1018 | BranchingOp([&]() { Emit(OpBranch(continue_label)); }); | 1063 | Emit(OpBranch(continue_label)); |
| 1064 | inside_branch = conditional_nest_count; | ||
| 1065 | if (conditional_nest_count == 0) { | ||
| 1066 | Emit(OpLabel()); | ||
| 1067 | } | ||
| 1019 | return {}; | 1068 | return {}; |
| 1020 | } | 1069 | } |
| 1021 | 1070 | ||
| 1022 | Id Exit(Operation operation) { | 1071 | Id PreExit() { |
| 1023 | switch (stage) { | 1072 | switch (stage) { |
| 1024 | case ShaderStage::Vertex: { | 1073 | case ShaderStage::Vertex: { |
| 1025 | // TODO(Rodrigo): We should use VK_EXT_depth_range_unrestricted instead, but it doesn't | 1074 | // TODO(Rodrigo): We should use VK_EXT_depth_range_unrestricted instead, but it doesn't |
| @@ -1067,12 +1116,35 @@ private: | |||
| 1067 | } | 1116 | } |
| 1068 | } | 1117 | } |
| 1069 | 1118 | ||
| 1070 | BranchingOp([&]() { Emit(OpReturn()); }); | 1119 | return {}; |
| 1120 | } | ||
| 1121 | |||
| 1122 | Id Exit(Operation operation) { | ||
| 1123 | PreExit(); | ||
| 1124 | inside_branch = conditional_nest_count; | ||
| 1125 | if (conditional_nest_count > 0) { | ||
| 1126 | Emit(OpReturn()); | ||
| 1127 | } else { | ||
| 1128 | const Id dummy = OpLabel(); | ||
| 1129 | Emit(OpBranch(dummy)); | ||
| 1130 | Emit(dummy); | ||
| 1131 | Emit(OpReturn()); | ||
| 1132 | Emit(OpLabel()); | ||
| 1133 | } | ||
| 1071 | return {}; | 1134 | return {}; |
| 1072 | } | 1135 | } |
| 1073 | 1136 | ||
| 1074 | Id Discard(Operation operation) { | 1137 | Id Discard(Operation operation) { |
| 1075 | BranchingOp([&]() { Emit(OpKill()); }); | 1138 | inside_branch = conditional_nest_count; |
| 1139 | if (conditional_nest_count > 0) { | ||
| 1140 | Emit(OpKill()); | ||
| 1141 | } else { | ||
| 1142 | const Id dummy = OpLabel(); | ||
| 1143 | Emit(OpBranch(dummy)); | ||
| 1144 | Emit(dummy); | ||
| 1145 | Emit(OpKill()); | ||
| 1146 | Emit(OpLabel()); | ||
| 1147 | } | ||
| 1076 | return {}; | 1148 | return {}; |
| 1077 | } | 1149 | } |
| 1078 | 1150 | ||
| @@ -1267,17 +1339,6 @@ private: | |||
| 1267 | return {}; | 1339 | return {}; |
| 1268 | } | 1340 | } |
| 1269 | 1341 | ||
| 1270 | void BranchingOp(std::function<void()> call) { | ||
| 1271 | const Id true_label = OpLabel(); | ||
| 1272 | const Id skip_label = OpLabel(); | ||
| 1273 | Emit(OpSelectionMerge(skip_label, spv::SelectionControlMask::Flatten)); | ||
| 1274 | Emit(OpBranchConditional(v_true, true_label, skip_label, 1, 0)); | ||
| 1275 | Emit(true_label); | ||
| 1276 | call(); | ||
| 1277 | |||
| 1278 | Emit(skip_label); | ||
| 1279 | } | ||
| 1280 | |||
| 1281 | std::tuple<Id, Id> CreateFlowStack() { | 1342 | std::tuple<Id, Id> CreateFlowStack() { |
| 1282 | // TODO(Rodrigo): Figure out the actual depth of the flow stack, for now it seems unlikely | 1343 | // TODO(Rodrigo): Figure out the actual depth of the flow stack, for now it seems unlikely |
| 1283 | // that shaders will use 20 nested SSYs and PBKs. | 1344 | // that shaders will use 20 nested SSYs and PBKs. |
| @@ -1483,6 +1544,8 @@ private: | |||
| 1483 | const ShaderIR& ir; | 1544 | const ShaderIR& ir; |
| 1484 | const ShaderStage stage; | 1545 | const ShaderStage stage; |
| 1485 | const Tegra::Shader::Header header; | 1546 | const Tegra::Shader::Header header; |
| 1547 | u64 conditional_nest_count{}; | ||
| 1548 | u64 inside_branch{}; | ||
| 1486 | 1549 | ||
| 1487 | const Id t_void = Name(TypeVoid(), "void"); | 1550 | const Id t_void = Name(TypeVoid(), "void"); |
| 1488 | 1551 | ||
| @@ -1545,6 +1608,7 @@ private: | |||
| 1545 | Id per_vertex{}; | 1608 | Id per_vertex{}; |
| 1546 | std::map<u32, Id> registers; | 1609 | std::map<u32, Id> registers; |
| 1547 | std::map<Tegra::Shader::Pred, Id> predicates; | 1610 | std::map<Tegra::Shader::Pred, Id> predicates; |
| 1611 | std::map<u32, Id> flow_variables; | ||
| 1548 | Id local_memory{}; | 1612 | Id local_memory{}; |
| 1549 | std::array<Id, INTERNAL_FLAGS_COUNT> internal_flags{}; | 1613 | std::array<Id, INTERNAL_FLAGS_COUNT> internal_flags{}; |
| 1550 | std::map<Attribute::Index, Id> input_attributes; | 1614 | std::map<Attribute::Index, Id> input_attributes; |
| @@ -1580,6 +1644,223 @@ private: | |||
| 1580 | std::map<u32, Id> labels; | 1644 | std::map<u32, Id> labels; |
| 1581 | }; | 1645 | }; |
| 1582 | 1646 | ||
| 1647 | class ExprDecompiler { | ||
| 1648 | public: | ||
| 1649 | explicit ExprDecompiler(SPIRVDecompiler& decomp) : decomp{decomp} {} | ||
| 1650 | |||
| 1651 | Id operator()(VideoCommon::Shader::ExprAnd& expr) { | ||
| 1652 | const Id type_def = decomp.GetTypeDefinition(Type::Bool); | ||
| 1653 | const Id op1 = Visit(expr.operand1); | ||
| 1654 | const Id op2 = Visit(expr.operand2); | ||
| 1655 | return decomp.Emit(decomp.OpLogicalAnd(type_def, op1, op2)); | ||
| 1656 | } | ||
| 1657 | |||
| 1658 | Id operator()(VideoCommon::Shader::ExprOr& expr) { | ||
| 1659 | const Id type_def = decomp.GetTypeDefinition(Type::Bool); | ||
| 1660 | const Id op1 = Visit(expr.operand1); | ||
| 1661 | const Id op2 = Visit(expr.operand2); | ||
| 1662 | return decomp.Emit(decomp.OpLogicalOr(type_def, op1, op2)); | ||
| 1663 | } | ||
| 1664 | |||
| 1665 | Id operator()(VideoCommon::Shader::ExprNot& expr) { | ||
| 1666 | const Id type_def = decomp.GetTypeDefinition(Type::Bool); | ||
| 1667 | const Id op1 = Visit(expr.operand1); | ||
| 1668 | return decomp.Emit(decomp.OpLogicalNot(type_def, op1)); | ||
| 1669 | } | ||
| 1670 | |||
| 1671 | Id operator()(VideoCommon::Shader::ExprPredicate& expr) { | ||
| 1672 | const auto pred = static_cast<Tegra::Shader::Pred>(expr.predicate); | ||
| 1673 | return decomp.Emit(decomp.OpLoad(decomp.t_bool, decomp.predicates.at(pred))); | ||
| 1674 | } | ||
| 1675 | |||
| 1676 | Id operator()(VideoCommon::Shader::ExprCondCode& expr) { | ||
| 1677 | const Node cc = decomp.ir.GetConditionCode(expr.cc); | ||
| 1678 | Id target; | ||
| 1679 | |||
| 1680 | if (const auto pred = std::get_if<PredicateNode>(&*cc)) { | ||
| 1681 | const auto index = pred->GetIndex(); | ||
| 1682 | switch (index) { | ||
| 1683 | case Tegra::Shader::Pred::NeverExecute: | ||
| 1684 | target = decomp.v_false; | ||
| 1685 | case Tegra::Shader::Pred::UnusedIndex: | ||
| 1686 | target = decomp.v_true; | ||
| 1687 | default: | ||
| 1688 | target = decomp.predicates.at(index); | ||
| 1689 | } | ||
| 1690 | } else if (const auto flag = std::get_if<InternalFlagNode>(&*cc)) { | ||
| 1691 | target = decomp.internal_flags.at(static_cast<u32>(flag->GetFlag())); | ||
| 1692 | } | ||
| 1693 | return decomp.Emit(decomp.OpLoad(decomp.t_bool, target)); | ||
| 1694 | } | ||
| 1695 | |||
| 1696 | Id operator()(VideoCommon::Shader::ExprVar& expr) { | ||
| 1697 | return decomp.Emit(decomp.OpLoad(decomp.t_bool, decomp.flow_variables.at(expr.var_index))); | ||
| 1698 | } | ||
| 1699 | |||
| 1700 | Id operator()(VideoCommon::Shader::ExprBoolean& expr) { | ||
| 1701 | return expr.value ? decomp.v_true : decomp.v_false; | ||
| 1702 | } | ||
| 1703 | |||
| 1704 | Id Visit(VideoCommon::Shader::Expr& node) { | ||
| 1705 | return std::visit(*this, *node); | ||
| 1706 | } | ||
| 1707 | |||
| 1708 | private: | ||
| 1709 | SPIRVDecompiler& decomp; | ||
| 1710 | }; | ||
| 1711 | |||
| 1712 | class ASTDecompiler { | ||
| 1713 | public: | ||
| 1714 | explicit ASTDecompiler(SPIRVDecompiler& decomp) : decomp{decomp} {} | ||
| 1715 | |||
| 1716 | void operator()(VideoCommon::Shader::ASTProgram& ast) { | ||
| 1717 | ASTNode current = ast.nodes.GetFirst(); | ||
| 1718 | while (current) { | ||
| 1719 | Visit(current); | ||
| 1720 | current = current->GetNext(); | ||
| 1721 | } | ||
| 1722 | } | ||
| 1723 | |||
| 1724 | void operator()(VideoCommon::Shader::ASTIfThen& ast) { | ||
| 1725 | ExprDecompiler expr_parser{decomp}; | ||
| 1726 | const Id condition = expr_parser.Visit(ast.condition); | ||
| 1727 | const Id then_label = decomp.OpLabel(); | ||
| 1728 | const Id endif_label = decomp.OpLabel(); | ||
| 1729 | decomp.Emit(decomp.OpSelectionMerge(endif_label, spv::SelectionControlMask::MaskNone)); | ||
| 1730 | decomp.Emit(decomp.OpBranchConditional(condition, then_label, endif_label)); | ||
| 1731 | decomp.Emit(then_label); | ||
| 1732 | ASTNode current = ast.nodes.GetFirst(); | ||
| 1733 | while (current) { | ||
| 1734 | Visit(current); | ||
| 1735 | current = current->GetNext(); | ||
| 1736 | } | ||
| 1737 | decomp.Emit(decomp.OpBranch(endif_label)); | ||
| 1738 | decomp.Emit(endif_label); | ||
| 1739 | } | ||
| 1740 | |||
| 1741 | void operator()(VideoCommon::Shader::ASTIfElse& ast) { | ||
| 1742 | UNREACHABLE(); | ||
| 1743 | } | ||
| 1744 | |||
| 1745 | void operator()(VideoCommon::Shader::ASTBlockEncoded& ast) { | ||
| 1746 | UNREACHABLE(); | ||
| 1747 | } | ||
| 1748 | |||
| 1749 | void operator()(VideoCommon::Shader::ASTBlockDecoded& ast) { | ||
| 1750 | decomp.VisitBasicBlock(ast.nodes); | ||
| 1751 | } | ||
| 1752 | |||
| 1753 | void operator()(VideoCommon::Shader::ASTVarSet& ast) { | ||
| 1754 | ExprDecompiler expr_parser{decomp}; | ||
| 1755 | const Id condition = expr_parser.Visit(ast.condition); | ||
| 1756 | decomp.Emit(decomp.OpStore(decomp.flow_variables.at(ast.index), condition)); | ||
| 1757 | } | ||
| 1758 | |||
| 1759 | void operator()(VideoCommon::Shader::ASTLabel& ast) { | ||
| 1760 | // Do nothing | ||
| 1761 | } | ||
| 1762 | |||
| 1763 | void operator()(VideoCommon::Shader::ASTGoto& ast) { | ||
| 1764 | UNREACHABLE(); | ||
| 1765 | } | ||
| 1766 | |||
| 1767 | void operator()(VideoCommon::Shader::ASTDoWhile& ast) { | ||
| 1768 | const Id loop_label = decomp.OpLabel(); | ||
| 1769 | const Id endloop_label = decomp.OpLabel(); | ||
| 1770 | const Id loop_start_block = decomp.OpLabel(); | ||
| 1771 | const Id loop_end_block = decomp.OpLabel(); | ||
| 1772 | current_loop_exit = endloop_label; | ||
| 1773 | decomp.Emit(decomp.OpBranch(loop_label)); | ||
| 1774 | decomp.Emit(loop_label); | ||
| 1775 | decomp.Emit( | ||
| 1776 | decomp.OpLoopMerge(endloop_label, loop_end_block, spv::LoopControlMask::MaskNone)); | ||
| 1777 | decomp.Emit(decomp.OpBranch(loop_start_block)); | ||
| 1778 | decomp.Emit(loop_start_block); | ||
| 1779 | ASTNode current = ast.nodes.GetFirst(); | ||
| 1780 | while (current) { | ||
| 1781 | Visit(current); | ||
| 1782 | current = current->GetNext(); | ||
| 1783 | } | ||
| 1784 | ExprDecompiler expr_parser{decomp}; | ||
| 1785 | const Id condition = expr_parser.Visit(ast.condition); | ||
| 1786 | decomp.Emit(decomp.OpBranchConditional(condition, loop_label, endloop_label)); | ||
| 1787 | decomp.Emit(endloop_label); | ||
| 1788 | } | ||
| 1789 | |||
| 1790 | void operator()(VideoCommon::Shader::ASTReturn& ast) { | ||
| 1791 | if (!VideoCommon::Shader::ExprIsTrue(ast.condition)) { | ||
| 1792 | ExprDecompiler expr_parser{decomp}; | ||
| 1793 | const Id condition = expr_parser.Visit(ast.condition); | ||
| 1794 | const Id then_label = decomp.OpLabel(); | ||
| 1795 | const Id endif_label = decomp.OpLabel(); | ||
| 1796 | decomp.Emit(decomp.OpSelectionMerge(endif_label, spv::SelectionControlMask::MaskNone)); | ||
| 1797 | decomp.Emit(decomp.OpBranchConditional(condition, then_label, endif_label)); | ||
| 1798 | decomp.Emit(then_label); | ||
| 1799 | if (ast.kills) { | ||
| 1800 | decomp.Emit(decomp.OpKill()); | ||
| 1801 | } else { | ||
| 1802 | decomp.PreExit(); | ||
| 1803 | decomp.Emit(decomp.OpReturn()); | ||
| 1804 | } | ||
| 1805 | decomp.Emit(endif_label); | ||
| 1806 | } else { | ||
| 1807 | const Id next_block = decomp.OpLabel(); | ||
| 1808 | decomp.Emit(decomp.OpBranch(next_block)); | ||
| 1809 | decomp.Emit(next_block); | ||
| 1810 | if (ast.kills) { | ||
| 1811 | decomp.Emit(decomp.OpKill()); | ||
| 1812 | } else { | ||
| 1813 | decomp.PreExit(); | ||
| 1814 | decomp.Emit(decomp.OpReturn()); | ||
| 1815 | } | ||
| 1816 | decomp.Emit(decomp.OpLabel()); | ||
| 1817 | } | ||
| 1818 | } | ||
| 1819 | |||
| 1820 | void operator()(VideoCommon::Shader::ASTBreak& ast) { | ||
| 1821 | if (!VideoCommon::Shader::ExprIsTrue(ast.condition)) { | ||
| 1822 | ExprDecompiler expr_parser{decomp}; | ||
| 1823 | const Id condition = expr_parser.Visit(ast.condition); | ||
| 1824 | const Id then_label = decomp.OpLabel(); | ||
| 1825 | const Id endif_label = decomp.OpLabel(); | ||
| 1826 | decomp.Emit(decomp.OpSelectionMerge(endif_label, spv::SelectionControlMask::MaskNone)); | ||
| 1827 | decomp.Emit(decomp.OpBranchConditional(condition, then_label, endif_label)); | ||
| 1828 | decomp.Emit(then_label); | ||
| 1829 | decomp.Emit(decomp.OpBranch(current_loop_exit)); | ||
| 1830 | decomp.Emit(endif_label); | ||
| 1831 | } else { | ||
| 1832 | const Id next_block = decomp.OpLabel(); | ||
| 1833 | decomp.Emit(decomp.OpBranch(next_block)); | ||
| 1834 | decomp.Emit(next_block); | ||
| 1835 | decomp.Emit(decomp.OpBranch(current_loop_exit)); | ||
| 1836 | decomp.Emit(decomp.OpLabel()); | ||
| 1837 | } | ||
| 1838 | } | ||
| 1839 | |||
| 1840 | void Visit(VideoCommon::Shader::ASTNode& node) { | ||
| 1841 | std::visit(*this, *node->GetInnerData()); | ||
| 1842 | } | ||
| 1843 | |||
| 1844 | private: | ||
| 1845 | SPIRVDecompiler& decomp; | ||
| 1846 | Id current_loop_exit{}; | ||
| 1847 | }; | ||
| 1848 | |||
| 1849 | void SPIRVDecompiler::DecompileAST() { | ||
| 1850 | const u32 num_flow_variables = ir.GetASTNumVariables(); | ||
| 1851 | for (u32 i = 0; i < num_flow_variables; i++) { | ||
| 1852 | const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false); | ||
| 1853 | Name(id, fmt::format("flow_var_{}", i)); | ||
| 1854 | flow_variables.emplace(i, AddGlobalVariable(id)); | ||
| 1855 | } | ||
| 1856 | ASTDecompiler decompiler{*this}; | ||
| 1857 | VideoCommon::Shader::ASTNode program = ir.GetASTProgram(); | ||
| 1858 | decompiler.Visit(program); | ||
| 1859 | const Id next_block = OpLabel(); | ||
| 1860 | Emit(OpBranch(next_block)); | ||
| 1861 | Emit(next_block); | ||
| 1862 | } | ||
| 1863 | |||
| 1583 | DecompilerResult Decompile(const VKDevice& device, const VideoCommon::Shader::ShaderIR& ir, | 1864 | DecompilerResult Decompile(const VKDevice& device, const VideoCommon::Shader::ShaderIR& ir, |
| 1584 | Maxwell::ShaderStage stage) { | 1865 | Maxwell::ShaderStage stage) { |
| 1585 | auto decompiler = std::make_unique<SPIRVDecompiler>(device, ir, stage); | 1866 | auto decompiler = std::make_unique<SPIRVDecompiler>(device, ir, stage); |
diff --git a/src/video_core/shader/ast.cpp b/src/video_core/shader/ast.cpp new file mode 100644 index 000000000..2eb065c3d --- /dev/null +++ b/src/video_core/shader/ast.cpp | |||
| @@ -0,0 +1,766 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <string> | ||
| 6 | |||
| 7 | #include "common/assert.h" | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "video_core/shader/ast.h" | ||
| 10 | #include "video_core/shader/expr.h" | ||
| 11 | |||
| 12 | namespace VideoCommon::Shader { | ||
| 13 | |||
| 14 | ASTZipper::ASTZipper() = default; | ||
| 15 | |||
| 16 | void ASTZipper::Init(const ASTNode new_first, const ASTNode parent) { | ||
| 17 | ASSERT(new_first->manager == nullptr); | ||
| 18 | first = new_first; | ||
| 19 | last = new_first; | ||
| 20 | ASTNode current = first; | ||
| 21 | while (current) { | ||
| 22 | current->manager = this; | ||
| 23 | current->parent = parent; | ||
| 24 | last = current; | ||
| 25 | current = current->next; | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | void ASTZipper::PushBack(const ASTNode new_node) { | ||
| 30 | ASSERT(new_node->manager == nullptr); | ||
| 31 | new_node->previous = last; | ||
| 32 | if (last) { | ||
| 33 | last->next = new_node; | ||
| 34 | } | ||
| 35 | new_node->next.reset(); | ||
| 36 | last = new_node; | ||
| 37 | if (!first) { | ||
| 38 | first = new_node; | ||
| 39 | } | ||
| 40 | new_node->manager = this; | ||
| 41 | } | ||
| 42 | |||
| 43 | void ASTZipper::PushFront(const ASTNode new_node) { | ||
| 44 | ASSERT(new_node->manager == nullptr); | ||
| 45 | new_node->previous.reset(); | ||
| 46 | new_node->next = first; | ||
| 47 | if (first) { | ||
| 48 | first->previous = new_node; | ||
| 49 | } | ||
| 50 | if (last == first) { | ||
| 51 | last = new_node; | ||
| 52 | } | ||
| 53 | first = new_node; | ||
| 54 | new_node->manager = this; | ||
| 55 | } | ||
| 56 | |||
| 57 | void ASTZipper::InsertAfter(const ASTNode new_node, const ASTNode at_node) { | ||
| 58 | ASSERT(new_node->manager == nullptr); | ||
| 59 | if (!at_node) { | ||
| 60 | PushFront(new_node); | ||
| 61 | return; | ||
| 62 | } | ||
| 63 | const ASTNode next = at_node->next; | ||
| 64 | if (next) { | ||
| 65 | next->previous = new_node; | ||
| 66 | } | ||
| 67 | new_node->previous = at_node; | ||
| 68 | if (at_node == last) { | ||
| 69 | last = new_node; | ||
| 70 | } | ||
| 71 | new_node->next = next; | ||
| 72 | at_node->next = new_node; | ||
| 73 | new_node->manager = this; | ||
| 74 | } | ||
| 75 | |||
| 76 | void ASTZipper::InsertBefore(const ASTNode new_node, const ASTNode at_node) { | ||
| 77 | ASSERT(new_node->manager == nullptr); | ||
| 78 | if (!at_node) { | ||
| 79 | PushBack(new_node); | ||
| 80 | return; | ||
| 81 | } | ||
| 82 | const ASTNode previous = at_node->previous; | ||
| 83 | if (previous) { | ||
| 84 | previous->next = new_node; | ||
| 85 | } | ||
| 86 | new_node->next = at_node; | ||
| 87 | if (at_node == first) { | ||
| 88 | first = new_node; | ||
| 89 | } | ||
| 90 | new_node->previous = previous; | ||
| 91 | at_node->previous = new_node; | ||
| 92 | new_node->manager = this; | ||
| 93 | } | ||
| 94 | |||
| 95 | void ASTZipper::DetachTail(const ASTNode node) { | ||
| 96 | ASSERT(node->manager == this); | ||
| 97 | if (node == first) { | ||
| 98 | first.reset(); | ||
| 99 | last.reset(); | ||
| 100 | return; | ||
| 101 | } | ||
| 102 | |||
| 103 | last = node->previous; | ||
| 104 | last->next.reset(); | ||
| 105 | node->previous.reset(); | ||
| 106 | ASTNode current = node; | ||
| 107 | while (current) { | ||
| 108 | current->manager = nullptr; | ||
| 109 | current->parent.reset(); | ||
| 110 | current = current->next; | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | void ASTZipper::DetachSegment(const ASTNode start, const ASTNode end) { | ||
| 115 | ASSERT(start->manager == this && end->manager == this); | ||
| 116 | if (start == end) { | ||
| 117 | DetachSingle(start); | ||
| 118 | return; | ||
| 119 | } | ||
| 120 | const ASTNode prev = start->previous; | ||
| 121 | const ASTNode post = end->next; | ||
| 122 | if (!prev) { | ||
| 123 | first = post; | ||
| 124 | } else { | ||
| 125 | prev->next = post; | ||
| 126 | } | ||
| 127 | if (!post) { | ||
| 128 | last = prev; | ||
| 129 | } else { | ||
| 130 | post->previous = prev; | ||
| 131 | } | ||
| 132 | start->previous.reset(); | ||
| 133 | end->next.reset(); | ||
| 134 | ASTNode current = start; | ||
| 135 | bool found = false; | ||
| 136 | while (current) { | ||
| 137 | current->manager = nullptr; | ||
| 138 | current->parent.reset(); | ||
| 139 | found |= current == end; | ||
| 140 | current = current->next; | ||
| 141 | } | ||
| 142 | ASSERT(found); | ||
| 143 | } | ||
| 144 | |||
| 145 | void ASTZipper::DetachSingle(const ASTNode node) { | ||
| 146 | ASSERT(node->manager == this); | ||
| 147 | const ASTNode prev = node->previous; | ||
| 148 | const ASTNode post = node->next; | ||
| 149 | node->previous.reset(); | ||
| 150 | node->next.reset(); | ||
| 151 | if (!prev) { | ||
| 152 | first = post; | ||
| 153 | } else { | ||
| 154 | prev->next = post; | ||
| 155 | } | ||
| 156 | if (!post) { | ||
| 157 | last = prev; | ||
| 158 | } else { | ||
| 159 | post->previous = prev; | ||
| 160 | } | ||
| 161 | |||
| 162 | node->manager = nullptr; | ||
| 163 | node->parent.reset(); | ||
| 164 | } | ||
| 165 | |||
| 166 | void ASTZipper::Remove(const ASTNode node) { | ||
| 167 | ASSERT(node->manager == this); | ||
| 168 | const ASTNode next = node->next; | ||
| 169 | const ASTNode previous = node->previous; | ||
| 170 | if (previous) { | ||
| 171 | previous->next = next; | ||
| 172 | } | ||
| 173 | if (next) { | ||
| 174 | next->previous = previous; | ||
| 175 | } | ||
| 176 | node->parent.reset(); | ||
| 177 | node->manager = nullptr; | ||
| 178 | if (node == last) { | ||
| 179 | last = previous; | ||
| 180 | } | ||
| 181 | if (node == first) { | ||
| 182 | first = next; | ||
| 183 | } | ||
| 184 | } | ||
| 185 | |||
| 186 | class ExprPrinter final { | ||
| 187 | public: | ||
| 188 | ExprPrinter() = default; | ||
| 189 | |||
| 190 | void operator()(ExprAnd const& expr) { | ||
| 191 | inner += "( "; | ||
| 192 | std::visit(*this, *expr.operand1); | ||
| 193 | inner += " && "; | ||
| 194 | std::visit(*this, *expr.operand2); | ||
| 195 | inner += ')'; | ||
| 196 | } | ||
| 197 | |||
| 198 | void operator()(ExprOr const& expr) { | ||
| 199 | inner += "( "; | ||
| 200 | std::visit(*this, *expr.operand1); | ||
| 201 | inner += " || "; | ||
| 202 | std::visit(*this, *expr.operand2); | ||
| 203 | inner += ')'; | ||
| 204 | } | ||
| 205 | |||
| 206 | void operator()(ExprNot const& expr) { | ||
| 207 | inner += "!"; | ||
| 208 | std::visit(*this, *expr.operand1); | ||
| 209 | } | ||
| 210 | |||
| 211 | void operator()(ExprPredicate const& expr) { | ||
| 212 | inner += "P" + std::to_string(expr.predicate); | ||
| 213 | } | ||
| 214 | |||
| 215 | void operator()(ExprCondCode const& expr) { | ||
| 216 | u32 cc = static_cast<u32>(expr.cc); | ||
| 217 | inner += "CC" + std::to_string(cc); | ||
| 218 | } | ||
| 219 | |||
| 220 | void operator()(ExprVar const& expr) { | ||
| 221 | inner += "V" + std::to_string(expr.var_index); | ||
| 222 | } | ||
| 223 | |||
| 224 | void operator()(ExprBoolean const& expr) { | ||
| 225 | inner += expr.value ? "true" : "false"; | ||
| 226 | } | ||
| 227 | |||
| 228 | std::string& GetResult() { | ||
| 229 | return inner; | ||
| 230 | } | ||
| 231 | |||
| 232 | std::string inner{}; | ||
| 233 | }; | ||
| 234 | |||
| 235 | class ASTPrinter { | ||
| 236 | public: | ||
| 237 | ASTPrinter() = default; | ||
| 238 | |||
| 239 | void operator()(ASTProgram& ast) { | ||
| 240 | scope++; | ||
| 241 | inner += "program {\n"; | ||
| 242 | ASTNode current = ast.nodes.GetFirst(); | ||
| 243 | while (current) { | ||
| 244 | Visit(current); | ||
| 245 | current = current->GetNext(); | ||
| 246 | } | ||
| 247 | inner += "}\n"; | ||
| 248 | scope--; | ||
| 249 | } | ||
| 250 | |||
| 251 | void operator()(ASTIfThen& ast) { | ||
| 252 | ExprPrinter expr_parser{}; | ||
| 253 | std::visit(expr_parser, *ast.condition); | ||
| 254 | inner += Ident() + "if (" + expr_parser.GetResult() + ") {\n"; | ||
| 255 | scope++; | ||
| 256 | ASTNode current = ast.nodes.GetFirst(); | ||
| 257 | while (current) { | ||
| 258 | Visit(current); | ||
| 259 | current = current->GetNext(); | ||
| 260 | } | ||
| 261 | scope--; | ||
| 262 | inner += Ident() + "}\n"; | ||
| 263 | } | ||
| 264 | |||
| 265 | void operator()(ASTIfElse& ast) { | ||
| 266 | inner += Ident() + "else {\n"; | ||
| 267 | scope++; | ||
| 268 | ASTNode current = ast.nodes.GetFirst(); | ||
| 269 | while (current) { | ||
| 270 | Visit(current); | ||
| 271 | current = current->GetNext(); | ||
| 272 | } | ||
| 273 | scope--; | ||
| 274 | inner += Ident() + "}\n"; | ||
| 275 | } | ||
| 276 | |||
| 277 | void operator()(ASTBlockEncoded& ast) { | ||
| 278 | inner += Ident() + "Block(" + std::to_string(ast.start) + ", " + std::to_string(ast.end) + | ||
| 279 | ");\n"; | ||
| 280 | } | ||
| 281 | |||
| 282 | void operator()(ASTBlockDecoded& ast) { | ||
| 283 | inner += Ident() + "Block;\n"; | ||
| 284 | } | ||
| 285 | |||
| 286 | void operator()(ASTVarSet& ast) { | ||
| 287 | ExprPrinter expr_parser{}; | ||
| 288 | std::visit(expr_parser, *ast.condition); | ||
| 289 | inner += | ||
| 290 | Ident() + "V" + std::to_string(ast.index) + " := " + expr_parser.GetResult() + ";\n"; | ||
| 291 | } | ||
| 292 | |||
| 293 | void operator()(ASTLabel& ast) { | ||
| 294 | inner += "Label_" + std::to_string(ast.index) + ":\n"; | ||
| 295 | } | ||
| 296 | |||
| 297 | void operator()(ASTGoto& ast) { | ||
| 298 | ExprPrinter expr_parser{}; | ||
| 299 | std::visit(expr_parser, *ast.condition); | ||
| 300 | inner += Ident() + "(" + expr_parser.GetResult() + ") -> goto Label_" + | ||
| 301 | std::to_string(ast.label) + ";\n"; | ||
| 302 | } | ||
| 303 | |||
| 304 | void operator()(ASTDoWhile& ast) { | ||
| 305 | ExprPrinter expr_parser{}; | ||
| 306 | std::visit(expr_parser, *ast.condition); | ||
| 307 | inner += Ident() + "do {\n"; | ||
| 308 | scope++; | ||
| 309 | ASTNode current = ast.nodes.GetFirst(); | ||
| 310 | while (current) { | ||
| 311 | Visit(current); | ||
| 312 | current = current->GetNext(); | ||
| 313 | } | ||
| 314 | scope--; | ||
| 315 | inner += Ident() + "} while (" + expr_parser.GetResult() + ");\n"; | ||
| 316 | } | ||
| 317 | |||
| 318 | void operator()(ASTReturn& ast) { | ||
| 319 | ExprPrinter expr_parser{}; | ||
| 320 | std::visit(expr_parser, *ast.condition); | ||
| 321 | inner += Ident() + "(" + expr_parser.GetResult() + ") -> " + | ||
| 322 | (ast.kills ? "discard" : "exit") + ";\n"; | ||
| 323 | } | ||
| 324 | |||
| 325 | void operator()(ASTBreak& ast) { | ||
| 326 | ExprPrinter expr_parser{}; | ||
| 327 | std::visit(expr_parser, *ast.condition); | ||
| 328 | inner += Ident() + "(" + expr_parser.GetResult() + ") -> break;\n"; | ||
| 329 | } | ||
| 330 | |||
| 331 | std::string& Ident() { | ||
| 332 | if (memo_scope == scope) { | ||
| 333 | return tabs_memo; | ||
| 334 | } | ||
| 335 | tabs_memo = tabs.substr(0, scope * 2); | ||
| 336 | memo_scope = scope; | ||
| 337 | return tabs_memo; | ||
| 338 | } | ||
| 339 | |||
| 340 | void Visit(ASTNode& node) { | ||
| 341 | std::visit(*this, *node->GetInnerData()); | ||
| 342 | } | ||
| 343 | |||
| 344 | std::string& GetResult() { | ||
| 345 | return inner; | ||
| 346 | } | ||
| 347 | |||
| 348 | private: | ||
| 349 | std::string inner{}; | ||
| 350 | u32 scope{}; | ||
| 351 | |||
| 352 | std::string tabs_memo{}; | ||
| 353 | u32 memo_scope{}; | ||
| 354 | |||
| 355 | static std::string tabs; | ||
| 356 | }; | ||
| 357 | |||
| 358 | std::string ASTPrinter::tabs = " "; | ||
| 359 | |||
| 360 | std::string ASTManager::Print() { | ||
| 361 | ASTPrinter printer{}; | ||
| 362 | printer.Visit(main_node); | ||
| 363 | return printer.GetResult(); | ||
| 364 | } | ||
| 365 | |||
| 366 | ASTManager::ASTManager(bool full_decompile, bool disable_else_derivation) | ||
| 367 | : full_decompile{full_decompile}, disable_else_derivation{disable_else_derivation} {}; | ||
| 368 | |||
| 369 | ASTManager::~ASTManager() { | ||
| 370 | Clear(); | ||
| 371 | } | ||
| 372 | |||
| 373 | void ASTManager::Init() { | ||
| 374 | main_node = ASTBase::Make<ASTProgram>(ASTNode{}); | ||
| 375 | program = std::get_if<ASTProgram>(main_node->GetInnerData()); | ||
| 376 | false_condition = MakeExpr<ExprBoolean>(false); | ||
| 377 | } | ||
| 378 | |||
| 379 | ASTManager::ASTManager(ASTManager&& other) noexcept | ||
| 380 | : labels_map(std::move(other.labels_map)), labels_count{other.labels_count}, | ||
| 381 | gotos(std::move(other.gotos)), labels(std::move(other.labels)), variables{other.variables}, | ||
| 382 | program{other.program}, main_node{other.main_node}, false_condition{other.false_condition}, | ||
| 383 | disable_else_derivation{other.disable_else_derivation} { | ||
| 384 | other.main_node.reset(); | ||
| 385 | } | ||
| 386 | |||
| 387 | ASTManager& ASTManager::operator=(ASTManager&& other) noexcept { | ||
| 388 | full_decompile = other.full_decompile; | ||
| 389 | labels_map = std::move(other.labels_map); | ||
| 390 | labels_count = other.labels_count; | ||
| 391 | gotos = std::move(other.gotos); | ||
| 392 | labels = std::move(other.labels); | ||
| 393 | variables = other.variables; | ||
| 394 | program = other.program; | ||
| 395 | main_node = other.main_node; | ||
| 396 | false_condition = other.false_condition; | ||
| 397 | disable_else_derivation = other.disable_else_derivation; | ||
| 398 | |||
| 399 | other.main_node.reset(); | ||
| 400 | return *this; | ||
| 401 | } | ||
| 402 | |||
| 403 | void ASTManager::DeclareLabel(u32 address) { | ||
| 404 | const auto pair = labels_map.emplace(address, labels_count); | ||
| 405 | if (pair.second) { | ||
| 406 | labels_count++; | ||
| 407 | labels.resize(labels_count); | ||
| 408 | } | ||
| 409 | } | ||
| 410 | |||
| 411 | void ASTManager::InsertLabel(u32 address) { | ||
| 412 | const u32 index = labels_map[address]; | ||
| 413 | const ASTNode label = ASTBase::Make<ASTLabel>(main_node, index); | ||
| 414 | labels[index] = label; | ||
| 415 | program->nodes.PushBack(label); | ||
| 416 | } | ||
| 417 | |||
| 418 | void ASTManager::InsertGoto(Expr condition, u32 address) { | ||
| 419 | const u32 index = labels_map[address]; | ||
| 420 | const ASTNode goto_node = ASTBase::Make<ASTGoto>(main_node, condition, index); | ||
| 421 | gotos.push_back(goto_node); | ||
| 422 | program->nodes.PushBack(goto_node); | ||
| 423 | } | ||
| 424 | |||
| 425 | void ASTManager::InsertBlock(u32 start_address, u32 end_address) { | ||
| 426 | const ASTNode block = ASTBase::Make<ASTBlockEncoded>(main_node, start_address, end_address); | ||
| 427 | program->nodes.PushBack(block); | ||
| 428 | } | ||
| 429 | |||
| 430 | void ASTManager::InsertReturn(Expr condition, bool kills) { | ||
| 431 | const ASTNode node = ASTBase::Make<ASTReturn>(main_node, condition, kills); | ||
| 432 | program->nodes.PushBack(node); | ||
| 433 | } | ||
| 434 | |||
| 435 | // The decompile algorithm is based on | ||
| 436 | // "Taming control flow: A structured approach to eliminating goto statements" | ||
| 437 | // by AM Erosa, LJ Hendren 1994. In general, the idea is to get gotos to be | ||
| 438 | // on the same structured level as the label which they jump to. This is done, | ||
| 439 | // through outward/inward movements and lifting. Once they are at the same | ||
| 440 | // level, you can enclose them in an "if" structure or a "do-while" structure. | ||
| 441 | void ASTManager::Decompile() { | ||
| 442 | auto it = gotos.begin(); | ||
| 443 | while (it != gotos.end()) { | ||
| 444 | const ASTNode goto_node = *it; | ||
| 445 | const auto label_index = goto_node->GetGotoLabel(); | ||
| 446 | if (!label_index) { | ||
| 447 | return; | ||
| 448 | } | ||
| 449 | const ASTNode label = labels[*label_index]; | ||
| 450 | if (!full_decompile) { | ||
| 451 | // We only decompile backward jumps | ||
| 452 | if (!IsBackwardsJump(goto_node, label)) { | ||
| 453 | it++; | ||
| 454 | continue; | ||
| 455 | } | ||
| 456 | } | ||
| 457 | if (IndirectlyRelated(goto_node, label)) { | ||
| 458 | while (!DirectlyRelated(goto_node, label)) { | ||
| 459 | MoveOutward(goto_node); | ||
| 460 | } | ||
| 461 | } | ||
| 462 | if (DirectlyRelated(goto_node, label)) { | ||
| 463 | u32 goto_level = goto_node->GetLevel(); | ||
| 464 | const u32 label_level = label->GetLevel(); | ||
| 465 | while (label_level < goto_level) { | ||
| 466 | MoveOutward(goto_node); | ||
| 467 | goto_level--; | ||
| 468 | } | ||
| 469 | // TODO(Blinkhawk): Implement Lifting and Inward Movements | ||
| 470 | } | ||
| 471 | if (label->GetParent() == goto_node->GetParent()) { | ||
| 472 | bool is_loop = false; | ||
| 473 | ASTNode current = goto_node->GetPrevious(); | ||
| 474 | while (current) { | ||
| 475 | if (current == label) { | ||
| 476 | is_loop = true; | ||
| 477 | break; | ||
| 478 | } | ||
| 479 | current = current->GetPrevious(); | ||
| 480 | } | ||
| 481 | |||
| 482 | if (is_loop) { | ||
| 483 | EncloseDoWhile(goto_node, label); | ||
| 484 | } else { | ||
| 485 | EncloseIfThen(goto_node, label); | ||
| 486 | } | ||
| 487 | it = gotos.erase(it); | ||
| 488 | continue; | ||
| 489 | } | ||
| 490 | it++; | ||
| 491 | } | ||
| 492 | if (full_decompile) { | ||
| 493 | for (const ASTNode& label : labels) { | ||
| 494 | auto& manager = label->GetManager(); | ||
| 495 | manager.Remove(label); | ||
| 496 | } | ||
| 497 | labels.clear(); | ||
| 498 | } else { | ||
| 499 | auto it = labels.begin(); | ||
| 500 | while (it != labels.end()) { | ||
| 501 | bool can_remove = true; | ||
| 502 | ASTNode label = *it; | ||
| 503 | for (const ASTNode& goto_node : gotos) { | ||
| 504 | const auto label_index = goto_node->GetGotoLabel(); | ||
| 505 | if (!label_index) { | ||
| 506 | return; | ||
| 507 | } | ||
| 508 | ASTNode& glabel = labels[*label_index]; | ||
| 509 | if (glabel == label) { | ||
| 510 | can_remove = false; | ||
| 511 | break; | ||
| 512 | } | ||
| 513 | } | ||
| 514 | if (can_remove) { | ||
| 515 | label->MarkLabelUnused(); | ||
| 516 | } | ||
| 517 | } | ||
| 518 | } | ||
| 519 | } | ||
| 520 | |||
| 521 | bool ASTManager::IsBackwardsJump(ASTNode goto_node, ASTNode label_node) const { | ||
| 522 | u32 goto_level = goto_node->GetLevel(); | ||
| 523 | u32 label_level = label_node->GetLevel(); | ||
| 524 | while (goto_level > label_level) { | ||
| 525 | goto_level--; | ||
| 526 | goto_node = goto_node->GetParent(); | ||
| 527 | } | ||
| 528 | while (label_level > goto_level) { | ||
| 529 | label_level--; | ||
| 530 | label_node = label_node->GetParent(); | ||
| 531 | } | ||
| 532 | while (goto_node->GetParent() != label_node->GetParent()) { | ||
| 533 | goto_node = goto_node->GetParent(); | ||
| 534 | label_node = label_node->GetParent(); | ||
| 535 | } | ||
| 536 | ASTNode current = goto_node->GetPrevious(); | ||
| 537 | while (current) { | ||
| 538 | if (current == label_node) { | ||
| 539 | return true; | ||
| 540 | } | ||
| 541 | current = current->GetPrevious(); | ||
| 542 | } | ||
| 543 | return false; | ||
| 544 | } | ||
| 545 | |||
| 546 | bool ASTManager::IndirectlyRelated(ASTNode first, ASTNode second) { | ||
| 547 | return !(first->GetParent() == second->GetParent() || DirectlyRelated(first, second)); | ||
| 548 | } | ||
| 549 | |||
| 550 | bool ASTManager::DirectlyRelated(ASTNode first, ASTNode second) { | ||
| 551 | if (first->GetParent() == second->GetParent()) { | ||
| 552 | return false; | ||
| 553 | } | ||
| 554 | const u32 first_level = first->GetLevel(); | ||
| 555 | const u32 second_level = second->GetLevel(); | ||
| 556 | u32 min_level; | ||
| 557 | u32 max_level; | ||
| 558 | ASTNode max; | ||
| 559 | ASTNode min; | ||
| 560 | if (first_level > second_level) { | ||
| 561 | min_level = second_level; | ||
| 562 | min = second; | ||
| 563 | max_level = first_level; | ||
| 564 | max = first; | ||
| 565 | } else { | ||
| 566 | min_level = first_level; | ||
| 567 | min = first; | ||
| 568 | max_level = second_level; | ||
| 569 | max = second; | ||
| 570 | } | ||
| 571 | |||
| 572 | while (max_level > min_level) { | ||
| 573 | max_level--; | ||
| 574 | max = max->GetParent(); | ||
| 575 | } | ||
| 576 | |||
| 577 | return min->GetParent() == max->GetParent(); | ||
| 578 | } | ||
| 579 | |||
| 580 | void ASTManager::ShowCurrentState(std::string state) { | ||
| 581 | LOG_CRITICAL(HW_GPU, "\nState {}:\n\n{}\n", state, Print()); | ||
| 582 | SanityCheck(); | ||
| 583 | } | ||
| 584 | |||
| 585 | void ASTManager::SanityCheck() { | ||
| 586 | for (auto& label : labels) { | ||
| 587 | if (!label->GetParent()) { | ||
| 588 | LOG_CRITICAL(HW_GPU, "Sanity Check Failed"); | ||
| 589 | } | ||
| 590 | } | ||
| 591 | } | ||
| 592 | |||
| 593 | void ASTManager::EncloseDoWhile(ASTNode goto_node, ASTNode label) { | ||
| 594 | ASTZipper& zipper = goto_node->GetManager(); | ||
| 595 | const ASTNode loop_start = label->GetNext(); | ||
| 596 | if (loop_start == goto_node) { | ||
| 597 | zipper.Remove(goto_node); | ||
| 598 | return; | ||
| 599 | } | ||
| 600 | const ASTNode parent = label->GetParent(); | ||
| 601 | const Expr condition = goto_node->GetGotoCondition(); | ||
| 602 | zipper.DetachSegment(loop_start, goto_node); | ||
| 603 | const ASTNode do_while_node = ASTBase::Make<ASTDoWhile>(parent, condition); | ||
| 604 | ASTZipper* sub_zipper = do_while_node->GetSubNodes(); | ||
| 605 | sub_zipper->Init(loop_start, do_while_node); | ||
| 606 | zipper.InsertAfter(do_while_node, label); | ||
| 607 | sub_zipper->Remove(goto_node); | ||
| 608 | } | ||
| 609 | |||
| 610 | void ASTManager::EncloseIfThen(ASTNode goto_node, ASTNode label) { | ||
| 611 | ASTZipper& zipper = goto_node->GetManager(); | ||
| 612 | const ASTNode if_end = label->GetPrevious(); | ||
| 613 | if (if_end == goto_node) { | ||
| 614 | zipper.Remove(goto_node); | ||
| 615 | return; | ||
| 616 | } | ||
| 617 | const ASTNode prev = goto_node->GetPrevious(); | ||
| 618 | const Expr condition = goto_node->GetGotoCondition(); | ||
| 619 | bool do_else = false; | ||
| 620 | if (!disable_else_derivation && prev->IsIfThen()) { | ||
| 621 | const Expr if_condition = prev->GetIfCondition(); | ||
| 622 | do_else = ExprAreEqual(if_condition, condition); | ||
| 623 | } | ||
| 624 | const ASTNode parent = label->GetParent(); | ||
| 625 | zipper.DetachSegment(goto_node, if_end); | ||
| 626 | ASTNode if_node; | ||
| 627 | if (do_else) { | ||
| 628 | if_node = ASTBase::Make<ASTIfElse>(parent); | ||
| 629 | } else { | ||
| 630 | Expr neg_condition = MakeExprNot(condition); | ||
| 631 | if_node = ASTBase::Make<ASTIfThen>(parent, neg_condition); | ||
| 632 | } | ||
| 633 | ASTZipper* sub_zipper = if_node->GetSubNodes(); | ||
| 634 | sub_zipper->Init(goto_node, if_node); | ||
| 635 | zipper.InsertAfter(if_node, prev); | ||
| 636 | sub_zipper->Remove(goto_node); | ||
| 637 | } | ||
| 638 | |||
| 639 | void ASTManager::MoveOutward(ASTNode goto_node) { | ||
| 640 | ASTZipper& zipper = goto_node->GetManager(); | ||
| 641 | const ASTNode parent = goto_node->GetParent(); | ||
| 642 | ASTZipper& zipper2 = parent->GetManager(); | ||
| 643 | const ASTNode grandpa = parent->GetParent(); | ||
| 644 | const bool is_loop = parent->IsLoop(); | ||
| 645 | const bool is_else = parent->IsIfElse(); | ||
| 646 | const bool is_if = parent->IsIfThen(); | ||
| 647 | |||
| 648 | const ASTNode prev = goto_node->GetPrevious(); | ||
| 649 | const ASTNode post = goto_node->GetNext(); | ||
| 650 | |||
| 651 | const Expr condition = goto_node->GetGotoCondition(); | ||
| 652 | zipper.DetachSingle(goto_node); | ||
| 653 | if (is_loop) { | ||
| 654 | const u32 var_index = NewVariable(); | ||
| 655 | const Expr var_condition = MakeExpr<ExprVar>(var_index); | ||
| 656 | const ASTNode var_node = ASTBase::Make<ASTVarSet>(parent, var_index, condition); | ||
| 657 | const ASTNode var_node_init = ASTBase::Make<ASTVarSet>(parent, var_index, false_condition); | ||
| 658 | zipper2.InsertBefore(var_node_init, parent); | ||
| 659 | zipper.InsertAfter(var_node, prev); | ||
| 660 | goto_node->SetGotoCondition(var_condition); | ||
| 661 | const ASTNode break_node = ASTBase::Make<ASTBreak>(parent, var_condition); | ||
| 662 | zipper.InsertAfter(break_node, var_node); | ||
| 663 | } else if (is_if || is_else) { | ||
| 664 | const u32 var_index = NewVariable(); | ||
| 665 | const Expr var_condition = MakeExpr<ExprVar>(var_index); | ||
| 666 | const ASTNode var_node = ASTBase::Make<ASTVarSet>(parent, var_index, condition); | ||
| 667 | const ASTNode var_node_init = ASTBase::Make<ASTVarSet>(parent, var_index, false_condition); | ||
| 668 | if (is_if) { | ||
| 669 | zipper2.InsertBefore(var_node_init, parent); | ||
| 670 | } else { | ||
| 671 | zipper2.InsertBefore(var_node_init, parent->GetPrevious()); | ||
| 672 | } | ||
| 673 | zipper.InsertAfter(var_node, prev); | ||
| 674 | goto_node->SetGotoCondition(var_condition); | ||
| 675 | if (post) { | ||
| 676 | zipper.DetachTail(post); | ||
| 677 | const ASTNode if_node = ASTBase::Make<ASTIfThen>(parent, MakeExprNot(var_condition)); | ||
| 678 | ASTZipper* sub_zipper = if_node->GetSubNodes(); | ||
| 679 | sub_zipper->Init(post, if_node); | ||
| 680 | zipper.InsertAfter(if_node, var_node); | ||
| 681 | } | ||
| 682 | } else { | ||
| 683 | UNREACHABLE(); | ||
| 684 | } | ||
| 685 | const ASTNode next = parent->GetNext(); | ||
| 686 | if (is_if && next && next->IsIfElse()) { | ||
| 687 | zipper2.InsertAfter(goto_node, next); | ||
| 688 | goto_node->SetParent(grandpa); | ||
| 689 | return; | ||
| 690 | } | ||
| 691 | zipper2.InsertAfter(goto_node, parent); | ||
| 692 | goto_node->SetParent(grandpa); | ||
| 693 | } | ||
| 694 | |||
| 695 | class ASTClearer { | ||
| 696 | public: | ||
| 697 | ASTClearer() = default; | ||
| 698 | |||
| 699 | void operator()(ASTProgram& ast) { | ||
| 700 | ASTNode current = ast.nodes.GetFirst(); | ||
| 701 | while (current) { | ||
| 702 | Visit(current); | ||
| 703 | current = current->GetNext(); | ||
| 704 | } | ||
| 705 | } | ||
| 706 | |||
| 707 | void operator()(ASTIfThen& ast) { | ||
| 708 | ASTNode current = ast.nodes.GetFirst(); | ||
| 709 | while (current) { | ||
| 710 | Visit(current); | ||
| 711 | current = current->GetNext(); | ||
| 712 | } | ||
| 713 | } | ||
| 714 | |||
| 715 | void operator()(ASTIfElse& ast) { | ||
| 716 | ASTNode current = ast.nodes.GetFirst(); | ||
| 717 | while (current) { | ||
| 718 | Visit(current); | ||
| 719 | current = current->GetNext(); | ||
| 720 | } | ||
| 721 | } | ||
| 722 | |||
| 723 | void operator()(ASTBlockEncoded& ast) {} | ||
| 724 | |||
| 725 | void operator()(ASTBlockDecoded& ast) { | ||
| 726 | ast.nodes.clear(); | ||
| 727 | } | ||
| 728 | |||
| 729 | void operator()(ASTVarSet& ast) {} | ||
| 730 | |||
| 731 | void operator()(ASTLabel& ast) {} | ||
| 732 | |||
| 733 | void operator()(ASTGoto& ast) {} | ||
| 734 | |||
| 735 | void operator()(ASTDoWhile& ast) { | ||
| 736 | ASTNode current = ast.nodes.GetFirst(); | ||
| 737 | while (current) { | ||
| 738 | Visit(current); | ||
| 739 | current = current->GetNext(); | ||
| 740 | } | ||
| 741 | } | ||
| 742 | |||
| 743 | void operator()(ASTReturn& ast) {} | ||
| 744 | |||
| 745 | void operator()(ASTBreak& ast) {} | ||
| 746 | |||
| 747 | void Visit(ASTNode& node) { | ||
| 748 | std::visit(*this, *node->GetInnerData()); | ||
| 749 | node->Clear(); | ||
| 750 | } | ||
| 751 | }; | ||
| 752 | |||
| 753 | void ASTManager::Clear() { | ||
| 754 | if (!main_node) { | ||
| 755 | return; | ||
| 756 | } | ||
| 757 | ASTClearer clearer{}; | ||
| 758 | clearer.Visit(main_node); | ||
| 759 | main_node.reset(); | ||
| 760 | program = nullptr; | ||
| 761 | labels_map.clear(); | ||
| 762 | labels.clear(); | ||
| 763 | gotos.clear(); | ||
| 764 | } | ||
| 765 | |||
| 766 | } // namespace VideoCommon::Shader | ||
diff --git a/src/video_core/shader/ast.h b/src/video_core/shader/ast.h new file mode 100644 index 000000000..ba234138e --- /dev/null +++ b/src/video_core/shader/ast.h | |||
| @@ -0,0 +1,391 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <functional> | ||
| 8 | #include <list> | ||
| 9 | #include <memory> | ||
| 10 | #include <optional> | ||
| 11 | #include <string> | ||
| 12 | #include <unordered_map> | ||
| 13 | #include <vector> | ||
| 14 | |||
| 15 | #include "video_core/shader/expr.h" | ||
| 16 | #include "video_core/shader/node.h" | ||
| 17 | |||
| 18 | namespace VideoCommon::Shader { | ||
| 19 | |||
| 20 | class ASTBase; | ||
| 21 | class ASTProgram; | ||
| 22 | class ASTIfThen; | ||
| 23 | class ASTIfElse; | ||
| 24 | class ASTBlockEncoded; | ||
| 25 | class ASTBlockDecoded; | ||
| 26 | class ASTVarSet; | ||
| 27 | class ASTGoto; | ||
| 28 | class ASTLabel; | ||
| 29 | class ASTDoWhile; | ||
| 30 | class ASTReturn; | ||
| 31 | class ASTBreak; | ||
| 32 | |||
| 33 | using ASTData = std::variant<ASTProgram, ASTIfThen, ASTIfElse, ASTBlockEncoded, ASTBlockDecoded, | ||
| 34 | ASTVarSet, ASTGoto, ASTLabel, ASTDoWhile, ASTReturn, ASTBreak>; | ||
| 35 | |||
| 36 | using ASTNode = std::shared_ptr<ASTBase>; | ||
| 37 | |||
| 38 | enum class ASTZipperType : u32 { | ||
| 39 | Program, | ||
| 40 | IfThen, | ||
| 41 | IfElse, | ||
| 42 | Loop, | ||
| 43 | }; | ||
| 44 | |||
| 45 | class ASTZipper final { | ||
| 46 | public: | ||
| 47 | explicit ASTZipper(); | ||
| 48 | |||
| 49 | void Init(ASTNode first, ASTNode parent); | ||
| 50 | |||
| 51 | ASTNode GetFirst() { | ||
| 52 | return first; | ||
| 53 | } | ||
| 54 | |||
| 55 | ASTNode GetLast() { | ||
| 56 | return last; | ||
| 57 | } | ||
| 58 | |||
| 59 | void PushBack(ASTNode new_node); | ||
| 60 | void PushFront(ASTNode new_node); | ||
| 61 | void InsertAfter(ASTNode new_node, ASTNode at_node); | ||
| 62 | void InsertBefore(ASTNode new_node, ASTNode at_node); | ||
| 63 | void DetachTail(ASTNode node); | ||
| 64 | void DetachSingle(ASTNode node); | ||
| 65 | void DetachSegment(ASTNode start, ASTNode end); | ||
| 66 | void Remove(ASTNode node); | ||
| 67 | |||
| 68 | ASTNode first{}; | ||
| 69 | ASTNode last{}; | ||
| 70 | }; | ||
| 71 | |||
| 72 | class ASTProgram { | ||
| 73 | public: | ||
| 74 | explicit ASTProgram() = default; | ||
| 75 | ASTZipper nodes{}; | ||
| 76 | }; | ||
| 77 | |||
| 78 | class ASTIfThen { | ||
| 79 | public: | ||
| 80 | explicit ASTIfThen(Expr condition) : condition(condition) {} | ||
| 81 | Expr condition; | ||
| 82 | ASTZipper nodes{}; | ||
| 83 | }; | ||
| 84 | |||
| 85 | class ASTIfElse { | ||
| 86 | public: | ||
| 87 | explicit ASTIfElse() = default; | ||
| 88 | ASTZipper nodes{}; | ||
| 89 | }; | ||
| 90 | |||
| 91 | class ASTBlockEncoded { | ||
| 92 | public: | ||
| 93 | explicit ASTBlockEncoded(u32 start, u32 end) : start{start}, end{end} {} | ||
| 94 | u32 start; | ||
| 95 | u32 end; | ||
| 96 | }; | ||
| 97 | |||
| 98 | class ASTBlockDecoded { | ||
| 99 | public: | ||
| 100 | explicit ASTBlockDecoded(NodeBlock&& new_nodes) : nodes(std::move(new_nodes)) {} | ||
| 101 | NodeBlock nodes; | ||
| 102 | }; | ||
| 103 | |||
| 104 | class ASTVarSet { | ||
| 105 | public: | ||
| 106 | explicit ASTVarSet(u32 index, Expr condition) : index{index}, condition{condition} {} | ||
| 107 | u32 index; | ||
| 108 | Expr condition; | ||
| 109 | }; | ||
| 110 | |||
| 111 | class ASTLabel { | ||
| 112 | public: | ||
| 113 | explicit ASTLabel(u32 index) : index{index} {} | ||
| 114 | u32 index; | ||
| 115 | bool unused{}; | ||
| 116 | }; | ||
| 117 | |||
| 118 | class ASTGoto { | ||
| 119 | public: | ||
| 120 | explicit ASTGoto(Expr condition, u32 label) : condition{condition}, label{label} {} | ||
| 121 | Expr condition; | ||
| 122 | u32 label; | ||
| 123 | }; | ||
| 124 | |||
| 125 | class ASTDoWhile { | ||
| 126 | public: | ||
| 127 | explicit ASTDoWhile(Expr condition) : condition(condition) {} | ||
| 128 | Expr condition; | ||
| 129 | ASTZipper nodes{}; | ||
| 130 | }; | ||
| 131 | |||
| 132 | class ASTReturn { | ||
| 133 | public: | ||
| 134 | explicit ASTReturn(Expr condition, bool kills) : condition{condition}, kills{kills} {} | ||
| 135 | Expr condition; | ||
| 136 | bool kills; | ||
| 137 | }; | ||
| 138 | |||
| 139 | class ASTBreak { | ||
| 140 | public: | ||
| 141 | explicit ASTBreak(Expr condition) : condition{condition} {} | ||
| 142 | Expr condition; | ||
| 143 | }; | ||
| 144 | |||
| 145 | class ASTBase { | ||
| 146 | public: | ||
| 147 | explicit ASTBase(ASTNode parent, ASTData data) : parent{parent}, data{data} {} | ||
| 148 | |||
| 149 | template <class U, class... Args> | ||
| 150 | static ASTNode Make(ASTNode parent, Args&&... args) { | ||
| 151 | return std::make_shared<ASTBase>(parent, ASTData(U(std::forward<Args>(args)...))); | ||
| 152 | } | ||
| 153 | |||
| 154 | void SetParent(ASTNode new_parent) { | ||
| 155 | parent = new_parent; | ||
| 156 | } | ||
| 157 | |||
| 158 | ASTNode& GetParent() { | ||
| 159 | return parent; | ||
| 160 | } | ||
| 161 | |||
| 162 | const ASTNode& GetParent() const { | ||
| 163 | return parent; | ||
| 164 | } | ||
| 165 | |||
| 166 | u32 GetLevel() const { | ||
| 167 | u32 level = 0; | ||
| 168 | auto next_parent = parent; | ||
| 169 | while (next_parent) { | ||
| 170 | next_parent = next_parent->GetParent(); | ||
| 171 | level++; | ||
| 172 | } | ||
| 173 | return level; | ||
| 174 | } | ||
| 175 | |||
| 176 | ASTData* GetInnerData() { | ||
| 177 | return &data; | ||
| 178 | } | ||
| 179 | |||
| 180 | ASTNode GetNext() const { | ||
| 181 | return next; | ||
| 182 | } | ||
| 183 | |||
| 184 | ASTNode GetPrevious() const { | ||
| 185 | return previous; | ||
| 186 | } | ||
| 187 | |||
| 188 | ASTZipper& GetManager() { | ||
| 189 | return *manager; | ||
| 190 | } | ||
| 191 | |||
| 192 | std::optional<u32> GetGotoLabel() const { | ||
| 193 | auto inner = std::get_if<ASTGoto>(&data); | ||
| 194 | if (inner) { | ||
| 195 | return {inner->label}; | ||
| 196 | } | ||
| 197 | return {}; | ||
| 198 | } | ||
| 199 | |||
| 200 | Expr GetGotoCondition() const { | ||
| 201 | auto inner = std::get_if<ASTGoto>(&data); | ||
| 202 | if (inner) { | ||
| 203 | return inner->condition; | ||
| 204 | } | ||
| 205 | return nullptr; | ||
| 206 | } | ||
| 207 | |||
| 208 | void MarkLabelUnused() { | ||
| 209 | auto inner = std::get_if<ASTLabel>(&data); | ||
| 210 | if (inner) { | ||
| 211 | inner->unused = true; | ||
| 212 | } | ||
| 213 | } | ||
| 214 | |||
| 215 | bool IsLabelUnused() const { | ||
| 216 | auto inner = std::get_if<ASTLabel>(&data); | ||
| 217 | if (inner) { | ||
| 218 | return inner->unused; | ||
| 219 | } | ||
| 220 | return true; | ||
| 221 | } | ||
| 222 | |||
| 223 | std::optional<u32> GetLabelIndex() const { | ||
| 224 | auto inner = std::get_if<ASTLabel>(&data); | ||
| 225 | if (inner) { | ||
| 226 | return {inner->index}; | ||
| 227 | } | ||
| 228 | return {}; | ||
| 229 | } | ||
| 230 | |||
| 231 | Expr GetIfCondition() const { | ||
| 232 | auto inner = std::get_if<ASTIfThen>(&data); | ||
| 233 | if (inner) { | ||
| 234 | return inner->condition; | ||
| 235 | } | ||
| 236 | return nullptr; | ||
| 237 | } | ||
| 238 | |||
| 239 | void SetGotoCondition(Expr new_condition) { | ||
| 240 | auto inner = std::get_if<ASTGoto>(&data); | ||
| 241 | if (inner) { | ||
| 242 | inner->condition = new_condition; | ||
| 243 | } | ||
| 244 | } | ||
| 245 | |||
| 246 | bool IsIfThen() const { | ||
| 247 | return std::holds_alternative<ASTIfThen>(data); | ||
| 248 | } | ||
| 249 | |||
| 250 | bool IsIfElse() const { | ||
| 251 | return std::holds_alternative<ASTIfElse>(data); | ||
| 252 | } | ||
| 253 | |||
| 254 | bool IsBlockEncoded() const { | ||
| 255 | return std::holds_alternative<ASTBlockEncoded>(data); | ||
| 256 | } | ||
| 257 | |||
| 258 | void TransformBlockEncoded(NodeBlock&& nodes) { | ||
| 259 | data = ASTBlockDecoded(std::move(nodes)); | ||
| 260 | } | ||
| 261 | |||
| 262 | bool IsLoop() const { | ||
| 263 | return std::holds_alternative<ASTDoWhile>(data); | ||
| 264 | } | ||
| 265 | |||
| 266 | ASTZipper* GetSubNodes() { | ||
| 267 | if (std::holds_alternative<ASTProgram>(data)) { | ||
| 268 | return &std::get_if<ASTProgram>(&data)->nodes; | ||
| 269 | } | ||
| 270 | if (std::holds_alternative<ASTIfThen>(data)) { | ||
| 271 | return &std::get_if<ASTIfThen>(&data)->nodes; | ||
| 272 | } | ||
| 273 | if (std::holds_alternative<ASTIfElse>(data)) { | ||
| 274 | return &std::get_if<ASTIfElse>(&data)->nodes; | ||
| 275 | } | ||
| 276 | if (std::holds_alternative<ASTDoWhile>(data)) { | ||
| 277 | return &std::get_if<ASTDoWhile>(&data)->nodes; | ||
| 278 | } | ||
| 279 | return nullptr; | ||
| 280 | } | ||
| 281 | |||
| 282 | void Clear() { | ||
| 283 | next.reset(); | ||
| 284 | previous.reset(); | ||
| 285 | parent.reset(); | ||
| 286 | manager = nullptr; | ||
| 287 | } | ||
| 288 | |||
| 289 | private: | ||
| 290 | friend class ASTZipper; | ||
| 291 | |||
| 292 | ASTData data; | ||
| 293 | ASTNode parent{}; | ||
| 294 | ASTNode next{}; | ||
| 295 | ASTNode previous{}; | ||
| 296 | ASTZipper* manager{}; | ||
| 297 | }; | ||
| 298 | |||
| 299 | class ASTManager final { | ||
| 300 | public: | ||
| 301 | ASTManager(bool full_decompile, bool disable_else_derivation); | ||
| 302 | ~ASTManager(); | ||
| 303 | |||
| 304 | ASTManager(const ASTManager& o) = delete; | ||
| 305 | ASTManager& operator=(const ASTManager& other) = delete; | ||
| 306 | |||
| 307 | ASTManager(ASTManager&& other) noexcept; | ||
| 308 | ASTManager& operator=(ASTManager&& other) noexcept; | ||
| 309 | |||
| 310 | void Init(); | ||
| 311 | |||
| 312 | void DeclareLabel(u32 address); | ||
| 313 | |||
| 314 | void InsertLabel(u32 address); | ||
| 315 | |||
| 316 | void InsertGoto(Expr condition, u32 address); | ||
| 317 | |||
| 318 | void InsertBlock(u32 start_address, u32 end_address); | ||
| 319 | |||
| 320 | void InsertReturn(Expr condition, bool kills); | ||
| 321 | |||
| 322 | std::string Print(); | ||
| 323 | |||
| 324 | void Decompile(); | ||
| 325 | |||
| 326 | void ShowCurrentState(std::string state); | ||
| 327 | |||
| 328 | void SanityCheck(); | ||
| 329 | |||
| 330 | void Clear(); | ||
| 331 | |||
| 332 | bool IsFullyDecompiled() const { | ||
| 333 | if (full_decompile) { | ||
| 334 | return gotos.size() == 0; | ||
| 335 | } else { | ||
| 336 | for (ASTNode goto_node : gotos) { | ||
| 337 | auto label_index = goto_node->GetGotoLabel(); | ||
| 338 | if (!label_index) { | ||
| 339 | return false; | ||
| 340 | } | ||
| 341 | ASTNode glabel = labels[*label_index]; | ||
| 342 | if (IsBackwardsJump(goto_node, glabel)) { | ||
| 343 | return false; | ||
| 344 | } | ||
| 345 | } | ||
| 346 | return true; | ||
| 347 | } | ||
| 348 | } | ||
| 349 | |||
| 350 | ASTNode GetProgram() const { | ||
| 351 | return main_node; | ||
| 352 | } | ||
| 353 | |||
| 354 | u32 GetVariables() const { | ||
| 355 | return variables; | ||
| 356 | } | ||
| 357 | |||
| 358 | const std::vector<ASTNode>& GetLabels() const { | ||
| 359 | return labels; | ||
| 360 | } | ||
| 361 | |||
| 362 | private: | ||
| 363 | bool IsBackwardsJump(ASTNode goto_node, ASTNode label_node) const; | ||
| 364 | |||
| 365 | bool IndirectlyRelated(ASTNode first, ASTNode second); | ||
| 366 | |||
| 367 | bool DirectlyRelated(ASTNode first, ASTNode second); | ||
| 368 | |||
| 369 | void EncloseDoWhile(ASTNode goto_node, ASTNode label); | ||
| 370 | |||
| 371 | void EncloseIfThen(ASTNode goto_node, ASTNode label); | ||
| 372 | |||
| 373 | void MoveOutward(ASTNode goto_node); | ||
| 374 | |||
| 375 | u32 NewVariable() { | ||
| 376 | return variables++; | ||
| 377 | } | ||
| 378 | |||
| 379 | bool full_decompile{}; | ||
| 380 | bool disable_else_derivation{}; | ||
| 381 | std::unordered_map<u32, u32> labels_map{}; | ||
| 382 | u32 labels_count{}; | ||
| 383 | std::vector<ASTNode> labels{}; | ||
| 384 | std::list<ASTNode> gotos{}; | ||
| 385 | u32 variables{}; | ||
| 386 | ASTProgram* program{}; | ||
| 387 | ASTNode main_node{}; | ||
| 388 | Expr false_condition{}; | ||
| 389 | }; | ||
| 390 | |||
| 391 | } // namespace VideoCommon::Shader | ||
diff --git a/src/video_core/shader/compiler_settings.cpp b/src/video_core/shader/compiler_settings.cpp new file mode 100644 index 000000000..cddcbd4f0 --- /dev/null +++ b/src/video_core/shader/compiler_settings.cpp | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "video_core/shader/compiler_settings.h" | ||
| 6 | |||
| 7 | namespace VideoCommon::Shader { | ||
| 8 | |||
| 9 | std::string CompileDepthAsString(const CompileDepth cd) { | ||
| 10 | switch (cd) { | ||
| 11 | case CompileDepth::BruteForce: | ||
| 12 | return "Brute Force Compile"; | ||
| 13 | case CompileDepth::FlowStack: | ||
| 14 | return "Simple Flow Stack Mode"; | ||
| 15 | case CompileDepth::NoFlowStack: | ||
| 16 | return "Remove Flow Stack"; | ||
| 17 | case CompileDepth::DecompileBackwards: | ||
| 18 | return "Decompile Backward Jumps"; | ||
| 19 | case CompileDepth::FullDecompile: | ||
| 20 | return "Full Decompilation"; | ||
| 21 | default: | ||
| 22 | return "Unknown Compiler Process"; | ||
| 23 | } | ||
| 24 | } | ||
| 25 | |||
| 26 | } // namespace VideoCommon::Shader | ||
diff --git a/src/video_core/shader/compiler_settings.h b/src/video_core/shader/compiler_settings.h new file mode 100644 index 000000000..916018c01 --- /dev/null +++ b/src/video_core/shader/compiler_settings.h | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "video_core/engines/shader_bytecode.h" | ||
| 8 | |||
| 9 | namespace VideoCommon::Shader { | ||
| 10 | |||
| 11 | enum class CompileDepth : u32 { | ||
| 12 | BruteForce = 0, | ||
| 13 | FlowStack = 1, | ||
| 14 | NoFlowStack = 2, | ||
| 15 | DecompileBackwards = 3, | ||
| 16 | FullDecompile = 4, | ||
| 17 | }; | ||
| 18 | |||
| 19 | std::string CompileDepthAsString(CompileDepth cd); | ||
| 20 | |||
| 21 | struct CompilerSettings { | ||
| 22 | CompileDepth depth{CompileDepth::NoFlowStack}; | ||
| 23 | bool disable_else_derivation{true}; | ||
| 24 | }; | ||
| 25 | |||
| 26 | } // namespace VideoCommon::Shader | ||
diff --git a/src/video_core/shader/control_flow.cpp b/src/video_core/shader/control_flow.cpp index ec3a76690..3c3a41ba6 100644 --- a/src/video_core/shader/control_flow.cpp +++ b/src/video_core/shader/control_flow.cpp | |||
| @@ -4,13 +4,14 @@ | |||
| 4 | 4 | ||
| 5 | #include <list> | 5 | #include <list> |
| 6 | #include <map> | 6 | #include <map> |
| 7 | #include <set> | ||
| 7 | #include <stack> | 8 | #include <stack> |
| 8 | #include <unordered_map> | 9 | #include <unordered_map> |
| 9 | #include <unordered_set> | ||
| 10 | #include <vector> | 10 | #include <vector> |
| 11 | 11 | ||
| 12 | #include "common/assert.h" | 12 | #include "common/assert.h" |
| 13 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 14 | #include "video_core/shader/ast.h" | ||
| 14 | #include "video_core/shader/control_flow.h" | 15 | #include "video_core/shader/control_flow.h" |
| 15 | #include "video_core/shader/shader_ir.h" | 16 | #include "video_core/shader/shader_ir.h" |
| 16 | 17 | ||
| @@ -64,12 +65,13 @@ struct CFGRebuildState { | |||
| 64 | std::list<u32> inspect_queries{}; | 65 | std::list<u32> inspect_queries{}; |
| 65 | std::list<Query> queries{}; | 66 | std::list<Query> queries{}; |
| 66 | std::unordered_map<u32, u32> registered{}; | 67 | std::unordered_map<u32, u32> registered{}; |
| 67 | std::unordered_set<u32> labels{}; | 68 | std::set<u32> labels{}; |
| 68 | std::map<u32, u32> ssy_labels{}; | 69 | std::map<u32, u32> ssy_labels{}; |
| 69 | std::map<u32, u32> pbk_labels{}; | 70 | std::map<u32, u32> pbk_labels{}; |
| 70 | std::unordered_map<u32, BlockStack> stacks{}; | 71 | std::unordered_map<u32, BlockStack> stacks{}; |
| 71 | const ProgramCode& program_code; | 72 | const ProgramCode& program_code; |
| 72 | const std::size_t program_size; | 73 | const std::size_t program_size; |
| 74 | ASTManager* manager; | ||
| 73 | }; | 75 | }; |
| 74 | 76 | ||
| 75 | enum class BlockCollision : u32 { None, Found, Inside }; | 77 | enum class BlockCollision : u32 { None, Found, Inside }; |
| @@ -415,38 +417,132 @@ bool TryQuery(CFGRebuildState& state) { | |||
| 415 | } | 417 | } |
| 416 | } // Anonymous namespace | 418 | } // Anonymous namespace |
| 417 | 419 | ||
| 418 | std::optional<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, | 420 | void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch) { |
| 419 | std::size_t program_size, u32 start_address) { | 421 | const auto get_expr = ([&](const Condition& cond) -> Expr { |
| 420 | CFGRebuildState state{program_code, program_size, start_address}; | 422 | Expr result{}; |
| 423 | if (cond.cc != ConditionCode::T) { | ||
| 424 | result = MakeExpr<ExprCondCode>(cond.cc); | ||
| 425 | } | ||
| 426 | if (cond.predicate != Pred::UnusedIndex) { | ||
| 427 | u32 pred = static_cast<u32>(cond.predicate); | ||
| 428 | bool negate = false; | ||
| 429 | if (pred > 7) { | ||
| 430 | negate = true; | ||
| 431 | pred -= 8; | ||
| 432 | } | ||
| 433 | Expr extra = MakeExpr<ExprPredicate>(pred); | ||
| 434 | if (negate) { | ||
| 435 | extra = MakeExpr<ExprNot>(extra); | ||
| 436 | } | ||
| 437 | if (result) { | ||
| 438 | return MakeExpr<ExprAnd>(extra, result); | ||
| 439 | } | ||
| 440 | return extra; | ||
| 441 | } | ||
| 442 | if (result) { | ||
| 443 | return result; | ||
| 444 | } | ||
| 445 | return MakeExpr<ExprBoolean>(true); | ||
| 446 | }); | ||
| 447 | if (branch.address < 0) { | ||
| 448 | if (branch.kill) { | ||
| 449 | mm.InsertReturn(get_expr(branch.condition), true); | ||
| 450 | return; | ||
| 451 | } | ||
| 452 | mm.InsertReturn(get_expr(branch.condition), false); | ||
| 453 | return; | ||
| 454 | } | ||
| 455 | mm.InsertGoto(get_expr(branch.condition), branch.address); | ||
| 456 | } | ||
| 421 | 457 | ||
| 458 | void DecompileShader(CFGRebuildState& state) { | ||
| 459 | state.manager->Init(); | ||
| 460 | for (auto label : state.labels) { | ||
| 461 | state.manager->DeclareLabel(label); | ||
| 462 | } | ||
| 463 | for (auto& block : state.block_info) { | ||
| 464 | if (state.labels.count(block.start) != 0) { | ||
| 465 | state.manager->InsertLabel(block.start); | ||
| 466 | } | ||
| 467 | u32 end = block.branch.ignore ? block.end + 1 : block.end; | ||
| 468 | state.manager->InsertBlock(block.start, end); | ||
| 469 | if (!block.branch.ignore) { | ||
| 470 | InsertBranch(*state.manager, block.branch); | ||
| 471 | } | ||
| 472 | } | ||
| 473 | state.manager->Decompile(); | ||
| 474 | } | ||
| 475 | |||
| 476 | std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 program_size, | ||
| 477 | u32 start_address, | ||
| 478 | const CompilerSettings& settings) { | ||
| 479 | auto result_out = std::make_unique<ShaderCharacteristics>(); | ||
| 480 | if (settings.depth == CompileDepth::BruteForce) { | ||
| 481 | result_out->settings.depth = CompileDepth::BruteForce; | ||
| 482 | return std::move(result_out); | ||
| 483 | } | ||
| 484 | |||
| 485 | CFGRebuildState state{program_code, program_size, start_address}; | ||
| 422 | // Inspect Code and generate blocks | 486 | // Inspect Code and generate blocks |
| 423 | state.labels.clear(); | 487 | state.labels.clear(); |
| 424 | state.labels.emplace(start_address); | 488 | state.labels.emplace(start_address); |
| 425 | state.inspect_queries.push_back(state.start); | 489 | state.inspect_queries.push_back(state.start); |
| 426 | while (!state.inspect_queries.empty()) { | 490 | while (!state.inspect_queries.empty()) { |
| 427 | if (!TryInspectAddress(state)) { | 491 | if (!TryInspectAddress(state)) { |
| 428 | return {}; | 492 | result_out->settings.depth = CompileDepth::BruteForce; |
| 493 | return std::move(result_out); | ||
| 429 | } | 494 | } |
| 430 | } | 495 | } |
| 431 | 496 | ||
| 432 | // Decompile Stacks | 497 | bool use_flow_stack = true; |
| 433 | state.queries.push_back(Query{state.start, {}, {}}); | 498 | |
| 434 | bool decompiled = true; | 499 | bool decompiled = false; |
| 435 | while (!state.queries.empty()) { | 500 | |
| 436 | if (!TryQuery(state)) { | 501 | if (settings.depth != CompileDepth::FlowStack) { |
| 437 | decompiled = false; | 502 | // Decompile Stacks |
| 438 | break; | 503 | state.queries.push_back(Query{state.start, {}, {}}); |
| 504 | decompiled = true; | ||
| 505 | while (!state.queries.empty()) { | ||
| 506 | if (!TryQuery(state)) { | ||
| 507 | decompiled = false; | ||
| 508 | break; | ||
| 509 | } | ||
| 439 | } | 510 | } |
| 440 | } | 511 | } |
| 441 | 512 | ||
| 513 | use_flow_stack = !decompiled; | ||
| 514 | |||
| 442 | // Sort and organize results | 515 | // Sort and organize results |
| 443 | std::sort(state.block_info.begin(), state.block_info.end(), | 516 | std::sort(state.block_info.begin(), state.block_info.end(), |
| 444 | [](const BlockInfo& a, const BlockInfo& b) { return a.start < b.start; }); | 517 | [](const BlockInfo& a, const BlockInfo& b) -> bool { return a.start < b.start; }); |
| 445 | ShaderCharacteristics result_out{}; | 518 | if (decompiled && settings.depth != CompileDepth::NoFlowStack) { |
| 446 | result_out.decompilable = decompiled; | 519 | ASTManager manager{settings.depth != CompileDepth::DecompileBackwards, |
| 447 | result_out.start = start_address; | 520 | settings.disable_else_derivation}; |
| 448 | result_out.end = start_address; | 521 | state.manager = &manager; |
| 449 | for (const auto& block : state.block_info) { | 522 | DecompileShader(state); |
| 523 | decompiled = state.manager->IsFullyDecompiled(); | ||
| 524 | if (!decompiled) { | ||
| 525 | if (settings.depth == CompileDepth::FullDecompile) { | ||
| 526 | LOG_CRITICAL(HW_GPU, "Failed to remove all the gotos!:"); | ||
| 527 | } else { | ||
| 528 | LOG_CRITICAL(HW_GPU, "Failed to remove all backward gotos!:"); | ||
| 529 | } | ||
| 530 | state.manager->ShowCurrentState("Of Shader"); | ||
| 531 | state.manager->Clear(); | ||
| 532 | } else { | ||
| 533 | auto result_out = std::make_unique<ShaderCharacteristics>(); | ||
| 534 | result_out->start = start_address; | ||
| 535 | result_out->settings.depth = settings.depth; | ||
| 536 | result_out->manager = std::move(manager); | ||
| 537 | result_out->end = state.block_info.back().end + 1; | ||
| 538 | return std::move(result_out); | ||
| 539 | } | ||
| 540 | } | ||
| 541 | result_out->start = start_address; | ||
| 542 | result_out->settings.depth = | ||
| 543 | use_flow_stack ? CompileDepth::FlowStack : CompileDepth::NoFlowStack; | ||
| 544 | result_out->blocks.clear(); | ||
| 545 | for (auto& block : state.block_info) { | ||
| 450 | ShaderBlock new_block{}; | 546 | ShaderBlock new_block{}; |
| 451 | new_block.start = block.start; | 547 | new_block.start = block.start; |
| 452 | new_block.end = block.end; | 548 | new_block.end = block.end; |
| @@ -456,26 +552,24 @@ std::optional<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, | |||
| 456 | new_block.branch.kills = block.branch.kill; | 552 | new_block.branch.kills = block.branch.kill; |
| 457 | new_block.branch.address = block.branch.address; | 553 | new_block.branch.address = block.branch.address; |
| 458 | } | 554 | } |
| 459 | result_out.end = std::max(result_out.end, block.end); | 555 | result_out->end = std::max(result_out->end, block.end); |
| 460 | result_out.blocks.push_back(new_block); | 556 | result_out->blocks.push_back(new_block); |
| 461 | } | 557 | } |
| 462 | if (result_out.decompilable) { | 558 | if (!use_flow_stack) { |
| 463 | result_out.labels = std::move(state.labels); | 559 | result_out->labels = std::move(state.labels); |
| 464 | return {std::move(result_out)}; | 560 | return std::move(result_out); |
| 465 | } | 561 | } |
| 466 | 562 | auto back = result_out->blocks.begin(); | |
| 467 | // If it's not decompilable, merge the unlabelled blocks together | ||
| 468 | auto back = result_out.blocks.begin(); | ||
| 469 | auto next = std::next(back); | 563 | auto next = std::next(back); |
| 470 | while (next != result_out.blocks.end()) { | 564 | while (next != result_out->blocks.end()) { |
| 471 | if (state.labels.count(next->start) == 0 && next->start == back->end + 1) { | 565 | if (state.labels.count(next->start) == 0 && next->start == back->end + 1) { |
| 472 | back->end = next->end; | 566 | back->end = next->end; |
| 473 | next = result_out.blocks.erase(next); | 567 | next = result_out->blocks.erase(next); |
| 474 | continue; | 568 | continue; |
| 475 | } | 569 | } |
| 476 | back = next; | 570 | back = next; |
| 477 | ++next; | 571 | ++next; |
| 478 | } | 572 | } |
| 479 | return {std::move(result_out)}; | 573 | return std::move(result_out); |
| 480 | } | 574 | } |
| 481 | } // namespace VideoCommon::Shader | 575 | } // namespace VideoCommon::Shader |
diff --git a/src/video_core/shader/control_flow.h b/src/video_core/shader/control_flow.h index b0a5e4f8c..74e54a5c7 100644 --- a/src/video_core/shader/control_flow.h +++ b/src/video_core/shader/control_flow.h | |||
| @@ -6,9 +6,11 @@ | |||
| 6 | 6 | ||
| 7 | #include <list> | 7 | #include <list> |
| 8 | #include <optional> | 8 | #include <optional> |
| 9 | #include <unordered_set> | 9 | #include <set> |
| 10 | 10 | ||
| 11 | #include "video_core/engines/shader_bytecode.h" | 11 | #include "video_core/engines/shader_bytecode.h" |
| 12 | #include "video_core/shader/ast.h" | ||
| 13 | #include "video_core/shader/compiler_settings.h" | ||
| 12 | #include "video_core/shader/shader_ir.h" | 14 | #include "video_core/shader/shader_ir.h" |
| 13 | 15 | ||
| 14 | namespace VideoCommon::Shader { | 16 | namespace VideoCommon::Shader { |
| @@ -67,13 +69,15 @@ struct ShaderBlock { | |||
| 67 | 69 | ||
| 68 | struct ShaderCharacteristics { | 70 | struct ShaderCharacteristics { |
| 69 | std::list<ShaderBlock> blocks{}; | 71 | std::list<ShaderBlock> blocks{}; |
| 70 | bool decompilable{}; | 72 | std::set<u32> labels{}; |
| 71 | u32 start{}; | 73 | u32 start{}; |
| 72 | u32 end{}; | 74 | u32 end{}; |
| 73 | std::unordered_set<u32> labels{}; | 75 | ASTManager manager{true, true}; |
| 76 | CompilerSettings settings{}; | ||
| 74 | }; | 77 | }; |
| 75 | 78 | ||
| 76 | std::optional<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, | 79 | std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 program_size, |
| 77 | std::size_t program_size, u32 start_address); | 80 | u32 start_address, |
| 81 | const CompilerSettings& settings); | ||
| 78 | 82 | ||
| 79 | } // namespace VideoCommon::Shader | 83 | } // namespace VideoCommon::Shader |
diff --git a/src/video_core/shader/decode.cpp b/src/video_core/shader/decode.cpp index 47a9fd961..2626b1616 100644 --- a/src/video_core/shader/decode.cpp +++ b/src/video_core/shader/decode.cpp | |||
| @@ -35,58 +35,138 @@ constexpr bool IsSchedInstruction(u32 offset, u32 main_offset) { | |||
| 35 | 35 | ||
| 36 | } // namespace | 36 | } // namespace |
| 37 | 37 | ||
| 38 | class ASTDecoder { | ||
| 39 | public: | ||
| 40 | ASTDecoder(ShaderIR& ir) : ir(ir) {} | ||
| 41 | |||
| 42 | void operator()(ASTProgram& ast) { | ||
| 43 | ASTNode current = ast.nodes.GetFirst(); | ||
| 44 | while (current) { | ||
| 45 | Visit(current); | ||
| 46 | current = current->GetNext(); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | void operator()(ASTIfThen& ast) { | ||
| 51 | ASTNode current = ast.nodes.GetFirst(); | ||
| 52 | while (current) { | ||
| 53 | Visit(current); | ||
| 54 | current = current->GetNext(); | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | void operator()(ASTIfElse& ast) { | ||
| 59 | ASTNode current = ast.nodes.GetFirst(); | ||
| 60 | while (current) { | ||
| 61 | Visit(current); | ||
| 62 | current = current->GetNext(); | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | void operator()(ASTBlockEncoded& ast) {} | ||
| 67 | |||
| 68 | void operator()(ASTBlockDecoded& ast) {} | ||
| 69 | |||
| 70 | void operator()(ASTVarSet& ast) {} | ||
| 71 | |||
| 72 | void operator()(ASTLabel& ast) {} | ||
| 73 | |||
| 74 | void operator()(ASTGoto& ast) {} | ||
| 75 | |||
| 76 | void operator()(ASTDoWhile& ast) { | ||
| 77 | ASTNode current = ast.nodes.GetFirst(); | ||
| 78 | while (current) { | ||
| 79 | Visit(current); | ||
| 80 | current = current->GetNext(); | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | void operator()(ASTReturn& ast) {} | ||
| 85 | |||
| 86 | void operator()(ASTBreak& ast) {} | ||
| 87 | |||
| 88 | void Visit(ASTNode& node) { | ||
| 89 | std::visit(*this, *node->GetInnerData()); | ||
| 90 | if (node->IsBlockEncoded()) { | ||
| 91 | auto block = std::get_if<ASTBlockEncoded>(node->GetInnerData()); | ||
| 92 | NodeBlock bb = ir.DecodeRange(block->start, block->end); | ||
| 93 | node->TransformBlockEncoded(std::move(bb)); | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | private: | ||
| 98 | ShaderIR& ir; | ||
| 99 | }; | ||
| 100 | |||
| 38 | void ShaderIR::Decode() { | 101 | void ShaderIR::Decode() { |
| 39 | std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header)); | 102 | std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header)); |
| 40 | 103 | ||
| 41 | disable_flow_stack = false; | 104 | decompiled = false; |
| 42 | const auto info = ScanFlow(program_code, program_size, main_offset); | 105 | auto info = ScanFlow(program_code, program_size, main_offset, settings); |
| 43 | if (info) { | 106 | auto& shader_info = *info; |
| 44 | const auto& shader_info = *info; | 107 | coverage_begin = shader_info.start; |
| 45 | coverage_begin = shader_info.start; | 108 | coverage_end = shader_info.end; |
| 46 | coverage_end = shader_info.end; | 109 | switch (shader_info.settings.depth) { |
| 47 | if (shader_info.decompilable) { | 110 | case CompileDepth::FlowStack: { |
| 48 | disable_flow_stack = true; | ||
| 49 | const auto insert_block = [this](NodeBlock& nodes, u32 label) { | ||
| 50 | if (label == static_cast<u32>(exit_branch)) { | ||
| 51 | return; | ||
| 52 | } | ||
| 53 | basic_blocks.insert({label, nodes}); | ||
| 54 | }; | ||
| 55 | const auto& blocks = shader_info.blocks; | ||
| 56 | NodeBlock current_block; | ||
| 57 | u32 current_label = static_cast<u32>(exit_branch); | ||
| 58 | for (auto& block : blocks) { | ||
| 59 | if (shader_info.labels.count(block.start) != 0) { | ||
| 60 | insert_block(current_block, current_label); | ||
| 61 | current_block.clear(); | ||
| 62 | current_label = block.start; | ||
| 63 | } | ||
| 64 | if (!block.ignore_branch) { | ||
| 65 | DecodeRangeInner(current_block, block.start, block.end); | ||
| 66 | InsertControlFlow(current_block, block); | ||
| 67 | } else { | ||
| 68 | DecodeRangeInner(current_block, block.start, block.end + 1); | ||
| 69 | } | ||
| 70 | } | ||
| 71 | insert_block(current_block, current_label); | ||
| 72 | return; | ||
| 73 | } | ||
| 74 | LOG_WARNING(HW_GPU, "Flow Stack Removing Failed! Falling back to old method"); | ||
| 75 | // we can't decompile it, fallback to standard method | ||
| 76 | for (const auto& block : shader_info.blocks) { | 111 | for (const auto& block : shader_info.blocks) { |
| 77 | basic_blocks.insert({block.start, DecodeRange(block.start, block.end + 1)}); | 112 | basic_blocks.insert({block.start, DecodeRange(block.start, block.end + 1)}); |
| 78 | } | 113 | } |
| 79 | return; | 114 | break; |
| 80 | } | 115 | } |
| 81 | LOG_WARNING(HW_GPU, "Flow Analysis Failed! Falling back to brute force compiling"); | 116 | case CompileDepth::NoFlowStack: { |
| 82 | 117 | disable_flow_stack = true; | |
| 83 | // Now we need to deal with an undecompilable shader. We need to brute force | 118 | const auto insert_block = [this](NodeBlock& nodes, u32 label) { |
| 84 | // a shader that captures every position. | 119 | if (label == static_cast<u32>(exit_branch)) { |
| 85 | coverage_begin = main_offset; | 120 | return; |
| 86 | const u32 shader_end = static_cast<u32>(program_size / sizeof(u64)); | 121 | } |
| 87 | coverage_end = shader_end; | 122 | basic_blocks.insert({label, nodes}); |
| 88 | for (u32 label = main_offset; label < shader_end; label++) { | 123 | }; |
| 89 | basic_blocks.insert({label, DecodeRange(label, label + 1)}); | 124 | const auto& blocks = shader_info.blocks; |
| 125 | NodeBlock current_block; | ||
| 126 | u32 current_label = static_cast<u32>(exit_branch); | ||
| 127 | for (auto& block : blocks) { | ||
| 128 | if (shader_info.labels.count(block.start) != 0) { | ||
| 129 | insert_block(current_block, current_label); | ||
| 130 | current_block.clear(); | ||
| 131 | current_label = block.start; | ||
| 132 | } | ||
| 133 | if (!block.ignore_branch) { | ||
| 134 | DecodeRangeInner(current_block, block.start, block.end); | ||
| 135 | InsertControlFlow(current_block, block); | ||
| 136 | } else { | ||
| 137 | DecodeRangeInner(current_block, block.start, block.end + 1); | ||
| 138 | } | ||
| 139 | } | ||
| 140 | insert_block(current_block, current_label); | ||
| 141 | break; | ||
| 142 | } | ||
| 143 | case CompileDepth::DecompileBackwards: | ||
| 144 | case CompileDepth::FullDecompile: { | ||
| 145 | program_manager = std::move(shader_info.manager); | ||
| 146 | disable_flow_stack = true; | ||
| 147 | decompiled = true; | ||
| 148 | ASTDecoder decoder{*this}; | ||
| 149 | ASTNode program = GetASTProgram(); | ||
| 150 | decoder.Visit(program); | ||
| 151 | break; | ||
| 152 | } | ||
| 153 | default: | ||
| 154 | LOG_CRITICAL(HW_GPU, "Unknown decompilation mode!"); | ||
| 155 | [[fallthrough]]; | ||
| 156 | case CompileDepth::BruteForce: { | ||
| 157 | coverage_begin = main_offset; | ||
| 158 | const u32 shader_end = static_cast<u32>(program_size / sizeof(u64)); | ||
| 159 | coverage_end = shader_end; | ||
| 160 | for (u32 label = main_offset; label < shader_end; label++) { | ||
| 161 | basic_blocks.insert({label, DecodeRange(label, label + 1)}); | ||
| 162 | } | ||
| 163 | break; | ||
| 164 | } | ||
| 165 | } | ||
| 166 | if (settings.depth != shader_info.settings.depth) { | ||
| 167 | LOG_WARNING( | ||
| 168 | HW_GPU, "Decompiling to this setting \"{}\" failed, downgrading to this setting \"{}\"", | ||
| 169 | CompileDepthAsString(settings.depth), CompileDepthAsString(shader_info.settings.depth)); | ||
| 90 | } | 170 | } |
| 91 | } | 171 | } |
| 92 | 172 | ||
diff --git a/src/video_core/shader/expr.cpp b/src/video_core/shader/expr.cpp new file mode 100644 index 000000000..ca633ffb1 --- /dev/null +++ b/src/video_core/shader/expr.cpp | |||
| @@ -0,0 +1,82 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <variant> | ||
| 9 | |||
| 10 | #include "video_core/shader/expr.h" | ||
| 11 | |||
| 12 | namespace VideoCommon::Shader { | ||
| 13 | |||
| 14 | bool ExprAnd::operator==(const ExprAnd& b) const { | ||
| 15 | return (*operand1 == *b.operand1) && (*operand2 == *b.operand2); | ||
| 16 | } | ||
| 17 | |||
| 18 | bool ExprOr::operator==(const ExprOr& b) const { | ||
| 19 | return (*operand1 == *b.operand1) && (*operand2 == *b.operand2); | ||
| 20 | } | ||
| 21 | |||
| 22 | bool ExprNot::operator==(const ExprNot& b) const { | ||
| 23 | return (*operand1 == *b.operand1); | ||
| 24 | } | ||
| 25 | |||
| 26 | bool ExprIsBoolean(Expr expr) { | ||
| 27 | return std::holds_alternative<ExprBoolean>(*expr); | ||
| 28 | } | ||
| 29 | |||
| 30 | bool ExprBooleanGet(Expr expr) { | ||
| 31 | return std::get_if<ExprBoolean>(expr.get())->value; | ||
| 32 | } | ||
| 33 | |||
| 34 | Expr MakeExprNot(Expr first) { | ||
| 35 | if (std::holds_alternative<ExprNot>(*first)) { | ||
| 36 | return std::get_if<ExprNot>(first.get())->operand1; | ||
| 37 | } | ||
| 38 | return MakeExpr<ExprNot>(first); | ||
| 39 | } | ||
| 40 | |||
| 41 | Expr MakeExprAnd(Expr first, Expr second) { | ||
| 42 | if (ExprIsBoolean(first)) { | ||
| 43 | return ExprBooleanGet(first) ? second : first; | ||
| 44 | } | ||
| 45 | if (ExprIsBoolean(second)) { | ||
| 46 | return ExprBooleanGet(second) ? first : second; | ||
| 47 | } | ||
| 48 | return MakeExpr<ExprAnd>(first, second); | ||
| 49 | } | ||
| 50 | |||
| 51 | Expr MakeExprOr(Expr first, Expr second) { | ||
| 52 | if (ExprIsBoolean(first)) { | ||
| 53 | return ExprBooleanGet(first) ? first : second; | ||
| 54 | } | ||
| 55 | if (ExprIsBoolean(second)) { | ||
| 56 | return ExprBooleanGet(second) ? second : first; | ||
| 57 | } | ||
| 58 | return MakeExpr<ExprOr>(first, second); | ||
| 59 | } | ||
| 60 | |||
| 61 | bool ExprAreEqual(Expr first, Expr second) { | ||
| 62 | return (*first) == (*second); | ||
| 63 | } | ||
| 64 | |||
| 65 | bool ExprAreOpposite(Expr first, Expr second) { | ||
| 66 | if (std::holds_alternative<ExprNot>(*first)) { | ||
| 67 | return ExprAreEqual(std::get_if<ExprNot>(first.get())->operand1, second); | ||
| 68 | } | ||
| 69 | if (std::holds_alternative<ExprNot>(*second)) { | ||
| 70 | return ExprAreEqual(std::get_if<ExprNot>(second.get())->operand1, first); | ||
| 71 | } | ||
| 72 | return false; | ||
| 73 | } | ||
| 74 | |||
| 75 | bool ExprIsTrue(Expr first) { | ||
| 76 | if (ExprIsBoolean(first)) { | ||
| 77 | return ExprBooleanGet(first); | ||
| 78 | } | ||
| 79 | return false; | ||
| 80 | } | ||
| 81 | |||
| 82 | } // namespace VideoCommon::Shader | ||
diff --git a/src/video_core/shader/expr.h b/src/video_core/shader/expr.h new file mode 100644 index 000000000..4c399cef9 --- /dev/null +++ b/src/video_core/shader/expr.h | |||
| @@ -0,0 +1,120 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <variant> | ||
| 9 | |||
| 10 | #include "video_core/engines/shader_bytecode.h" | ||
| 11 | |||
| 12 | namespace VideoCommon::Shader { | ||
| 13 | |||
| 14 | using Tegra::Shader::ConditionCode; | ||
| 15 | using Tegra::Shader::Pred; | ||
| 16 | |||
| 17 | class ExprAnd; | ||
| 18 | class ExprOr; | ||
| 19 | class ExprNot; | ||
| 20 | class ExprPredicate; | ||
| 21 | class ExprCondCode; | ||
| 22 | class ExprVar; | ||
| 23 | class ExprBoolean; | ||
| 24 | |||
| 25 | using ExprData = | ||
| 26 | std::variant<ExprVar, ExprCondCode, ExprPredicate, ExprNot, ExprOr, ExprAnd, ExprBoolean>; | ||
| 27 | using Expr = std::shared_ptr<ExprData>; | ||
| 28 | |||
| 29 | class ExprAnd final { | ||
| 30 | public: | ||
| 31 | explicit ExprAnd(Expr a, Expr b) : operand1{a}, operand2{b} {} | ||
| 32 | |||
| 33 | bool operator==(const ExprAnd& b) const; | ||
| 34 | |||
| 35 | Expr operand1; | ||
| 36 | Expr operand2; | ||
| 37 | }; | ||
| 38 | |||
| 39 | class ExprOr final { | ||
| 40 | public: | ||
| 41 | explicit ExprOr(Expr a, Expr b) : operand1{a}, operand2{b} {} | ||
| 42 | |||
| 43 | bool operator==(const ExprOr& b) const; | ||
| 44 | |||
| 45 | Expr operand1; | ||
| 46 | Expr operand2; | ||
| 47 | }; | ||
| 48 | |||
| 49 | class ExprNot final { | ||
| 50 | public: | ||
| 51 | explicit ExprNot(Expr a) : operand1{a} {} | ||
| 52 | |||
| 53 | bool operator==(const ExprNot& b) const; | ||
| 54 | |||
| 55 | Expr operand1; | ||
| 56 | }; | ||
| 57 | |||
| 58 | class ExprVar final { | ||
| 59 | public: | ||
| 60 | explicit ExprVar(u32 index) : var_index{index} {} | ||
| 61 | |||
| 62 | bool operator==(const ExprVar& b) const { | ||
| 63 | return var_index == b.var_index; | ||
| 64 | } | ||
| 65 | |||
| 66 | u32 var_index; | ||
| 67 | }; | ||
| 68 | |||
| 69 | class ExprPredicate final { | ||
| 70 | public: | ||
| 71 | explicit ExprPredicate(u32 predicate) : predicate{predicate} {} | ||
| 72 | |||
| 73 | bool operator==(const ExprPredicate& b) const { | ||
| 74 | return predicate == b.predicate; | ||
| 75 | } | ||
| 76 | |||
| 77 | u32 predicate; | ||
| 78 | }; | ||
| 79 | |||
| 80 | class ExprCondCode final { | ||
| 81 | public: | ||
| 82 | explicit ExprCondCode(ConditionCode cc) : cc{cc} {} | ||
| 83 | |||
| 84 | bool operator==(const ExprCondCode& b) const { | ||
| 85 | return cc == b.cc; | ||
| 86 | } | ||
| 87 | |||
| 88 | ConditionCode cc; | ||
| 89 | }; | ||
| 90 | |||
| 91 | class ExprBoolean final { | ||
| 92 | public: | ||
| 93 | explicit ExprBoolean(bool val) : value{val} {} | ||
| 94 | |||
| 95 | bool operator==(const ExprBoolean& b) const { | ||
| 96 | return value == b.value; | ||
| 97 | } | ||
| 98 | |||
| 99 | bool value; | ||
| 100 | }; | ||
| 101 | |||
| 102 | template <typename T, typename... Args> | ||
| 103 | Expr MakeExpr(Args&&... args) { | ||
| 104 | static_assert(std::is_convertible_v<T, ExprData>); | ||
| 105 | return std::make_shared<ExprData>(T(std::forward<Args>(args)...)); | ||
| 106 | } | ||
| 107 | |||
| 108 | bool ExprAreEqual(Expr first, Expr second); | ||
| 109 | |||
| 110 | bool ExprAreOpposite(Expr first, Expr second); | ||
| 111 | |||
| 112 | Expr MakeExprNot(Expr first); | ||
| 113 | |||
| 114 | Expr MakeExprAnd(Expr first, Expr second); | ||
| 115 | |||
| 116 | Expr MakeExprOr(Expr first, Expr second); | ||
| 117 | |||
| 118 | bool ExprIsTrue(Expr first); | ||
| 119 | |||
| 120 | } // namespace VideoCommon::Shader | ||
diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp index 2c357f310..c1f2b88c8 100644 --- a/src/video_core/shader/shader_ir.cpp +++ b/src/video_core/shader/shader_ir.cpp | |||
| @@ -22,8 +22,10 @@ using Tegra::Shader::PredCondition; | |||
| 22 | using Tegra::Shader::PredOperation; | 22 | using Tegra::Shader::PredOperation; |
| 23 | using Tegra::Shader::Register; | 23 | using Tegra::Shader::Register; |
| 24 | 24 | ||
| 25 | ShaderIR::ShaderIR(const ProgramCode& program_code, u32 main_offset, const std::size_t size) | 25 | ShaderIR::ShaderIR(const ProgramCode& program_code, u32 main_offset, const std::size_t size, |
| 26 | : program_code{program_code}, main_offset{main_offset}, program_size{size} { | 26 | CompilerSettings settings) |
| 27 | : program_code{program_code}, main_offset{main_offset}, program_size{size}, basic_blocks{}, | ||
| 28 | program_manager{true, true}, settings{settings} { | ||
| 27 | Decode(); | 29 | Decode(); |
| 28 | } | 30 | } |
| 29 | 31 | ||
| @@ -137,7 +139,7 @@ Node ShaderIR::GetOutputAttribute(Attribute::Index index, u64 element, Node buff | |||
| 137 | return MakeNode<AbufNode>(index, static_cast<u32>(element), std::move(buffer)); | 139 | return MakeNode<AbufNode>(index, static_cast<u32>(element), std::move(buffer)); |
| 138 | } | 140 | } |
| 139 | 141 | ||
| 140 | Node ShaderIR::GetInternalFlag(InternalFlag flag, bool negated) { | 142 | Node ShaderIR::GetInternalFlag(InternalFlag flag, bool negated) const { |
| 141 | const Node node = MakeNode<InternalFlagNode>(flag); | 143 | const Node node = MakeNode<InternalFlagNode>(flag); |
| 142 | if (negated) { | 144 | if (negated) { |
| 143 | return Operation(OperationCode::LogicalNegate, node); | 145 | return Operation(OperationCode::LogicalNegate, node); |
| @@ -367,13 +369,13 @@ OperationCode ShaderIR::GetPredicateCombiner(PredOperation operation) { | |||
| 367 | return op->second; | 369 | return op->second; |
| 368 | } | 370 | } |
| 369 | 371 | ||
| 370 | Node ShaderIR::GetConditionCode(Tegra::Shader::ConditionCode cc) { | 372 | Node ShaderIR::GetConditionCode(Tegra::Shader::ConditionCode cc) const { |
| 371 | switch (cc) { | 373 | switch (cc) { |
| 372 | case Tegra::Shader::ConditionCode::NEU: | 374 | case Tegra::Shader::ConditionCode::NEU: |
| 373 | return GetInternalFlag(InternalFlag::Zero, true); | 375 | return GetInternalFlag(InternalFlag::Zero, true); |
| 374 | default: | 376 | default: |
| 375 | UNIMPLEMENTED_MSG("Unimplemented condition code: {}", static_cast<u32>(cc)); | 377 | UNIMPLEMENTED_MSG("Unimplemented condition code: {}", static_cast<u32>(cc)); |
| 376 | return GetPredicate(static_cast<u64>(Pred::NeverExecute)); | 378 | return MakeNode<PredicateNode>(Pred::NeverExecute, false); |
| 377 | } | 379 | } |
| 378 | } | 380 | } |
| 379 | 381 | ||
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index 6f666ee30..105981d67 100644 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h | |||
| @@ -15,6 +15,8 @@ | |||
| 15 | #include "video_core/engines/maxwell_3d.h" | 15 | #include "video_core/engines/maxwell_3d.h" |
| 16 | #include "video_core/engines/shader_bytecode.h" | 16 | #include "video_core/engines/shader_bytecode.h" |
| 17 | #include "video_core/engines/shader_header.h" | 17 | #include "video_core/engines/shader_header.h" |
| 18 | #include "video_core/shader/ast.h" | ||
| 19 | #include "video_core/shader/compiler_settings.h" | ||
| 18 | #include "video_core/shader/node.h" | 20 | #include "video_core/shader/node.h" |
| 19 | 21 | ||
| 20 | namespace VideoCommon::Shader { | 22 | namespace VideoCommon::Shader { |
| @@ -64,7 +66,8 @@ struct GlobalMemoryUsage { | |||
| 64 | 66 | ||
| 65 | class ShaderIR final { | 67 | class ShaderIR final { |
| 66 | public: | 68 | public: |
| 67 | explicit ShaderIR(const ProgramCode& program_code, u32 main_offset, std::size_t size); | 69 | explicit ShaderIR(const ProgramCode& program_code, u32 main_offset, std::size_t size, |
| 70 | CompilerSettings settings); | ||
| 68 | ~ShaderIR(); | 71 | ~ShaderIR(); |
| 69 | 72 | ||
| 70 | const std::map<u32, NodeBlock>& GetBasicBlocks() const { | 73 | const std::map<u32, NodeBlock>& GetBasicBlocks() const { |
| @@ -144,11 +147,31 @@ public: | |||
| 144 | return disable_flow_stack; | 147 | return disable_flow_stack; |
| 145 | } | 148 | } |
| 146 | 149 | ||
| 150 | bool IsDecompiled() const { | ||
| 151 | return decompiled; | ||
| 152 | } | ||
| 153 | |||
| 154 | const ASTManager& GetASTManager() const { | ||
| 155 | return program_manager; | ||
| 156 | } | ||
| 157 | |||
| 158 | ASTNode GetASTProgram() const { | ||
| 159 | return program_manager.GetProgram(); | ||
| 160 | } | ||
| 161 | |||
| 162 | u32 GetASTNumVariables() const { | ||
| 163 | return program_manager.GetVariables(); | ||
| 164 | } | ||
| 165 | |||
| 147 | u32 ConvertAddressToNvidiaSpace(const u32 address) const { | 166 | u32 ConvertAddressToNvidiaSpace(const u32 address) const { |
| 148 | return (address - main_offset) * sizeof(Tegra::Shader::Instruction); | 167 | return (address - main_offset) * sizeof(Tegra::Shader::Instruction); |
| 149 | } | 168 | } |
| 150 | 169 | ||
| 170 | /// Returns a condition code evaluated from internal flags | ||
| 171 | Node GetConditionCode(Tegra::Shader::ConditionCode cc) const; | ||
| 172 | |||
| 151 | private: | 173 | private: |
| 174 | friend class ASTDecoder; | ||
| 152 | void Decode(); | 175 | void Decode(); |
| 153 | 176 | ||
| 154 | NodeBlock DecodeRange(u32 begin, u32 end); | 177 | NodeBlock DecodeRange(u32 begin, u32 end); |
| @@ -213,7 +236,7 @@ private: | |||
| 213 | /// Generates a node representing an output attribute. Keeps track of used attributes. | 236 | /// Generates a node representing an output attribute. Keeps track of used attributes. |
| 214 | Node GetOutputAttribute(Tegra::Shader::Attribute::Index index, u64 element, Node buffer); | 237 | Node GetOutputAttribute(Tegra::Shader::Attribute::Index index, u64 element, Node buffer); |
| 215 | /// Generates a node representing an internal flag | 238 | /// Generates a node representing an internal flag |
| 216 | Node GetInternalFlag(InternalFlag flag, bool negated = false); | 239 | Node GetInternalFlag(InternalFlag flag, bool negated = false) const; |
| 217 | /// Generates a node representing a local memory address | 240 | /// Generates a node representing a local memory address |
| 218 | Node GetLocalMemory(Node address); | 241 | Node GetLocalMemory(Node address); |
| 219 | /// Generates a node representing a shared memory address | 242 | /// Generates a node representing a shared memory address |
| @@ -271,9 +294,6 @@ private: | |||
| 271 | /// Returns a predicate combiner operation | 294 | /// Returns a predicate combiner operation |
| 272 | OperationCode GetPredicateCombiner(Tegra::Shader::PredOperation operation); | 295 | OperationCode GetPredicateCombiner(Tegra::Shader::PredOperation operation); |
| 273 | 296 | ||
| 274 | /// Returns a condition code evaluated from internal flags | ||
| 275 | Node GetConditionCode(Tegra::Shader::ConditionCode cc); | ||
| 276 | |||
| 277 | /// Accesses a texture sampler | 297 | /// Accesses a texture sampler |
| 278 | const Sampler& GetSampler(const Tegra::Shader::Sampler& sampler, | 298 | const Sampler& GetSampler(const Tegra::Shader::Sampler& sampler, |
| 279 | Tegra::Shader::TextureType type, bool is_array, bool is_shadow); | 299 | Tegra::Shader::TextureType type, bool is_array, bool is_shadow); |
| @@ -357,6 +377,7 @@ private: | |||
| 357 | const ProgramCode& program_code; | 377 | const ProgramCode& program_code; |
| 358 | const u32 main_offset; | 378 | const u32 main_offset; |
| 359 | const std::size_t program_size; | 379 | const std::size_t program_size; |
| 380 | bool decompiled{}; | ||
| 360 | bool disable_flow_stack{}; | 381 | bool disable_flow_stack{}; |
| 361 | 382 | ||
| 362 | u32 coverage_begin{}; | 383 | u32 coverage_begin{}; |
| @@ -364,6 +385,8 @@ private: | |||
| 364 | 385 | ||
| 365 | std::map<u32, NodeBlock> basic_blocks; | 386 | std::map<u32, NodeBlock> basic_blocks; |
| 366 | NodeBlock global_code; | 387 | NodeBlock global_code; |
| 388 | ASTManager program_manager; | ||
| 389 | CompilerSettings settings{}; | ||
| 367 | 390 | ||
| 368 | std::set<u32> used_registers; | 391 | std::set<u32> used_registers; |
| 369 | std::set<Tegra::Shader::Pred> used_predicates; | 392 | std::set<Tegra::Shader::Pred> used_predicates; |