summaryrefslogtreecommitdiff
path: root/src/core/hw/gpu.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hw/gpu.h')
-rw-r--r--src/core/hw/gpu.h297
1 files changed, 189 insertions, 108 deletions
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,