summaryrefslogtreecommitdiff
path: root/src/core/hw/gpu.cpp
diff options
context:
space:
mode:
authorGravatar Yuri Kunde Schlesner2015-01-14 01:19:08 -0200
committerGravatar Yuri Kunde Schlesner2015-01-14 05:20:13 -0200
commit9e084826b8764853e363593ba8793e7b17eaa4f8 (patch)
treeaf57c96ca0223b6aa266c4359e74d83271333704 /src/core/hw/gpu.cpp
parentGPU: Correct wrong default framebuffer address for sub-screen. (diff)
downloadyuzu-9e084826b8764853e363593ba8793e7b17eaa4f8.tar.gz
yuzu-9e084826b8764853e363593ba8793e7b17eaa4f8.tar.xz
yuzu-9e084826b8764853e363593ba8793e7b17eaa4f8.zip
GPU: Do periodic VBlank updates using CoreTiming
Diffstat (limited to 'src/core/hw/gpu.cpp')
-rw-r--r--src/core/hw/gpu.cpp91
1 files changed, 44 insertions, 47 deletions
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index 49136b7e1..256e11c37 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,12 +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 last_update_tick = 0; ///< CPU ticl count from last GPU update 32static u64 frame_ticks;
31static u64 frame_count = 0; ///< Number of frames drawn 33/// Event id for CoreTiming
32static bool last_skip_frame = false; ///< True if the last frame was skipped 34static int vblank_event;
35/// Total number of frames drawn
36static u64 frame_count;
37/// True if the last frame was skipped
38static bool last_skip_frame = false;
33 39
34template <typename T> 40template <typename T>
35inline void Read(T &var, const u32 raw_addr) { 41inline void Read(T &var, const u32 raw_addr) {
@@ -192,50 +198,39 @@ template void Write<u16>(u32 addr, const u16 data);
192template void Write<u8>(u32 addr, const u8 data); 198template void Write<u8>(u32 addr, const u8 data);
193 199
194/// Update hardware 200/// Update hardware
195void Update() { 201static void VBlankCallback(u64 userdata, int cycles_late) {
196 auto& framebuffer_top = g_regs.framebuffer_config[0]; 202 auto& framebuffer_top = g_regs.framebuffer_config[0];
197 203
198 // Synchronize GPU on a thread reschedule: Because we cannot accurately predict a vertical 204 frame_count++;
199 // blank, we need to simulate it. Based on testing, it seems that retail applications work more 205 last_skip_frame = g_skip_frame;
200 // accurately when this is signalled between thread switches. 206 g_skip_frame = (frame_count & Settings::values.frame_skip) != 0;
201 207
202 u64 current_ticks = Core::g_app_core->GetTicks(); 208 // Swap buffers based on the frameskip mode, which is a little bit tricky. When
203 209 // a frame is being skipped, nothing is being rendered to the internal framebuffer(s).
204 210 // So, we should only swap frames if the last frame was rendered. The rules are:
205 if (HLE::g_reschedule) { 211 // - If frameskip == 0 (disabled), always swap buffers
206 212 // - If frameskip == 1, swap buffers every other frame (starting from the first frame)
207 // Synchronize frame... 213 // - If frameskip > 1, swap buffers every frameskip^n frames (starting from the second frame)
208 if ((current_ticks - last_update_tick) >= frame_ticks) { 214 if ((((Settings::values.frame_skip != 1) ^ last_skip_frame) && last_skip_frame != g_skip_frame) ||
209 last_update_tick += frame_ticks; 215 Settings::values.frame_skip == 0) {
210 frame_count++; 216 VideoCore::g_renderer->SwapBuffers();
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 // TODO(yuriks): hwtest to determine if PDC0 is for the Top screen and PDC1 for the Sub
227 // screen, or if both use the same interrupts and these two instead determine the
228 // beginning and end of the VBlank period. If needed, split the interrupt firing into
229 // two different intervals.
230 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC0);
231 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC1);
232
233 // TODO(bunnei): Fake a DSP interrupt on each frame. This does not belong here, but
234 // until we can emulate DSP interrupts, this is probably the only reasonable place to do
235 // this. Certain games expect this to be periodically signaled.
236 DSP_DSP::SignalInterrupt();
237 }
238 } 217 }
218
219 // Signal to GSP that GPU interrupt has occurred
220 // TODO(yuriks): hwtest to determine if PDC0 is for the Top screen and PDC1 for the Sub
221 // screen, or if both use the same interrupts and these two instead determine the
222 // beginning and end of the VBlank period. If needed, split the interrupt firing into
223 // two different intervals.
224 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC0);
225 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC1);
226
227 // TODO(bunnei): Fake a DSP interrupt on each frame. This does not belong here, but
228 // until we can emulate DSP interrupts, this is probably the only reasonable place to do
229 // this. Certain games expect this to be periodically signaled.
230 DSP_DSP::SignalInterrupt();
231
232 // Reschedule recurrent event
233 CoreTiming::ScheduleEvent(frame_ticks - cycles_late, vblank_event);
239} 234}
240 235
241/// Initialize hardware 236/// Initialize hardware
@@ -269,10 +264,12 @@ void Init() {
269 framebuffer_sub.active_fb = 0; 264 framebuffer_sub.active_fb = 0;
270 265
271 frame_ticks = 268123480 / Settings::values.gpu_refresh_rate; 266 frame_ticks = 268123480 / Settings::values.gpu_refresh_rate;
272 last_update_tick = Core::g_app_core->GetTicks();
273 last_skip_frame = false; 267 last_skip_frame = false;
274 g_skip_frame = false; 268 g_skip_frame = false;
275 269
270 vblank_event = CoreTiming::RegisterEvent("GPU::VBlankCallback", VBlankCallback);
271 CoreTiming::ScheduleEvent(frame_ticks, vblank_event);
272
276 LOG_DEBUG(HW_GPU, "initialized OK"); 273 LOG_DEBUG(HW_GPU, "initialized OK");
277} 274}
278 275