diff options
| -rw-r--r-- | src/video_core/engines/shader_bytecode.h | 10 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_decompiler.cpp | 55 |
2 files changed, 58 insertions, 7 deletions
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 58f2904ce..d6e2397f2 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h | |||
| @@ -67,6 +67,13 @@ private: | |||
| 67 | u64 value{}; | 67 | u64 value{}; |
| 68 | }; | 68 | }; |
| 69 | 69 | ||
| 70 | enum class AttributeSize : u64 { | ||
| 71 | Word = 0, | ||
| 72 | DoubleWord = 1, | ||
| 73 | TripleWord = 2, | ||
| 74 | QuadWord = 3, | ||
| 75 | }; | ||
| 76 | |||
| 70 | union Attribute { | 77 | union Attribute { |
| 71 | Attribute() = default; | 78 | Attribute() = default; |
| 72 | 79 | ||
| @@ -87,9 +94,10 @@ union Attribute { | |||
| 87 | }; | 94 | }; |
| 88 | 95 | ||
| 89 | union { | 96 | union { |
| 97 | BitField<20, 10, u64> immediate; | ||
| 90 | BitField<22, 2, u64> element; | 98 | BitField<22, 2, u64> element; |
| 91 | BitField<24, 6, Index> index; | 99 | BitField<24, 6, Index> index; |
| 92 | BitField<47, 3, u64> size; | 100 | BitField<47, 3, AttributeSize> size; |
| 93 | } fmt20; | 101 | } fmt20; |
| 94 | 102 | ||
| 95 | union { | 103 | union { |
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 2d56370c7..81c0662d0 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -1772,13 +1772,34 @@ private: | |||
| 1772 | case OpCode::Type::Memory: { | 1772 | case OpCode::Type::Memory: { |
| 1773 | switch (opcode->GetId()) { | 1773 | switch (opcode->GetId()) { |
| 1774 | case OpCode::Id::LD_A: { | 1774 | case OpCode::Id::LD_A: { |
| 1775 | ASSERT_MSG(instr.attribute.fmt20.size == 0, "untested"); | ||
| 1776 | // Note: Shouldn't this be interp mode flat? As in no interpolation made. | 1775 | // Note: Shouldn't this be interp mode flat? As in no interpolation made. |
| 1776 | ASSERT_MSG(instr.gpr8.Value() == Register::ZeroIndex, | ||
| 1777 | "Indirect attribute loads are not supported"); | ||
| 1778 | ASSERT_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) == 0, | ||
| 1779 | "Unaligned attribute loads are not supported"); | ||
| 1777 | 1780 | ||
| 1778 | Tegra::Shader::IpaMode input_mode{Tegra::Shader::IpaInterpMode::Perspective, | 1781 | Tegra::Shader::IpaMode input_mode{Tegra::Shader::IpaInterpMode::Perspective, |
| 1779 | Tegra::Shader::IpaSampleMode::Default}; | 1782 | Tegra::Shader::IpaSampleMode::Default}; |
| 1780 | regs.SetRegisterToInputAttibute(instr.gpr0, instr.attribute.fmt20.element, | 1783 | |
| 1781 | instr.attribute.fmt20.index, input_mode); | 1784 | u32 next_element = instr.attribute.fmt20.element; |
| 1785 | u32 next_index = static_cast<u32>(instr.attribute.fmt20.index.Value()); | ||
| 1786 | |||
| 1787 | const auto LoadNextElement = [&](u32 reg_offset) { | ||
| 1788 | regs.SetRegisterToInputAttibute(instr.gpr0.Value() + reg_offset, next_element, | ||
| 1789 | static_cast<Attribute::Index>(next_index), | ||
| 1790 | input_mode); | ||
| 1791 | |||
| 1792 | // Load the next attribute element into the following register. If the element | ||
| 1793 | // to load goes beyond the vec4 size, load the first element of the next | ||
| 1794 | // attribute. | ||
| 1795 | next_element = (next_element + 1) % 4; | ||
| 1796 | next_index = next_index + (next_element == 0 ? 1 : 0); | ||
| 1797 | }; | ||
| 1798 | |||
| 1799 | const u32 num_words = static_cast<u32>(instr.attribute.fmt20.size.Value()) + 1; | ||
| 1800 | for (u32 reg_offset = 0; reg_offset < num_words; ++reg_offset) { | ||
| 1801 | LoadNextElement(reg_offset); | ||
| 1802 | } | ||
| 1782 | break; | 1803 | break; |
| 1783 | } | 1804 | } |
| 1784 | case OpCode::Id::LD_C: { | 1805 | case OpCode::Id::LD_C: { |
| @@ -1820,9 +1841,31 @@ private: | |||
| 1820 | break; | 1841 | break; |
| 1821 | } | 1842 | } |
| 1822 | case OpCode::Id::ST_A: { | 1843 | case OpCode::Id::ST_A: { |
| 1823 | ASSERT_MSG(instr.attribute.fmt20.size == 0, "untested"); | 1844 | ASSERT_MSG(instr.gpr8.Value() == Register::ZeroIndex, |
| 1824 | regs.SetOutputAttributeToRegister(instr.attribute.fmt20.index, | 1845 | "Indirect attribute loads are not supported"); |
| 1825 | instr.attribute.fmt20.element, instr.gpr0); | 1846 | ASSERT_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) == 0, |
| 1847 | "Unaligned attribute loads are not supported"); | ||
| 1848 | |||
| 1849 | u32 next_element = instr.attribute.fmt20.element; | ||
| 1850 | u32 next_index = static_cast<u32>(instr.attribute.fmt20.index.Value()); | ||
| 1851 | |||
| 1852 | const auto StoreNextElement = [&](u32 reg_offset) { | ||
| 1853 | regs.SetOutputAttributeToRegister(static_cast<Attribute::Index>(next_index), | ||
| 1854 | next_element, | ||
| 1855 | instr.gpr0.Value() + reg_offset); | ||
| 1856 | |||
| 1857 | // Load the next attribute element into the following register. If the element | ||
| 1858 | // to load goes beyond the vec4 size, load the first element of the next | ||
| 1859 | // attribute. | ||
| 1860 | next_element = (next_element + 1) % 4; | ||
| 1861 | next_index = next_index + (next_element == 0 ? 1 : 0); | ||
| 1862 | }; | ||
| 1863 | |||
| 1864 | const u32 num_words = static_cast<u32>(instr.attribute.fmt20.size.Value()) + 1; | ||
| 1865 | for (u32 reg_offset = 0; reg_offset < num_words; ++reg_offset) { | ||
| 1866 | StoreNextElement(reg_offset); | ||
| 1867 | } | ||
| 1868 | |||
| 1826 | break; | 1869 | break; |
| 1827 | } | 1870 | } |
| 1828 | case OpCode::Id::TEX: { | 1871 | case OpCode::Id::TEX: { |