| 
									
										
										
										
											2018-12-29 02:44:54 -03:00
										 |  |  | // Copyright 2018 yuzu Emulator Project
 | 
					
						
							|  |  |  | // Licensed under GPLv2 or any later version
 | 
					
						
							|  |  |  | // Refer to the license.txt file included.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <algorithm>
 | 
					
						
							|  |  |  | #include <utility>
 | 
					
						
							|  |  |  | #include <variant>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-29 18:36:07 -03:00
										 |  |  | #include "common/common_types.h"
 | 
					
						
							| 
									
										
										
										
											2019-11-01 00:34:38 -03:00
										 |  |  | #include "video_core/shader/node.h"
 | 
					
						
							| 
									
										
										
										
											2020-01-05 15:23:24 -04:00
										 |  |  | #include "video_core/shader/node_helper.h"
 | 
					
						
							| 
									
										
										
										
											2018-12-29 02:44:54 -03:00
										 |  |  | #include "video_core/shader/shader_ir.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace VideoCommon::Shader { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace { | 
					
						
							| 
									
										
										
										
											2020-06-04 23:02:55 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-30 02:09:40 -03:00
										 |  |  | std::pair<Node, s64> FindOperation(const NodeBlock& code, s64 cursor, | 
					
						
							| 
									
										
										
										
											2018-12-29 02:44:54 -03:00
										 |  |  |                                    OperationCode operation_code) { | 
					
						
							|  |  |  |     for (; cursor >= 0; --cursor) { | 
					
						
							| 
									
										
										
										
											2019-07-16 10:37:11 -04:00
										 |  |  |         Node node = code.at(cursor); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-04 22:44:06 -03:00
										 |  |  |         if (const auto operation = std::get_if<OperationNode>(&*node)) { | 
					
						
							| 
									
										
										
										
											2019-05-19 04:10:32 -04:00
										 |  |  |             if (operation->GetCode() == operation_code) { | 
					
						
							| 
									
										
										
										
											2019-07-16 10:37:11 -04:00
										 |  |  |                 return {std::move(node), cursor}; | 
					
						
							| 
									
										
										
										
											2019-05-19 04:10:32 -04:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2018-12-29 02:44:54 -03:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-07-16 10:37:11 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-04 22:44:06 -03:00
										 |  |  |         if (const auto conditional = std::get_if<ConditionalNode>(&*node)) { | 
					
						
							| 
									
										
										
										
											2019-02-25 09:10:59 -05:00
										 |  |  |             const auto& conditional_code = conditional->GetCode(); | 
					
						
							| 
									
										
										
										
											2020-04-15 15:59:23 -04:00
										 |  |  |             auto result = FindOperation( | 
					
						
							| 
									
										
										
										
											2019-02-25 09:10:59 -05:00
										 |  |  |                 conditional_code, static_cast<s64>(conditional_code.size() - 1), operation_code); | 
					
						
							| 
									
										
										
										
											2020-04-15 15:59:23 -04:00
										 |  |  |             auto& found = result.first; | 
					
						
							| 
									
										
										
										
											2019-05-19 04:10:32 -04:00
										 |  |  |             if (found) { | 
					
						
							| 
									
										
										
										
											2019-07-16 10:37:11 -04:00
										 |  |  |                 return {std::move(found), cursor}; | 
					
						
							| 
									
										
										
										
											2019-05-19 04:10:32 -04:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2019-01-30 02:20:05 -03:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-12-29 02:44:54 -03:00
										 |  |  |     } | 
					
						
							|  |  |  |     return {}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-05 15:23:24 -04:00
										 |  |  | std::optional<std::pair<Node, Node>> DecoupleIndirectRead(const OperationNode& operation) { | 
					
						
							|  |  |  |     if (operation.GetCode() != OperationCode::UAdd) { | 
					
						
							|  |  |  |         return std::nullopt; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-02-01 22:49:25 -03:00
										 |  |  |     Node gpr; | 
					
						
							|  |  |  |     Node offset; | 
					
						
							| 
									
										
										
										
											2020-01-24 10:44:34 -04:00
										 |  |  |     ASSERT(operation.GetOperandsCount() == 2); | 
					
						
							| 
									
										
										
										
											2020-01-05 15:23:24 -04:00
										 |  |  |     for (std::size_t i = 0; i < operation.GetOperandsCount(); i++) { | 
					
						
							|  |  |  |         Node operand = operation[i]; | 
					
						
							|  |  |  |         if (std::holds_alternative<ImmediateNode>(*operand)) { | 
					
						
							|  |  |  |             offset = operation[i]; | 
					
						
							|  |  |  |         } else if (std::holds_alternative<GprNode>(*operand)) { | 
					
						
							|  |  |  |             gpr = operation[i]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (offset && gpr) { | 
					
						
							| 
									
										
										
										
											2020-01-24 10:44:34 -04:00
										 |  |  |         return std::make_pair(gpr, offset); | 
					
						
							| 
									
										
										
										
											2020-01-05 15:23:24 -04:00
										 |  |  |     } | 
					
						
							|  |  |  |     return std::nullopt; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-07 17:45:12 -04:00
										 |  |  | bool AmendNodeCv(std::size_t amend_index, Node node) { | 
					
						
							|  |  |  |     if (const auto operation = std::get_if<OperationNode>(&*node)) { | 
					
						
							|  |  |  |         operation->SetAmendIndex(amend_index); | 
					
						
							|  |  |  |         return true; | 
					
						
							| 
									
										
										
										
											2020-06-04 23:03:49 -03:00
										 |  |  |     } | 
					
						
							|  |  |  |     if (const auto conditional = std::get_if<ConditionalNode>(&*node)) { | 
					
						
							| 
									
										
										
										
											2020-01-07 17:45:12 -04:00
										 |  |  |         conditional->SetAmendIndex(amend_index); | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-24 10:44:34 -04:00
										 |  |  | } // Anonymous namespace
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-04 23:02:55 -03:00
										 |  |  | std::pair<Node, TrackSampler> ShaderIR::TrackBindlessSampler(Node tracked, const NodeBlock& code, | 
					
						
							|  |  |  |                                                              s64 cursor) { | 
					
						
							| 
									
										
										
										
											2020-01-05 15:23:24 -04:00
										 |  |  |     if (const auto cbuf = std::get_if<CbufNode>(&*tracked)) { | 
					
						
							| 
									
										
										
										
											2020-06-04 23:02:55 -03:00
										 |  |  |         const u32 cbuf_index = cbuf->GetIndex(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-05 15:23:24 -04:00
										 |  |  |         // Constant buffer found, test if it's an immediate
 | 
					
						
							| 
									
										
										
										
											2020-04-15 21:50:06 -04:00
										 |  |  |         const auto& offset = cbuf->GetOffset(); | 
					
						
							| 
									
										
										
										
											2020-01-05 15:23:24 -04:00
										 |  |  |         if (const auto immediate = std::get_if<ImmediateNode>(&*offset)) { | 
					
						
							| 
									
										
										
										
											2020-06-04 23:02:55 -03:00
										 |  |  |             auto track = MakeTrackSampler<BindlessSamplerNode>(cbuf_index, immediate->GetValue()); | 
					
						
							| 
									
										
										
										
											2020-01-05 15:23:24 -04:00
										 |  |  |             return {tracked, track}; | 
					
						
							| 
									
										
										
										
											2020-04-15 21:50:06 -04:00
										 |  |  |         } | 
					
						
							|  |  |  |         if (const auto operation = std::get_if<OperationNode>(&*offset)) { | 
					
						
							| 
									
										
										
										
											2020-02-29 03:49:51 -03:00
										 |  |  |             const u32 bound_buffer = registry.GetBoundBuffer(); | 
					
						
							| 
									
										
										
										
											2020-06-04 23:02:55 -03:00
										 |  |  |             if (bound_buffer != cbuf_index) { | 
					
						
							| 
									
										
										
										
											2020-01-05 15:23:24 -04:00
										 |  |  |                 return {}; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2020-06-04 23:02:55 -03:00
										 |  |  |             if (const std::optional pair = DecoupleIndirectRead(*operation)) { | 
					
						
							|  |  |  |                 auto [gpr, base_offset] = *pair; | 
					
						
							|  |  |  |                 return HandleBindlessIndirectRead(*cbuf, *operation, gpr, base_offset, tracked, | 
					
						
							|  |  |  |                                                   code, cursor); | 
					
						
							| 
									
										
										
										
											2020-01-05 15:23:24 -04:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return {}; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (const auto gpr = std::get_if<GprNode>(&*tracked)) { | 
					
						
							|  |  |  |         if (gpr->GetIndex() == Tegra::Shader::Register::ZeroIndex) { | 
					
						
							|  |  |  |             return {}; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         // Reduce the cursor in one to avoid infinite loops when the instruction sets the same
 | 
					
						
							|  |  |  |         // register that it uses as operand
 | 
					
						
							|  |  |  |         const auto [source, new_cursor] = TrackRegister(gpr, code, cursor - 1); | 
					
						
							|  |  |  |         if (!source) { | 
					
						
							|  |  |  |             return {}; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-01-08 15:59:21 -04:00
										 |  |  |         return TrackBindlessSampler(source, code, new_cursor); | 
					
						
							| 
									
										
										
										
											2020-01-05 15:23:24 -04:00
										 |  |  |     } | 
					
						
							|  |  |  |     if (const auto operation = std::get_if<OperationNode>(&*tracked)) { | 
					
						
							| 
									
										
										
										
											2020-06-04 23:03:49 -03:00
										 |  |  |         const OperationNode& op = *operation; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const OperationCode opcode = operation->GetCode(); | 
					
						
							|  |  |  |         if (opcode == OperationCode::IBitwiseOr || opcode == OperationCode::UBitwiseOr) { | 
					
						
							|  |  |  |             ASSERT(op.GetOperandsCount() == 2); | 
					
						
							|  |  |  |             auto [node_a, index_a, offset_a] = TrackCbuf(op[0], code, cursor); | 
					
						
							|  |  |  |             auto [node_b, index_b, offset_b] = TrackCbuf(op[1], code, cursor); | 
					
						
							|  |  |  |             if (node_a && node_b) { | 
					
						
							|  |  |  |                 auto track = MakeTrackSampler<SeparateSamplerNode>(std::pair{index_a, index_b}, | 
					
						
							|  |  |  |                                                                    std::pair{offset_a, offset_b}); | 
					
						
							|  |  |  |                 return {tracked, std::move(track)}; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         std::size_t i = op.GetOperandsCount(); | 
					
						
							|  |  |  |         while (i--) { | 
					
						
							|  |  |  |             if (auto found = TrackBindlessSampler(op[i - 1], code, cursor); std::get<0>(found)) { | 
					
						
							|  |  |  |                 // Constant buffer found in operand.
 | 
					
						
							| 
									
										
										
										
											2020-01-05 15:23:24 -04:00
										 |  |  |                 return found; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return {}; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (const auto conditional = std::get_if<ConditionalNode>(&*tracked)) { | 
					
						
							|  |  |  |         const auto& conditional_code = conditional->GetCode(); | 
					
						
							| 
									
										
										
										
											2020-01-08 15:59:21 -04:00
										 |  |  |         return TrackBindlessSampler(tracked, conditional_code, | 
					
						
							|  |  |  |                                     static_cast<s64>(conditional_code.size())); | 
					
						
							| 
									
										
										
										
											2020-01-05 15:23:24 -04:00
										 |  |  |     } | 
					
						
							|  |  |  |     return {}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-04 23:02:55 -03:00
										 |  |  | std::pair<Node, TrackSampler> ShaderIR::HandleBindlessIndirectRead( | 
					
						
							|  |  |  |     const CbufNode& cbuf, const OperationNode& operation, Node gpr, Node base_offset, Node tracked, | 
					
						
							|  |  |  |     const NodeBlock& code, s64 cursor) { | 
					
						
							|  |  |  |     const auto offset_imm = std::get<ImmediateNode>(*base_offset); | 
					
						
							|  |  |  |     const auto& gpu_driver = registry.AccessGuestDriverProfile(); | 
					
						
							|  |  |  |     const u32 bindless_cv = NewCustomVariable(); | 
					
						
							|  |  |  |     const u32 texture_handler_size = gpu_driver.GetTextureHandlerSize(); | 
					
						
							|  |  |  |     Node op = Operation(OperationCode::UDiv, gpr, Immediate(texture_handler_size)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Node cv_node = GetCustomVariable(bindless_cv); | 
					
						
							|  |  |  |     Node amend_op = Operation(OperationCode::Assign, std::move(cv_node), std::move(op)); | 
					
						
							|  |  |  |     const std::size_t amend_index = DeclareAmend(std::move(amend_op)); | 
					
						
							|  |  |  |     AmendNodeCv(amend_index, code[cursor]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // TODO: Implement bindless index custom variable
 | 
					
						
							|  |  |  |     auto track = | 
					
						
							|  |  |  |         MakeTrackSampler<ArraySamplerNode>(cbuf.GetIndex(), offset_imm.GetValue(), bindless_cv); | 
					
						
							|  |  |  |     return {tracked, track}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-31 19:14:34 -03:00
										 |  |  | std::tuple<Node, u32, u32> ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code, | 
					
						
							|  |  |  |                                                s64 cursor) const { | 
					
						
							| 
									
										
										
										
											2019-06-04 22:44:06 -03:00
										 |  |  |     if (const auto cbuf = std::get_if<CbufNode>(&*tracked)) { | 
					
						
							| 
									
										
										
										
											2019-05-31 19:14:34 -03:00
										 |  |  |         // Constant buffer found, test if it's an immediate
 | 
					
						
							| 
									
										
										
										
											2020-04-15 21:50:06 -04:00
										 |  |  |         const auto& offset = cbuf->GetOffset(); | 
					
						
							| 
									
										
										
										
											2019-05-31 19:14:34 -03:00
										 |  |  |         if (const auto immediate = std::get_if<ImmediateNode>(&*offset)) { | 
					
						
							|  |  |  |             return {tracked, cbuf->GetIndex(), immediate->GetValue()}; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return {}; | 
					
						
							| 
									
										
										
										
											2018-12-29 02:44:54 -03:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-06-04 22:44:06 -03:00
										 |  |  |     if (const auto gpr = std::get_if<GprNode>(&*tracked)) { | 
					
						
							| 
									
										
										
										
											2018-12-29 02:44:54 -03:00
										 |  |  |         if (gpr->GetIndex() == Tegra::Shader::Register::ZeroIndex) { | 
					
						
							| 
									
										
										
										
											2019-05-31 19:14:34 -03:00
										 |  |  |             return {}; | 
					
						
							| 
									
										
										
										
											2018-12-29 02:44:54 -03:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-04-24 11:00:54 -04:00
										 |  |  |         // Reduce the cursor in one to avoid infinite loops when the instruction sets the same
 | 
					
						
							|  |  |  |         // register that it uses as operand
 | 
					
						
							|  |  |  |         const auto [source, new_cursor] = TrackRegister(gpr, code, cursor - 1); | 
					
						
							|  |  |  |         if (!source) { | 
					
						
							|  |  |  |             return {}; | 
					
						
							| 
									
										
										
										
											2018-12-29 02:44:54 -03:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-04-24 11:00:54 -04:00
										 |  |  |         return TrackCbuf(source, code, new_cursor); | 
					
						
							| 
									
										
										
										
											2018-12-29 02:44:54 -03:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-06-04 22:44:06 -03:00
										 |  |  |     if (const auto operation = std::get_if<OperationNode>(&*tracked)) { | 
					
						
							| 
									
										
										
										
											2019-07-15 15:46:35 -04:00
										 |  |  |         for (std::size_t i = operation->GetOperandsCount(); i > 0; --i) { | 
					
						
							|  |  |  |             if (auto found = TrackCbuf((*operation)[i - 1], code, cursor); std::get<0>(found)) { | 
					
						
							| 
									
										
										
										
											2019-05-31 19:14:34 -03:00
										 |  |  |                 // Cbuf found in operand.
 | 
					
						
							| 
									
										
										
										
											2018-12-29 02:44:54 -03:00
										 |  |  |                 return found; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-05-31 19:14:34 -03:00
										 |  |  |         return {}; | 
					
						
							| 
									
										
										
										
											2018-12-29 02:44:54 -03:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-06-04 22:44:06 -03:00
										 |  |  |     if (const auto conditional = std::get_if<ConditionalNode>(&*tracked)) { | 
					
						
							| 
									
										
										
										
											2019-02-25 09:10:59 -05:00
										 |  |  |         const auto& conditional_code = conditional->GetCode(); | 
					
						
							|  |  |  |         return TrackCbuf(tracked, conditional_code, static_cast<s64>(conditional_code.size())); | 
					
						
							| 
									
										
										
										
											2019-01-30 02:20:05 -03:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-05-31 19:14:34 -03:00
										 |  |  |     return {}; | 
					
						
							| 
									
										
										
										
											2018-12-29 02:44:54 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-19 04:10:32 -04:00
										 |  |  | std::optional<u32> ShaderIR::TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor) const { | 
					
						
							| 
									
										
										
										
											2019-03-29 18:36:07 -03:00
										 |  |  |     // Reduce the cursor in one to avoid infinite loops when the instruction sets the same register
 | 
					
						
							|  |  |  |     // that it uses as operand
 | 
					
						
							| 
									
										
										
										
											2020-04-15 15:59:23 -04:00
										 |  |  |     const auto result = TrackRegister(&std::get<GprNode>(*tracked), code, cursor - 1); | 
					
						
							|  |  |  |     const auto& found = result.first; | 
					
						
							| 
									
										
										
										
											2019-03-29 18:36:07 -03:00
										 |  |  |     if (!found) { | 
					
						
							|  |  |  |         return {}; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-06-04 22:44:06 -03:00
										 |  |  |     if (const auto immediate = std::get_if<ImmediateNode>(&*found)) { | 
					
						
							| 
									
										
										
										
											2019-03-29 18:36:07 -03:00
										 |  |  |         return immediate->GetValue(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return {}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-30 02:09:40 -03:00
										 |  |  | std::pair<Node, s64> ShaderIR::TrackRegister(const GprNode* tracked, const NodeBlock& code, | 
					
						
							| 
									
										
										
										
											2019-05-19 04:10:32 -04:00
										 |  |  |                                              s64 cursor) const { | 
					
						
							| 
									
										
										
										
											2018-12-29 02:44:54 -03:00
										 |  |  |     for (; cursor >= 0; --cursor) { | 
					
						
							|  |  |  |         const auto [found_node, new_cursor] = FindOperation(code, cursor, OperationCode::Assign); | 
					
						
							|  |  |  |         if (!found_node) { | 
					
						
							|  |  |  |             return {}; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-06-04 22:44:06 -03:00
										 |  |  |         const auto operation = std::get_if<OperationNode>(&*found_node); | 
					
						
							| 
									
										
										
										
											2018-12-29 02:44:54 -03:00
										 |  |  |         ASSERT(operation); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const auto& target = (*operation)[0]; | 
					
						
							| 
									
										
										
										
											2019-06-04 22:44:06 -03:00
										 |  |  |         if (const auto gpr_target = std::get_if<GprNode>(&*target)) { | 
					
						
							| 
									
										
										
										
											2018-12-29 02:44:54 -03:00
										 |  |  |             if (gpr_target->GetIndex() == tracked->GetIndex()) { | 
					
						
							|  |  |  |                 return {(*operation)[1], new_cursor}; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return {}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } // namespace VideoCommon::Shader
 |