diff options
Diffstat (limited to 'src/core/hw')
-rw-r--r-- | src/core/hw/gpu.cpp | 96 | ||||
-rw-r--r-- | src/core/hw/gpu.h | 5 | ||||
-rw-r--r-- | src/core/hw/hw.cpp | 23 | ||||
-rw-r--r-- | src/core/hw/hw.h | 2 | ||||
-rw-r--r-- | src/core/hw/ndma.cpp | 47 | ||||
-rw-r--r-- | src/core/hw/ndma.h | 26 |
6 files changed, 60 insertions, 139 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 @@ // Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include "common/common_types.h" @@ -21,12 +21,14 @@ namespace GPU { Regs g_regs; -u32 g_cur_line = 0; ///< Current vertical screen line -u64 g_last_line_ticks = 0; ///< CPU tick count from last vertical screen line -u64 g_last_frame_ticks = 0; ///< CPU tick count from last frame +bool g_skip_frame = false; ///< True if the current frame was skipped -static u32 kFrameCycles = 0; ///< 268MHz / 60 frames per second -static u32 kFrameTicks = 0; ///< Approximate number of instructions/frame +static u64 frame_ticks = 0; ///< 268MHz / gpu_refresh_rate frames per second +static u64 line_ticks = 0; ///< Number of ticks for a screen line +static u32 cur_line = 0; ///< Current screen line +static u64 last_update_tick = 0; ///< CPU ticl count from last GPU update +static u64 frame_count = 0; ///< Number of frames drawn +static bool last_skip_frame = false; ///< True if the last frame was skipped template <typename T> inline void Read(T &var, const u32 raw_addr) { @@ -34,8 +36,8 @@ inline void Read(T &var, const u32 raw_addr) { u32 index = addr / 4; // Reads other than u32 are untested, so I'd rather have them abort than silently fail - if (index >= Regs::NumIds() || !std::is_same<T,u32>::value) { - ERROR_LOG(GPU, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr); + if (index >= Regs::NumIds() || !std::is_same<T, u32>::value) { + LOG_ERROR(HW_GPU, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr); return; } @@ -48,8 +50,8 @@ inline void Write(u32 addr, const T data) { u32 index = addr / 4; // Writes other than u32 are untested, so I'd rather have them abort than silently fail - if (index >= Regs::NumIds() || !std::is_same<T,u32>::value) { - ERROR_LOG(GPU, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr); + if (index >= Regs::NumIds() || !std::is_same<T, u32>::value) { + LOG_ERROR(HW_GPU, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data, addr); return; } @@ -73,7 +75,7 @@ inline void Write(u32 addr, const T data) { for (u32* ptr = start; ptr < end; ++ptr) *ptr = bswap32(config.value); // TODO: This is just a workaround to missing framebuffer format emulation - DEBUG_LOG(GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress()); + LOG_TRACE(HW_GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress()); } break; } @@ -105,7 +107,7 @@ inline void Write(u32 addr, const T data) { } default: - ERROR_LOG(GPU, "Unknown source framebuffer format %x", config.input_format.Value()); + LOG_ERROR(HW_GPU, "Unknown source framebuffer format %x", config.input_format.Value()); break; } @@ -132,16 +134,16 @@ inline void Write(u32 addr, const T data) { } default: - ERROR_LOG(GPU, "Unknown destination framebuffer format %x", config.output_format.Value()); + LOG_ERROR(HW_GPU, "Unknown destination framebuffer format %x", config.output_format.Value()); break; } } } - DEBUG_LOG(GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), dst format %x", + LOG_TRACE(HW_GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), dst format %x", config.output_height * config.output_width * 4, - config.GetPhysicalInputAddress(), config.input_width, config.input_height, - config.GetPhysicalOutputAddress(), config.output_width, config.output_height, + config.GetPhysicalInputAddress(), (u32)config.input_width, (u32)config.input_height, + config.GetPhysicalOutputAddress(), (u32)config.output_width, (u32)config.output_height, config.output_format.Value()); } break; @@ -154,8 +156,7 @@ inline void Write(u32 addr, const T data) { if (config.trigger & 1) { u32* buffer = (u32*)Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalAddress())); - u32 size = config.size << 3; - Pica::CommandProcessor::ProcessCommandList(buffer, size); + Pica::CommandProcessor::ProcessCommandList(buffer, config.size); } break; } @@ -180,37 +181,41 @@ template void Write<u8>(u32 addr, const u8 data); /// Update hardware void Update() { auto& framebuffer_top = g_regs.framebuffer_config[0]; - u64 current_ticks = Core::g_app_core->GetTicks(); - - // Update the frame after a certain number of CPU ticks have elapsed. This assumes that the - // active frame in memory is always complete to render. There also may be issues with this - // becoming out-of-synch with GSP synchrinization code (as follows). At this time, this seems to - // be the most effective solution for both homebrew and retail applications. With retail, this - // could be moved below (and probably would guarantee more accurate synchronization). However, - // primitive homebrew relies on a vertical blank interrupt to happen inevitably (regardless of a - // threading reschedule). - - if ((current_ticks - g_last_frame_ticks) > GPU::kFrameTicks) { - VideoCore::g_renderer->SwapBuffers(); - g_last_frame_ticks = current_ticks; - } // Synchronize GPU on a thread reschedule: Because we cannot accurately predict a vertical // blank, we need to simulate it. Based on testing, it seems that retail applications work more // accurately when this is signalled between thread switches. if (HLE::g_reschedule) { + u64 current_ticks = Core::g_app_core->GetTicks(); + u32 num_lines = static_cast<u32>((current_ticks - last_update_tick) / line_ticks); // Synchronize line... - if ((current_ticks - g_last_line_ticks) >= GPU::kFrameTicks / framebuffer_top.height) { + if (num_lines > 0) { GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC0); - g_cur_line++; - g_last_line_ticks = current_ticks; + cur_line += num_lines; + last_update_tick += (num_lines * line_ticks); } // Synchronize frame... - if (g_cur_line >= framebuffer_top.height) { - g_cur_line = 0; + if (cur_line >= framebuffer_top.height) { + cur_line = 0; + frame_count++; + last_skip_frame = g_skip_frame; + g_skip_frame = (frame_count & Settings::values.frame_skip) != 0; + + // Swap buffers based on the frameskip mode, which is a little bit tricky. When + // a frame is being skipped, nothing is being rendered to the internal framebuffer(s). + // So, we should only swap frames if the last frame was rendered. The rules are: + // - If frameskip == 0 (disabled), always swap buffers + // - If frameskip == 1, swap buffers every other frame (starting from the first frame) + // - If frameskip > 1, swap buffers every frameskip^n frames (starting from the second frame) + + if ((((Settings::values.frame_skip != 1) ^ last_skip_frame) && last_skip_frame != g_skip_frame) || + Settings::values.frame_skip == 0) { + VideoCore::g_renderer->SwapBuffers(); + } + GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC1); } } @@ -218,12 +223,6 @@ void Update() { /// Initialize hardware void Init() { - kFrameCycles = 268123480 / Settings::values.gpu_refresh_rate; - kFrameTicks = kFrameCycles / 3; - - g_cur_line = 0; - g_last_frame_ticks = g_last_line_ticks = Core::g_app_core->GetTicks(); - auto& framebuffer_top = g_regs.framebuffer_config[0]; auto& framebuffer_sub = g_regs.framebuffer_config[1]; @@ -252,12 +251,19 @@ void Init() { framebuffer_sub.color_format = Regs::PixelFormat::RGB8; framebuffer_sub.active_fb = 0; - NOTICE_LOG(GPU, "initialized OK"); + frame_ticks = 268123480 / Settings::values.gpu_refresh_rate; + line_ticks = (GPU::frame_ticks / framebuffer_top.height); + cur_line = 0; + last_update_tick = Core::g_app_core->GetTicks(); + last_skip_frame = false; + g_skip_frame = false; + + LOG_DEBUG(HW_GPU, "initialized OK"); } /// Shutdown hardware void Shutdown() { - NOTICE_LOG(GPU, "shutdown OK"); + LOG_DEBUG(HW_GPU, "shutdown OK"); } } // namespace diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h index 3fa7b9ccf..292f496c1 100644 --- a/src/core/hw/gpu.h +++ b/src/core/hw/gpu.h @@ -1,5 +1,5 @@ // Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once @@ -169,7 +169,7 @@ struct Regs { INSERT_PADDING_WORDS(0x331); struct { - // command list size + // command list size (in bytes) u32 size; INSERT_PADDING_WORDS(0x1); @@ -241,6 +241,7 @@ ASSERT_REG_POSITION(command_processor_config, 0x00638); static_assert(sizeof(Regs) == 0x1000 * sizeof(u32), "Invalid total size of register set"); extern Regs g_regs; +extern bool g_skip_frame; template <typename T> void Read(T &var, const u32 addr); diff --git a/src/core/hw/hw.cpp b/src/core/hw/hw.cpp index 4d0719263..848ab5348 100644 --- a/src/core/hw/hw.cpp +++ b/src/core/hw/hw.cpp @@ -1,12 +1,11 @@ // Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include "common/common_types.h" #include "core/hw/hw.h" #include "core/hw/gpu.h" -#include "core/hw/ndma.h" namespace HW { @@ -40,17 +39,12 @@ template <typename T> inline void Read(T &var, const u32 addr) { switch (addr & 0xFFFFF000) { - // TODO(bunnei): What is the virtual address of NDMA? - // case VADDR_NDMA: - // NDMA::Read(var, addr); - // break; - case VADDR_GPU: GPU::Read(var, addr); break; default: - ERROR_LOG(HW, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr); + LOG_ERROR(HW_Memory, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr); } } @@ -58,17 +52,12 @@ template <typename T> inline void Write(u32 addr, const T data) { switch (addr & 0xFFFFF000) { - // TODO(bunnei): What is the virtual address of NDMA? - // case VADDR_NDMA - // NDMA::Write(addr, data); - // break; - case VADDR_GPU: GPU::Write(addr, data); break; default: - ERROR_LOG(HW, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr); + LOG_ERROR(HW_Memory, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data, addr); } } @@ -87,19 +76,17 @@ template void Write<u8>(u32 addr, const u8 data); /// Update hardware void Update() { GPU::Update(); - NDMA::Update(); } /// Initialize hardware void Init() { GPU::Init(); - NDMA::Init(); - NOTICE_LOG(HW, "initialized OK"); + LOG_DEBUG(HW, "initialized OK"); } /// Shutdown hardware void Shutdown() { - NOTICE_LOG(HW, "shutdown OK"); + LOG_DEBUG(HW, "shutdown OK"); } }
\ No newline at end of file diff --git a/src/core/hw/hw.h b/src/core/hw/hw.h index 1055ed94f..991c0a07d 100644 --- a/src/core/hw/hw.h +++ b/src/core/hw/hw.h @@ -1,5 +1,5 @@ // Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once diff --git a/src/core/hw/ndma.cpp b/src/core/hw/ndma.cpp deleted file mode 100644 index e29a773f1..000000000 --- a/src/core/hw/ndma.cpp +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 -// Refer to the license.txt file included. - -#include "common/common_types.h" - -#include "core/hw/ndma.h" - -namespace NDMA { - -template <typename T> -inline void Read(T &var, const u32 addr) { - ERROR_LOG(NDMA, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr); -} - -template <typename T> -inline void Write(u32 addr, const T data) { - ERROR_LOG(NDMA, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr); -} - -// Explicitly instantiate template functions because we aren't defining this in the header: - -template void Read<u64>(u64 &var, const u32 addr); -template void Read<u32>(u32 &var, const u32 addr); -template void Read<u16>(u16 &var, const u32 addr); -template void Read<u8>(u8 &var, const u32 addr); - -template void Write<u64>(u32 addr, const u64 data); -template void Write<u32>(u32 addr, const u32 data); -template void Write<u16>(u32 addr, const u16 data); -template void Write<u8>(u32 addr, const u8 data); - -/// Update hardware -void Update() { -} - -/// Initialize hardware -void Init() { - NOTICE_LOG(GPU, "initialized OK"); -} - -/// Shutdown hardware -void Shutdown() { - NOTICE_LOG(GPU, "shutdown OK"); -} - -} // namespace diff --git a/src/core/hw/ndma.h b/src/core/hw/ndma.h deleted file mode 100644 index d8fa3d40b..000000000 --- a/src/core/hw/ndma.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 -// Refer to the license.txt file included. - -#pragma once - -#include "common/common_types.h" - -namespace NDMA { - -template <typename T> -inline void Read(T &var, const u32 addr); - -template <typename T> -inline void Write(u32 addr, const T data); - -/// Update hardware -void Update(); - -/// Initialize hardware -void Init(); - -/// Shutdown hardware -void Shutdown(); - -} // namespace |