summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Tony Wasserka2014-08-03 16:00:52 +0200
committerGravatar Tony Wasserka2014-08-12 02:17:21 +0200
commit9c781a6c7646a3f30c23adae75e1879b7fc47d0f (patch)
tree76f32e7dc447e661f9f34303609cf4aed23d7a5b
parentMerge pull request #40 from bentley/master (diff)
downloadyuzu-9c781a6c7646a3f30c23adae75e1879b7fc47d0f.tar.gz
yuzu-9c781a6c7646a3f30c23adae75e1879b7fc47d0f.tar.xz
yuzu-9c781a6c7646a3f30c23adae75e1879b7fc47d0f.zip
Remove the fancy RegisterSet class introduced in 4c2bff61e.
While it was some nice and fancy template usage, it ultimately had many practical issues regarding length of involved expressions under regular usage as well as common code completion tools not being able to handle the structures. Instead, we now use a more conventional approach which is a lot more clean to use.
Diffstat (limited to '')
-rw-r--r--src/citra_qt/debugger/graphics_cmdlists.cpp2
-rw-r--r--src/common/common.vcxproj1
-rw-r--r--src/common/common.vcxproj.filters1
-rw-r--r--src/common/register_set.h163
-rw-r--r--src/core/hle/service/gsp.cpp36
-rw-r--r--src/core/hw/gpu.cpp47
-rw-r--r--src/core/hw/gpu.h297
-rw-r--r--src/video_core/pica.h242
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp4
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 */
85template<typename BaseType, typename RegDefinition>
86struct 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
154private:
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
20namespace GPU { 20namespace GPU {
21 21
22RegisterSet<u32, Regs> g_regs; 22Regs g_regs;
23 23
24u32 g_cur_line = 0; ///< Current vertical screen line 24u32 g_cur_line = 0; ///< Current vertical screen line
25u64 g_last_line_ticks = 0; ///< CPU tick count from last vertical screen line 25u64 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
119template <typename T> 118template <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
254void Update() { 253void 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
11namespace GPU { 12namespace GPU {
12 13
13static const u32 kFrameCycles = 268123480 / 60; ///< 268MHz / 60 frames per second 14static const u32 kFrameCycles = 268123480 / 60; ///< 268MHz / 60 frames per second
14static const u32 kFrameTicks = kFrameCycles / 3; ///< Approximate number of instructions/frame 15static 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
17struct Regs { 38struct 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
43template<> 67 INSERT_PADDING_WORDS(0x4);
44struct 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};
58static_assert(sizeof(Regs::Struct<Regs::MemoryFill>) == 0x10, "Structure size and register block length don't match");
59 78
60template<> 79 inline u32 GetEndAddress() const {
61struct 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
101template<> 120 // Distance between two pixel rows, in bytes
102struct Regs::Struct<Regs::FramebufferBottom> : public Regs::Struct<Regs::FramebufferTop> { 121 u32 stride;
103};
104static_assert(sizeof(Regs::Struct<Regs::FramebufferTop>) == 0x40, "Structure size and register block length don't match");
105 122
106template<> 123 u32 address_right1;
107struct 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};
149static_assert(sizeof(Regs::Struct<Regs::DisplayTransfer>) == 0x1C, "Structure size and register block length don't match");
150 162
151template<> 163 BitField< 0, 1, u32> flip_data; // flips input data horizontally (TODO) if true
152struct 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;
166static_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
169extern 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
218private:
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};
227static_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
237ASSERT_REG_POSITION(memory_fill_config[0], 0x00004);
238ASSERT_REG_POSITION(memory_fill_config[1], 0x00008);
239ASSERT_REG_POSITION(framebuffer_config[0], 0x00117);
240ASSERT_REG_POSITION(framebuffer_config[1], 0x00157);
241ASSERT_REG_POSITION(display_transfer_config, 0x00300);
242ASSERT_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.
248static_assert(sizeof(Regs) == 0x1000 * sizeof(u32), "Invalid total size of register set");
249
250extern Regs g_regs;
170 251
171enum { 252enum {
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
14namespace Pica { 14namespace 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
16struct Regs { 36struct 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
138private:
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
42static 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
154ASSERT_REG_POSITION(viewport_size_x, 0x41);
155ASSERT_REG_POSITION(viewport_size_y, 0x43);
156ASSERT_REG_POSITION(vertex_descriptor, 0x200);
46 157
47static 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
52static 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{ 162static_assert(sizeof(Regs) == 0x300 * sizeof(u32), "Invalid total size of register set");
54 return static_cast<Regs::Id>(0x205 + 3*n);
55}
56 163
57union CommandHeader { 164union 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
68static 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
81template<>
82union Regs::Struct<Regs::ViewportSizeX> {
83 BitField<0, 24, u32> value;
84};
85
86template<>
87union Regs::Struct<Regs::ViewportSizeY> {
88 BitField<0, 24, u32> value;
89};
90
91template<>
92union 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 */
79void RendererOpenGL::RenderXFB(const common::Rect& src_rect, const common::Rect& dst_rect) { 79void 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;