summaryrefslogtreecommitdiff
path: root/src/core/hw/gpu.cpp
diff options
context:
space:
mode:
authorGravatar bunnei2014-07-22 19:20:57 -0400
committerGravatar bunnei2014-07-22 19:20:57 -0400
commitdaa924b906ff3a6f54d00c5d19874c2f839af0a3 (patch)
tree127b4998ece87140690b7e74853215522d57ecaa /src/core/hw/gpu.cpp
parentMerge pull request #32 from yuriks/master (diff)
parentUse uniform formatting when printing hexadecimal numbers. (diff)
downloadyuzu-daa924b906ff3a6f54d00c5d19874c2f839af0a3.tar.gz
yuzu-daa924b906ff3a6f54d00c5d19874c2f839af0a3.tar.xz
yuzu-daa924b906ff3a6f54d00c5d19874c2f839af0a3.zip
Merge pull request #31 from neobrain/gpu_framebuffer
GPU framebuffer emulation improvements
Diffstat (limited to 'src/core/hw/gpu.cpp')
-rw-r--r--src/core/hw/gpu.cpp253
1 files changed, 175 insertions, 78 deletions
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index f0ca4eada..c00be2a83 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -15,48 +15,58 @@
15 15
16namespace GPU { 16namespace GPU {
17 17
18Registers g_regs; 18RegisterSet<u32, Regs> g_regs;
19 19
20u64 g_last_ticks = 0; ///< Last CPU ticks 20u64 g_last_ticks = 0; ///< Last CPU ticks
21 21
22/** 22/**
23 * Sets whether the framebuffers are in the GSP heap (FCRAM) or VRAM 23 * Sets whether the framebuffers are in the GSP heap (FCRAM) or VRAM
24 * @param 24 * @param
25 */ 25 */
26void SetFramebufferLocation(const FramebufferLocation mode) { 26void SetFramebufferLocation(const FramebufferLocation mode) {
27 switch (mode) { 27 switch (mode) {
28 case FRAMEBUFFER_LOCATION_FCRAM: 28 case FRAMEBUFFER_LOCATION_FCRAM:
29 g_regs.framebuffer_top_left_1 = PADDR_TOP_LEFT_FRAME1; 29 {
30 g_regs.framebuffer_top_left_2 = PADDR_TOP_LEFT_FRAME2; 30 auto& framebuffer_top = g_regs.Get<Regs::FramebufferTop>();
31 g_regs.framebuffer_top_right_1 = PADDR_TOP_RIGHT_FRAME1; 31 auto& framebuffer_sub = g_regs.Get<Regs::FramebufferBottom>();
32 g_regs.framebuffer_top_right_2 = PADDR_TOP_RIGHT_FRAME2; 32
33 g_regs.framebuffer_sub_left_1 = PADDR_SUB_FRAME1; 33 framebuffer_top.address_left1 = PADDR_TOP_LEFT_FRAME1;
34 //g_regs.framebuffer_sub_left_2 = unknown; 34 framebuffer_top.address_left2 = PADDR_TOP_LEFT_FRAME2;
35 g_regs.framebuffer_sub_right_1 = PADDR_SUB_FRAME2; 35 framebuffer_top.address_right1 = PADDR_TOP_RIGHT_FRAME1;
36 //g_regs.framebufferr_sub_right_2 = unknown; 36 framebuffer_top.address_right2 = PADDR_TOP_RIGHT_FRAME2;
37 framebuffer_sub.address_left1 = PADDR_SUB_FRAME1;
38 //framebuffer_sub.address_left2 = unknown;
39 framebuffer_sub.address_right1 = PADDR_SUB_FRAME2;
40 //framebuffer_sub.address_right2 = unknown;
37 break; 41 break;
42 }
38 43
39 case FRAMEBUFFER_LOCATION_VRAM: 44 case FRAMEBUFFER_LOCATION_VRAM:
40 g_regs.framebuffer_top_left_1 = PADDR_VRAM_TOP_LEFT_FRAME1; 45 {
41 g_regs.framebuffer_top_left_2 = PADDR_VRAM_TOP_LEFT_FRAME2; 46 auto& framebuffer_top = g_regs.Get<Regs::FramebufferTop>();
42 g_regs.framebuffer_top_right_1 = PADDR_VRAM_TOP_RIGHT_FRAME1; 47 auto& framebuffer_sub = g_regs.Get<Regs::FramebufferBottom>();
43 g_regs.framebuffer_top_right_2 = PADDR_VRAM_TOP_RIGHT_FRAME2; 48
44 g_regs.framebuffer_sub_left_1 = PADDR_VRAM_SUB_FRAME1; 49 framebuffer_top.address_left1 = PADDR_VRAM_TOP_LEFT_FRAME1;
45 //g_regs.framebuffer_sub_left_2 = unknown; 50 framebuffer_top.address_left2 = PADDR_VRAM_TOP_LEFT_FRAME2;
46 g_regs.framebuffer_sub_right_1 = PADDR_VRAM_SUB_FRAME2; 51 framebuffer_top.address_right1 = PADDR_VRAM_TOP_RIGHT_FRAME1;
47 //g_regs.framebufferr_sub_right_2 = unknown; 52 framebuffer_top.address_right2 = PADDR_VRAM_TOP_RIGHT_FRAME2;
53 framebuffer_sub.address_left1 = PADDR_VRAM_SUB_FRAME1;
54 //framebuffer_sub.address_left2 = unknown;
55 framebuffer_sub.address_right1 = PADDR_VRAM_SUB_FRAME2;
56 //framebuffer_sub.address_right2 = unknown;
48 break; 57 break;
49 } 58 }
59 }
50} 60}
51 61
52/** 62/**
53 * Gets the location of the framebuffers 63 * Gets the location of the framebuffers
54 * @return Location of framebuffers as FramebufferLocation enum 64 * @return Location of framebuffers as FramebufferLocation enum
55 */ 65 */
56const FramebufferLocation GetFramebufferLocation() { 66FramebufferLocation GetFramebufferLocation(u32 address) {
57 if ((g_regs.framebuffer_top_right_1 & ~Memory::VRAM_MASK) == Memory::VRAM_PADDR) { 67 if ((address & ~Memory::VRAM_MASK) == Memory::VRAM_PADDR) {
58 return FRAMEBUFFER_LOCATION_VRAM; 68 return FRAMEBUFFER_LOCATION_VRAM;
59 } else if ((g_regs.framebuffer_top_right_1 & ~Memory::FCRAM_MASK) == Memory::FCRAM_PADDR) { 69 } else if ((address & ~Memory::FCRAM_MASK) == Memory::FCRAM_PADDR) {
60 return FRAMEBUFFER_LOCATION_FCRAM; 70 return FRAMEBUFFER_LOCATION_FCRAM;
61 } else { 71 } else {
62 ERROR_LOG(GPU, "unknown framebuffer location!"); 72 ERROR_LOG(GPU, "unknown framebuffer location!");
@@ -64,91 +74,161 @@ const FramebufferLocation GetFramebufferLocation() {
64 return FRAMEBUFFER_LOCATION_UNKNOWN; 74 return FRAMEBUFFER_LOCATION_UNKNOWN;
65} 75}
66 76
77u32 GetFramebufferAddr(const u32 address) {
78 switch (GetFramebufferLocation(address)) {
79 case FRAMEBUFFER_LOCATION_FCRAM:
80 return Memory::VirtualAddressFromPhysical_FCRAM(address);
81 case FRAMEBUFFER_LOCATION_VRAM:
82 return Memory::VirtualAddressFromPhysical_VRAM(address);
83 default:
84 ERROR_LOG(GPU, "unknown framebuffer location");
85 }
86 return 0;
87}
88
67/** 89/**
68 * Gets a read-only pointer to a framebuffer in memory 90 * Gets a read-only pointer to a framebuffer in memory
69 * @param address Physical address of framebuffer 91 * @param address Physical address of framebuffer
70 * @return Returns const pointer to raw framebuffer 92 * @return Returns const pointer to raw framebuffer
71 */ 93 */
72const u8* GetFramebufferPointer(const u32 address) { 94const u8* GetFramebufferPointer(const u32 address) {
73 switch (GetFramebufferLocation()) { 95 u32 addr = GetFramebufferAddr(address);
74 case FRAMEBUFFER_LOCATION_FCRAM: 96 return (addr != 0) ? Memory::GetPointer(addr) : nullptr;
75 return (const u8*)Memory::GetPointer(Memory::VirtualAddressFromPhysical_FCRAM(address));
76 case FRAMEBUFFER_LOCATION_VRAM:
77 return (const u8*)Memory::GetPointer(Memory::VirtualAddressFromPhysical_VRAM(address));
78 default:
79 ERROR_LOG(GPU, "unknown framebuffer location");
80 }
81 return NULL;
82} 97}
83 98
84template <typename T> 99template <typename T>
85inline void Read(T &var, const u32 addr) { 100inline void Read(T &var, const u32 raw_addr) {
86 switch (addr) { 101 u32 addr = raw_addr - 0x1EF00000;
87 case Registers::FramebufferTopLeft1: 102 int index = addr / 4;
88 var = g_regs.framebuffer_top_left_1;
89 break;
90 103
91 case Registers::FramebufferTopLeft2: 104 // Reads other than u32 are untested, so I'd rather have them abort than silently fail
92 var = g_regs.framebuffer_top_left_2; 105 if (index >= Regs::NumIds || !std::is_same<T,u32>::value)
93 break; 106 {
107 ERROR_LOG(GPU, "unknown Read%d @ 0x%08X", sizeof(var) * 8, addr);
108 return;
109 }
94 110
95 case Registers::FramebufferTopRight1: 111 var = g_regs[static_cast<Regs::Id>(addr / 4)];
96 var = g_regs.framebuffer_top_right_1; 112}
97 break;
98 113
99 case Registers::FramebufferTopRight2: 114template <typename T>
100 var = g_regs.framebuffer_top_right_2; 115inline void Write(u32 addr, const T data) {
101 break; 116 addr -= 0x1EF00000;
117 int index = addr / 4;
102 118
103 case Registers::FramebufferSubLeft1: 119 // Writes other than u32 are untested, so I'd rather have them abort than silently fail
104 var = g_regs.framebuffer_sub_left_1; 120 if (index >= Regs::NumIds || !std::is_same<T,u32>::value)
105 break; 121 {
122 ERROR_LOG(GPU, "unknown Write%d 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr);
123 return;
124 }
106 125
107 case Registers::FramebufferSubRight1: 126 g_regs[static_cast<Regs::Id>(index)] = data;
108 var = g_regs.framebuffer_sub_right_1;
109 break;
110 127
111 case Registers::CommandListSize: 128 switch (static_cast<Regs::Id>(index)) {
112 var = g_regs.command_list_size;
113 break;
114 129
115 case Registers::CommandListAddress: 130 // Memory fills are triggered once the fill value is written.
116 var = g_regs.command_list_address; 131 // NOTE: This is not verified.
117 break; 132 case Regs::MemoryFill + 3:
133 case Regs::MemoryFill + 7:
134 {
135 const auto& config = g_regs.Get<Regs::MemoryFill>(static_cast<Regs::Id>(index - 3));
118 136
119 case Registers::ProcessCommandList: 137 // TODO: Not sure if this check should be done at GSP level instead
120 var = g_regs.command_processing_enabled; 138 if (config.address_start) {
121 break; 139 // TODO: Not sure if this algorithm is correct, particularly because it doesn't use the size member at all
140 u32* start = (u32*)Memory::GetPointer(config.GetStartAddress());
141 u32* end = (u32*)Memory::GetPointer(config.GetEndAddress());
142 for (u32* ptr = start; ptr < end; ++ptr)
143 *ptr = bswap32(config.value); // TODO: This is just a workaround to missing framebuffer format emulation
122 144
123 default: 145 DEBUG_LOG(GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress());
124 ERROR_LOG(GPU, "unknown Read%d @ 0x%08X", sizeof(var) * 8, addr); 146 }
125 break; 147 break;
126 } 148 }
127}
128
129template <typename T>
130inline void Write(u32 addr, const T data) {
131 switch (static_cast<Registers::Id>(addr)) {
132 case Registers::CommandListSize:
133 g_regs.command_list_size = data;
134 break;
135 149
136 case Registers::CommandListAddress: 150 case Regs::DisplayTransfer + 6:
137 g_regs.command_list_address = data; 151 {
152 const auto& config = g_regs.Get<Regs::DisplayTransfer>();
153 if (config.trigger & 1) {
154 u8* source_pointer = Memory::GetPointer(config.GetPhysicalInputAddress());
155 u8* dest_pointer = Memory::GetPointer(config.GetPhysicalOutputAddress());
156
157 for (int y = 0; y < config.output_height; ++y) {
158 // TODO: Why does the register seem to hold twice the framebuffer width?
159 for (int x = 0; x < config.output_width / 2; ++x) {
160 struct {
161 int r, g, b, a;
162 } source_color = { 0, 0, 0, 0 };
163
164 switch (config.input_format) {
165 case Regs::FramebufferFormat::RGBA8:
166 {
167 // TODO: Most likely got the component order messed up.
168 u8* srcptr = source_pointer + x * 4 + y * config.input_width * 4 / 2;
169 source_color.r = srcptr[0]; // blue
170 source_color.g = srcptr[1]; // green
171 source_color.b = srcptr[2]; // red
172 source_color.a = srcptr[3]; // alpha
173 break;
174 }
175
176 default:
177 ERROR_LOG(GPU, "Unknown source framebuffer format %x", config.input_format.Value());
178 break;
179 }
180
181 switch (config.output_format) {
182 /*case Regs::FramebufferFormat::RGBA8:
183 {
184 // TODO: Untested
185 u8* dstptr = (u32*)(dest_pointer + x * 4 + y * config.output_width * 4);
186 dstptr[0] = source_color.r;
187 dstptr[1] = source_color.g;
188 dstptr[2] = source_color.b;
189 dstptr[3] = source_color.a;
190 break;
191 }*/
192
193 case Regs::FramebufferFormat::RGB8:
194 {
195 // TODO: Most likely got the component order messed up.
196 u8* dstptr = dest_pointer + x * 3 + y * config.output_width * 3 / 2;
197 dstptr[0] = source_color.r; // blue
198 dstptr[1] = source_color.g; // green
199 dstptr[2] = source_color.b; // red
200 break;
201 }
202
203 default:
204 ERROR_LOG(GPU, "Unknown destination framebuffer format %x", config.output_format.Value());
205 break;
206 }
207 }
208 }
209
210 DEBUG_LOG(GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%dx%d)-> 0x%08x(%dx%d), dst format %x",
211 config.output_height * config.output_width * 4,
212 config.GetPhysicalInputAddress(), (int)config.input_width, (int)config.input_height,
213 config.GetPhysicalOutputAddress(), (int)config.output_width, (int)config.output_height,
214 config.output_format.Value());
215 }
138 break; 216 break;
217 }
139 218
140 case Registers::ProcessCommandList: 219 case Regs::CommandProcessor + 4:
141 g_regs.command_processing_enabled = data; 220 {
142 if (g_regs.command_processing_enabled & 1) 221 const auto& config = g_regs.Get<Regs::CommandProcessor>();
222 if (config.trigger & 1)
143 { 223 {
144 // u32* buffer = (u32*)Memory::GetPointer(g_regs.command_list_address << 3); 224 // u32* buffer = (u32*)Memory::GetPointer(config.address << 3);
145 ERROR_LOG(GPU, "Beginning %x bytes of commands from address %x", g_regs.command_list_size, g_regs.command_list_address << 3); 225 ERROR_LOG(GPU, "Beginning 0x%08x bytes of commands from address 0x%08x", config.size, config.address << 3);
146 // TODO: Process command list! 226 // TODO: Process command list!
147 } 227 }
148 break; 228 break;
229 }
149 230
150 default: 231 default:
151 ERROR_LOG(GPU, "unknown Write%d 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr);
152 break; 232 break;
153 } 233 }
154} 234}
@@ -180,7 +260,24 @@ void Update() {
180/// Initialize hardware 260/// Initialize hardware
181void Init() { 261void Init() {
182 g_last_ticks = Core::g_app_core->GetTicks(); 262 g_last_ticks = Core::g_app_core->GetTicks();
183 SetFramebufferLocation(FRAMEBUFFER_LOCATION_FCRAM); 263// SetFramebufferLocation(FRAMEBUFFER_LOCATION_FCRAM);
264 SetFramebufferLocation(FRAMEBUFFER_LOCATION_VRAM);
265
266 auto& framebuffer_top = g_regs.Get<Regs::FramebufferTop>();
267 auto& framebuffer_sub = g_regs.Get<Regs::FramebufferBottom>();
268 // TODO: Width should be 240 instead?
269 framebuffer_top.width = 480;
270 framebuffer_top.height = 400;
271 framebuffer_top.stride = 480*3;
272 framebuffer_top.color_format = Regs::FramebufferFormat::RGB8;
273 framebuffer_top.active_fb = 0;
274
275 framebuffer_sub.width = 480;
276 framebuffer_sub.height = 400;
277 framebuffer_sub.stride = 480*3;
278 framebuffer_sub.color_format = Regs::FramebufferFormat::RGB8;
279 framebuffer_sub.active_fb = 0;
280
184 NOTICE_LOG(GPU, "initialized OK"); 281 NOTICE_LOG(GPU, "initialized OK");
185} 282}
186 283