summaryrefslogtreecommitdiff
path: root/src/core/hw/gpu.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hw/gpu.cpp')
-rw-r--r--src/core/hw/gpu.cpp96
1 files changed, 51 insertions, 45 deletions
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index 3ad801c63..dd619cb16 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/common_types.h" 5#include "common/common_types.h"
@@ -21,12 +21,14 @@ namespace GPU {
21 21
22Regs g_regs; 22Regs g_regs;
23 23
24u32 g_cur_line = 0; ///< Current vertical screen line 24bool g_skip_frame = false; ///< True if the current frame was skipped
25u64 g_last_line_ticks = 0; ///< CPU tick count from last vertical screen line
26u64 g_last_frame_ticks = 0; ///< CPU tick count from last frame
27 25
28static u32 kFrameCycles = 0; ///< 268MHz / 60 frames per second 26static u64 frame_ticks = 0; ///< 268MHz / gpu_refresh_rate frames per second
29static u32 kFrameTicks = 0; ///< Approximate number of instructions/frame 27static u64 line_ticks = 0; ///< Number of ticks for a screen line
28static u32 cur_line = 0; ///< Current screen line
29static u64 last_update_tick = 0; ///< CPU ticl count from last GPU update
30static u64 frame_count = 0; ///< Number of frames drawn
31static bool last_skip_frame = false; ///< True if the last frame was skipped
30 32
31template <typename T> 33template <typename T>
32inline void Read(T &var, const u32 raw_addr) { 34inline void Read(T &var, const u32 raw_addr) {
@@ -34,8 +36,8 @@ inline void Read(T &var, const u32 raw_addr) {
34 u32 index = addr / 4; 36 u32 index = addr / 4;
35 37
36 // Reads other than u32 are untested, so I'd rather have them abort than silently fail 38 // Reads other than u32 are untested, so I'd rather have them abort than silently fail
37 if (index >= Regs::NumIds() || !std::is_same<T,u32>::value) { 39 if (index >= Regs::NumIds() || !std::is_same<T, u32>::value) {
38 ERROR_LOG(GPU, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr); 40 LOG_ERROR(HW_GPU, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr);
39 return; 41 return;
40 } 42 }
41 43
@@ -48,8 +50,8 @@ inline void Write(u32 addr, const T data) {
48 u32 index = addr / 4; 50 u32 index = addr / 4;
49 51
50 // Writes other than u32 are untested, so I'd rather have them abort than silently fail 52 // Writes other than u32 are untested, so I'd rather have them abort than silently fail
51 if (index >= Regs::NumIds() || !std::is_same<T,u32>::value) { 53 if (index >= Regs::NumIds() || !std::is_same<T, u32>::value) {
52 ERROR_LOG(GPU, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr); 54 LOG_ERROR(HW_GPU, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data, addr);
53 return; 55 return;
54 } 56 }
55 57
@@ -73,7 +75,7 @@ inline void Write(u32 addr, const T data) {
73 for (u32* ptr = start; ptr < end; ++ptr) 75 for (u32* ptr = start; ptr < end; ++ptr)
74 *ptr = bswap32(config.value); // TODO: This is just a workaround to missing framebuffer format emulation 76 *ptr = bswap32(config.value); // TODO: This is just a workaround to missing framebuffer format emulation
75 77
76 DEBUG_LOG(GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress()); 78 LOG_TRACE(HW_GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress());
77 } 79 }
78 break; 80 break;
79 } 81 }
@@ -105,7 +107,7 @@ inline void Write(u32 addr, const T data) {
105 } 107 }
106 108
107 default: 109 default:
108 ERROR_LOG(GPU, "Unknown source framebuffer format %x", config.input_format.Value()); 110 LOG_ERROR(HW_GPU, "Unknown source framebuffer format %x", config.input_format.Value());
109 break; 111 break;
110 } 112 }
111 113
@@ -132,16 +134,16 @@ inline void Write(u32 addr, const T data) {
132 } 134 }
133 135
134 default: 136 default:
135 ERROR_LOG(GPU, "Unknown destination framebuffer format %x", config.output_format.Value()); 137 LOG_ERROR(HW_GPU, "Unknown destination framebuffer format %x", config.output_format.Value());
136 break; 138 break;
137 } 139 }
138 } 140 }
139 } 141 }
140 142
141 DEBUG_LOG(GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), dst format %x", 143 LOG_TRACE(HW_GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), dst format %x",
142 config.output_height * config.output_width * 4, 144 config.output_height * config.output_width * 4,
143 config.GetPhysicalInputAddress(), config.input_width, config.input_height, 145 config.GetPhysicalInputAddress(), (u32)config.input_width, (u32)config.input_height,
144 config.GetPhysicalOutputAddress(), config.output_width, config.output_height, 146 config.GetPhysicalOutputAddress(), (u32)config.output_width, (u32)config.output_height,
145 config.output_format.Value()); 147 config.output_format.Value());
146 } 148 }
147 break; 149 break;
@@ -154,8 +156,7 @@ inline void Write(u32 addr, const T data) {
154 if (config.trigger & 1) 156 if (config.trigger & 1)
155 { 157 {
156 u32* buffer = (u32*)Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalAddress())); 158 u32* buffer = (u32*)Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalAddress()));
157 u32 size = config.size << 3; 159 Pica::CommandProcessor::ProcessCommandList(buffer, config.size);
158 Pica::CommandProcessor::ProcessCommandList(buffer, size);
159 } 160 }
160 break; 161 break;
161 } 162 }
@@ -180,37 +181,41 @@ template void Write<u8>(u32 addr, const u8 data);
180/// Update hardware 181/// Update hardware
181void Update() { 182void Update() {
182 auto& framebuffer_top = g_regs.framebuffer_config[0]; 183 auto& framebuffer_top = g_regs.framebuffer_config[0];
183 u64 current_ticks = Core::g_app_core->GetTicks();
184
185 // Update the frame after a certain number of CPU ticks have elapsed. This assumes that the
186 // active frame in memory is always complete to render. There also may be issues with this
187 // becoming out-of-synch with GSP synchrinization code (as follows). At this time, this seems to
188 // be the most effective solution for both homebrew and retail applications. With retail, this
189 // could be moved below (and probably would guarantee more accurate synchronization). However,
190 // primitive homebrew relies on a vertical blank interrupt to happen inevitably (regardless of a
191 // threading reschedule).
192
193 if ((current_ticks - g_last_frame_ticks) > GPU::kFrameTicks) {
194 VideoCore::g_renderer->SwapBuffers();
195 g_last_frame_ticks = current_ticks;
196 }
197 184
198 // Synchronize GPU on a thread reschedule: Because we cannot accurately predict a vertical 185 // Synchronize GPU on a thread reschedule: Because we cannot accurately predict a vertical
199 // blank, we need to simulate it. Based on testing, it seems that retail applications work more 186 // blank, we need to simulate it. Based on testing, it seems that retail applications work more
200 // accurately when this is signalled between thread switches. 187 // accurately when this is signalled between thread switches.
201 188
202 if (HLE::g_reschedule) { 189 if (HLE::g_reschedule) {
190 u64 current_ticks = Core::g_app_core->GetTicks();
191 u32 num_lines = static_cast<u32>((current_ticks - last_update_tick) / line_ticks);
203 192
204 // Synchronize line... 193 // Synchronize line...
205 if ((current_ticks - g_last_line_ticks) >= GPU::kFrameTicks / framebuffer_top.height) { 194 if (num_lines > 0) {
206 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC0); 195 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC0);
207 g_cur_line++; 196 cur_line += num_lines;
208 g_last_line_ticks = current_ticks; 197 last_update_tick += (num_lines * line_ticks);
209 } 198 }
210 199
211 // Synchronize frame... 200 // Synchronize frame...
212 if (g_cur_line >= framebuffer_top.height) { 201 if (cur_line >= framebuffer_top.height) {
213 g_cur_line = 0; 202 cur_line = 0;
203 frame_count++;
204 last_skip_frame = g_skip_frame;
205 g_skip_frame = (frame_count & Settings::values.frame_skip) != 0;
206
207 // Swap buffers based on the frameskip mode, which is a little bit tricky. When
208 // a frame is being skipped, nothing is being rendered to the internal framebuffer(s).
209 // So, we should only swap frames if the last frame was rendered. The rules are:
210 // - If frameskip == 0 (disabled), always swap buffers
211 // - If frameskip == 1, swap buffers every other frame (starting from the first frame)
212 // - If frameskip > 1, swap buffers every frameskip^n frames (starting from the second frame)
213
214 if ((((Settings::values.frame_skip != 1) ^ last_skip_frame) && last_skip_frame != g_skip_frame) ||
215 Settings::values.frame_skip == 0) {
216 VideoCore::g_renderer->SwapBuffers();
217 }
218
214 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC1); 219 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC1);
215 } 220 }
216 } 221 }
@@ -218,12 +223,6 @@ void Update() {
218 223
219/// Initialize hardware 224/// Initialize hardware
220void Init() { 225void Init() {
221 kFrameCycles = 268123480 / Settings::values.gpu_refresh_rate;
222 kFrameTicks = kFrameCycles / 3;
223
224 g_cur_line = 0;
225 g_last_frame_ticks = g_last_line_ticks = Core::g_app_core->GetTicks();
226
227 auto& framebuffer_top = g_regs.framebuffer_config[0]; 226 auto& framebuffer_top = g_regs.framebuffer_config[0];
228 auto& framebuffer_sub = g_regs.framebuffer_config[1]; 227 auto& framebuffer_sub = g_regs.framebuffer_config[1];
229 228
@@ -252,12 +251,19 @@ void Init() {
252 framebuffer_sub.color_format = Regs::PixelFormat::RGB8; 251 framebuffer_sub.color_format = Regs::PixelFormat::RGB8;
253 framebuffer_sub.active_fb = 0; 252 framebuffer_sub.active_fb = 0;
254 253
255 NOTICE_LOG(GPU, "initialized OK"); 254 frame_ticks = 268123480 / Settings::values.gpu_refresh_rate;
255 line_ticks = (GPU::frame_ticks / framebuffer_top.height);
256 cur_line = 0;
257 last_update_tick = Core::g_app_core->GetTicks();
258 last_skip_frame = false;
259 g_skip_frame = false;
260
261 LOG_DEBUG(HW_GPU, "initialized OK");
256} 262}
257 263
258/// Shutdown hardware 264/// Shutdown hardware
259void Shutdown() { 265void Shutdown() {
260 NOTICE_LOG(GPU, "shutdown OK"); 266 LOG_DEBUG(HW_GPU, "shutdown OK");
261} 267}
262 268
263} // namespace 269} // namespace