diff options
| author | 2015-01-14 01:19:08 -0200 | |
|---|---|---|
| committer | 2015-01-14 05:20:13 -0200 | |
| commit | 9e084826b8764853e363593ba8793e7b17eaa4f8 (patch) | |
| tree | af57c96ca0223b6aa266c4359e74d83271333704 /src/core/hw/gpu.cpp | |
| parent | GPU: Correct wrong default framebuffer address for sub-screen. (diff) | |
| download | yuzu-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.cpp | 91 |
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 | ||
| 25 | Regs g_regs; | 26 | Regs g_regs; |
| 26 | 27 | ||
| 27 | bool g_skip_frame = false; ///< True if the current frame was skipped | 28 | /// True if the current frame was skipped |
| 29 | bool g_skip_frame = false; | ||
| 28 | 30 | ||
| 29 | static u64 frame_ticks = 0; ///< 268MHz / gpu_refresh_rate frames per second | 31 | /// 268MHz / gpu_refresh_rate frames per second |
| 30 | static u64 last_update_tick = 0; ///< CPU ticl count from last GPU update | 32 | static u64 frame_ticks; |
| 31 | static u64 frame_count = 0; ///< Number of frames drawn | 33 | /// Event id for CoreTiming |
| 32 | static bool last_skip_frame = false; ///< True if the last frame was skipped | 34 | static int vblank_event; |
| 35 | /// Total number of frames drawn | ||
| 36 | static u64 frame_count; | ||
| 37 | /// True if the last frame was skipped | ||
| 38 | static bool last_skip_frame = false; | ||
| 33 | 39 | ||
| 34 | template <typename T> | 40 | template <typename T> |
| 35 | inline void Read(T &var, const u32 raw_addr) { | 41 | inline void Read(T &var, const u32 raw_addr) { |
| @@ -192,50 +198,39 @@ template void Write<u16>(u32 addr, const u16 data); | |||
| 192 | template void Write<u8>(u32 addr, const u8 data); | 198 | template void Write<u8>(u32 addr, const u8 data); |
| 193 | 199 | ||
| 194 | /// Update hardware | 200 | /// Update hardware |
| 195 | void Update() { | 201 | static 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 | ||