diff options
| author | 2015-01-15 18:11:03 -0500 | |
|---|---|---|
| committer | 2015-01-15 18:11:03 -0500 | |
| commit | 4b47ed6194484e6cff264553bb383b7e9c608a9c (patch) | |
| tree | 468b9214a0d0989d8702ba4baee221d5c028e5e7 /src/core | |
| parent | Merge pull request #481 from Subv/hm_b (diff) | |
| parent | GPU: Fix buffer overrun in Display Transfers (diff) | |
| download | yuzu-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.cpp | 35 | ||||
| -rw-r--r-- | src/core/hle/service/gsp_gpu.h | 25 | ||||
| -rw-r--r-- | src/core/hw/gpu.cpp | 129 | ||||
| -rw-r--r-- | src/core/hw/gpu.h | 3 | ||||
| -rw-r--r-- | src/core/hw/hw.cpp | 1 |
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 |
| 47 | struct InterruptRelayQueue { | 47 | struct 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 | ||
| 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 line_ticks = 0; ///< Number of ticks for a screen line | 32 | static u64 frame_ticks; |
| 31 | static u32 cur_line = 0; ///< Current screen line | 33 | /// Event id for CoreTiming |
| 32 | static u64 last_update_tick = 0; ///< CPU ticl count from last GPU update | 34 | static int vblank_event; |
| 33 | static u64 frame_count = 0; ///< Number of frames drawn | 35 | /// Total number of frames drawn |
| 34 | static bool last_skip_frame = false; ///< True if the last frame was skipped | 36 | static u64 frame_count; |
| 37 | /// True if the last frame was skipped | ||
| 38 | static bool last_skip_frame = false; | ||
| 35 | 39 | ||
| 36 | template <typename T> | 40 | template <typename T> |
| 37 | inline void Read(T &var, const u32 raw_addr) { | 41 | inline 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); | |||
| 186 | template void Write<u8>(u32 addr, const u8 data); | 201 | template void Write<u8>(u32 addr, const u8 data); |
| 187 | 202 | ||
| 188 | /// Update hardware | 203 | /// Update hardware |
| 189 | void Update() { | 204 | static 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); | |||
| 252 | template <typename T> | 252 | template <typename T> |
| 253 | void Write(u32 addr, const T data); | 253 | void Write(u32 addr, const T data); |
| 254 | 254 | ||
| 255 | /// Update hardware | ||
| 256 | void Update(); | ||
| 257 | |||
| 258 | /// Initialize hardware | 255 | /// Initialize hardware |
| 259 | void Init(); | 256 | void 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 |
| 77 | void Update() { | 77 | void Update() { |
| 78 | GPU::Update(); | ||
| 79 | } | 78 | } |
| 80 | 79 | ||
| 81 | /// Initialize hardware | 80 | /// Initialize hardware |