diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/citra_qt/debugger/graphics_cmdlists.cpp | 2 | ||||
| -rw-r--r-- | src/common/common.vcxproj | 1 | ||||
| -rw-r--r-- | src/common/common.vcxproj.filters | 1 | ||||
| -rw-r--r-- | src/common/register_set.h | 163 | ||||
| -rw-r--r-- | src/core/hle/service/gsp.cpp | 36 | ||||
| -rw-r--r-- | src/core/hw/gpu.cpp | 47 | ||||
| -rw-r--r-- | src/core/hw/gpu.h | 297 | ||||
| -rw-r--r-- | src/video_core/pica.h | 242 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/renderer_opengl.cpp | 4 |
9 files changed, 377 insertions, 416 deletions
diff --git a/src/citra_qt/debugger/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics_cmdlists.cpp index 195197ef5..30b8b5dae 100644 --- a/src/citra_qt/debugger/graphics_cmdlists.cpp +++ b/src/citra_qt/debugger/graphics_cmdlists.cpp | |||
| @@ -83,7 +83,7 @@ QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const | |||
| 83 | if (role == Qt::DisplayRole) { | 83 | if (role == Qt::DisplayRole) { |
| 84 | QString content; | 84 | QString content; |
| 85 | if (index.column() == 0) { | 85 | if (index.column() == 0) { |
| 86 | content = Pica::command_names[header.cmd_id]; | 86 | content = QString::fromLatin1(Pica::Regs::GetCommandName(header.cmd_id).c_str()); |
| 87 | content.append(" "); | 87 | content.append(" "); |
| 88 | } else if (index.column() == 1) { | 88 | } else if (index.column() == 1) { |
| 89 | for (int j = 0; j < cmd.size(); ++j) | 89 | for (int j = 0; j < cmd.size(); ++j) |
diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj index 1f5c714c3..341d3a813 100644 --- a/src/common/common.vcxproj +++ b/src/common/common.vcxproj | |||
| @@ -182,7 +182,6 @@ | |||
| 182 | <ClInclude Include="mem_arena.h" /> | 182 | <ClInclude Include="mem_arena.h" /> |
| 183 | <ClInclude Include="msg_handler.h" /> | 183 | <ClInclude Include="msg_handler.h" /> |
| 184 | <ClInclude Include="platform.h" /> | 184 | <ClInclude Include="platform.h" /> |
| 185 | <ClInclude Include="register_set.h" /> | ||
| 186 | <ClInclude Include="scm_rev.h" /> | 185 | <ClInclude Include="scm_rev.h" /> |
| 187 | <ClInclude Include="std_condition_variable.h" /> | 186 | <ClInclude Include="std_condition_variable.h" /> |
| 188 | <ClInclude Include="std_mutex.h" /> | 187 | <ClInclude Include="std_mutex.h" /> |
diff --git a/src/common/common.vcxproj.filters b/src/common/common.vcxproj.filters index e8c4ce360..59268ce5a 100644 --- a/src/common/common.vcxproj.filters +++ b/src/common/common.vcxproj.filters | |||
| @@ -29,7 +29,6 @@ | |||
| 29 | <ClInclude Include="memory_util.h" /> | 29 | <ClInclude Include="memory_util.h" /> |
| 30 | <ClInclude Include="msg_handler.h" /> | 30 | <ClInclude Include="msg_handler.h" /> |
| 31 | <ClInclude Include="platform.h" /> | 31 | <ClInclude Include="platform.h" /> |
| 32 | <ClInclude Include="register_set.h" /> | ||
| 33 | <ClInclude Include="std_condition_variable.h" /> | 32 | <ClInclude Include="std_condition_variable.h" /> |
| 34 | <ClInclude Include="std_mutex.h" /> | 33 | <ClInclude Include="std_mutex.h" /> |
| 35 | <ClInclude Include="std_thread.h" /> | 34 | <ClInclude Include="std_thread.h" /> |
diff --git a/src/common/register_set.h b/src/common/register_set.h deleted file mode 100644 index ba19a2614..000000000 --- a/src/common/register_set.h +++ /dev/null | |||
| @@ -1,163 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | // Copyright 2014 Tony Wasserka | ||
| 8 | // All rights reserved. | ||
| 9 | // | ||
| 10 | // Redistribution and use in source and binary forms, with or without | ||
| 11 | // modification, are permitted provided that the following conditions are met: | ||
| 12 | // | ||
| 13 | // * Redistributions of source code must retain the above copyright | ||
| 14 | // notice, this list of conditions and the following disclaimer. | ||
| 15 | // * Redistributions in binary form must reproduce the above copyright | ||
| 16 | // notice, this list of conditions and the following disclaimer in the | ||
| 17 | // documentation and/or other materials provided with the distribution. | ||
| 18 | // * Neither the name of the owner nor the names of its contributors may | ||
| 19 | // be used to endorse or promote products derived from this software | ||
| 20 | // without specific prior written permission. | ||
| 21 | // | ||
| 22 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 23 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 24 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 25 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 26 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 27 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 28 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 29 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 30 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 31 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 32 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 33 | |||
| 34 | /* | ||
| 35 | * Standardized way to define a group of registers and corresponding data structures. To define | ||
| 36 | * a new register set, first define struct containing an enumeration called "Id" containing | ||
| 37 | * all register IDs and a template struct called "Struct". Specialize the Struct struct for any | ||
| 38 | * register ID which needs to be accessed in a specialized way. You can then declare the object | ||
| 39 | * containing all register values using the RegisterSet<BaseType, DefiningStruct> type, where | ||
| 40 | * BaseType is the underlying type of each register (e.g. u32). | ||
| 41 | * Of course, you'll usually want to implement the Struct template such that they are of the same | ||
| 42 | * size as BaseType. However, it's also possible to make it larger, e.g. when you want to describe | ||
| 43 | * multiple registers with the same structure. | ||
| 44 | * | ||
| 45 | * Example: | ||
| 46 | * | ||
| 47 | * struct Regs { | ||
| 48 | * enum Id : u32 { | ||
| 49 | * Value1 = 0, | ||
| 50 | * Value2 = 1, | ||
| 51 | * Value3 = 2, | ||
| 52 | * NumIds = 3 | ||
| 53 | * }; | ||
| 54 | * | ||
| 55 | * // declare register definition structures | ||
| 56 | * template<Id id> | ||
| 57 | * struct Struct; | ||
| 58 | * }; | ||
| 59 | * | ||
| 60 | * // Define register set object | ||
| 61 | * RegisterSet<u32, CommandIds> registers; | ||
| 62 | * | ||
| 63 | * // define register definition structures | ||
| 64 | * template<> | ||
| 65 | * struct Regs::Struct<Regs::Value1> { | ||
| 66 | * union { | ||
| 67 | * BitField<0, 4, u32> some_field; | ||
| 68 | * BitField<4, 3, u32> some_other_field; | ||
| 69 | * }; | ||
| 70 | * }; | ||
| 71 | * | ||
| 72 | * Usage in external code (within SomeNamespace scope): | ||
| 73 | * | ||
| 74 | * For a register which maps to a single index: | ||
| 75 | * registers.Get<Regs::Value1>().some_field = some_value; | ||
| 76 | * | ||
| 77 | * For a register which maps to different indices, e.g. a group of similar registers | ||
| 78 | * registers.Get<Regs::Value1>(index).some_field = some_value; | ||
| 79 | * | ||
| 80 | * | ||
| 81 | * @tparam BaseType Base type used for storing individual registers, e.g. u32 | ||
| 82 | * @tparam RegDefinition Class defining an enumeration called "Id" and a template<Id id> struct, as described above. | ||
| 83 | * @note RegDefinition::Id needs to have an enum value called NumIds defining the number of registers to be allocated. | ||
| 84 | */ | ||
| 85 | template<typename BaseType, typename RegDefinition> | ||
| 86 | struct RegisterSet { | ||
| 87 | // Register IDs | ||
| 88 | using Id = typename RegDefinition::Id; | ||
| 89 | |||
| 90 | // type used for *this | ||
| 91 | using ThisType = RegisterSet<BaseType, RegDefinition>; | ||
| 92 | |||
| 93 | // Register definition structs, defined in RegDefinition | ||
| 94 | template<Id id> | ||
| 95 | using Struct = typename RegDefinition::template Struct<id>; | ||
| 96 | |||
| 97 | |||
| 98 | /* | ||
| 99 | * Lookup register with the given id and return it as the corresponding structure type. | ||
| 100 | * @note This just forwards the arguments to Get(Id). | ||
| 101 | */ | ||
| 102 | template<Id id> | ||
| 103 | const Struct<id>& Get() const { | ||
| 104 | return Get<id>(id); | ||
| 105 | } | ||
| 106 | |||
| 107 | /* | ||
| 108 | * Lookup register with the given id and return it as the corresponding structure type. | ||
| 109 | * @note This just forwards the arguments to Get(Id). | ||
| 110 | */ | ||
| 111 | template<Id id> | ||
| 112 | Struct<id>& Get() { | ||
| 113 | return Get<id>(id); | ||
| 114 | } | ||
| 115 | |||
| 116 | /* | ||
| 117 | * Lookup register with the given index and return it as the corresponding structure type. | ||
| 118 | * @todo Is this portable with regards to structures larger than BaseType? | ||
| 119 | * @note if index==id, you don't need to specify the function parameter. | ||
| 120 | */ | ||
| 121 | template<Id id> | ||
| 122 | const Struct<id>& Get(const Id& index) const { | ||
| 123 | const int idx = static_cast<size_t>(index); | ||
| 124 | return *reinterpret_cast<const Struct<id>*>(&raw[idx]); | ||
| 125 | } | ||
| 126 | |||
| 127 | /* | ||
| 128 | * Lookup register with the given index and return it as the corresponding structure type. | ||
| 129 | * @note This just forwards the arguments to the const version of Get(Id). | ||
| 130 | * @note if index==id, you don't need to specify the function parameter. | ||
| 131 | */ | ||
| 132 | template<Id id> | ||
| 133 | Struct<id>& Get(const Id& index) { | ||
| 134 | return const_cast<Struct<id>&>(GetThis().Get<id>(index)); | ||
| 135 | } | ||
| 136 | |||
| 137 | /* | ||
| 138 | * Plain array access. | ||
| 139 | * @note If you want to have this casted to a register defininition struct, use Get() instead. | ||
| 140 | */ | ||
| 141 | const BaseType& operator[] (const Id& id) const { | ||
| 142 | return raw[static_cast<size_t>(id)]; | ||
| 143 | } | ||
| 144 | |||
| 145 | /* | ||
| 146 | * Plain array access. | ||
| 147 | * @note If you want to have this casted to a register defininition struct, use Get() instead. | ||
| 148 | * @note This operator just forwards its argument to the const version. | ||
| 149 | */ | ||
| 150 | BaseType& operator[] (const Id& id) { | ||
| 151 | return const_cast<BaseType&>(GetThis()[id]); | ||
| 152 | } | ||
| 153 | |||
| 154 | private: | ||
| 155 | /* | ||
| 156 | * Returns a const reference to "this". | ||
| 157 | */ | ||
| 158 | const ThisType& GetThis() const { | ||
| 159 | return static_cast<const ThisType&>(*this); | ||
| 160 | } | ||
| 161 | |||
| 162 | BaseType raw[Id::NumIds]; | ||
| 163 | }; | ||
diff --git a/src/core/hle/service/gsp.cpp b/src/core/hle/service/gsp.cpp index e241b31c8..08e65612e 100644 --- a/src/core/hle/service/gsp.cpp +++ b/src/core/hle/service/gsp.cpp | |||
| @@ -173,11 +173,11 @@ void ExecuteCommand(const Command& command) { | |||
| 173 | case CommandId::SET_COMMAND_LIST_LAST: | 173 | case CommandId::SET_COMMAND_LIST_LAST: |
| 174 | { | 174 | { |
| 175 | auto& params = command.set_command_list_last; | 175 | auto& params = command.set_command_list_last; |
| 176 | WriteGPURegister(GPU::Regs::CommandProcessor + 2, params.address >> 3); | 176 | WriteGPURegister(GPU_REG_INDEX(command_processor_config.address), params.address >> 3); |
| 177 | WriteGPURegister(GPU::Regs::CommandProcessor, params.size >> 3); | 177 | WriteGPURegister(GPU_REG_INDEX(command_processor_config.size), params.size >> 3); |
| 178 | 178 | ||
| 179 | // TODO: Not sure if we are supposed to always write this .. seems to trigger processing though | 179 | // TODO: Not sure if we are supposed to always write this .. seems to trigger processing though |
| 180 | WriteGPURegister(GPU::Regs::CommandProcessor + 4, 1); | 180 | WriteGPURegister(GPU_REG_INDEX(command_processor_config.trigger), 1); |
| 181 | 181 | ||
| 182 | // TODO: Move this to GPU | 182 | // TODO: Move this to GPU |
| 183 | // TODO: Not sure what units the size is measured in | 183 | // TODO: Not sure what units the size is measured in |
| @@ -193,15 +193,15 @@ void ExecuteCommand(const Command& command) { | |||
| 193 | case CommandId::SET_MEMORY_FILL: | 193 | case CommandId::SET_MEMORY_FILL: |
| 194 | { | 194 | { |
| 195 | auto& params = command.memory_fill; | 195 | auto& params = command.memory_fill; |
| 196 | WriteGPURegister(GPU::Regs::MemoryFill, params.start1 >> 3); | 196 | WriteGPURegister(GPU_REG_INDEX(memory_fill_config[0].address_start), params.start1 >> 3); |
| 197 | WriteGPURegister(GPU::Regs::MemoryFill + 1, params.end1 >> 3); | 197 | WriteGPURegister(GPU_REG_INDEX(memory_fill_config[0].address_end), params.end1 >> 3); |
| 198 | WriteGPURegister(GPU::Regs::MemoryFill + 2, params.end1 - params.start1); | 198 | WriteGPURegister(GPU_REG_INDEX(memory_fill_config[0].size), params.end1 - params.start1); |
| 199 | WriteGPURegister(GPU::Regs::MemoryFill + 3, params.value1); | 199 | WriteGPURegister(GPU_REG_INDEX(memory_fill_config[0].value), params.value1); |
| 200 | 200 | ||
| 201 | WriteGPURegister(GPU::Regs::MemoryFill + 4, params.start2 >> 3); | 201 | WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].address_start), params.start2 >> 3); |
| 202 | WriteGPURegister(GPU::Regs::MemoryFill + 5, params.end2 >> 3); | 202 | WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].address_end), params.end2 >> 3); |
| 203 | WriteGPURegister(GPU::Regs::MemoryFill + 6, params.end2 - params.start2); | 203 | WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].size), params.end2 - params.start2); |
| 204 | WriteGPURegister(GPU::Regs::MemoryFill + 7, params.value2); | 204 | WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].value), params.value2); |
| 205 | break; | 205 | break; |
| 206 | } | 206 | } |
| 207 | 207 | ||
| @@ -220,15 +220,15 @@ void ExecuteCommand(const Command& command) { | |||
| 220 | case CommandId::SET_TEXTURE_COPY: | 220 | case CommandId::SET_TEXTURE_COPY: |
| 221 | { | 221 | { |
| 222 | auto& params = command.image_copy; | 222 | auto& params = command.image_copy; |
| 223 | WriteGPURegister(GPU::Regs::DisplayTransfer, params.in_buffer_address >> 3); | 223 | WriteGPURegister(GPU_REG_INDEX(display_transfer_config.input_address), params.in_buffer_address >> 3); |
| 224 | WriteGPURegister(GPU::Regs::DisplayTransfer + 1, params.out_buffer_address >> 3); | 224 | WriteGPURegister(GPU_REG_INDEX(display_transfer_config.output_address), params.out_buffer_address >> 3); |
| 225 | WriteGPURegister(GPU::Regs::DisplayTransfer + 3, params.in_buffer_size); | 225 | WriteGPURegister(GPU_REG_INDEX(display_transfer_config.input_size), params.in_buffer_size); |
| 226 | WriteGPURegister(GPU::Regs::DisplayTransfer + 2, params.out_buffer_size); | 226 | WriteGPURegister(GPU_REG_INDEX(display_transfer_config.output_size), params.out_buffer_size); |
| 227 | WriteGPURegister(GPU::Regs::DisplayTransfer + 4, params.flags); | 227 | WriteGPURegister(GPU_REG_INDEX(display_transfer_config.flags), params.flags); |
| 228 | 228 | ||
| 229 | // TODO: Should this only be ORed with 1 for texture copies? | 229 | // TODO: Should this only be ORed with 1 for texture copies? |
| 230 | // trigger transfer | 230 | // trigger transfer |
| 231 | WriteGPURegister(GPU::Regs::DisplayTransfer + 6, 1); | 231 | WriteGPURegister(GPU_REG_INDEX(display_transfer_config.trigger), 1); |
| 232 | break; | 232 | break; |
| 233 | } | 233 | } |
| 234 | 234 | ||
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index d94c2329b..fd40f8ac0 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp | |||
| @@ -19,7 +19,7 @@ | |||
| 19 | 19 | ||
| 20 | namespace GPU { | 20 | namespace GPU { |
| 21 | 21 | ||
| 22 | RegisterSet<u32, Regs> g_regs; | 22 | Regs g_regs; |
| 23 | 23 | ||
| 24 | u32 g_cur_line = 0; ///< Current vertical screen line | 24 | u32 g_cur_line = 0; ///< Current vertical screen line |
| 25 | u64 g_last_line_ticks = 0; ///< CPU tick count from last vertical screen line | 25 | u64 g_last_line_ticks = 0; ///< CPU tick count from last vertical screen line |
| @@ -32,8 +32,8 @@ void SetFramebufferLocation(const FramebufferLocation mode) { | |||
| 32 | switch (mode) { | 32 | switch (mode) { |
| 33 | case FRAMEBUFFER_LOCATION_FCRAM: | 33 | case FRAMEBUFFER_LOCATION_FCRAM: |
| 34 | { | 34 | { |
| 35 | auto& framebuffer_top = g_regs.Get<Regs::FramebufferTop>(); | 35 | auto& framebuffer_top = g_regs.framebuffer_config[0]; |
| 36 | auto& framebuffer_sub = g_regs.Get<Regs::FramebufferBottom>(); | 36 | auto& framebuffer_sub = g_regs.framebuffer_config[1]; |
| 37 | 37 | ||
| 38 | framebuffer_top.address_left1 = PADDR_TOP_LEFT_FRAME1; | 38 | framebuffer_top.address_left1 = PADDR_TOP_LEFT_FRAME1; |
| 39 | framebuffer_top.address_left2 = PADDR_TOP_LEFT_FRAME2; | 39 | framebuffer_top.address_left2 = PADDR_TOP_LEFT_FRAME2; |
| @@ -48,8 +48,8 @@ void SetFramebufferLocation(const FramebufferLocation mode) { | |||
| 48 | 48 | ||
| 49 | case FRAMEBUFFER_LOCATION_VRAM: | 49 | case FRAMEBUFFER_LOCATION_VRAM: |
| 50 | { | 50 | { |
| 51 | auto& framebuffer_top = g_regs.Get<Regs::FramebufferTop>(); | 51 | auto& framebuffer_top = g_regs.framebuffer_config[0]; |
| 52 | auto& framebuffer_sub = g_regs.Get<Regs::FramebufferBottom>(); | 52 | auto& framebuffer_sub = g_regs.framebuffer_config[1]; |
| 53 | 53 | ||
| 54 | framebuffer_top.address_left1 = PADDR_VRAM_TOP_LEFT_FRAME1; | 54 | framebuffer_top.address_left1 = PADDR_VRAM_TOP_LEFT_FRAME1; |
| 55 | framebuffer_top.address_left2 = PADDR_VRAM_TOP_LEFT_FRAME2; | 55 | framebuffer_top.address_left2 = PADDR_VRAM_TOP_LEFT_FRAME2; |
| @@ -107,13 +107,12 @@ inline void Read(T &var, const u32 raw_addr) { | |||
| 107 | int index = addr / 4; | 107 | int index = addr / 4; |
| 108 | 108 | ||
| 109 | // Reads other than u32 are untested, so I'd rather have them abort than silently fail | 109 | // Reads other than u32 are untested, so I'd rather have them abort than silently fail |
| 110 | if (index >= Regs::NumIds || !std::is_same<T,u32>::value) | 110 | if (index >= Regs::NumIds() || !std::is_same<T,u32>::value) { |
| 111 | { | ||
| 112 | ERROR_LOG(GPU, "unknown Read%d @ 0x%08X", sizeof(var) * 8, addr); | 111 | ERROR_LOG(GPU, "unknown Read%d @ 0x%08X", sizeof(var) * 8, addr); |
| 113 | return; | 112 | return; |
| 114 | } | 113 | } |
| 115 | 114 | ||
| 116 | var = g_regs[static_cast<Regs::Id>(addr / 4)]; | 115 | var = g_regs[addr / 4]; |
| 117 | } | 116 | } |
| 118 | 117 | ||
| 119 | template <typename T> | 118 | template <typename T> |
| @@ -122,22 +121,22 @@ inline void Write(u32 addr, const T data) { | |||
| 122 | int index = addr / 4; | 121 | int index = addr / 4; |
| 123 | 122 | ||
| 124 | // Writes other than u32 are untested, so I'd rather have them abort than silently fail | 123 | // Writes other than u32 are untested, so I'd rather have them abort than silently fail |
| 125 | if (index >= Regs::NumIds || !std::is_same<T,u32>::value) | 124 | if (index >= Regs::NumIds() || !std::is_same<T,u32>::value) { |
| 126 | { | ||
| 127 | ERROR_LOG(GPU, "unknown Write%d 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr); | 125 | ERROR_LOG(GPU, "unknown Write%d 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr); |
| 128 | return; | 126 | return; |
| 129 | } | 127 | } |
| 130 | 128 | ||
| 131 | g_regs[static_cast<Regs::Id>(index)] = data; | 129 | g_regs[index] = data; |
| 132 | 130 | ||
| 133 | switch (static_cast<Regs::Id>(index)) { | 131 | switch (index) { |
| 134 | 132 | ||
| 135 | // Memory fills are triggered once the fill value is written. | 133 | // Memory fills are triggered once the fill value is written. |
| 136 | // NOTE: This is not verified. | 134 | // NOTE: This is not verified. |
| 137 | case Regs::MemoryFill + 3: | 135 | case GPU_REG_INDEX_WORKAROUND(memory_fill_config[0].value, 0x00004 + 0x3): |
| 138 | case Regs::MemoryFill + 7: | 136 | case GPU_REG_INDEX_WORKAROUND(memory_fill_config[1].value, 0x00008 + 0x3): |
| 139 | { | 137 | { |
| 140 | const auto& config = g_regs.Get<Regs::MemoryFill>(static_cast<Regs::Id>(index - 3)); | 138 | const bool is_second_filler = (index != GPU_REG_INDEX(memory_fill_config[0].value)); |
| 139 | const auto& config = g_regs.memory_fill_config[is_second_filler]; | ||
| 141 | 140 | ||
| 142 | // TODO: Not sure if this check should be done at GSP level instead | 141 | // TODO: Not sure if this check should be done at GSP level instead |
| 143 | if (config.address_start) { | 142 | if (config.address_start) { |
| @@ -152,9 +151,9 @@ inline void Write(u32 addr, const T data) { | |||
| 152 | break; | 151 | break; |
| 153 | } | 152 | } |
| 154 | 153 | ||
| 155 | case Regs::DisplayTransfer + 6: | 154 | case GPU_REG_INDEX(display_transfer_config.trigger): |
| 156 | { | 155 | { |
| 157 | const auto& config = g_regs.Get<Regs::DisplayTransfer>(); | 156 | const auto& config = g_regs.display_transfer_config; |
| 158 | if (config.trigger & 1) { | 157 | if (config.trigger & 1) { |
| 159 | u8* source_pointer = Memory::GetPointer(config.GetPhysicalInputAddress()); | 158 | u8* source_pointer = Memory::GetPointer(config.GetPhysicalInputAddress()); |
| 160 | u8* dest_pointer = Memory::GetPointer(config.GetPhysicalOutputAddress()); | 159 | u8* dest_pointer = Memory::GetPointer(config.GetPhysicalOutputAddress()); |
| @@ -221,13 +220,13 @@ inline void Write(u32 addr, const T data) { | |||
| 221 | break; | 220 | break; |
| 222 | } | 221 | } |
| 223 | 222 | ||
| 224 | case Regs::CommandProcessor + 4: | 223 | case GPU_REG_INDEX(command_processor_config.trigger): |
| 225 | { | 224 | { |
| 226 | const auto& config = g_regs.Get<Regs::CommandProcessor>(); | 225 | const auto& config = g_regs.command_processor_config; |
| 227 | if (config.trigger & 1) | 226 | if (config.trigger & 1) |
| 228 | { | 227 | { |
| 229 | // u32* buffer = (u32*)Memory::GetPointer(config.address << 3); | 228 | // u32* buffer = (u32*)Memory::GetPointer(config.GetPhysicalAddress()); |
| 230 | ERROR_LOG(GPU, "Beginning 0x%08x bytes of commands from address 0x%08x", config.size, config.address << 3); | 229 | ERROR_LOG(GPU, "Beginning 0x%08x bytes of commands from address 0x%08x", config.size, config.GetPhysicalAddress()); |
| 231 | // TODO: Process command list! | 230 | // TODO: Process command list! |
| 232 | } | 231 | } |
| 233 | break; | 232 | break; |
| @@ -252,7 +251,7 @@ template void Write<u8>(u32 addr, const u8 data); | |||
| 252 | 251 | ||
| 253 | /// Update hardware | 252 | /// Update hardware |
| 254 | void Update() { | 253 | void Update() { |
| 255 | auto& framebuffer_top = g_regs.Get<Regs::FramebufferTop>(); | 254 | auto& framebuffer_top = g_regs.framebuffer_config[0]; |
| 256 | u64 current_ticks = Core::g_app_core->GetTicks(); | 255 | u64 current_ticks = Core::g_app_core->GetTicks(); |
| 257 | 256 | ||
| 258 | // Synchronize line... | 257 | // Synchronize line... |
| @@ -280,8 +279,8 @@ void Init() { | |||
| 280 | // SetFramebufferLocation(FRAMEBUFFER_LOCATION_FCRAM); | 279 | // SetFramebufferLocation(FRAMEBUFFER_LOCATION_FCRAM); |
| 281 | SetFramebufferLocation(FRAMEBUFFER_LOCATION_VRAM); | 280 | SetFramebufferLocation(FRAMEBUFFER_LOCATION_VRAM); |
| 282 | 281 | ||
| 283 | auto& framebuffer_top = g_regs.Get<Regs::FramebufferTop>(); | 282 | auto& framebuffer_top = g_regs.framebuffer_config[0]; |
| 284 | auto& framebuffer_sub = g_regs.Get<Regs::FramebufferBottom>(); | 283 | auto& framebuffer_sub = g_regs.framebuffer_config[1]; |
| 285 | // TODO: Width should be 240 instead? | 284 | // TODO: Width should be 240 instead? |
| 286 | framebuffer_top.width = 480; | 285 | framebuffer_top.width = 480; |
| 287 | framebuffer_top.height = 400; | 286 | framebuffer_top.height = 400; |
diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h index 42f18a0e7..3065da891 100644 --- a/src/core/hw/gpu.h +++ b/src/core/hw/gpu.h | |||
| @@ -4,32 +4,57 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <cstddef> | ||
| 8 | |||
| 7 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 8 | #include "common/bit_field.h" | 10 | #include "common/bit_field.h" |
| 9 | #include "common/register_set.h" | ||
| 10 | 11 | ||
| 11 | namespace GPU { | 12 | namespace GPU { |
| 12 | 13 | ||
| 13 | static const u32 kFrameCycles = 268123480 / 60; ///< 268MHz / 60 frames per second | 14 | static const u32 kFrameCycles = 268123480 / 60; ///< 268MHz / 60 frames per second |
| 14 | static const u32 kFrameTicks = kFrameCycles / 3; ///< Approximate number of instructions/frame | 15 | static const u32 kFrameTicks = kFrameCycles / 3; ///< Approximate number of instructions/frame |
| 15 | 16 | ||
| 17 | // Returns index corresponding to the Regs member labeled by field_name | ||
| 18 | // TODO: Due to Visual studio bug 209229, offsetof does not return constant expressions | ||
| 19 | // when used with array elements (e.g. GPU_REG_INDEX(memory_fill_config[0])). | ||
| 20 | // For details cf. https://connect.microsoft.com/VisualStudio/feedback/details/209229/offsetof-does-not-produce-a-constant-expression-for-array-members | ||
| 21 | // Hopefully, this will be fixed sometime in the future. | ||
| 22 | // For lack of better alternatives, we currently hardcode the offsets when constant | ||
| 23 | // expressions are needed via GPU_REG_INDEX_WORKAROUND (on sane compilers, static_asserts | ||
| 24 | // will then make sure the offsets indeed match the automatically calculated ones). | ||
| 25 | #define GPU_REG_INDEX(field_name) (offsetof(GPU::Regs, field_name) / sizeof(u32)) | ||
| 26 | #if defined(_MSC_VER) | ||
| 27 | #define GPU_REG_INDEX_WORKAROUND(field_name, backup_workaround_index) (backup_workaround_index) | ||
| 28 | #else | ||
| 29 | // NOTE: Yeah, hacking in a static_assert here just to workaround the lacking MSVC compiler | ||
| 30 | // really is this annoying. This macro just forwards its first argument to GPU_REG_INDEX | ||
| 31 | // and then performs a (no-op) cast to size_t iff the second argument matches the expected | ||
| 32 | // field offset. Otherwise, the compiler will fail to compile this code. | ||
| 33 | #define GPU_REG_INDEX_WORKAROUND(field_name, backup_workaround_index) \ | ||
| 34 | ((typename std::enable_if<backup_workaround_index == GPU_REG_INDEX(field_name), size_t>::type)GPU_REG_INDEX(field_name)) | ||
| 35 | #endif | ||
| 36 | |||
| 16 | // MMIO region 0x1EFxxxxx | 37 | // MMIO region 0x1EFxxxxx |
| 17 | struct Regs { | 38 | struct Regs { |
| 18 | enum Id : u32 { | ||
| 19 | MemoryFill = 0x00004, // + 5,6,7; second block at 8-11 | ||
| 20 | |||
| 21 | FramebufferTop = 0x00117, // + 11a,11b,11c,11d(?),11e...126 | ||
| 22 | FramebufferBottom = 0x00157, // + 15a,15b,15c,15d(?),15e...166 | ||
| 23 | |||
| 24 | DisplayTransfer = 0x00300, // + 301,302,303,304,305,306 | ||
| 25 | |||
| 26 | CommandProcessor = 0x00638, // + 63a,63c | ||
| 27 | 39 | ||
| 28 | NumIds = 0x01000 | 40 | // helper macro to properly align structure members. |
| 29 | }; | 41 | // Calling INSERT_PADDING_WORDS will add a new member variable with a name like "pad121", |
| 30 | 42 | // depending on the current source line to make sure variable names are unique. | |
| 31 | template<Id id> | 43 | #define INSERT_PADDING_WORDS_HELPER1(x, y) x ## y |
| 32 | struct Struct; | 44 | #define INSERT_PADDING_WORDS_HELPER2(x, y) INSERT_PADDING_WORDS_HELPER1(x, y) |
| 45 | #define INSERT_PADDING_WORDS(num_words) u32 INSERT_PADDING_WORDS_HELPER2(pad, __LINE__)[(num_words)]; | ||
| 46 | |||
| 47 | // helper macro to make sure the defined structures are of the expected size. | ||
| 48 | #if defined(_MSC_VER) | ||
| 49 | // TODO: MSVC does not support using sizeof() on non-static data members even though this | ||
| 50 | // is technically allowed since C++11. This macro should be enabled once MSVC adds | ||
| 51 | // support for that. | ||
| 52 | #define ASSERT_MEMBER_SIZE(name, size_in_bytes) | ||
| 53 | #else | ||
| 54 | #define ASSERT_MEMBER_SIZE(name, size_in_bytes) \ | ||
| 55 | static_assert(sizeof(name) == size_in_bytes, \ | ||
| 56 | "Structure size and register block length don't match"); | ||
| 57 | #endif | ||
| 33 | 58 | ||
| 34 | enum class FramebufferFormat : u32 { | 59 | enum class FramebufferFormat : u32 { |
| 35 | RGBA8 = 0, | 60 | RGBA8 = 0, |
| @@ -38,135 +63,191 @@ struct Regs { | |||
| 38 | RGB5A1 = 3, | 63 | RGB5A1 = 3, |
| 39 | RGBA4 = 4, | 64 | RGBA4 = 4, |
| 40 | }; | 65 | }; |
| 41 | }; | ||
| 42 | 66 | ||
| 43 | template<> | 67 | INSERT_PADDING_WORDS(0x4); |
| 44 | struct Regs::Struct<Regs::MemoryFill> { | ||
| 45 | u32 address_start; | ||
| 46 | u32 address_end; // ? | ||
| 47 | u32 size; | ||
| 48 | u32 value; // ? | ||
| 49 | 68 | ||
| 50 | inline u32 GetStartAddress() const { | 69 | struct { |
| 51 | return address_start * 8; | 70 | u32 address_start; |
| 52 | } | 71 | u32 address_end; // ? |
| 72 | u32 size; | ||
| 73 | u32 value; // ? | ||
| 53 | 74 | ||
| 54 | inline u32 GetEndAddress() const { | 75 | inline u32 GetStartAddress() const { |
| 55 | return address_end * 8; | 76 | return DecodeAddressRegister(address_start); |
| 56 | } | 77 | } |
| 57 | }; | ||
| 58 | static_assert(sizeof(Regs::Struct<Regs::MemoryFill>) == 0x10, "Structure size and register block length don't match"); | ||
| 59 | 78 | ||
| 60 | template<> | 79 | inline u32 GetEndAddress() const { |
| 61 | struct Regs::Struct<Regs::FramebufferTop> { | 80 | return DecodeAddressRegister(address_end); |
| 62 | using Format = Regs::FramebufferFormat; | 81 | } |
| 82 | } memory_fill_config[2]; | ||
| 83 | ASSERT_MEMBER_SIZE(memory_fill_config[0], 0x10); | ||
| 63 | 84 | ||
| 64 | union { | 85 | INSERT_PADDING_WORDS(0x10b); |
| 65 | u32 size; | ||
| 66 | 86 | ||
| 67 | BitField< 0, 16, u32> width; | 87 | struct { |
| 68 | BitField<16, 16, u32> height; | 88 | using Format = Regs::FramebufferFormat; |
| 69 | }; | ||
| 70 | 89 | ||
| 71 | u32 pad0[2]; | 90 | union { |
| 91 | u32 size; | ||
| 72 | 92 | ||
| 73 | u32 address_left1; | 93 | BitField< 0, 16, u32> width; |
| 74 | u32 address_left2; | 94 | BitField<16, 16, u32> height; |
| 95 | }; | ||
| 75 | 96 | ||
| 76 | union { | 97 | INSERT_PADDING_WORDS(0x2); |
| 77 | u32 format; | ||
| 78 | 98 | ||
| 79 | BitField< 0, 3, Format> color_format; | 99 | u32 address_left1; |
| 80 | }; | 100 | u32 address_left2; |
| 81 | 101 | ||
| 82 | u32 pad1; | 102 | union { |
| 103 | u32 format; | ||
| 83 | 104 | ||
| 84 | union { | 105 | BitField< 0, 3, Format> color_format; |
| 85 | u32 active_fb; | 106 | }; |
| 86 | 107 | ||
| 87 | // 0: Use parameters ending with "1" | 108 | INSERT_PADDING_WORDS(0x1); |
| 88 | // 1: Use parameters ending with "2" | ||
| 89 | BitField<0, 1, u32> second_fb_active; | ||
| 90 | }; | ||
| 91 | 109 | ||
| 92 | u32 pad2[5]; | 110 | union { |
| 111 | u32 active_fb; | ||
| 93 | 112 | ||
| 94 | // Distance between two pixel rows, in bytes | 113 | // 0: Use parameters ending with "1" |
| 95 | u32 stride; | 114 | // 1: Use parameters ending with "2" |
| 115 | BitField<0, 1, u32> second_fb_active; | ||
| 116 | }; | ||
| 96 | 117 | ||
| 97 | u32 address_right1; | 118 | INSERT_PADDING_WORDS(0x5); |
| 98 | u32 address_right2; | ||
| 99 | }; | ||
| 100 | 119 | ||
| 101 | template<> | 120 | // Distance between two pixel rows, in bytes |
| 102 | struct Regs::Struct<Regs::FramebufferBottom> : public Regs::Struct<Regs::FramebufferTop> { | 121 | u32 stride; |
| 103 | }; | ||
| 104 | static_assert(sizeof(Regs::Struct<Regs::FramebufferTop>) == 0x40, "Structure size and register block length don't match"); | ||
| 105 | 122 | ||
| 106 | template<> | 123 | u32 address_right1; |
| 107 | struct Regs::Struct<Regs::DisplayTransfer> { | 124 | u32 address_right2; |
| 108 | using Format = Regs::FramebufferFormat; | ||
| 109 | 125 | ||
| 110 | u32 input_address; | 126 | INSERT_PADDING_WORDS(0x30); |
| 111 | u32 output_address; | 127 | } framebuffer_config[2]; |
| 128 | ASSERT_MEMBER_SIZE(framebuffer_config[0], 0x100); | ||
| 112 | 129 | ||
| 113 | inline u32 GetPhysicalInputAddress() const { | 130 | INSERT_PADDING_WORDS(0x169); |
| 114 | return input_address * 8; | ||
| 115 | } | ||
| 116 | 131 | ||
| 117 | inline u32 GetPhysicalOutputAddress() const { | 132 | struct { |
| 118 | return output_address * 8; | 133 | using Format = Regs::FramebufferFormat; |
| 119 | } | ||
| 120 | 134 | ||
| 121 | union { | 135 | u32 input_address; |
| 122 | u32 output_size; | 136 | u32 output_address; |
| 123 | 137 | ||
| 124 | BitField< 0, 16, u32> output_width; | 138 | inline u32 GetPhysicalInputAddress() const { |
| 125 | BitField<16, 16, u32> output_height; | 139 | return DecodeAddressRegister(input_address); |
| 126 | }; | 140 | } |
| 127 | 141 | ||
| 128 | union { | 142 | inline u32 GetPhysicalOutputAddress() const { |
| 129 | u32 input_size; | 143 | return DecodeAddressRegister(output_address); |
| 144 | } | ||
| 130 | 145 | ||
| 131 | BitField< 0, 16, u32> input_width; | 146 | union { |
| 132 | BitField<16, 16, u32> input_height; | 147 | u32 output_size; |
| 133 | }; | ||
| 134 | 148 | ||
| 135 | union { | 149 | BitField< 0, 16, u32> output_width; |
| 136 | u32 flags; | 150 | BitField<16, 16, u32> output_height; |
| 151 | }; | ||
| 137 | 152 | ||
| 138 | BitField< 0, 1, u32> flip_data; // flips input data horizontally (TODO) if true | 153 | union { |
| 139 | BitField< 8, 3, Format> input_format; | 154 | u32 input_size; |
| 140 | BitField<12, 3, Format> output_format; | ||
| 141 | BitField<16, 1, u32> output_tiled; // stores output in a tiled format | ||
| 142 | }; | ||
| 143 | 155 | ||
| 144 | u32 unknown; | 156 | BitField< 0, 16, u32> input_width; |
| 157 | BitField<16, 16, u32> input_height; | ||
| 158 | }; | ||
| 145 | 159 | ||
| 146 | // it seems that writing to this field triggers the display transfer | 160 | union { |
| 147 | u32 trigger; | 161 | u32 flags; |
| 148 | }; | ||
| 149 | static_assert(sizeof(Regs::Struct<Regs::DisplayTransfer>) == 0x1C, "Structure size and register block length don't match"); | ||
| 150 | 162 | ||
| 151 | template<> | 163 | BitField< 0, 1, u32> flip_data; // flips input data horizontally (TODO) if true |
| 152 | struct Regs::Struct<Regs::CommandProcessor> { | 164 | BitField< 8, 3, Format> input_format; |
| 153 | // command list size | 165 | BitField<12, 3, Format> output_format; |
| 154 | u32 size; | 166 | BitField<16, 1, u32> output_tiled; // stores output in a tiled format |
| 167 | }; | ||
| 155 | 168 | ||
| 156 | u32 pad0; | 169 | INSERT_PADDING_WORDS(0x1); |
| 157 | 170 | ||
| 158 | // command list address | 171 | // it seems that writing to this field triggers the display transfer |
| 159 | u32 address; | 172 | u32 trigger; |
| 173 | } display_transfer_config; | ||
| 174 | ASSERT_MEMBER_SIZE(display_transfer_config, 0x1c); | ||
| 160 | 175 | ||
| 161 | u32 pad1; | 176 | INSERT_PADDING_WORDS(0x331); |
| 162 | 177 | ||
| 163 | // it seems that writing to this field triggers command list processing | 178 | struct { |
| 164 | u32 trigger; | 179 | // command list size |
| 165 | }; | 180 | u32 size; |
| 166 | static_assert(sizeof(Regs::Struct<Regs::CommandProcessor>) == 0x14, "Structure size and register block length don't match"); | 181 | |
| 182 | INSERT_PADDING_WORDS(0x1); | ||
| 183 | |||
| 184 | // command list address | ||
| 185 | u32 address; | ||
| 186 | |||
| 187 | INSERT_PADDING_WORDS(0x1); | ||
| 188 | |||
| 189 | // it seems that writing to this field triggers command list processing | ||
| 190 | u32 trigger; | ||
| 167 | 191 | ||
| 192 | inline u32 GetPhysicalAddress() const { | ||
| 193 | return DecodeAddressRegister(address); | ||
| 194 | } | ||
| 195 | } command_processor_config; | ||
| 196 | ASSERT_MEMBER_SIZE(command_processor_config, 0x14); | ||
| 168 | 197 | ||
| 169 | extern RegisterSet<u32, Regs> g_regs; | 198 | INSERT_PADDING_WORDS(0x9c3); |
| 199 | |||
| 200 | #undef INSERT_PADDING_WORDS_HELPER1 | ||
| 201 | #undef INSERT_PADDING_WORDS_HELPER2 | ||
| 202 | #undef INSERT_PADDING_WORDS | ||
| 203 | |||
| 204 | static inline int NumIds() { | ||
| 205 | return sizeof(Regs) / sizeof(u32); | ||
| 206 | } | ||
| 207 | |||
| 208 | u32& operator [] (int index) const { | ||
| 209 | u32* content = (u32*)this; | ||
| 210 | return content[index]; | ||
| 211 | } | ||
| 212 | |||
| 213 | u32& operator [] (int index) { | ||
| 214 | u32* content = (u32*)this; | ||
| 215 | return content[index]; | ||
| 216 | } | ||
| 217 | |||
| 218 | private: | ||
| 219 | /* | ||
| 220 | * Most physical addresses which GPU registers refer to are 8-byte aligned. | ||
| 221 | * This function should be used to get the address from a raw register value. | ||
| 222 | */ | ||
| 223 | static inline u32 DecodeAddressRegister(u32 register_value) { | ||
| 224 | return register_value * 8; | ||
| 225 | } | ||
| 226 | }; | ||
| 227 | static_assert(std::is_standard_layout<Regs>::value, "Structure does not use standard layout"); | ||
| 228 | |||
| 229 | // TODO: MSVC does not support using offsetof() on non-static data members even though this | ||
| 230 | // is technically allowed since C++11. This macro should be enabled once MSVC adds | ||
| 231 | // support for that. | ||
| 232 | #ifndef _MSC_VER | ||
| 233 | #define ASSERT_REG_POSITION(field_name, position) \ | ||
| 234 | static_assert(offsetof(Regs, field_name) == position * 4, \ | ||
| 235 | "Field "#field_name" has invalid position") | ||
| 236 | |||
| 237 | ASSERT_REG_POSITION(memory_fill_config[0], 0x00004); | ||
| 238 | ASSERT_REG_POSITION(memory_fill_config[1], 0x00008); | ||
| 239 | ASSERT_REG_POSITION(framebuffer_config[0], 0x00117); | ||
| 240 | ASSERT_REG_POSITION(framebuffer_config[1], 0x00157); | ||
| 241 | ASSERT_REG_POSITION(display_transfer_config, 0x00300); | ||
| 242 | ASSERT_REG_POSITION(command_processor_config, 0x00638); | ||
| 243 | |||
| 244 | #undef ASSERT_REG_POSITION | ||
| 245 | #endif // !defined(_MSC_VER) | ||
| 246 | |||
| 247 | // The total number of registers is chosen arbitrarily, but let's make sure it's not some odd value anyway. | ||
| 248 | static_assert(sizeof(Regs) == 0x1000 * sizeof(u32), "Invalid total size of register set"); | ||
| 249 | |||
| 250 | extern Regs g_regs; | ||
| 170 | 251 | ||
| 171 | enum { | 252 | enum { |
| 172 | TOP_ASPECT_X = 0x5, | 253 | TOP_ASPECT_X = 0x5, |
diff --git a/src/video_core/pica.h b/src/video_core/pica.h index f0fa3aba9..d64559d72 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h | |||
| @@ -4,127 +4,173 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <cstddef> | ||
| 7 | #include <initializer_list> | 8 | #include <initializer_list> |
| 8 | #include <map> | 9 | #include <map> |
| 9 | 10 | ||
| 10 | #include "common/bit_field.h" | 11 | #include "common/bit_field.h" |
| 11 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 12 | #include "common/register_set.h" | ||
| 13 | 13 | ||
| 14 | namespace Pica { | 14 | namespace Pica { |
| 15 | 15 | ||
| 16 | // Returns index corresponding to the Regs member labeled by field_name | ||
| 17 | // TODO: Due to Visual studio bug 209229, offsetof does not return constant expressions | ||
| 18 | // when used with array elements (e.g. PICA_REG_INDEX(vs_uniform_setup.set_value[1])). | ||
| 19 | // For details cf. https://connect.microsoft.com/VisualStudio/feedback/details/209229/offsetof-does-not-produce-a-constant-expression-for-array-members | ||
| 20 | // Hopefully, this will be fixed sometime in the future. | ||
| 21 | // For lack of better alternatives, we currently hardcode the offsets when constant | ||
| 22 | // expressions are needed via PICA_REG_INDEX_WORKAROUND (on sane compilers, static_asserts | ||
| 23 | // will then make sure the offsets indeed match the automatically calculated ones). | ||
| 24 | #define PICA_REG_INDEX(field_name) (offsetof(Pica::Regs, field_name) / sizeof(u32)) | ||
| 25 | #if defined(_MSC_VER) | ||
| 26 | #define PICA_REG_INDEX_WORKAROUND(field_name, backup_workaround_index) (backup_workaround_index) | ||
| 27 | #else | ||
| 28 | // NOTE: Yeah, hacking in a static_assert here just to workaround the lacking MSVC compiler | ||
| 29 | // really is this annoying. This macro just forwards its first argument to PICA_REG_INDEX | ||
| 30 | // and then performs a (no-op) cast to size_t iff the second argument matches the expected | ||
| 31 | // field offset. Otherwise, the compiler will fail to compile this code. | ||
| 32 | #define PICA_REG_INDEX_WORKAROUND(field_name, backup_workaround_index) \ | ||
| 33 | ((typename std::enable_if<backup_workaround_index == PICA_REG_INDEX(field_name), size_t>::type)PICA_REG_INDEX(field_name)) | ||
| 34 | #endif // _MSC_VER | ||
| 35 | |||
| 16 | struct Regs { | 36 | struct Regs { |
| 17 | enum Id : u32 { | 37 | |
| 18 | ViewportSizeX = 0x41, | 38 | // helper macro to properly align structure members. |
| 19 | ViewportInvSizeX = 0x42, | 39 | // Calling INSERT_PADDING_WORDS will add a new member variable with a name like "pad121", |
| 20 | ViewportSizeY = 0x43, | 40 | // depending on the current source line to make sure variable names are unique. |
| 21 | ViewportInvSizeY = 0x44, | 41 | #define INSERT_PADDING_WORDS_HELPER1(x, y) x ## y |
| 22 | ViewportCorner = 0x68, | 42 | #define INSERT_PADDING_WORDS_HELPER2(x, y) INSERT_PADDING_WORDS_HELPER1(x, y) |
| 23 | DepthBufferFormat = 0x116, | 43 | #define INSERT_PADDING_WORDS(num_words) u32 INSERT_PADDING_WORDS_HELPER2(pad, __LINE__)[(num_words)]; |
| 24 | ColorBufferFormat = 0x117, | 44 | |
| 25 | DepthBufferAddress = 0x11C, | 45 | INSERT_PADDING_WORDS(0x41); |
| 26 | ColorBufferAddress = 0x11D, | 46 | |
| 27 | ColorBufferSize = 0x11E, | 47 | BitField<0, 24, u32> viewport_size_x; |
| 28 | 48 | INSERT_PADDING_WORDS(1); | |
| 29 | VertexArrayBaseAddr = 0x200, | 49 | BitField<0, 24, u32> viewport_size_y; |
| 30 | VertexDescriptor = 0x201, // 0x202 | 50 | |
| 31 | VertexAttributeOffset = 0x203, // 0x206,0x209,0x20C,0x20F,0x212,0x215,0x218,0x21B,0x21E,0x221,0x224 | 51 | INSERT_PADDING_WORDS(0x1bc); |
| 32 | VertexAttributeInfo0 = 0x204, // 0x207,0x20A,0x20D,0x210,0x213,0x216,0x219,0x21C,0x21F,0x222,0x225 | 52 | |
| 33 | VertexAttributeInfo1 = 0x205, // 0x208,0x20B,0x20E,0x211,0x214,0x217,0x21A,0x21D,0x220,0x223,0x226 | 53 | union { |
| 34 | 54 | enum class Format : u64 { | |
| 35 | NumIds = 0x300, | 55 | BYTE = 0, |
| 36 | }; | 56 | UBYTE = 1, |
| 37 | 57 | SHORT = 2, | |
| 38 | template<Id id> | 58 | FLOAT = 3, |
| 39 | union Struct; | 59 | }; |
| 60 | |||
| 61 | BitField< 0, 2, Format> format0; | ||
| 62 | BitField< 2, 2, u64> size0; // number of elements minus 1 | ||
| 63 | BitField< 4, 2, Format> format1; | ||
| 64 | BitField< 6, 2, u64> size1; | ||
| 65 | BitField< 8, 2, Format> format2; | ||
| 66 | BitField<10, 2, u64> size2; | ||
| 67 | BitField<12, 2, Format> format3; | ||
| 68 | BitField<14, 2, u64> size3; | ||
| 69 | BitField<16, 2, Format> format4; | ||
| 70 | BitField<18, 2, u64> size4; | ||
| 71 | BitField<20, 2, Format> format5; | ||
| 72 | BitField<22, 2, u64> size5; | ||
| 73 | BitField<24, 2, Format> format6; | ||
| 74 | BitField<26, 2, u64> size6; | ||
| 75 | BitField<28, 2, Format> format7; | ||
| 76 | BitField<30, 2, u64> size7; | ||
| 77 | BitField<32, 2, Format> format8; | ||
| 78 | BitField<34, 2, u64> size8; | ||
| 79 | BitField<36, 2, Format> format9; | ||
| 80 | BitField<38, 2, u64> size9; | ||
| 81 | BitField<40, 2, Format> format10; | ||
| 82 | BitField<42, 2, u64> size10; | ||
| 83 | BitField<44, 2, Format> format11; | ||
| 84 | BitField<46, 2, u64> size11; | ||
| 85 | |||
| 86 | BitField<48, 12, u64> attribute_mask; | ||
| 87 | BitField<60, 4, u64> num_attributes; // number of total attributes minus 1 | ||
| 88 | } vertex_descriptor; | ||
| 89 | |||
| 90 | INSERT_PADDING_WORDS(0xfe); | ||
| 91 | |||
| 92 | #undef INSERT_PADDING_WORDS_HELPER1 | ||
| 93 | #undef INSERT_PADDING_WORDS_HELPER2 | ||
| 94 | #undef INSERT_PADDING_WORDS | ||
| 95 | |||
| 96 | // Map register indices to names readable by humans | ||
| 97 | // Used for debugging purposes, so performance is not an issue here | ||
| 98 | static std::string GetCommandName(int index) { | ||
| 99 | std::map<u32, std::string> map; | ||
| 100 | Regs regs; | ||
| 101 | |||
| 102 | // TODO: MSVC does not support using offsetof() on non-static data members even though this | ||
| 103 | // is technically allowed since C++11. Hence, this functionality is disabled until | ||
| 104 | // MSVC properly supports it. | ||
| 105 | #ifndef _MSC_VER | ||
| 106 | #define ADD_FIELD(name) \ | ||
| 107 | do { \ | ||
| 108 | map.insert({PICA_REG_INDEX(name), #name}); \ | ||
| 109 | for (u32 i = PICA_REG_INDEX(name) + 1; i < PICA_REG_INDEX(name) + sizeof(regs.name) / 4; ++i) \ | ||
| 110 | map.insert({i, #name + std::string("+") + std::to_string(i-PICA_REG_INDEX(name))}); \ | ||
| 111 | } while(false) | ||
| 112 | |||
| 113 | ADD_FIELD(viewport_size_x); | ||
| 114 | ADD_FIELD(viewport_size_y); | ||
| 115 | ADD_FIELD(vertex_descriptor); | ||
| 116 | |||
| 117 | #undef ADD_FIELD | ||
| 118 | #endif // _MSC_VER | ||
| 119 | |||
| 120 | // Return empty string if no match is found | ||
| 121 | return map[index]; | ||
| 122 | } | ||
| 123 | |||
| 124 | static inline int NumIds() { | ||
| 125 | return sizeof(Regs) / sizeof(u32); | ||
| 126 | } | ||
| 127 | |||
| 128 | u32& operator [] (int index) const { | ||
| 129 | u32* content = (u32*)this; | ||
| 130 | return content[index]; | ||
| 131 | } | ||
| 132 | |||
| 133 | u32& operator [] (int index) { | ||
| 134 | u32* content = (u32*)this; | ||
| 135 | return content[index]; | ||
| 136 | } | ||
| 137 | |||
| 138 | private: | ||
| 139 | /* | ||
| 140 | * Most physical addresses which Pica registers refer to are 8-byte aligned. | ||
| 141 | * This function should be used to get the address from a raw register value. | ||
| 142 | */ | ||
| 143 | static inline u32 DecodeAddressRegister(u32 register_value) { | ||
| 144 | return register_value * 8; | ||
| 145 | } | ||
| 40 | }; | 146 | }; |
| 41 | 147 | ||
| 42 | static inline Regs::Id VertexAttributeOffset(int n) | 148 | // TODO: MSVC does not support using offsetof() on non-static data members even though this |
| 43 | { | 149 | // is technically allowed since C++11. This macro should be enabled once MSVC adds |
| 44 | return static_cast<Regs::Id>(0x203 + 3*n); | 150 | // support for that. |
| 45 | } | 151 | #ifndef _MSC_VER |
| 152 | #define ASSERT_REG_POSITION(field_name, position) static_assert(offsetof(Regs, field_name) == position * 4, "Field "#field_name" has invalid position") | ||
| 153 | |||
| 154 | ASSERT_REG_POSITION(viewport_size_x, 0x41); | ||
| 155 | ASSERT_REG_POSITION(viewport_size_y, 0x43); | ||
| 156 | ASSERT_REG_POSITION(vertex_descriptor, 0x200); | ||
| 46 | 157 | ||
| 47 | static inline Regs::Id VertexAttributeInfo0(int n) | 158 | #undef ASSERT_REG_POSITION |
| 48 | { | 159 | #endif // !defined(_MSC_VER) |
| 49 | return static_cast<Regs::Id>(0x204 + 3*n); | ||
| 50 | } | ||
| 51 | 160 | ||
| 52 | static inline Regs::Id VertexAttributeInfo1(int n) | 161 | // The total number of registers is chosen arbitrarily, but let's make sure it's not some odd value anyway. |
| 53 | { | 162 | static_assert(sizeof(Regs) == 0x300 * sizeof(u32), "Invalid total size of register set"); |
| 54 | return static_cast<Regs::Id>(0x205 + 3*n); | ||
| 55 | } | ||
| 56 | 163 | ||
| 57 | union CommandHeader { | 164 | union CommandHeader { |
| 58 | CommandHeader(u32 h) : hex(h) {} | 165 | CommandHeader(u32 h) : hex(h) {} |
| 59 | 166 | ||
| 60 | u32 hex; | 167 | u32 hex; |
| 61 | 168 | ||
| 62 | BitField< 0, 16, Regs::Id> cmd_id; | 169 | BitField< 0, 16, u32> cmd_id; |
| 63 | BitField<16, 4, u32> parameter_mask; | 170 | BitField<16, 4, u32> parameter_mask; |
| 64 | BitField<20, 11, u32> extra_data_length; | 171 | BitField<20, 11, u32> extra_data_length; |
| 65 | BitField<31, 1, u32> group_commands; | 172 | BitField<31, 1, u32> group_commands; |
| 66 | }; | 173 | }; |
| 67 | 174 | ||
| 68 | static std::map<Regs::Id, const char*> command_names = { | ||
| 69 | {Regs::ViewportSizeX, "ViewportSizeX" }, | ||
| 70 | {Regs::ViewportInvSizeX, "ViewportInvSizeX" }, | ||
| 71 | {Regs::ViewportSizeY, "ViewportSizeY" }, | ||
| 72 | {Regs::ViewportInvSizeY, "ViewportInvSizeY" }, | ||
| 73 | {Regs::ViewportCorner, "ViewportCorner" }, | ||
| 74 | {Regs::DepthBufferFormat, "DepthBufferFormat" }, | ||
| 75 | {Regs::ColorBufferFormat, "ColorBufferFormat" }, | ||
| 76 | {Regs::DepthBufferAddress, "DepthBufferAddress" }, | ||
| 77 | {Regs::ColorBufferAddress, "ColorBufferAddress" }, | ||
| 78 | {Regs::ColorBufferSize, "ColorBufferSize" }, | ||
| 79 | }; | ||
| 80 | |||
| 81 | template<> | ||
| 82 | union Regs::Struct<Regs::ViewportSizeX> { | ||
| 83 | BitField<0, 24, u32> value; | ||
| 84 | }; | ||
| 85 | |||
| 86 | template<> | ||
| 87 | union Regs::Struct<Regs::ViewportSizeY> { | ||
| 88 | BitField<0, 24, u32> value; | ||
| 89 | }; | ||
| 90 | |||
| 91 | template<> | ||
| 92 | union Regs::Struct<Regs::VertexDescriptor> { | ||
| 93 | enum class Format : u64 { | ||
| 94 | BYTE = 0, | ||
| 95 | UBYTE = 1, | ||
| 96 | SHORT = 2, | ||
| 97 | FLOAT = 3, | ||
| 98 | }; | ||
| 99 | |||
| 100 | BitField< 0, 2, Format> format0; | ||
| 101 | BitField< 2, 2, u64> size0; // number of elements minus 1 | ||
| 102 | BitField< 4, 2, Format> format1; | ||
| 103 | BitField< 6, 2, u64> size1; | ||
| 104 | BitField< 8, 2, Format> format2; | ||
| 105 | BitField<10, 2, u64> size2; | ||
| 106 | BitField<12, 2, Format> format3; | ||
| 107 | BitField<14, 2, u64> size3; | ||
| 108 | BitField<16, 2, Format> format4; | ||
| 109 | BitField<18, 2, u64> size4; | ||
| 110 | BitField<20, 2, Format> format5; | ||
| 111 | BitField<22, 2, u64> size5; | ||
| 112 | BitField<24, 2, Format> format6; | ||
| 113 | BitField<26, 2, u64> size6; | ||
| 114 | BitField<28, 2, Format> format7; | ||
| 115 | BitField<30, 2, u64> size7; | ||
| 116 | BitField<32, 2, Format> format8; | ||
| 117 | BitField<34, 2, u64> size8; | ||
| 118 | BitField<36, 2, Format> format9; | ||
| 119 | BitField<38, 2, u64> size9; | ||
| 120 | BitField<40, 2, Format> format10; | ||
| 121 | BitField<42, 2, u64> size10; | ||
| 122 | BitField<44, 2, Format> format11; | ||
| 123 | BitField<46, 2, u64> size11; | ||
| 124 | |||
| 125 | BitField<48, 12, u64> attribute_mask; | ||
| 126 | BitField<60, 4, u64> num_attributes; // number of total attributes minus 1 | ||
| 127 | }; | ||
| 128 | |||
| 129 | 175 | ||
| 130 | } // namespace | 176 | } // namespace |
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index d0a8ec1da..02b174562 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp | |||
| @@ -78,8 +78,8 @@ void RendererOpenGL::FlipFramebuffer(const u8* in, u8* out) { | |||
| 78 | */ | 78 | */ |
| 79 | void RendererOpenGL::RenderXFB(const common::Rect& src_rect, const common::Rect& dst_rect) { | 79 | void RendererOpenGL::RenderXFB(const common::Rect& src_rect, const common::Rect& dst_rect) { |
| 80 | 80 | ||
| 81 | const auto& framebuffer_top = GPU::g_regs.Get<GPU::Regs::FramebufferTop>(); | 81 | const auto& framebuffer_top = GPU::g_regs.framebuffer_config[0]; |
| 82 | const auto& framebuffer_sub = GPU::g_regs.Get<GPU::Regs::FramebufferBottom>(); | 82 | const auto& framebuffer_sub = GPU::g_regs.framebuffer_config[1]; |
| 83 | const u32 active_fb_top = (framebuffer_top.active_fb == 1) | 83 | const u32 active_fb_top = (framebuffer_top.active_fb == 1) |
| 84 | ? framebuffer_top.address_left2 | 84 | ? framebuffer_top.address_left2 |
| 85 | : framebuffer_top.address_left1; | 85 | : framebuffer_top.address_left1; |