summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar bunnei2014-12-25 22:52:40 -0500
committerGravatar bunnei2014-12-25 22:52:40 -0500
commite5ddbfee0211642858f0114691966db7c17e5f7e (patch)
tree44d89c26c5f4dc976dadd7322c9269f026c7b98d /src
parentMerge pull request #330 from purpasmart96/new_srv (diff)
parentGPU: Further improve synchronization. (diff)
downloadyuzu-e5ddbfee0211642858f0114691966db7c17e5f7e.tar.gz
yuzu-e5ddbfee0211642858f0114691966db7c17e5f7e.tar.xz
yuzu-e5ddbfee0211642858f0114691966db7c17e5f7e.zip
Merge pull request #339 from bunnei/fixup-gsp-synch
Fixup gsp synch
Diffstat (limited to 'src')
-rw-r--r--src/core/arm/arm_interface.h6
-rw-r--r--src/core/arm/dyncom/arm_dyncom.cpp49
-rw-r--r--src/core/arm/dyncom/arm_dyncom.h14
-rw-r--r--src/core/arm/interpreter/arm_interpreter.cpp51
-rw-r--r--src/core/arm/interpreter/arm_interpreter.h6
-rw-r--r--src/core/hle/hle.cpp8
-rw-r--r--src/core/hw/gpu.cpp42
7 files changed, 59 insertions, 117 deletions
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index c59355339..3b7209418 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -78,6 +78,12 @@ public:
78 virtual u64 GetTicks() const = 0; 78 virtual u64 GetTicks() const = 0;
79 79
80 /** 80 /**
81 * Advance the CPU core by the specified number of ticks (e.g. to simulate CPU execution time)
82 * @param ticks Number of ticks to advance the CPU core
83 */
84 virtual void AddTicks(u64 ticks) = 0;
85
86 /**
81 * Saves the current CPU context 87 * Saves the current CPU context
82 * @param ctx Thread context to save 88 * @param ctx Thread context to save
83 */ 89 */
diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp
index 6d4fb1b48..a838fd25a 100644
--- a/src/core/arm/dyncom/arm_dyncom.cpp
+++ b/src/core/arm/dyncom/arm_dyncom.cpp
@@ -47,68 +47,38 @@ ARM_DynCom::ARM_DynCom() : ticks(0) {
47ARM_DynCom::~ARM_DynCom() { 47ARM_DynCom::~ARM_DynCom() {
48} 48}
49 49
50/**
51 * Set the Program Counter to an address
52 * @param addr Address to set PC to
53 */
54void ARM_DynCom::SetPC(u32 pc) { 50void ARM_DynCom::SetPC(u32 pc) {
55 state->pc = state->Reg[15] = pc; 51 state->pc = state->Reg[15] = pc;
56} 52}
57 53
58/*
59 * Get the current Program Counter
60 * @return Returns current PC
61 */
62u32 ARM_DynCom::GetPC() const { 54u32 ARM_DynCom::GetPC() const {
63 return state->Reg[15]; 55 return state->Reg[15];
64} 56}
65 57
66/**
67 * Get an ARM register
68 * @param index Register index (0-15)
69 * @return Returns the value in the register
70 */
71u32 ARM_DynCom::GetReg(int index) const { 58u32 ARM_DynCom::GetReg(int index) const {
72 return state->Reg[index]; 59 return state->Reg[index];
73} 60}
74 61
75/**
76 * Set an ARM register
77 * @param index Register index (0-15)
78 * @param value Value to set register to
79 */
80void ARM_DynCom::SetReg(int index, u32 value) { 62void ARM_DynCom::SetReg(int index, u32 value) {
81 state->Reg[index] = value; 63 state->Reg[index] = value;
82} 64}
83 65
84/**
85 * Get the current CPSR register
86 * @return Returns the value of the CPSR register
87 */
88u32 ARM_DynCom::GetCPSR() const { 66u32 ARM_DynCom::GetCPSR() const {
89 return state->Cpsr; 67 return state->Cpsr;
90} 68}
91 69
92/**
93 * Set the current CPSR register
94 * @param cpsr Value to set CPSR to
95 */
96void ARM_DynCom::SetCPSR(u32 cpsr) { 70void ARM_DynCom::SetCPSR(u32 cpsr) {
97 state->Cpsr = cpsr; 71 state->Cpsr = cpsr;
98} 72}
99 73
100/**
101 * Returns the number of clock ticks since the last reset
102 * @return Returns number of clock ticks
103 */
104u64 ARM_DynCom::GetTicks() const { 74u64 ARM_DynCom::GetTicks() const {
105 return ticks; 75 return ticks;
106} 76}
107 77
108/** 78void ARM_DynCom::AddTicks(u64 ticks) {
109 * Executes the given number of instructions 79 this->ticks += ticks;
110 * @param num_instructions Number of instructions to executes 80}
111 */ 81
112void ARM_DynCom::ExecuteInstructions(int num_instructions) { 82void ARM_DynCom::ExecuteInstructions(int num_instructions) {
113 state->NumInstrsToExecute = num_instructions; 83 state->NumInstrsToExecute = num_instructions;
114 84
@@ -118,11 +88,6 @@ void ARM_DynCom::ExecuteInstructions(int num_instructions) {
118 ticks += InterpreterMainLoop(state.get()); 88 ticks += InterpreterMainLoop(state.get());
119} 89}
120 90
121/**
122 * Saves the current CPU context
123 * @param ctx Thread context to save
124 * @todo Do we need to save Reg[15] and NextInstr?
125 */
126void ARM_DynCom::SaveContext(ThreadContext& ctx) { 91void ARM_DynCom::SaveContext(ThreadContext& ctx) {
127 memcpy(ctx.cpu_registers, state->Reg, sizeof(ctx.cpu_registers)); 92 memcpy(ctx.cpu_registers, state->Reg, sizeof(ctx.cpu_registers));
128 memcpy(ctx.fpu_registers, state->ExtReg, sizeof(ctx.fpu_registers)); 93 memcpy(ctx.fpu_registers, state->ExtReg, sizeof(ctx.fpu_registers));
@@ -139,11 +104,6 @@ void ARM_DynCom::SaveContext(ThreadContext& ctx) {
139 ctx.mode = state->NextInstr; 104 ctx.mode = state->NextInstr;
140} 105}
141 106
142/**
143 * Loads a CPU context
144 * @param ctx Thread context to load
145 * @param Do we need to load Reg[15] and NextInstr?
146 */
147void ARM_DynCom::LoadContext(const ThreadContext& ctx) { 107void ARM_DynCom::LoadContext(const ThreadContext& ctx) {
148 memcpy(state->Reg, ctx.cpu_registers, sizeof(ctx.cpu_registers)); 108 memcpy(state->Reg, ctx.cpu_registers, sizeof(ctx.cpu_registers));
149 memcpy(state->ExtReg, ctx.fpu_registers, sizeof(ctx.fpu_registers)); 109 memcpy(state->ExtReg, ctx.fpu_registers, sizeof(ctx.fpu_registers));
@@ -160,7 +120,6 @@ void ARM_DynCom::LoadContext(const ThreadContext& ctx) {
160 state->NextInstr = ctx.mode; 120 state->NextInstr = ctx.mode;
161} 121}
162 122
163/// Prepare core for thread reschedule (if needed to correctly handle state)
164void ARM_DynCom::PrepareReschedule() { 123void ARM_DynCom::PrepareReschedule() {
165 state->NumInstrsToExecute = 0; 124 state->NumInstrsToExecute = 0;
166} 125}
diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h
index 6fa2a0ba7..7284dcd07 100644
--- a/src/core/arm/dyncom/arm_dyncom.h
+++ b/src/core/arm/dyncom/arm_dyncom.h
@@ -27,14 +27,14 @@ public:
27 * Get the current Program Counter 27 * Get the current Program Counter
28 * @return Returns current PC 28 * @return Returns current PC
29 */ 29 */
30 u32 GetPC() const; 30 u32 GetPC() const override;
31 31
32 /** 32 /**
33 * Get an ARM register 33 * Get an ARM register
34 * @param index Register index (0-15) 34 * @param index Register index (0-15)
35 * @return Returns the value in the register 35 * @return Returns the value in the register
36 */ 36 */
37 u32 GetReg(int index) const; 37 u32 GetReg(int index) const override;
38 38
39 /** 39 /**
40 * Set an ARM register 40 * Set an ARM register
@@ -47,7 +47,7 @@ public:
47 * Get the current CPSR register 47 * Get the current CPSR register
48 * @return Returns the value of the CPSR register 48 * @return Returns the value of the CPSR register
49 */ 49 */
50 u32 GetCPSR() const; 50 u32 GetCPSR() const override;
51 51
52 /** 52 /**
53 * Set the current CPSR register 53 * Set the current CPSR register
@@ -59,7 +59,13 @@ public:
59 * Returns the number of clock ticks since the last reset 59 * Returns the number of clock ticks since the last reset
60 * @return Returns number of clock ticks 60 * @return Returns number of clock ticks
61 */ 61 */
62 u64 GetTicks() const; 62 u64 GetTicks() const override;
63
64 /**
65 * Advance the CPU core by the specified number of ticks (e.g. to simulate CPU execution time)
66 * @param ticks Number of ticks to advance the CPU core
67 */
68 void AddTicks(u64 ticks) override;
63 69
64 /** 70 /**
65 * Saves the current CPU context 71 * Saves the current CPU context
diff --git a/src/core/arm/interpreter/arm_interpreter.cpp b/src/core/arm/interpreter/arm_interpreter.cpp
index be04fc1a1..80ebc359e 100644
--- a/src/core/arm/interpreter/arm_interpreter.cpp
+++ b/src/core/arm/interpreter/arm_interpreter.cpp
@@ -38,78 +38,43 @@ ARM_Interpreter::~ARM_Interpreter() {
38 delete state; 38 delete state;
39} 39}
40 40
41/**
42 * Set the Program Counter to an address
43 * @param addr Address to set PC to
44 */
45void ARM_Interpreter::SetPC(u32 pc) { 41void ARM_Interpreter::SetPC(u32 pc) {
46 state->pc = state->Reg[15] = pc; 42 state->pc = state->Reg[15] = pc;
47} 43}
48 44
49/*
50 * Get the current Program Counter
51 * @return Returns current PC
52 */
53u32 ARM_Interpreter::GetPC() const { 45u32 ARM_Interpreter::GetPC() const {
54 return state->pc; 46 return state->pc;
55} 47}
56 48
57/**
58 * Get an ARM register
59 * @param index Register index (0-15)
60 * @return Returns the value in the register
61 */
62u32 ARM_Interpreter::GetReg(int index) const { 49u32 ARM_Interpreter::GetReg(int index) const {
63 return state->Reg[index]; 50 return state->Reg[index];
64} 51}
65 52
66/**
67 * Set an ARM register
68 * @param index Register index (0-15)
69 * @param value Value to set register to
70 */
71void ARM_Interpreter::SetReg(int index, u32 value) { 53void ARM_Interpreter::SetReg(int index, u32 value) {
72 state->Reg[index] = value; 54 state->Reg[index] = value;
73} 55}
74 56
75/**
76 * Get the current CPSR register
77 * @return Returns the value of the CPSR register
78 */
79u32 ARM_Interpreter::GetCPSR() const { 57u32 ARM_Interpreter::GetCPSR() const {
80 return state->Cpsr; 58 return state->Cpsr;
81} 59}
82 60
83/**
84 * Set the current CPSR register
85 * @param cpsr Value to set CPSR to
86 */
87void ARM_Interpreter::SetCPSR(u32 cpsr) { 61void ARM_Interpreter::SetCPSR(u32 cpsr) {
88 state->Cpsr = cpsr; 62 state->Cpsr = cpsr;
89} 63}
90 64
91/**
92 * Returns the number of clock ticks since the last reset
93 * @return Returns number of clock ticks
94 */
95u64 ARM_Interpreter::GetTicks() const { 65u64 ARM_Interpreter::GetTicks() const {
96 return ARMul_Time(state); 66 return state->NumInstrs;
67}
68
69void ARM_Interpreter::AddTicks(u64 ticks) {
70 state->NumInstrs += ticks;
97} 71}
98 72
99/**
100 * Executes the given number of instructions
101 * @param num_instructions Number of instructions to executes
102 */
103void ARM_Interpreter::ExecuteInstructions(int num_instructions) { 73void ARM_Interpreter::ExecuteInstructions(int num_instructions) {
104 state->NumInstrsToExecute = num_instructions - 1; 74 state->NumInstrsToExecute = num_instructions - 1;
105 ARMul_Emulate32(state); 75 ARMul_Emulate32(state);
106} 76}
107 77
108/**
109 * Saves the current CPU context
110 * @param ctx Thread context to save
111 * @todo Do we need to save Reg[15] and NextInstr?
112 */
113void ARM_Interpreter::SaveContext(ThreadContext& ctx) { 78void ARM_Interpreter::SaveContext(ThreadContext& ctx) {
114 memcpy(ctx.cpu_registers, state->Reg, sizeof(ctx.cpu_registers)); 79 memcpy(ctx.cpu_registers, state->Reg, sizeof(ctx.cpu_registers));
115 memcpy(ctx.fpu_registers, state->ExtReg, sizeof(ctx.fpu_registers)); 80 memcpy(ctx.fpu_registers, state->ExtReg, sizeof(ctx.fpu_registers));
@@ -126,11 +91,6 @@ void ARM_Interpreter::SaveContext(ThreadContext& ctx) {
126 ctx.mode = state->NextInstr; 91 ctx.mode = state->NextInstr;
127} 92}
128 93
129/**
130 * Loads a CPU context
131 * @param ctx Thread context to load
132 * @param Do we need to load Reg[15] and NextInstr?
133 */
134void ARM_Interpreter::LoadContext(const ThreadContext& ctx) { 94void ARM_Interpreter::LoadContext(const ThreadContext& ctx) {
135 memcpy(state->Reg, ctx.cpu_registers, sizeof(ctx.cpu_registers)); 95 memcpy(state->Reg, ctx.cpu_registers, sizeof(ctx.cpu_registers));
136 memcpy(state->ExtReg, ctx.fpu_registers, sizeof(ctx.fpu_registers)); 96 memcpy(state->ExtReg, ctx.fpu_registers, sizeof(ctx.fpu_registers));
@@ -147,7 +107,6 @@ void ARM_Interpreter::LoadContext(const ThreadContext& ctx) {
147 state->NextInstr = ctx.mode; 107 state->NextInstr = ctx.mode;
148} 108}
149 109
150/// Prepare core for thread reschedule (if needed to correctly handle state)
151void ARM_Interpreter::PrepareReschedule() { 110void ARM_Interpreter::PrepareReschedule() {
152 state->NumInstrsToExecute = 0; 111 state->NumInstrsToExecute = 0;
153} 112}
diff --git a/src/core/arm/interpreter/arm_interpreter.h b/src/core/arm/interpreter/arm_interpreter.h
index b685215a0..019dad5df 100644
--- a/src/core/arm/interpreter/arm_interpreter.h
+++ b/src/core/arm/interpreter/arm_interpreter.h
@@ -61,6 +61,12 @@ public:
61 u64 GetTicks() const override; 61 u64 GetTicks() const override;
62 62
63 /** 63 /**
64 * Advance the CPU core by the specified number of ticks (e.g. to simulate CPU execution time)
65 * @param ticks Number of ticks to advance the CPU core
66 */
67 void AddTicks(u64 ticks) override;
68
69 /**
64 * Saves the current CPU context 70 * Saves the current CPU context
65 * @param ctx Thread context to save 71 * @param ctx Thread context to save
66 */ 72 */
diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp
index 2d314a4cf..33ac12507 100644
--- a/src/core/hle/hle.cpp
+++ b/src/core/hle/hle.cpp
@@ -43,7 +43,15 @@ void CallSVC(u32 opcode) {
43 43
44void Reschedule(const char *reason) { 44void Reschedule(const char *reason) {
45 _dbg_assert_msg_(Kernel, reason != 0 && strlen(reason) < 256, "Reschedule: Invalid or too long reason."); 45 _dbg_assert_msg_(Kernel, reason != 0 && strlen(reason) < 256, "Reschedule: Invalid or too long reason.");
46
47 // TODO(bunnei): It seems that games depend on some CPU execution time elapsing during HLE
48 // routines. This simulates that time by artificially advancing the number of CPU "ticks".
49 // The value was chosen empirically, it seems to work well enough for everything tested, but
50 // is likely not ideal. We should find a more accurate way to simulate timing with HLE.
51 Core::g_app_core->AddTicks(4000);
52
46 Core::g_app_core->PrepareReschedule(); 53 Core::g_app_core->PrepareReschedule();
54
47 g_reschedule = true; 55 g_reschedule = true;
48} 56}
49 57
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index 67a8bc324..7e70b34c1 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -21,12 +21,10 @@ namespace GPU {
21 21
22Regs g_regs; 22Regs g_regs;
23 23
24u32 g_cur_line = 0; ///< Current vertical screen line 24static u64 frame_ticks = 0; ///< 268MHz / 60 frames per second
25u64 g_last_line_ticks = 0; ///< CPU tick count from last vertical screen line 25static u32 cur_line = 0; ///< Current vertical screen line
26u64 g_last_frame_ticks = 0; ///< CPU tick count from last frame 26static u64 last_frame_ticks = 0; ///< CPU tick count from last frame
27 27static u64 last_update_tick = 0; ///< CPU ticl count from last GPU update
28static u32 kFrameCycles = 0; ///< 268MHz / 60 frames per second
29static u32 kFrameTicks = 0; ///< Approximate number of instructions/frame
30 28
31template <typename T> 29template <typename T>
32inline void Read(T &var, const u32 raw_addr) { 30inline void Read(T &var, const u32 raw_addr) {
@@ -34,7 +32,7 @@ inline void Read(T &var, const u32 raw_addr) {
34 u32 index = addr / 4; 32 u32 index = addr / 4;
35 33
36 // Reads other than u32 are untested, so I'd rather have them abort than silently fail 34 // 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) { 35 if (index >= Regs::NumIds() || !std::is_same<T, u32>::value) {
38 LOG_ERROR(HW_GPU, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr); 36 LOG_ERROR(HW_GPU, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr);
39 return; 37 return;
40 } 38 }
@@ -48,7 +46,7 @@ inline void Write(u32 addr, const T data) {
48 u32 index = addr / 4; 46 u32 index = addr / 4;
49 47
50 // Writes other than u32 are untested, so I'd rather have them abort than silently fail 48 // 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) { 49 if (index >= Regs::NumIds() || !std::is_same<T, u32>::value) {
52 LOG_ERROR(HW_GPU, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data, addr); 50 LOG_ERROR(HW_GPU, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data, addr);
53 return; 51 return;
54 } 52 }
@@ -179,7 +177,6 @@ template void Write<u8>(u32 addr, const u8 data);
179/// Update hardware 177/// Update hardware
180void Update() { 178void Update() {
181 auto& framebuffer_top = g_regs.framebuffer_config[0]; 179 auto& framebuffer_top = g_regs.framebuffer_config[0];
182 u64 current_ticks = Core::g_app_core->GetTicks();
183 180
184 // Update the frame after a certain number of CPU ticks have elapsed. This assumes that the 181 // Update the frame after a certain number of CPU ticks have elapsed. This assumes that the
185 // active frame in memory is always complete to render. There also may be issues with this 182 // active frame in memory is always complete to render. There also may be issues with this
@@ -189,9 +186,9 @@ void Update() {
189 // primitive homebrew relies on a vertical blank interrupt to happen inevitably (regardless of a 186 // primitive homebrew relies on a vertical blank interrupt to happen inevitably (regardless of a
190 // threading reschedule). 187 // threading reschedule).
191 188
192 if ((current_ticks - g_last_frame_ticks) > GPU::kFrameTicks) { 189 if ((Core::g_app_core->GetTicks() - last_frame_ticks) > (GPU::frame_ticks)) {
193 VideoCore::g_renderer->SwapBuffers(); 190 VideoCore::g_renderer->SwapBuffers();
194 g_last_frame_ticks = current_ticks; 191 last_frame_ticks = Core::g_app_core->GetTicks();
195 } 192 }
196 193
197 // Synchronize GPU on a thread reschedule: Because we cannot accurately predict a vertical 194 // Synchronize GPU on a thread reschedule: Because we cannot accurately predict a vertical
@@ -199,17 +196,20 @@ void Update() {
199 // accurately when this is signalled between thread switches. 196 // accurately when this is signalled between thread switches.
200 197
201 if (HLE::g_reschedule) { 198 if (HLE::g_reschedule) {
199 u64 current_ticks = Core::g_app_core->GetTicks();
200 u64 line_ticks = (GPU::frame_ticks / framebuffer_top.height) * 16;
202 201
203 // Synchronize line... 202 //// Synchronize line...
204 if ((current_ticks - g_last_line_ticks) >= GPU::kFrameTicks / framebuffer_top.height) { 203 if ((current_ticks - last_update_tick) >= line_ticks) {
205 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC0); 204 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC0);
206 g_cur_line++; 205 cur_line++;
207 g_last_line_ticks = current_ticks; 206 last_update_tick += line_ticks;
208 } 207 }
209 208
210 // Synchronize frame... 209 // Synchronize frame...
211 if (g_cur_line >= framebuffer_top.height) { 210 if (cur_line >= framebuffer_top.height) {
212 g_cur_line = 0; 211 cur_line = 0;
212 VideoCore::g_renderer->SwapBuffers();
213 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC1); 213 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC1);
214 } 214 }
215 } 215 }
@@ -217,11 +217,9 @@ void Update() {
217 217
218/// Initialize hardware 218/// Initialize hardware
219void Init() { 219void Init() {
220 kFrameCycles = 268123480 / Settings::values.gpu_refresh_rate; 220 frame_ticks = 268123480 / Settings::values.gpu_refresh_rate;
221 kFrameTicks = kFrameCycles / 3; 221 cur_line = 0;
222 222 last_update_tick = last_frame_ticks = Core::g_app_core->GetTicks();
223 g_cur_line = 0;
224 g_last_frame_ticks = g_last_line_ticks = Core::g_app_core->GetTicks();
225 223
226 auto& framebuffer_top = g_regs.framebuffer_config[0]; 224 auto& framebuffer_top = g_regs.framebuffer_config[0];
227 auto& framebuffer_sub = g_regs.framebuffer_config[1]; 225 auto& framebuffer_sub = g_regs.framebuffer_config[1];