summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorGravatar bunnei2015-01-15 18:11:03 -0500
committerGravatar bunnei2015-01-15 18:11:03 -0500
commit4b47ed6194484e6cff264553bb383b7e9c608a9c (patch)
tree468b9214a0d0989d8702ba4baee221d5c028e5e7 /src/core
parentMerge pull request #481 from Subv/hm_b (diff)
parentGPU: Fix buffer overrun in Display Transfers (diff)
downloadyuzu-4b47ed6194484e6cff264553bb383b7e9c608a9c.tar.gz
yuzu-4b47ed6194484e6cff264553bb383b7e9c608a9c.tar.xz
yuzu-4b47ed6194484e6cff264553bb383b7e9c608a9c.zip
Merge pull request #482 from yuriks/fix-vblank
Correctness fixes for GPU flipping and interrupts
Diffstat (limited to 'src/core')
-rw-r--r--src/core/hle/service/gsp_gpu.cpp35
-rw-r--r--src/core/hle/service/gsp_gpu.h25
-rw-r--r--src/core/hw/gpu.cpp129
-rw-r--r--src/core/hw/gpu.h3
-rw-r--r--src/core/hw/hw.cpp1
5 files changed, 91 insertions, 102 deletions
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp
index 2b115240f..4ca2b9bd0 100644
--- a/src/core/hle/service/gsp_gpu.cpp
+++ b/src/core/hle/service/gsp_gpu.cpp
@@ -210,14 +210,27 @@ void SignalInterrupt(InterruptId interrupt_id) {
210 } 210 }
211 for (int thread_id = 0; thread_id < 0x4; ++thread_id) { 211 for (int thread_id = 0; thread_id < 0x4; ++thread_id) {
212 InterruptRelayQueue* interrupt_relay_queue = GetInterruptRelayQueue(thread_id); 212 InterruptRelayQueue* interrupt_relay_queue = GetInterruptRelayQueue(thread_id);
213 interrupt_relay_queue->number_interrupts = interrupt_relay_queue->number_interrupts + 1;
214
215 u8 next = interrupt_relay_queue->index; 213 u8 next = interrupt_relay_queue->index;
216 next += interrupt_relay_queue->number_interrupts; 214 next += interrupt_relay_queue->number_interrupts;
217 next = next % 0x34; // 0x34 is the number of interrupt slots 215 next = next % 0x34; // 0x34 is the number of interrupt slots
218 216
217 interrupt_relay_queue->number_interrupts += 1;
218
219 interrupt_relay_queue->slot[next] = interrupt_id; 219 interrupt_relay_queue->slot[next] = interrupt_id;
220 interrupt_relay_queue->error_code = 0x0; // No error 220 interrupt_relay_queue->error_code = 0x0; // No error
221
222 // Update framebuffer information if requested
223 // TODO(yuriks): Confirm where this code should be called. It is definitely updated without
224 // executing any GSP commands, only waiting on the event.
225 for (int screen_id = 0; screen_id < 2; ++screen_id) {
226 FrameBufferUpdate* info = GetFrameBufferInfo(thread_id, screen_id);
227
228 if (info->is_dirty) {
229 SetBufferSwap(screen_id, info->framebuffer_info[info->index]);
230 }
231
232 info->is_dirty = false;
233 }
221 } 234 }
222 Kernel::SignalEvent(g_interrupt_event); 235 Kernel::SignalEvent(g_interrupt_event);
223} 236}
@@ -269,8 +282,6 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
269 WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].address_end), Memory::VirtualToPhysicalAddress(params.end2) >> 3); 282 WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].address_end), Memory::VirtualToPhysicalAddress(params.end2) >> 3);
270 WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].size), params.end2 - params.start2); 283 WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].size), params.end2 - params.start2);
271 WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].value), params.value2); 284 WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].value), params.value2);
272
273 SignalInterrupt(InterruptId::PSC0);
274 break; 285 break;
275 } 286 }
276 287
@@ -283,22 +294,6 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
283 WriteGPURegister(GPU_REG_INDEX(display_transfer_config.output_size), params.out_buffer_size); 294 WriteGPURegister(GPU_REG_INDEX(display_transfer_config.output_size), params.out_buffer_size);
284 WriteGPURegister(GPU_REG_INDEX(display_transfer_config.flags), params.flags); 295 WriteGPURegister(GPU_REG_INDEX(display_transfer_config.flags), params.flags);
285 WriteGPURegister(GPU_REG_INDEX(display_transfer_config.trigger), 1); 296 WriteGPURegister(GPU_REG_INDEX(display_transfer_config.trigger), 1);
286
287 // TODO(bunnei): Determine if these interrupts should be signalled here.
288 SignalInterrupt(InterruptId::PSC1);
289 SignalInterrupt(InterruptId::PPF);
290
291 // Update framebuffer information if requested
292 for (int screen_id = 0; screen_id < 2; ++screen_id) {
293 FrameBufferUpdate* info = GetFrameBufferInfo(thread_id, screen_id);
294
295 if (info->is_dirty) {
296 SetBufferSwap(screen_id, info->framebuffer_info[info->index]);
297 info->framebuffer_info->active_fb = info->framebuffer_info->active_fb ^ 1;
298 }
299
300 info->is_dirty = false;
301 }
302 break; 297 break;
303 } 298 }
304 299
diff --git a/src/core/hle/service/gsp_gpu.h b/src/core/hle/service/gsp_gpu.h
index 932b6170f..65abb194a 100644
--- a/src/core/hle/service/gsp_gpu.h
+++ b/src/core/hle/service/gsp_gpu.h
@@ -45,21 +45,16 @@ enum class CommandId : u32 {
45 45
46/// GSP thread interrupt relay queue 46/// GSP thread interrupt relay queue
47struct InterruptRelayQueue { 47struct InterruptRelayQueue {
48 union { 48 // Index of last interrupt in the queue
49 u32 hex; 49 u8 index;
50 50 // Number of interrupts remaining to be processed by the userland code
51 // Index of last interrupt in the queue 51 u8 number_interrupts;
52 BitField<0,8,u32> index; 52 // Error code - zero on success, otherwise an error has occurred
53 53 u8 error_code;
54 // Number of interrupts remaining to be processed by the userland code 54 u8 padding1;
55 BitField<8,8,u32> number_interrupts; 55
56 56 u32 missed_PDC0;
57 // Error code - zero on success, otherwise an error has occurred 57 u32 missed_PDC1;
58 BitField<16,8,u32> error_code;
59 };
60
61 u32 unk0;
62 u32 unk1;
63 58
64 InterruptId slot[0x34]; ///< Interrupt ID slots 59 InterruptId slot[0x34]; ///< Interrupt ID slots
65}; 60};
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index 3b730a0de..58eec3005 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -9,6 +9,7 @@
9#include "core/settings.h" 9#include "core/settings.h"
10#include "core/core.h" 10#include "core/core.h"
11#include "core/mem_map.h" 11#include "core/mem_map.h"
12#include "core/core_timing.h"
12 13
13#include "core/hle/hle.h" 14#include "core/hle/hle.h"
14#include "core/hle/service/gsp_gpu.h" 15#include "core/hle/service/gsp_gpu.h"
@@ -24,14 +25,17 @@ namespace GPU {
24 25
25Regs g_regs; 26Regs g_regs;
26 27
27bool g_skip_frame = false; ///< True if the current frame was skipped 28/// True if the current frame was skipped
29bool g_skip_frame = false;
28 30
29static u64 frame_ticks = 0; ///< 268MHz / gpu_refresh_rate frames per second 31/// 268MHz / gpu_refresh_rate frames per second
30static u64 line_ticks = 0; ///< Number of ticks for a screen line 32static u64 frame_ticks;
31static u32 cur_line = 0; ///< Current screen line 33/// Event id for CoreTiming
32static u64 last_update_tick = 0; ///< CPU ticl count from last GPU update 34static int vblank_event;
33static u64 frame_count = 0; ///< Number of frames drawn 35/// Total number of frames drawn
34static bool last_skip_frame = false; ///< True if the last frame was skipped 36static u64 frame_count;
37/// True if the last frame was skipped
38static bool last_skip_frame = false;
35 39
36template <typename T> 40template <typename T>
37inline void Read(T &var, const u32 raw_addr) { 41inline void Read(T &var, const u32 raw_addr) {
@@ -79,6 +83,12 @@ inline void Write(u32 addr, const T data) {
79 *ptr = bswap32(config.value); // TODO: This is just a workaround to missing framebuffer format emulation 83 *ptr = bswap32(config.value); // TODO: This is just a workaround to missing framebuffer format emulation
80 84
81 LOG_TRACE(HW_GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress()); 85 LOG_TRACE(HW_GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress());
86
87 if (!is_second_filler) {
88 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC0);
89 } else {
90 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC1);
91 }
82 } 92 }
83 break; 93 break;
84 } 94 }
@@ -90,22 +100,25 @@ inline void Write(u32 addr, const T data) {
90 u8* source_pointer = Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalInputAddress())); 100 u8* source_pointer = Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalInputAddress()));
91 u8* dest_pointer = Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalOutputAddress())); 101 u8* dest_pointer = Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalOutputAddress()));
92 102
103 // Cheap emulation of horizontal scaling: Just skip each second pixel of the
104 // input framebuffer. We keep track of this in the pixel_skip variable.
105 unsigned pixel_skip = (config.scale_horizontally != 0) ? 2 : 1;
106
107 u32 output_width = config.output_width / pixel_skip;
108
93 for (u32 y = 0; y < config.output_height; ++y) { 109 for (u32 y = 0; y < config.output_height; ++y) {
94 // TODO: Why does the register seem to hold twice the framebuffer width? 110 // TODO: Why does the register seem to hold twice the framebuffer width?
95 for (u32 x = 0; x < config.output_width; ++x) { 111
112 for (u32 x = 0; x < output_width; ++x) {
96 struct { 113 struct {
97 int r, g, b, a; 114 int r, g, b, a;
98 } source_color = { 0, 0, 0, 0 }; 115 } source_color = { 0, 0, 0, 0 };
99 116
100 // Cheap emulation of horizontal scaling: Just skip each second pixel of the
101 // input framebuffer. We keep track of this in the pixel_skip variable.
102 unsigned pixel_skip = (config.scale_horizontally != 0) ? 2 : 1;
103
104 switch (config.input_format) { 117 switch (config.input_format) {
105 case Regs::PixelFormat::RGBA8: 118 case Regs::PixelFormat::RGBA8:
106 { 119 {
107 // TODO: Most likely got the component order messed up. 120 // TODO: Most likely got the component order messed up.
108 u8* srcptr = source_pointer + x * 4 * pixel_skip + y * config.input_width * 4 * pixel_skip; 121 u8* srcptr = source_pointer + (x * pixel_skip + y * config.input_width) * 4;
109 source_color.r = srcptr[0]; // blue 122 source_color.r = srcptr[0]; // blue
110 source_color.g = srcptr[1]; // green 123 source_color.g = srcptr[1]; // green
111 source_color.b = srcptr[2]; // red 124 source_color.b = srcptr[2]; // red
@@ -133,7 +146,7 @@ inline void Write(u32 addr, const T data) {
133 case Regs::PixelFormat::RGB8: 146 case Regs::PixelFormat::RGB8:
134 { 147 {
135 // TODO: Most likely got the component order messed up. 148 // TODO: Most likely got the component order messed up.
136 u8* dstptr = dest_pointer + x * 3 + y * config.output_width * 3; 149 u8* dstptr = dest_pointer + (x + y * output_width) * 3;
137 dstptr[0] = source_color.r; // blue 150 dstptr[0] = source_color.r; // blue
138 dstptr[1] = source_color.g; // green 151 dstptr[1] = source_color.g; // green
139 dstptr[2] = source_color.b; // red 152 dstptr[2] = source_color.b; // red
@@ -148,10 +161,12 @@ inline void Write(u32 addr, const T data) {
148 } 161 }
149 162
150 LOG_TRACE(HW_GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), dst format %x", 163 LOG_TRACE(HW_GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), dst format %x",
151 config.output_height * config.output_width * 4, 164 config.output_height * output_width * 4,
152 config.GetPhysicalInputAddress(), (u32)config.input_width, (u32)config.input_height, 165 config.GetPhysicalInputAddress(), (u32)config.input_width, (u32)config.input_height,
153 config.GetPhysicalOutputAddress(), (u32)config.output_width, (u32)config.output_height, 166 config.GetPhysicalOutputAddress(), (u32)output_width, (u32)config.output_height,
154 config.output_format.Value()); 167 config.output_format.Value());
168
169 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF);
155 } 170 }
156 break; 171 break;
157 } 172 }
@@ -186,51 +201,39 @@ template void Write<u16>(u32 addr, const u16 data);
186template void Write<u8>(u32 addr, const u8 data); 201template void Write<u8>(u32 addr, const u8 data);
187 202
188/// Update hardware 203/// Update hardware
189void Update() { 204static void VBlankCallback(u64 userdata, int cycles_late) {
190 auto& framebuffer_top = g_regs.framebuffer_config[0]; 205 auto& framebuffer_top = g_regs.framebuffer_config[0];
191 206
192 // Synchronize GPU on a thread reschedule: Because we cannot accurately predict a vertical 207 frame_count++;
193 // blank, we need to simulate it. Based on testing, it seems that retail applications work more 208 last_skip_frame = g_skip_frame;
194 // accurately when this is signalled between thread switches. 209 g_skip_frame = (frame_count & Settings::values.frame_skip) != 0;
195 210
196 if (HLE::g_reschedule) { 211 // Swap buffers based on the frameskip mode, which is a little bit tricky. When
197 u64 current_ticks = Core::g_app_core->GetTicks(); 212 // a frame is being skipped, nothing is being rendered to the internal framebuffer(s).
198 u32 num_lines = static_cast<u32>((current_ticks - last_update_tick) / line_ticks); 213 // So, we should only swap frames if the last frame was rendered. The rules are:
199 214 // - If frameskip == 0 (disabled), always swap buffers
200 // Synchronize line... 215 // - If frameskip == 1, swap buffers every other frame (starting from the first frame)
201 if (num_lines > 0) { 216 // - If frameskip > 1, swap buffers every frameskip^n frames (starting from the second frame)
202 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC0); 217 if ((((Settings::values.frame_skip != 1) ^ last_skip_frame) && last_skip_frame != g_skip_frame) ||
203 cur_line += num_lines; 218 Settings::values.frame_skip == 0) {
204 last_update_tick += (num_lines * line_ticks); 219 VideoCore::g_renderer->SwapBuffers();
205 }
206
207 // Synchronize frame...
208 if (cur_line >= framebuffer_top.height) {
209 cur_line = 0;
210 frame_count++;
211 last_skip_frame = g_skip_frame;
212 g_skip_frame = (frame_count & Settings::values.frame_skip) != 0;
213
214 // Swap buffers based on the frameskip mode, which is a little bit tricky. When
215 // a frame is being skipped, nothing is being rendered to the internal framebuffer(s).
216 // So, we should only swap frames if the last frame was rendered. The rules are:
217 // - If frameskip == 0 (disabled), always swap buffers
218 // - If frameskip == 1, swap buffers every other frame (starting from the first frame)
219 // - If frameskip > 1, swap buffers every frameskip^n frames (starting from the second frame)
220 if ((((Settings::values.frame_skip != 1) ^ last_skip_frame) && last_skip_frame != g_skip_frame) ||
221 Settings::values.frame_skip == 0) {
222 VideoCore::g_renderer->SwapBuffers();
223 }
224
225 // Signal to GSP that GPU interrupt has occurred
226 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC1);
227
228 // TODO(bunnei): Fake a DSP interrupt on each frame. This does not belong here, but
229 // until we can emulate DSP interrupts, this is probably the only reasonable place to do
230 // this. Certain games expect this to be periodically signaled.
231 DSP_DSP::SignalInterrupt();
232 }
233 } 220 }
221
222 // Signal to GSP that GPU interrupt has occurred
223 // TODO(yuriks): hwtest to determine if PDC0 is for the Top screen and PDC1 for the Sub
224 // screen, or if both use the same interrupts and these two instead determine the
225 // beginning and end of the VBlank period. If needed, split the interrupt firing into
226 // two different intervals.
227 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC0);
228 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC1);
229
230 // TODO(bunnei): Fake a DSP interrupt on each frame. This does not belong here, but
231 // until we can emulate DSP interrupts, this is probably the only reasonable place to do
232 // this. Certain games expect this to be periodically signaled.
233 DSP_DSP::SignalInterrupt();
234
235 // Reschedule recurrent event
236 CoreTiming::ScheduleEvent(frame_ticks - cycles_late, vblank_event);
234} 237}
235 238
236/// Initialize hardware 239/// Initialize hardware
@@ -247,8 +250,8 @@ void Init() {
247 framebuffer_top.address_right1 = 0x18273000; 250 framebuffer_top.address_right1 = 0x18273000;
248 framebuffer_top.address_right2 = 0x182B9800; 251 framebuffer_top.address_right2 = 0x182B9800;
249 framebuffer_sub.address_left1 = 0x1848F000; 252 framebuffer_sub.address_left1 = 0x1848F000;
250 //framebuffer_sub.address_left2 = unknown; 253 framebuffer_sub.address_left2 = 0x184C7800;
251 framebuffer_sub.address_right1 = 0x184C7800; 254 //framebuffer_sub.address_right1 = unknown;
252 //framebuffer_sub.address_right2 = unknown; 255 //framebuffer_sub.address_right2 = unknown;
253 256
254 framebuffer_top.width = 240; 257 framebuffer_top.width = 240;
@@ -264,12 +267,12 @@ void Init() {
264 framebuffer_sub.active_fb = 0; 267 framebuffer_sub.active_fb = 0;
265 268
266 frame_ticks = 268123480 / Settings::values.gpu_refresh_rate; 269 frame_ticks = 268123480 / Settings::values.gpu_refresh_rate;
267 line_ticks = (GPU::frame_ticks / framebuffer_top.height);
268 cur_line = 0;
269 last_update_tick = Core::g_app_core->GetTicks();
270 last_skip_frame = false; 270 last_skip_frame = false;
271 g_skip_frame = false; 271 g_skip_frame = false;
272 272
273 vblank_event = CoreTiming::RegisterEvent("GPU::VBlankCallback", VBlankCallback);
274 CoreTiming::ScheduleEvent(frame_ticks, vblank_event);
275
273 LOG_DEBUG(HW_GPU, "initialized OK"); 276 LOG_DEBUG(HW_GPU, "initialized OK");
274} 277}
275 278
diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h
index 7de055232..7c3a17ee5 100644
--- a/src/core/hw/gpu.h
+++ b/src/core/hw/gpu.h
@@ -252,9 +252,6 @@ void Read(T &var, const u32 addr);
252template <typename T> 252template <typename T>
253void Write(u32 addr, const T data); 253void Write(u32 addr, const T data);
254 254
255/// Update hardware
256void Update();
257
258/// Initialize hardware 255/// Initialize hardware
259void Init(); 256void Init();
260 257
diff --git a/src/core/hw/hw.cpp b/src/core/hw/hw.cpp
index 848ab5348..503200629 100644
--- a/src/core/hw/hw.cpp
+++ b/src/core/hw/hw.cpp
@@ -75,7 +75,6 @@ template void Write<u8>(u32 addr, const u8 data);
75 75
76/// Update hardware 76/// Update hardware
77void Update() { 77void Update() {
78 GPU::Update();
79} 78}
80 79
81/// Initialize hardware 80/// Initialize hardware