diff --git a/src/nba/CMakeLists.txt b/src/nba/CMakeLists.txt index ce145f93f..b6d044848 100644 --- a/src/nba/CMakeLists.txt +++ b/src/nba/CMakeLists.txt @@ -98,7 +98,6 @@ set(HEADERS_PUBLIC include/nba/common/punning.hpp include/nba/common/scope_exit.hpp include/nba/device/audio_device.hpp - include/nba/device/input_device.hpp include/nba/device/video_device.hpp include/nba/rom/backup/backup.hpp include/nba/rom/backup/backup_file.hpp diff --git a/src/nba/include/nba/config.hpp b/src/nba/include/nba/config.hpp index 0bf838c0e..54029d802 100644 --- a/src/nba/include/nba/config.hpp +++ b/src/nba/include/nba/config.hpp @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -47,7 +46,6 @@ struct Config { } audio; std::shared_ptr audio_dev = std::make_shared(); - std::shared_ptr input_dev = std::make_shared(); std::shared_ptr video_dev = std::make_shared(); }; diff --git a/src/nba/include/nba/core.hpp b/src/nba/include/nba/core.hpp index 88f688606..853821050 100644 --- a/src/nba/include/nba/core.hpp +++ b/src/nba/include/nba/core.hpp @@ -19,6 +19,20 @@ namespace nba { +enum class Key : u8 { + A = 0, + B = 1, + Select = 2, + Start = 3, + Right = 4, + Left = 5, + Up = 6, + Down = 7, + L = 8, + R = 9, + Count = 10 +}; + struct CoreBase { static constexpr int kCyclesPerFrame = 280896; @@ -32,6 +46,7 @@ struct CoreBase { virtual auto CreateSolarSensor() -> std::unique_ptr = 0; virtual void LoadState(SaveState const& state) = 0; virtual void CopyState(SaveState& state) = 0; + virtual void SetKeyStatus(Key key, bool pressed) = 0; virtual void Run(int cycles) = 0; virtual auto GetROM() -> ROM& = 0; diff --git a/src/nba/include/nba/device/input_device.hpp b/src/nba/include/nba/device/input_device.hpp deleted file mode 100644 index 6dfdd63e6..000000000 --- a/src/nba/include/nba/device/input_device.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2023 fleroviux - * - * Licensed under GPLv3 or any later version. - * Refer to the included LICENSE file. - */ - -#pragma once - -#include - -namespace nba { - -struct InputDevice { - virtual ~InputDevice() = default; - - enum class Key { - Up, - Down, - Left, - Right, - Start, - Select, - A, - B, - L, - R - }; - - static constexpr int kKeyCount = 10; - - virtual auto Poll(Key key) -> bool = 0; - virtual void SetOnChangeCallback(std::function callback) = 0; -}; - -struct NullInputDevice : InputDevice { - auto Poll(Key key) -> bool final { - return false; - } - - void SetOnChangeCallback(std::function callback) final {} -}; - -struct BasicInputDevice : InputDevice { - void SetKeyStatus(Key key, bool pressed) { - key_status[static_cast(key)] = pressed; - keypress_callback(); - } - - auto Poll(Key key) -> bool final { - return key_status[static_cast(key)]; - } - - void SetOnChangeCallback(std::function callback) final { - keypress_callback = callback; - } -private: - std::function keypress_callback; - bool key_status[kKeyCount]; -}; - -} // namespace nba diff --git a/src/nba/include/nba/integer.hpp b/src/nba/include/nba/integer.hpp index 6b6188c1f..0a2a73441 100644 --- a/src/nba/include/nba/integer.hpp +++ b/src/nba/include/nba/integer.hpp @@ -23,6 +23,8 @@ using u64 = std::uint64_t; using s64 = std::int64_t; using uint = unsigned int; +using u8bool = u8; + #ifdef NBA_NO_EXPORT_INT_TYPES } // namespace nba #endif diff --git a/src/nba/include/nba/scheduler.hpp b/src/nba/include/nba/scheduler.hpp index d09dfa195..092d8bec1 100644 --- a/src/nba/include/nba/scheduler.hpp +++ b/src/nba/include/nba/scheduler.hpp @@ -67,8 +67,6 @@ struct Scheduler { SIO_transfer_done, - KeyPad_Poll, - EndOfQueue, Count }; diff --git a/src/nba/src/core.cpp b/src/nba/src/core.cpp index 1368143a6..8db307ebb 100644 --- a/src/nba/src/core.cpp +++ b/src/nba/src/core.cpp @@ -23,7 +23,7 @@ Core::Core(std::shared_ptr config) , apu(scheduler, dma, bus, config) , ppu(scheduler, irq, dma, config) , timer(scheduler, irq, apu) - , keypad(scheduler, irq, config) + , keypad(scheduler, irq) , bus(scheduler, {cpu, irq, dma, apu, ppu, timer, keypad}) { Reset(); } @@ -71,6 +71,10 @@ auto Core::CreateSolarSensor() -> std::unique_ptr { return std::make_unique(); } +void Core::SetKeyStatus(Key key, bool pressed) { + keypad.SetKeyStatus(key, pressed); +} + void Core::Run(int cycles) { using HaltControl = Bus::Hardware::HaltControl; diff --git a/src/nba/src/core.hpp b/src/nba/src/core.hpp index 27a1f64bf..32630f9f4 100644 --- a/src/nba/src/core.hpp +++ b/src/nba/src/core.hpp @@ -30,6 +30,7 @@ struct Core final : CoreBase { auto CreateSolarSensor() -> std::unique_ptr override; void LoadState(SaveState const& state) override; void CopyState(SaveState& state) override; + void SetKeyStatus(Key key, bool pressed) override; void Run(int cycles) override; auto GetROM() -> ROM& override; diff --git a/src/nba/src/hw/keypad/keypad.cpp b/src/nba/src/hw/keypad/keypad.cpp index 8376c641d..48ffe804e 100644 --- a/src/nba/src/hw/keypad/keypad.cpp +++ b/src/nba/src/hw/keypad/keypad.cpp @@ -11,12 +11,9 @@ namespace nba::core { -KeyPad::KeyPad(Scheduler& scheduler, IRQ& irq, std::shared_ptr config) +KeyPad::KeyPad(Scheduler& scheduler, IRQ& irq) : scheduler(scheduler) - , irq(irq) - , config(config) { - scheduler.Register(Scheduler::EventClass::KeyPad_Poll, this, &KeyPad::Poll); - + , irq(irq) { Reset(); } @@ -25,29 +22,18 @@ void KeyPad::Reset() { input.keypad = this; control = {}; control.keypad = this; - config->input_dev->SetOnChangeCallback(std::bind(&KeyPad::UpdateInput, this)); - - input_queue.Reset(); - scheduler.Add(k_poll_interval, Scheduler::EventClass::KeyPad_Poll); } -void KeyPad::UpdateInput() { - auto& input_device = config->input_dev; +void KeyPad::SetKeyStatus(Key key, bool pressed) { + const u16 bit = 1 << (int)key; - u16 input = 0; - - if(!input_device->Poll(Key::A)) input |= 1; - if(!input_device->Poll(Key::B)) input |= 2; - if(!input_device->Poll(Key::Select)) input |= 4; - if(!input_device->Poll(Key::Start)) input |= 8; - if(!input_device->Poll(Key::Right)) input |= 16; - if(!input_device->Poll(Key::Left)) input |= 32; - if(!input_device->Poll(Key::Up)) input |= 64; - if(!input_device->Poll(Key::Down)) input |= 128; - if(!input_device->Poll(Key::R)) input |= 256; - if(!input_device->Poll(Key::L)) input |= 512; + if(pressed) { + input.value &= ~bit; + } else { + input.value |= bit; + } - input_queue.Enqueue(input); + UpdateIRQ(); } void KeyPad::UpdateIRQ() { @@ -64,21 +50,7 @@ void KeyPad::UpdateIRQ() { } } -void KeyPad::Poll() { - PollInternal(); - scheduler.Add(k_poll_interval, Scheduler::EventClass::KeyPad_Poll); -} - -void KeyPad::PollInternal() { - while(input_queue.DataAvailable()) { - input.value = input_queue.Dequeue(); - UpdateIRQ(); - } -} - auto KeyPad::KeyInput::ReadByte(uint offset) -> u8 { - keypad->PollInternal(); - switch(offset) { case 0: return u8(value); @@ -133,38 +105,4 @@ void KeyPad::KeyControl::WriteHalf(u16 value) { keypad->UpdateIRQ(); } -void KeyPad::InputQueue::Reset() { - rd_ptr = 0; - wr_ptr = 0; - size = 0; -} - -bool KeyPad::InputQueue::DataAvailable() { - return size > 0; -} - -void KeyPad::InputQueue::Enqueue(u16 input) { - data[wr_ptr] = input; - wr_ptr = (wr_ptr + 1) % k_buffer_size; - - std::size_t current_size = size.load(); - - while(!size.compare_exchange_weak(current_size, std::min(current_size + 1u, k_buffer_size))) { - current_size = size.load(); - } -} - -auto KeyPad::InputQueue::Dequeue() -> u16 { - const u16 value = data[rd_ptr]; - rd_ptr = (rd_ptr + 1) % k_buffer_size; - - std::size_t current_size = size.load(); - - while(!size.compare_exchange_weak(current_size, current_size - 1)) { - current_size = size.load(); - } - - return value; -} - } // namespace nba::core diff --git a/src/nba/src/hw/keypad/keypad.hpp b/src/nba/src/hw/keypad/keypad.hpp index 19bb7514d..3d2d08130 100644 --- a/src/nba/src/hw/keypad/keypad.hpp +++ b/src/nba/src/hw/keypad/keypad.hpp @@ -7,8 +7,7 @@ #pragma once -#include -#include +#include #include #include #include @@ -18,9 +17,10 @@ namespace nba::core { struct KeyPad { - KeyPad(Scheduler& scheduler, IRQ& irq, std::shared_ptr config); + KeyPad(Scheduler& scheduler, IRQ& irq); void Reset(); + void SetKeyStatus(Key key, bool pressed); struct KeyInput { u16 value = 0x3FF; @@ -50,33 +50,10 @@ struct KeyPad { void CopyState(SaveState& state); private: - static constexpr int k_poll_interval = 16384; - - using Key = InputDevice::Key; - - void UpdateInput(); void UpdateIRQ(); - void Poll(); - void PollInternal(); - Scheduler& scheduler; IRQ& irq; - std::shared_ptr config; - - struct InputQueue { - static constexpr std::size_t k_buffer_size = 16; - - std::atomic data[k_buffer_size]; - std::atomic size = 0; - std::size_t rd_ptr = 0; - std::size_t wr_ptr = 0; - - void Reset(); - bool DataAvailable(); - void Enqueue(u16 input); - auto Dequeue() -> u16; - } input_queue{}; }; } // namespace nba::core diff --git a/src/platform/core/include/platform/emulator_thread.hpp b/src/platform/core/include/platform/emulator_thread.hpp index 34f1ca438..8327d6627 100644 --- a/src/platform/core/include/platform/emulator_thread.hpp +++ b/src/platform/core/include/platform/emulator_thread.hpp @@ -10,13 +10,16 @@ #include #include #include +#include #include -#include +#include +#include +#include namespace nba { struct EmulatorThread { - EmulatorThread(std::unique_ptr& core); + EmulatorThread(); ~EmulatorThread(); bool IsRunning() const; @@ -26,11 +29,44 @@ struct EmulatorThread { void SetFastForward(bool enabled); void SetFrameRateCallback(std::function callback); void SetPerFrameCallback(std::function callback); - void Start(); - void Stop(); + + void Start(std::unique_ptr core); + std::unique_ptr Stop(); + + void Reset(); + void SetKeyStatus(Key key, bool pressed); private: - std::unique_ptr& core; + enum class MessageType : u8 { + Reset, + SetKeyStatus + }; + + struct Message { + MessageType type; + union { + struct { + Key key; + u8bool pressed; + } set_key_status; + }; + }; + + void PushMessage(const Message& message); + void ProcessMessages(); + void ProcessMessage(const Message& message); + + static constexpr int k_number_of_input_subframes = 4; + static constexpr int k_cycles_per_second = 16777216; + static constexpr int k_cycles_per_frame = 280896; + static constexpr int k_cycles_per_subframe = k_cycles_per_frame / k_number_of_input_subframes; + + static_assert(k_cycles_per_frame % k_number_of_input_subframes == 0); + + std::queue msg_queue; + std::mutex msg_queue_mutex; + + std::unique_ptr core; FrameLimiter frame_limiter; std::thread thread; std::atomic_bool running = false; diff --git a/src/platform/core/src/emulator_thread.cpp b/src/platform/core/src/emulator_thread.cpp index 4ba9de1df..aaf0f4029 100644 --- a/src/platform/core/src/emulator_thread.cpp +++ b/src/platform/core/src/emulator_thread.cpp @@ -5,18 +5,17 @@ * Refer to the included LICENSE file. */ +#include #include namespace nba { -EmulatorThread::EmulatorThread( - std::unique_ptr& core -) : core(core) { - frame_limiter.Reset(59.7275); +EmulatorThread::EmulatorThread() { + frame_limiter.Reset(k_cycles_per_second / (float)k_cycles_per_subframe); } EmulatorThread::~EmulatorThread() { - if(IsRunning()) Stop(); + Stop(); } bool EmulatorThread::IsRunning() const { @@ -47,34 +46,99 @@ void EmulatorThread::SetPerFrameCallback(std::function callback) { per_frame_cb = callback; } -void EmulatorThread::Start() { - if(!running) { - running = true; - thread = std::thread{[this]() { - frame_limiter.Reset(); - - while(running.load()) { - frame_limiter.Run([this]() { - if(!paused) { - per_frame_cb(); - core->RunForOneFrame(); - } - }, [this](float fps) { - if(paused) { - fps = 0; - } - frame_rate_cb(fps); - }); - } - }}; - } +void EmulatorThread::Start(std::unique_ptr core) { + Assert(!running, "Started an emulator thread which was already running"); + + this->core = std::move(core); + running = true; + + thread = std::thread{[this]() { + frame_limiter.Reset(); + + while(running.load()) { + ProcessMessages(); + + frame_limiter.Run([this]() { + if(!paused) { + // @todo: decide what to do with the per_frame_cb(). + per_frame_cb(); + this->core->Run(k_cycles_per_subframe); + } + }, [this](float fps) { + float real_fps = fps / k_number_of_input_subframes; + if(paused) { + real_fps = 0; + } + frame_rate_cb(real_fps); + }); + } + + // Make sure all messages are handled before exiting + ProcessMessages(); + }}; } -void EmulatorThread::Stop() { +std::unique_ptr EmulatorThread::Stop() { if(IsRunning()) { running = false; thread.join(); } + + return std::move(core); +} + +void EmulatorThread::Reset() { + PushMessage({.type = MessageType::Reset}); +} + +void EmulatorThread::SetKeyStatus(Key key, bool pressed) { + PushMessage({ + .type = MessageType::SetKeyStatus, + .set_key_status = {.key = key, .pressed = (u8bool)pressed} + }); +} + +void EmulatorThread::PushMessage(const Message& message) { + // @todo: think of the best way to transparently handle messages + // sent while the emulator thread isn't running. + if(!IsRunning()) { + return; + } + + if(std::this_thread::get_id() == thread.get_id()) { + // Messages sent on the emulator thread (i.e. from a callback) do + // not need to be pushed to the message queue. + // Process them right away instead to reduce latency. + ProcessMessage(message); + } else { + std::lock_guard lock_guard{msg_queue_mutex}; + msg_queue.push(message); // @todo: maybe use emplace. + } +} + +void EmulatorThread::ProcessMessages() { + // potential optimization: use a separate std::atomic_int to track the number of messages in the queue + // use atomic_int to do early bail out without acquiring the mutex. + std::lock_guard lock_guard{msg_queue_mutex}; + + while(!msg_queue.empty()) { + ProcessMessage(msg_queue.front()); + msg_queue.pop(); + } +} + +void EmulatorThread::ProcessMessage(const Message& message) { + switch(message.type) { + case MessageType::Reset: { + core->Reset(); + break; + } + case MessageType::SetKeyStatus: { + core->SetKeyStatus(message.set_key_status.key, message.set_key_status.pressed); + break; + } + default: Assert(false, "unhandled message type: {}", (int)message.type); + } } } // namespace nba diff --git a/src/platform/qt/rc/config.toml b/src/platform/qt/rc/config.toml index 89d0bf168..76f3b8973 100644 --- a/src/platform/qt/rc/config.toml +++ b/src/platform/qt/rc/config.toml @@ -30,20 +30,20 @@ mp2k_hle_cubic = true mp2k_hle_force_reverb = true [input] -fast_forward = [32, -1, -1, -1, 0] hold_fast_forward = true +fast_forward = [32, -1, -1, -1, 0] controller_guid = "" [input.gba] -b = [83, -1, -1, -1, 0] -a = [65, -1, -1, -1, 0] -select = [16777219, -1, -1, -1, 0] -right = [16777236, -1, -1, -1, 0] -r = [70, -1, -1, -1, 0] -l = [68, -1, -1, -1, 0] -left = [16777234, -1, -1, -1, 0] -start = [16777220, -1, -1, -1, 0] -down = [16777237, -1, -1, -1, 0] -up = [16777235, -1, -1, -1, 0] +a = [65,-1,-1,-1,0] +b = [83,-1,-1,-1,0] +select = [16777219,-1,-1,-1,0] +start = [16777220,-1,-1,-1,0] +right = [16777236,-1,-1,-1,0] +left = [16777234,-1,-1,-1,0] +up = [16777235,-1,-1,-1,0] +down = [16777237,-1,-1,-1,0] +l = [68,-1,-1,-1,0] +r = [70,-1,-1,-1,0] [window] fullscreen = false diff --git a/src/platform/qt/src/config.cpp b/src/platform/qt/src/config.cpp index 1d7371870..e4954dc5b 100644 --- a/src/platform/qt/src/config.cpp +++ b/src/platform/qt/src/config.cpp @@ -31,14 +31,14 @@ void QtConfig::LoadCustomData(toml::value const& data) { if(gba_result.is_ok()) { auto gba = gba_result.unwrap(); - input.gba[0] = get_map(gba, "up"); - input.gba[1] = get_map(gba, "down"); - input.gba[2] = get_map(gba, "left"); - input.gba[3] = get_map(gba, "right"); - input.gba[4] = get_map(gba, "start"); - input.gba[5] = get_map(gba, "select"); - input.gba[6] = get_map(gba, "a"); - input.gba[7] = get_map(gba, "b"); + input.gba[0] = get_map(gba, "a"); + input.gba[1] = get_map(gba, "b"); + input.gba[2] = get_map(gba, "select"); + input.gba[3] = get_map(gba, "start"); + input.gba[4] = get_map(gba, "right"); + input.gba[5] = get_map(gba, "left"); + input.gba[6] = get_map(gba, "up"); + input.gba[7] = get_map(gba, "down"); input.gba[8] = get_map(gba, "l"); input.gba[9] = get_map(gba, "r"); } @@ -72,14 +72,14 @@ void QtConfig::SaveCustomData( data["input"]["fast_forward"] = input.fast_forward.Array(); data["input"]["hold_fast_forward"] = input.hold_fast_forward; - data["input"]["gba"]["up"] = input.gba[0].Array(); - data["input"]["gba"]["down"] = input.gba[1].Array(); - data["input"]["gba"]["left"] = input.gba[2].Array(); - data["input"]["gba"]["right"] = input.gba[3].Array(); - data["input"]["gba"]["start"] = input.gba[4].Array(); - data["input"]["gba"]["select"] = input.gba[5].Array(); - data["input"]["gba"]["a"] = input.gba[6].Array(); - data["input"]["gba"]["b"] = input.gba[7].Array(); + data["input"]["gba"]["a"] = input.gba[0].Array(); + data["input"]["gba"]["b"] = input.gba[1].Array(); + data["input"]["gba"]["select"] = input.gba[2].Array(); + data["input"]["gba"]["start"] = input.gba[3].Array(); + data["input"]["gba"]["right"] = input.gba[4].Array(); + data["input"]["gba"]["left"] = input.gba[5].Array(); + data["input"]["gba"]["up"] = input.gba[6].Array(); + data["input"]["gba"]["down"] = input.gba[7].Array(); data["input"]["gba"]["l"] = input.gba[8].Array(); data["input"]["gba"]["r"] = input.gba[9].Array(); diff --git a/src/platform/qt/src/config.hpp b/src/platform/qt/src/config.hpp index bec62472d..99b8a95a0 100644 --- a/src/platform/qt/src/config.hpp +++ b/src/platform/qt/src/config.hpp @@ -7,6 +7,7 @@ #pragma once +#include #include #include #include @@ -49,15 +50,15 @@ struct QtConfig final : nba::PlatformConfig { } }; - Map gba[nba::InputDevice::kKeyCount] { - {Qt::Key_Up}, - {Qt::Key_Down}, - {Qt::Key_Left}, - {Qt::Key_Right}, - {Qt::Key_Return}, - {Qt::Key_Backspace}, + Map gba[(int)nba::Key::Count] { {Qt::Key_A}, {Qt::Key_S}, + {Qt::Key_Backspace}, + {Qt::Key_Return}, + {Qt::Key_Right}, + {Qt::Key_Left}, + {Qt::Key_Up}, + {Qt::Key_Down}, {Qt::Key_D}, {Qt::Key_F} }; diff --git a/src/platform/qt/src/widget/controller_manager.cpp b/src/platform/qt/src/widget/controller_manager.cpp index eb8077b27..6ecd1be0b 100644 --- a/src/platform/qt/src/widget/controller_manager.cpp +++ b/src/platform/qt/src/widget/controller_manager.cpp @@ -119,7 +119,7 @@ void ControllerManager::Open(std::string const& guid) { } void ControllerManager::Close() { - using Key = nba::InputDevice::Key; + using Key = nba::Key; // Unset all keys in case any key is currently pressed. main_window->SetKeyStatus(1, Key::Up, false); @@ -211,7 +211,7 @@ void ControllerManager::ProcessEvents() { } void ControllerManager::UpdateKeyState() { - using Key = nba::InputDevice::Key; + using Key = nba::Key; #if defined(__APPLE__) std::lock_guard guard{lock}; @@ -253,9 +253,9 @@ void ControllerManager::UpdateKeyState() { return pressed; }; - for(int i = 0; i < nba::InputDevice::kKeyCount; i++) { + for(int i = 0; i < (int)nba::Key::Count; i++) { main_window->SetKeyStatus( - 1, static_cast(i), evaluate(input.gba[i])); + 1, static_cast(i), evaluate(input.gba[i])); } bool fast_forward_button = evaluate(input.fast_forward); diff --git a/src/platform/qt/src/widget/controller_manager.hpp b/src/platform/qt/src/widget/controller_manager.hpp index 547e1fc12..39f9a8af8 100644 --- a/src/platform/qt/src/widget/controller_manager.hpp +++ b/src/platform/qt/src/widget/controller_manager.hpp @@ -7,8 +7,8 @@ #pragma once +#include #include -#include #include #include #include diff --git a/src/platform/qt/src/widget/input_window.hpp b/src/platform/qt/src/widget/input_window.hpp index d69c5866e..2f96a99a9 100644 --- a/src/platform/qt/src/widget/input_window.hpp +++ b/src/platform/qt/src/widget/input_window.hpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include #include @@ -34,7 +34,7 @@ inline auto GetJoystickGUIDStringFromIndex(int device_index) -> std::string { } struct InputWindow : QDialog { - using Key = nba::InputDevice::Key; + using Key = nba::Key; InputWindow( QApplication* app, diff --git a/src/platform/qt/src/widget/main_window.cpp b/src/platform/qt/src/widget/main_window.cpp index 472c972a2..fbe8ffc4d 100644 --- a/src/platform/qt/src/widget/main_window.cpp +++ b/src/platform/qt/src/widget/main_window.cpp @@ -55,9 +55,9 @@ MainWindow::MainWindow( config->video_dev = screen; config->audio_dev = std::make_shared(); - config->input_dev = input_device; core = nba::CreateCore(config); - emu_thread = std::make_unique(core); + core_not_thread_safe = core.get(); + emu_thread = std::make_unique(); app->installEventFilter(this); @@ -384,7 +384,7 @@ void MainWindow::CreateToolsMenu() { connect(tools_menu->addAction(tr("Palette Viewer")), &QAction::triggered, [this]() { if(!palette_viewer_window) { - palette_viewer_window = new PaletteViewerWindow{core.get(), this}; + palette_viewer_window = new PaletteViewerWindow{core_not_thread_safe, this}; connect(screen.get(), &Screen::RequestDraw, palette_viewer_window, &PaletteViewerWindow::Update); } @@ -393,7 +393,7 @@ void MainWindow::CreateToolsMenu() { connect(tools_menu->addAction(tr("Background Viewer")), &QAction::triggered, [this]() { if(!background_viewer_window) { - background_viewer_window = new BackgroundViewerWindow{core.get(), this}; + background_viewer_window = new BackgroundViewerWindow{core_not_thread_safe, this}; connect(screen.get(), &Screen::RequestDraw, background_viewer_window, &BackgroundViewerWindow::Update); } @@ -401,7 +401,7 @@ void MainWindow::CreateToolsMenu() { }); connect(tools_menu->addAction(tr("Sprite Viewer")), &QAction::triggered, [this]() { - const auto sprite_viewer_window = new SpriteViewerWindow{core.get(), this}; + const auto sprite_viewer_window = new SpriteViewerWindow{core_not_thread_safe, this}; connect(screen.get(), &Screen::RequestDraw, sprite_viewer_window, &SpriteViewerWindow::Update); sprite_viewer_window->show(); }); @@ -611,9 +611,9 @@ bool MainWindow::eventFilter(QObject* obj, QEvent* event) { auto pressed = type == QEvent::KeyPress; auto const& input = config->input; - for(int i = 0; i < nba::InputDevice::kKeyCount; i++) { + for(int i = 0; i < (int)nba::Key::Count; i++) { if(input.gba[i].keyboard == key) { - SetKeyStatus(0, static_cast(i), pressed); + SetKeyStatus(0, static_cast(i), pressed); } } @@ -666,13 +666,7 @@ void MainWindow::FileOpen() { } void MainWindow::Reset() { - bool was_running = emu_thread->IsRunning(); - - emu_thread->Stop(); - core->Reset(); - if(was_running) { - emu_thread->Start(); - } + emu_thread->Reset(); } void MainWindow::SetPause(bool paused) { @@ -687,7 +681,7 @@ void MainWindow::SetPause(bool paused) { void MainWindow::Stop() { if(emu_thread->IsRunning()) { - emu_thread->Stop(); + core = emu_thread->Stop(); config->audio_dev->Close(); screen->SetForceClear(true); @@ -813,7 +807,7 @@ void MainWindow::LoadROM(std::u16string const& path) { // Reset the core and start the emulation thread. // If the emulator is currently paused force-clear the screen. core->Reset(); - emu_thread->Start(); + emu_thread->Start(std::move(core)); /** * If the the emulator is paused we force-clear the screen to avoid @@ -828,7 +822,7 @@ void MainWindow::LoadROM(std::u16string const& path) { auto MainWindow::LoadState(std::u16string const& path) -> nba::SaveStateLoader::Result { bool was_running = emu_thread->IsRunning(); - emu_thread->Stop(); + core = emu_thread->Stop(); auto result = nba::SaveStateLoader::Load(core, path); @@ -861,7 +855,7 @@ auto MainWindow::LoadState(std::u16string const& path) -> nba::SaveStateLoader:: } if(was_running) { - emu_thread->Start(); + emu_thread->Start(std::move(core)); } return result; @@ -869,7 +863,7 @@ auto MainWindow::LoadState(std::u16string const& path) -> nba::SaveStateLoader:: auto MainWindow::SaveState(std::u16string const& path) -> nba::SaveStateWriter::Result { bool was_running = emu_thread->IsRunning(); - emu_thread->Stop(); + core = emu_thread->Stop(); auto result = nba::SaveStateWriter::Write(core, path); @@ -882,7 +876,7 @@ auto MainWindow::SaveState(std::u16string const& path) -> nba::SaveStateWriter:: } if(was_running) { - emu_thread->Start(); + emu_thread->Start(std::move(core)); } return result; @@ -902,11 +896,10 @@ auto MainWindow::GetSavePath(fs::path const& rom_path, fs::path const& extension return fs::path{rom_path}.replace_extension(extension); } -void MainWindow::SetKeyStatus(int channel, nba::InputDevice::Key key, bool pressed) { - key_input[channel][int(key)] = pressed; +void MainWindow::SetKeyStatus(int channel, nba::Key key, bool pressed) { + key_input[channel][(int)key] = pressed; - input_device->SetKeyStatus(key, - key_input[0][int(key)] || key_input[1][int(key)]); + emu_thread->SetKeyStatus(key, key_input[0][(int)key] || key_input[1][(int)key]); } void MainWindow::SetFastForward(int channel, bool pressed) { diff --git a/src/platform/qt/src/widget/main_window.hpp b/src/platform/qt/src/widget/main_window.hpp index 1a3c66739..a6db68fe5 100644 --- a/src/platform/qt/src/widget/main_window.hpp +++ b/src/platform/qt/src/widget/main_window.hpp @@ -117,7 +117,7 @@ private slots: void UpdateMenuBarVisibility(); void UpdateMainWindowActionList(); - void SetKeyStatus(int channel, nba::InputDevice::Key key, bool pressed); + void SetKeyStatus(int channel, nba::Key key, bool pressed); void SetFastForward(int channel, bool pressed); void UpdateWindowSize(); void SetFullscreen(bool value); @@ -130,14 +130,17 @@ private slots: auto GetSavePath(fs::path const& rom_path, fs::path const& extension) -> fs::path; std::shared_ptr screen; - std::shared_ptr input_device = std::make_shared(); std::shared_ptr config = std::make_shared(); std::unique_ptr core; std::unique_ptr emu_thread; - bool key_input[2][nba::InputDevice::kKeyCount] {false}; + bool key_input[2][(int)nba::Key::Count] {false}; bool fast_forward[2] {false}; ControllerManager* controller_manager; + // The PPU debuggers do not access the core in a thread-safe way yet. + // So until that is fixed we have to keep a raw pointer around... + nba::CoreBase* core_not_thread_safe; + QAction* pause_action; InputWindow* input_window; QMenu* recent_menu;