From aa6a9699fb130f355a0b84aa7e96a1e93fdb41bd Mon Sep 17 00:00:00 2001 From: Sour Date: Fri, 4 Mar 2022 22:08:29 -0500 Subject: [PATCH] UI: Added global input options (deadzone, display inputs on screen, etc.) --- Core/Core.vcxproj | 1 + Core/Core.vcxproj.filters | 1 + Core/Gameboy/GbPpu.cpp | 4 +- Core/Gameboy/Input/GbController.h | 20 ++ Core/NES/Input/NesController.h | 20 ++ Core/NES/NesPpu.cpp | 7 +- Core/SNES/Input/Multitap.cpp | 42 +++ Core/SNES/Input/Multitap.h | 5 + Core/SNES/Input/SnesController.cpp | 25 ++ Core/SNES/Input/SnesController.h | 2 + Core/SNES/Input/SnesMouse.h | 11 + Core/SNES/SnesPpu.cpp | 3 +- Core/Shared/BaseControlDevice.h | 3 + Core/Shared/BaseControlManager.cpp | 2 +- Core/Shared/EmuSettings.cpp | 2 +- Core/Shared/EmuSettings.h | 2 +- Core/Shared/InputHud.cpp | 279 +++++++++--------- Core/Shared/InputHud.h | 18 +- Core/Shared/Interfaces/IControlManager.h | 3 + Core/Shared/Interfaces/IRenderingDevice.h | 4 +- Core/Shared/RenderedFrame.h | 38 +++ Core/Shared/RewindManager.cpp | 5 +- Core/Shared/RewindManager.h | 5 +- Core/Shared/SaveStateManager.cpp | 1 + Core/Shared/SettingTypes.h | 24 -- Core/Shared/Video/DrawCommand.h | 36 ++- Core/Shared/Video/VideoDecoder.cpp | 3 +- Core/Shared/Video/VideoDecoder.h | 1 + Core/Shared/Video/VideoRenderer.cpp | 15 +- Core/Shared/Video/VideoRenderer.h | 8 +- NewUI/Debugger/Utilities/ContextMenuAction.cs | 2 + NewUI/Localization/resources.en.xml | 27 +- NewUI/NewUI.csproj | 3 + NewUI/Utilities/ReactiveHelper.cs | 19 +- NewUI/ViewModels/ConfigViewModel.cs | 18 +- NewUI/ViewModels/EmulationConfigViewModel.cs | 4 +- NewUI/ViewModels/InputConfigViewModel.cs | 23 ++ NewUI/ViewModels/MainMenuViewModel.cs | 4 + NewUI/Views/EmulationConfigView.axaml | 6 +- NewUI/Views/InputConfigView.axaml | 89 ++++++ NewUI/Views/InputConfigView.axaml.cs | 21 ++ NewUI/Windows/ConfigWindow.axaml | 11 + Windows/Renderer.cpp | 65 +--- Windows/Renderer.h | 4 +- 44 files changed, 601 insertions(+), 285 deletions(-) create mode 100644 Core/Shared/RenderedFrame.h create mode 100644 NewUI/ViewModels/InputConfigViewModel.cs create mode 100644 NewUI/Views/InputConfigView.axaml create mode 100644 NewUI/Views/InputConfigView.axaml.cs diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 21f30a7f..852cbcf4 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -122,6 +122,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 47912be5..ea22f29b 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -819,6 +819,7 @@ + diff --git a/Core/Gameboy/GbPpu.cpp b/Core/Gameboy/GbPpu.cpp index c502ab94..4158b464 100644 --- a/Core/Gameboy/GbPpu.cpp +++ b/Core/Gameboy/GbPpu.cpp @@ -8,6 +8,8 @@ #include "Shared/Emulator.h" #include "Shared/EmuSettings.h" #include "Shared/RewindManager.h" +#include "Shared/Interfaces/IControlManager.h" +#include "Shared/RenderedFrame.h" #include "Shared/Video/VideoDecoder.h" #include "Shared/NotificationManager.h" #include "Shared/MessageManager.h" @@ -650,7 +652,7 @@ void GbPpu::SendFrame() } _isFirstFrame = false; - RenderedFrame frame(_currentBuffer, GbConstants::ScreenWidth, GbConstants::ScreenHeight, 1.0, _state.FrameCount); + RenderedFrame frame(_currentBuffer, GbConstants::ScreenWidth, GbConstants::ScreenHeight, 1.0, _state.FrameCount, _gameboy->GetControlManager()->GetPortStates()); #ifdef LIBRETRO _emu->GetVideoDecoder()->UpdateFrame(frame, true, false); #else diff --git a/Core/Gameboy/Input/GbController.h b/Core/Gameboy/Input/GbController.h index 88fd10f6..65f07102 100644 --- a/Core/Gameboy/Input/GbController.h +++ b/Core/Gameboy/Input/GbController.h @@ -3,6 +3,7 @@ #include "Shared/BaseControlDevice.h" #include "Shared/Emulator.h" #include "Shared/EmuSettings.h" +#include "Shared/InputHud.h" #include "Utilities/Serializer.h" class GbController : public BaseControlDevice @@ -57,4 +58,23 @@ public: void WriteRam(uint16_t addr, uint8_t value) override { } + + void DrawController(InputHud& hud) + { + hud.DrawOutline(35, 14); + + hud.DrawButton(5, 3, 3, 3, IsPressed(Buttons::Up)); + hud.DrawButton(5, 9, 3, 3, IsPressed(Buttons::Down)); + hud.DrawButton(2, 6, 3, 3, IsPressed(Buttons::Left)); + hud.DrawButton(8, 6, 3, 3, IsPressed(Buttons::Right)); + hud.DrawButton(5, 6, 3, 3, false); + + hud.DrawButton(30, 7, 3, 3, IsPressed(Buttons::A)); + hud.DrawButton(25, 7, 3, 3, IsPressed(Buttons::B)); + + hud.DrawButton(13, 9, 4, 2, IsPressed(Buttons::Select)); + hud.DrawButton(18, 9, 4, 2, IsPressed(Buttons::Start)); + + hud.DrawNumber(_port + 1, 16, 2); + } }; \ No newline at end of file diff --git a/Core/NES/Input/NesController.h b/Core/NES/Input/NesController.h index ff32b13d..36f4f5f4 100644 --- a/Core/NES/Input/NesController.h +++ b/Core/NES/Input/NesController.h @@ -3,6 +3,7 @@ #include "Shared/BaseControlDevice.h" #include "Shared/Emulator.h" #include "Shared/EmuSettings.h" +#include "Shared/InputHud.h" #include "Utilities/Serializer.h" class NesController : public BaseControlDevice @@ -132,4 +133,23 @@ public: { StrobeProcessWrite(value); } + + void DrawController(InputHud& hud) + { + hud.DrawOutline(35, 14); + + hud.DrawButton(5, 3, 3, 3, IsPressed(Buttons::Up)); + hud.DrawButton(5, 9, 3, 3, IsPressed(Buttons::Down)); + hud.DrawButton(2, 6, 3, 3, IsPressed(Buttons::Left)); + hud.DrawButton(8, 6, 3, 3, IsPressed(Buttons::Right)); + hud.DrawButton(5, 6, 3, 3, false); + + hud.DrawButton(30, 7, 3, 3, IsPressed(Buttons::A)); + hud.DrawButton(25, 7, 3, 3, IsPressed(Buttons::B)); + + hud.DrawButton(13, 9, 4, 2, IsPressed(Buttons::Select)); + hud.DrawButton(18, 9, 4, 2, IsPressed(Buttons::Start)); + + hud.DrawNumber(_port + 1, 16, 2); + } }; \ No newline at end of file diff --git a/Core/NES/NesPpu.cpp b/Core/NES/NesPpu.cpp index b91b59f4..f697cba4 100644 --- a/Core/NES/NesPpu.cpp +++ b/Core/NES/NesPpu.cpp @@ -20,6 +20,7 @@ #include "Shared/Video/VideoDecoder.h" #include "Shared/RewindManager.h" #include "Shared/NotificationManager.h" +#include "Shared/RenderedFrame.h" #include "MemoryOperationType.h" #include "EventType.h" @@ -1071,7 +1072,7 @@ template void NesPpu::SendFrame() _emu->GetNotificationManager()->SendNotification(ConsoleNotificationType::PpuFrameDone, _currentOutputBuffer); } - RenderedFrame frame(_currentOutputBuffer, NesConstants::ScreenWidth, NesConstants::ScreenHeight, 1.0, _frameCount); + RenderedFrame frame(_currentOutputBuffer, NesConstants::ScreenWidth, NesConstants::ScreenHeight, 1.0, _frameCount, _console->GetControlManager()->GetPortStates()); frame.Data = frameData; //HD packs #ifdef LIBRETRO @@ -1097,7 +1098,7 @@ template void NesPpu::SendFrameVsDualSystem() NesConfig& cfg = _settings->GetNesConfig(); bool forRewind = _emu->GetRewindManager()->IsRewinding(); - RenderedFrame frame(_currentOutputBuffer, NesConstants::ScreenWidth, NesConstants::ScreenHeight, 1.0, _frameCount); + RenderedFrame frame(_currentOutputBuffer, NesConstants::ScreenWidth, NesConstants::ScreenHeight, 1.0, _frameCount, _console->GetControlManager()->GetPortStates()); if(cfg.VsDualVideoOutput == VsDualOutputOption::MainSystemOnly && _console->IsVsMainConsole()) { _emu->GetVideoDecoder()->UpdateFrame(frame, forRewind, forRewind); @@ -1119,7 +1120,7 @@ template void NesPpu::SendFrameVsDualSystem() in2 += NesConstants::ScreenWidth; } - RenderedFrame mergedFrame(mergedBuffer, NesConstants::ScreenWidth*2, NesConstants::ScreenHeight, 1.0, _frameCount); + RenderedFrame mergedFrame(mergedBuffer, NesConstants::ScreenWidth*2, NesConstants::ScreenHeight, 1.0, _frameCount, _console->GetControlManager()->GetPortStates()); _emu->GetVideoDecoder()->UpdateFrame(mergedFrame, true, forRewind); delete[] mergedBuffer; } diff --git a/Core/SNES/Input/Multitap.cpp b/Core/SNES/Input/Multitap.cpp index 426d27f0..85de9182 100644 --- a/Core/SNES/Input/Multitap.cpp +++ b/Core/SNES/Input/Multitap.cpp @@ -3,7 +3,9 @@ #include "SNES/Input/SnesController.h" #include "SNES/SnesConsole.h" #include "SNES/InternalRegisters.h" +#include "Shared/InputHud.h" #include "Shared/Emulator.h" +#include "Shared/EmuSettings.h" string Multitap::GetKeyNames() { @@ -149,3 +151,43 @@ void Multitap::WriteRam(uint16_t addr, uint8_t value) StrobeProcessWrite(value); } } + +void Multitap::DrawController(InputHud& hud) +{ + InputConfig& cfg = _emu->GetSettings()->GetInputConfig(); + + for(int j = 0; j < 4; j++) { + int port = j == 0 ? _port : (j + 1); + if(cfg.DisplayInputPort[port]) { + DrawController(hud, port); + } + } +} + +void Multitap::DrawController(InputHud& hud, int port) +{ + int offset = port * Multitap::ButtonCount; + + hud.DrawOutline(35, 14); + + hud.DrawButton(5, 3, 3, 3, IsPressed(Buttons::Up + offset)); + hud.DrawButton(5, 9, 3, 3, IsPressed(Buttons::Down + offset)); + hud.DrawButton(2, 6, 3, 3, IsPressed(Buttons::Left + offset)); + hud.DrawButton(8, 6, 3, 3, IsPressed(Buttons::Right + offset)); + hud.DrawButton(5, 6, 3, 3, false); + + hud.DrawButton(27, 3, 3, 3, IsPressed(Buttons::X + offset)); + hud.DrawButton(27, 9, 3, 3, IsPressed(Buttons::B + offset)); + hud.DrawButton(30, 6, 3, 3, IsPressed(Buttons::A + offset)); + hud.DrawButton(24, 6, 3, 3, IsPressed(Buttons::Y + offset)); + + hud.DrawButton(4, 0, 5, 2, IsPressed(Buttons::L + offset)); + hud.DrawButton(26, 0, 5, 2, IsPressed(Buttons::R + offset)); + + hud.DrawButton(13, 9, 4, 2, IsPressed(Buttons::Select + offset)); + hud.DrawButton(18, 9, 4, 2, IsPressed(Buttons::Start + offset)); + + hud.DrawNumber(port + 1, 16, 2); + + hud.EndDrawController(); +} \ No newline at end of file diff --git a/Core/SNES/Input/Multitap.h b/Core/SNES/Input/Multitap.h index aa9ebdc5..98affe7f 100644 --- a/Core/SNES/Input/Multitap.h +++ b/Core/SNES/Input/Multitap.h @@ -6,6 +6,7 @@ class InternalRegisters; class SnesController; class SnesConsole; +class InputHud; class Multitap : public BaseControlDevice { @@ -18,6 +19,8 @@ private: uint16_t _stateBuffer[4] = {}; InternalRegisters *_internalRegs = nullptr; + void DrawController(InputHud& hud, int port); + protected: string GetKeyNames() override; void InternalSetStateFromInput() override; @@ -33,4 +36,6 @@ public: uint8_t ReadRam(uint16_t addr) override; void WriteRam(uint16_t addr, uint8_t value) override; + + void DrawController(InputHud& hud) override; }; \ No newline at end of file diff --git a/Core/SNES/Input/SnesController.cpp b/Core/SNES/Input/SnesController.cpp index 7507091d..35209156 100644 --- a/Core/SNES/Input/SnesController.cpp +++ b/Core/SNES/Input/SnesController.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include "SNES/Input/SnesController.h" #include "Shared/Emulator.h" +#include "Shared/InputHud.h" SnesController::SnesController(Emulator* emu, uint8_t port, KeyMappingSet keyMappings) : BaseControlDevice(emu, ControllerType::SnesController, port, keyMappings) { @@ -96,3 +97,27 @@ void SnesController::WriteRam(uint16_t addr, uint8_t value) { StrobeProcessWrite(value); } + +void SnesController::DrawController(InputHud& hud) +{ + hud.DrawOutline(35, 14); + + hud.DrawButton(5, 3, 3, 3, IsPressed(Buttons::Up)); + hud.DrawButton(5, 9, 3, 3, IsPressed(Buttons::Down)); + hud.DrawButton(2, 6, 3, 3, IsPressed(Buttons::Left)); + hud.DrawButton(8, 6, 3, 3, IsPressed(Buttons::Right)); + hud.DrawButton(5, 6, 3, 3, false); + + hud.DrawButton(27, 3, 3, 3, IsPressed(Buttons::X)); + hud.DrawButton(27, 9, 3, 3, IsPressed(Buttons::B)); + hud.DrawButton(30, 6, 3, 3, IsPressed(Buttons::A)); + hud.DrawButton(24, 6, 3, 3, IsPressed(Buttons::Y)); + + hud.DrawButton(4, 0, 5, 2, IsPressed(Buttons::L)); + hud.DrawButton(26, 0, 5, 2, IsPressed(Buttons::R)); + + hud.DrawButton(13, 9, 4, 2, IsPressed(Buttons::Select)); + hud.DrawButton(18, 9, 4, 2, IsPressed(Buttons::Start)); + + hud.DrawNumber(_port + 1, 16, 2); +} diff --git a/Core/SNES/Input/SnesController.h b/Core/SNES/Input/SnesController.h index 71e9250e..307fa637 100644 --- a/Core/SNES/Input/SnesController.h +++ b/Core/SNES/Input/SnesController.h @@ -23,4 +23,6 @@ public: uint8_t ReadRam(uint16_t addr) override; void WriteRam(uint16_t addr, uint8_t value) override; + + void DrawController(InputHud& hud) override; }; \ No newline at end of file diff --git a/Core/SNES/Input/SnesMouse.h b/Core/SNES/Input/SnesMouse.h index 1626413c..c290a2b9 100644 --- a/Core/SNES/Input/SnesMouse.h +++ b/Core/SNES/Input/SnesMouse.h @@ -3,6 +3,7 @@ #include "Shared/BaseControlDevice.h" #include "Shared/Interfaces/IKeyManager.h" #include "Shared/KeyManager.h" +#include "Shared/InputHud.h" #include "Shared/Emulator.h" #include "Shared/EmuSettings.h" #include "Utilities/Serializer.h" @@ -85,4 +86,14 @@ public: _stateBuffer = (byte1 << 24) | (byte2 << 16) | (byte3 << 8) | byte4; } + + void DrawController(InputHud& hud) + { + hud.DrawOutline(11, 14); + + hud.DrawButton(1, 1, 4, 5, IsPressed(Buttons::Left)); + hud.DrawButton(6, 1, 4, 5, IsPressed(Buttons::Right)); + + hud.DrawNumber(_port + 1, 4, 7); + } }; \ No newline at end of file diff --git a/Core/SNES/SnesPpu.cpp b/Core/SNES/SnesPpu.cpp index 372a07e9..2e4ebd20 100644 --- a/Core/SNES/SnesPpu.cpp +++ b/Core/SNES/SnesPpu.cpp @@ -12,6 +12,7 @@ #include "Shared/Video/VideoDecoder.h" #include "Shared/Video/VideoRenderer.h" #include "Shared/NotificationManager.h" +#include "Shared/RenderedFrame.h" #include "Shared/MessageManager.h" #include "EventType.h" #include "Shared/RewindManager.h" @@ -1476,7 +1477,7 @@ void SnesPpu::SendFrame() bool isRewinding = _emu->GetRewindManager()->IsRewinding(); - RenderedFrame frame(_currentBuffer, width, height, _useHighResOutput ? 0.5 : 1.0, _frameCount); + RenderedFrame frame(_currentBuffer, width, height, _useHighResOutput ? 0.5 : 1.0, _frameCount, _console->GetControlManager()->GetPortStates()); #ifdef LIBRETRO _emu->GetVideoDecoder()->UpdateFrame(frame, true, isRewinding); #else diff --git a/Core/Shared/BaseControlDevice.h b/Core/Shared/BaseControlDevice.h index 1b1fdd74..504aec01 100644 --- a/Core/Shared/BaseControlDevice.h +++ b/Core/Shared/BaseControlDevice.h @@ -7,6 +7,7 @@ #include "Utilities/ISerializable.h" class Emulator; +class InputHud; class BaseControlDevice : public ISerializable { @@ -81,6 +82,8 @@ public: void SetRawState(ControlDeviceState state); ControlDeviceState GetRawState(); + virtual void DrawController(InputHud& hud) {} + virtual uint8_t ReadRam(uint16_t addr) = 0; virtual void WriteRam(uint16_t addr, uint8_t value) = 0; diff --git a/Core/Shared/BaseControlManager.cpp b/Core/Shared/BaseControlManager.cpp index ad5c96d2..4a737867 100644 --- a/Core/Shared/BaseControlManager.cpp +++ b/Core/Shared/BaseControlManager.cpp @@ -66,7 +66,7 @@ vector BaseControlManager::GetPortStates() auto lock = _deviceLock.AcquireSafe(); vector states; - for(int i = 0; i < 2; i++) { + for(int i = 0; i < BaseControlDevice::PortCount; i++) { shared_ptr device = GetControlDevice(i); if(device) { states.push_back({ device->GetControllerType(), device->GetRawState() }); diff --git a/Core/Shared/EmuSettings.cpp b/Core/Shared/EmuSettings.cpp index 8cd09d9b..285de211 100644 --- a/Core/Shared/EmuSettings.cpp +++ b/Core/Shared/EmuSettings.cpp @@ -82,7 +82,7 @@ void EmuSettings::SetInputConfig(InputConfig config) }*/ } -InputConfig EmuSettings::GetInputConfig() +InputConfig& EmuSettings::GetInputConfig() { return _input; } diff --git a/Core/Shared/EmuSettings.h b/Core/Shared/EmuSettings.h index a5baf86c..a478c27d 100644 --- a/Core/Shared/EmuSettings.h +++ b/Core/Shared/EmuSettings.h @@ -53,7 +53,7 @@ public: AudioConfig GetAudioConfig(); void SetInputConfig(InputConfig config); - InputConfig GetInputConfig(); + InputConfig& GetInputConfig(); void SetEmulationConfig(EmulationConfig config); EmulationConfig GetEmulationConfig(); diff --git a/Core/Shared/InputHud.cpp b/Core/Shared/InputHud.cpp index 1b800f24..060a0307 100644 --- a/Core/Shared/InputHud.cpp +++ b/Core/Shared/InputHud.cpp @@ -1,180 +1,181 @@ #include "stdafx.h" -#include "InputHud.h" +#include "Shared/InputHud.h" +#include "Shared/Emulator.h" +#include "Shared/BaseControlManager.h" +#include "Shared/Interfaces/IControlManager.h" #include "Shared/BaseControlDevice.h" #include "Shared/EmuSettings.h" #include "Shared/Video/DebugHud.h" static constexpr int color[2] = { 0x00111111, 0x00FFFFFF }; -InputHud::InputHud(Emulator* emu) +InputHud::InputHud(Emulator* emu, DebugHud* hud) { _emu = emu; + _hud = hud; } -void InputHud::DrawController(int port, ControlDeviceState state, int x, int y, int frameNumber) +void InputHud::DrawButton(int x, int y, int width, int height, bool pressed) { - //TODO - /*SnesController controller(_console, 0, KeyMappingSet()); - controller.SetRawState(state); - - shared_ptr hud = _console->GetDebugHud(); - hud->DrawRectangle(0 + x, 0 + y, 35, 14, 0x80CCCCCC, true, 1, frameNumber); - hud->DrawRectangle(0 + x, 0 + y, 35, 14, color[0], false, 1, frameNumber); - hud->DrawRectangle(5 + x, 3 + y, 3, 3, color[controller.IsPressed(SnesController::Buttons::Up)], true, 1, frameNumber); - hud->DrawRectangle(5 + x, 9 + y, 3, 3, color[controller.IsPressed(SnesController::Buttons::Down)], true, 1, frameNumber); - hud->DrawRectangle(2 + x, 6 + y, 3, 3, color[controller.IsPressed(SnesController::Buttons::Left)], true, 1, frameNumber); - hud->DrawRectangle(8 + x, 6 + y, 3, 3, color[controller.IsPressed(SnesController::Buttons::Right)], true, 1, frameNumber); - hud->DrawRectangle(5 + x, 6 + y, 3, 3, color[0], true, 1, frameNumber); - - hud->DrawRectangle(27 + x, 3 + y, 3, 3, color[controller.IsPressed(SnesController::Buttons::X)], true, 1, frameNumber); - hud->DrawRectangle(27 + x, 9 + y, 3, 3, color[controller.IsPressed(SnesController::Buttons::B)], true, 1, frameNumber); - hud->DrawRectangle(30 + x, 6 + y, 3, 3, color[controller.IsPressed(SnesController::Buttons::A)], true, 1, frameNumber); - hud->DrawRectangle(24 + x, 6 + y, 3, 3, color[controller.IsPressed(SnesController::Buttons::Y)], true, 1, frameNumber); - - hud->DrawRectangle(4 + x, 0 + y, 5, 2, color[controller.IsPressed(SnesController::Buttons::L)], true, 1, frameNumber); - hud->DrawRectangle(26 + x, 0 + y, 5, 2, color[controller.IsPressed(SnesController::Buttons::R)], true, 1, frameNumber); - - hud->DrawRectangle(13 + x, 9 + y, 4, 2, color[controller.IsPressed(SnesController::Buttons::Select)], true, 1, frameNumber); - hud->DrawRectangle(18 + x, 9 + y, 4, 2, color[controller.IsPressed(SnesController::Buttons::Start)], true, 1, frameNumber); - - switch(port) { - case 0: - //1 - hud->DrawLine(17 + x, 2 + y, 17 + x, 6 + y, color[0], 1, frameNumber); - hud->DrawLine(16 + x, 6 + y, 18 + x, 6 + y, color[0], 1, frameNumber); - hud->DrawPixel(16 + x, 3 + y, color[0], 1, frameNumber); - break; + _hud->DrawRectangle(_xOffset + x, _yOffset + y, width, height, color[pressed], true, 1); +} +void InputHud::DrawNumber(int number, int x, int y) +{ + switch(number) { case 1: - //2 - hud->DrawLine(16 + x, 2 + y, 18 + x, 2 + y, color[0], 1, frameNumber); - hud->DrawPixel(18 + x, 3 + y, color[0], 1, frameNumber); - hud->DrawLine(16 + x, 4 + y, 18 + x, 4 + y, color[0], 1, frameNumber); - hud->DrawPixel(16 + x, 5 + y, color[0], 1, frameNumber); - hud->DrawLine(16 + x, 6 + y, 18 + x, 6 + y, color[0], 1, frameNumber); + _hud->DrawLine(x+1 + _xOffset, y + _yOffset, x+1 + _xOffset, 4 + y + _yOffset, color[0], 1); + _hud->DrawLine(x + _xOffset, 4 + y + _yOffset, x+2 + _xOffset, 4 + y + _yOffset, color[0], 1); + _hud->DrawPixel(x + _xOffset, 1 + y + _yOffset, color[0], 1); break; case 2: - //3 - hud->DrawLine(16 + x, 2 + y, 18 + x, 2 + y, color[0], 1, frameNumber); - hud->DrawPixel(18 + x, 3 + y, color[0], 1, frameNumber); - hud->DrawLine(16 + x, 4 + y, 18 + x, 4 + y, color[0], 1, frameNumber); - hud->DrawPixel(18 + x, 5 + y, color[0], 1, frameNumber); - hud->DrawLine(16 + x, 6 + y, 18 + x, 6 + y, color[0], 1, frameNumber); + _hud->DrawLine(x + _xOffset, y + _yOffset, x+2 + _xOffset, y + _yOffset, color[0], 1); + _hud->DrawPixel(x+2 + _xOffset, 1 + y + _yOffset, color[0], 1); + _hud->DrawLine(x + _xOffset, 2 + y + _yOffset, x+2 + _xOffset, 2 + y + _yOffset, color[0], 1); + _hud->DrawPixel(x + _xOffset, 3 + y + _yOffset, color[0], 1); + _hud->DrawLine(x + _xOffset, 4 + y + _yOffset, x+2 + _xOffset, 4 + y + _yOffset, color[0], 1); break; case 3: - //4 - hud->DrawLine(16 + x, 2 + y, 16 + x, 4 + y, color[0], 1, frameNumber); - hud->DrawLine(18 + x, 2 + y, 18 + x, 6 + y, color[0], 1, frameNumber); - hud->DrawLine(16 + x, 4 + y, 18 + x, 4 + y, color[0], 1, frameNumber); + _hud->DrawLine(x + _xOffset, y + _yOffset, x+2 + _xOffset, y + _yOffset, color[0], 1); + _hud->DrawPixel(x+2 + _xOffset, 1 + y + _yOffset, color[0], 1); + _hud->DrawLine(x + _xOffset, 2 + y + _yOffset, x+2 + _xOffset, 2 + y + _yOffset, color[0], 1); + _hud->DrawPixel(x+2 + _xOffset, 3 + y + _yOffset, color[0], 1); + _hud->DrawLine(x + _xOffset, 4 + y + _yOffset, x+2 + _xOffset, 4 + y + _yOffset, color[0], 1); break; case 4: - //5 - hud->DrawLine(16 + x, 2 + y, 18 + x, 2 + y, color[0], 1, frameNumber); - hud->DrawPixel(16 + x, 3 + y, color[0], 1, frameNumber); - hud->DrawLine(16 + x, 4 + y, 18 + x, 4 + y, color[0], 1, frameNumber); - hud->DrawPixel(18 + x, 5 + y, color[0], 1, frameNumber); - hud->DrawLine(16 + x, 6 + y, 18 + x, 6 + y, color[0], 1, frameNumber); + _hud->DrawLine(x + _xOffset, y + _yOffset, x + _xOffset, 2 + y + _yOffset, color[0], 1); + _hud->DrawLine(x+2 + _xOffset, y + _yOffset, x+2 + _xOffset, 4 + y + _yOffset, color[0], 1); + _hud->DrawLine(x + _xOffset, 2 + y + _yOffset, x+2 + _xOffset, 2 + y + _yOffset, color[0], 1); break; - }*/ + + case 5: + _hud->DrawLine(x + _xOffset, y + _yOffset, x+2 + _xOffset, y + _yOffset, color[0], 1); + _hud->DrawPixel(x + _xOffset, 1 + y + _yOffset, color[0], 1); + _hud->DrawLine(x + _xOffset, 2 + y + _yOffset, x+2 + _xOffset, 2 + y + _yOffset, color[0], 1); + _hud->DrawPixel(x+2 + _xOffset, 3 + y + _yOffset, color[0], 1); + _hud->DrawLine(x + _xOffset, 4 + y + _yOffset, x+2 + _xOffset, 4 + y + _yOffset, color[0], 1); + break; + + default: + break; + } } -void InputHud::DrawControllers(OverscanDimensions overscan, int frameNumber) +void InputHud::DrawOutline(int width, int height) { - //TODO - /* - vector controllerData = _console->GetControlManager()->GetPortStates(); - InputConfig cfg = _console->GetSettings()->GetInputConfig(); - - int xStart; - int yStart; - int xOffset = cfg.DisplayInputHorizontally ? 38 : 0; - int yOffset = cfg.DisplayInputHorizontally ? 0 : 16; + InputConfig& cfg = _emu->GetSettings()->GetInputConfig(); switch(cfg.DisplayInputPosition) { default: case InputDisplayPosition::TopLeft: - xStart = overscan.Left + 3; - yStart = overscan.Top + 3; break; + case InputDisplayPosition::TopRight: - xStart = 256 - overscan.Right - 38; - yStart = overscan.Top + 3; - xOffset = -xOffset; + _xOffset -= width + 1; break; + case InputDisplayPosition::BottomLeft: - xStart = overscan.Left + 3; - yStart = 240 - overscan.Bottom - 18; - yOffset = -yOffset; + _yOffset -= height + 1; break; + case InputDisplayPosition::BottomRight: - xStart = 256 - overscan.Right - 38; - yStart = 240 - overscan.Bottom - 18; - xOffset = -xOffset; - yOffset = -yOffset; + _yOffset -= height + 1; + _xOffset -= width + 1; break; } - for(int i = 0; i < (int)controllerData.size(); i++) { - if(controllerData[i].Type == ControllerType::SnesController) { - if(cfg.DisplayInputPort[i]) { - DrawController(i, controllerData[i].State, xStart, yStart, frameNumber); - xStart += xOffset; - yStart += yOffset; - } - } else if(controllerData[i].Type == ControllerType::Multitap) { - uint64_t rawData = 0; - for(int j = (int)controllerData[i].State.State.size() - 1; j >= 0; j--) { - rawData <<= 8; - rawData |= controllerData[i].State.State[j]; - } - - ControlDeviceState controllers[4] = {}; - for(int j = 0; j < 4; j++) { - controllers[j].State.push_back(rawData & 0xFF); - controllers[j].State.push_back((rawData >> 8) & 0x0F); - rawData >>= 12; - } - - if(cfg.DisplayInputPort[i]) { - DrawController(i, controllers[0], xStart, yStart, frameNumber); - xStart += xOffset; - yStart += yOffset; - } - - for(int j = 1; j < 4; j++) { - if(cfg.DisplayInputPort[j + 1]) { - DrawController(j + 1, controllers[j], xStart, yStart, frameNumber); - xStart += xOffset; - yStart += yOffset; - } - } - } else if(controllerData[i].Type == ControllerType::SuperScope) { - if(cfg.DisplayInputPort[i]) { - SuperScope scope(_console, 0, KeyMappingSet()); - scope.SetRawState(controllerData[i].State); - MousePosition pos = scope.GetCoordinates(); - - shared_ptr hud = _console->GetDebugHud(); - hud->DrawRectangle(pos.X - 1, pos.Y - 1, 3, 3, 0x00111111, true, 1, frameNumber); - hud->DrawRectangle(pos.X - 1, pos.Y - 1, 3, 3, 0x80CCCCCC, false, 1, frameNumber); - } - } else if(controllerData[i].Type == ControllerType::SnesMouse) { - if(cfg.DisplayInputPort[i]) { - SnesMouse mouse(_console, 0); - mouse.SetRawState(controllerData[i].State); - - shared_ptr hud = _console->GetDebugHud(); - hud->DrawRectangle(xStart + 12, yStart, 11, 14, 0x00AAAAAA, true, 1, frameNumber); - hud->DrawRectangle(xStart + 12, yStart, 11, 14, color[0], false, 1, frameNumber); - hud->DrawRectangle(xStart + 13, yStart + 1, 4, 5, color[mouse.IsPressed(SnesMouse::Buttons::Left)], true, 1, frameNumber); - hud->DrawRectangle(xStart + 18, yStart + 1, 4, 5, color[mouse.IsPressed(SnesMouse::Buttons::Right)], true, 1, frameNumber); - - xStart += xOffset; - yStart += yOffset; - } - } - }*/ + _hud->DrawRectangle(_xOffset, _yOffset, width, height, 0x80CCCCCC, true, 1); + _hud->DrawRectangle(_xOffset, _yOffset, width, height, color[0], false, 1); + + _outlineWidth = width; + _outlineHeight = height; +} + +void InputHud::DrawController(ControllerType controllerType, int port, ControlDeviceState state) +{ + shared_ptr controller = ((BaseControlManager*)_emu->GetControlManager())->CreateControllerDevice(controllerType, port); + if(!controller) { + return; + } + + controller->SetRawState(state); + controller->DrawController(*this); + EndDrawController(); +} + +void InputHud::EndDrawController() +{ + if(_outlineHeight > 0 && _outlineWidth > 0) { + InputConfig& cfg = _emu->GetSettings()->GetInputConfig(); + + switch(cfg.DisplayInputPosition) { + default: + case InputDisplayPosition::TopLeft: + if(cfg.DisplayInputHorizontally) { + _xOffset += _outlineWidth + 1; + } else { + _yOffset += _outlineHeight + 1; + } + break; + + case InputDisplayPosition::TopRight: + if(!cfg.DisplayInputHorizontally) { + _xOffset += _outlineWidth + 1; + _yOffset += _outlineHeight + 1; + } + break; + + case InputDisplayPosition::BottomLeft: + if(cfg.DisplayInputHorizontally) { + _xOffset += _outlineWidth + 1; + _yOffset += _outlineHeight + 1; + } + break; + + case InputDisplayPosition::BottomRight: + if(cfg.DisplayInputHorizontally) { + _yOffset += _outlineHeight + 1; + } else { + _xOffset += _outlineWidth + 1; + } + break; + } + + _outlineWidth = 0; + _outlineHeight = 0; + } +} + +void InputHud::DrawControllers(FrameInfo size, vector controllerData) +{ + InputConfig& cfg = _emu->GetSettings()->GetInputConfig(); + + switch(cfg.DisplayInputPosition) { + default: + case InputDisplayPosition::TopLeft: + _xOffset = 2; + _yOffset = 2; + break; + case InputDisplayPosition::TopRight: + _xOffset = size.Width - 1; + _yOffset = 2; + break; + case InputDisplayPosition::BottomLeft: + _xOffset = 2; + _yOffset = size.Height - 1; + break; + case InputDisplayPosition::BottomRight: + _xOffset = size.Width - 1; + _yOffset = size.Height - 1; + break; + } + + for(int i = 0; i < (int)controllerData.size(); i++) { + if(controllerData[i].Type != ControllerType::None) { + DrawController(controllerData[i].Type, i, controllerData[i].State); + } + } } diff --git a/Core/Shared/InputHud.h b/Core/Shared/InputHud.h index 3b2874e1..7a678958 100644 --- a/Core/Shared/InputHud.h +++ b/Core/Shared/InputHud.h @@ -4,16 +4,28 @@ #include "ControlDeviceState.h" class Emulator; +class DebugHud; class InputHud { private: Emulator* _emu; + DebugHud* _hud; - void DrawController(int port, ControlDeviceState state, int x, int y, int frameNumber); + int _xOffset = 0; + int _yOffset = 0; + int _outlineWidth = 0; + int _outlineHeight = 0; + + void DrawController(ControllerType type, int port, ControlDeviceState state); public: - InputHud(Emulator *emu); + InputHud(Emulator *emu, DebugHud* hud); - void DrawControllers(OverscanDimensions overscan, int frameNumber); + void DrawOutline(int width, int height); + void DrawButton(int x, int y, int width, int height, bool pressed); + void DrawNumber(int number, int x, int y); + void EndDrawController(); + + void DrawControllers(FrameInfo size, vector controllerData); }; \ No newline at end of file diff --git a/Core/Shared/Interfaces/IControlManager.h b/Core/Shared/Interfaces/IControlManager.h index 9ace94b4..0195a6bb 100644 --- a/Core/Shared/Interfaces/IControlManager.h +++ b/Core/Shared/Interfaces/IControlManager.h @@ -4,6 +4,7 @@ #include "IInputProvider.h" enum class ControllerType; +struct ControllerData; class IControlManager { @@ -20,6 +21,8 @@ public: virtual shared_ptr CreateControllerDevice(ControllerType type, uint8_t port) = 0; virtual bool HasControlDevice(ControllerType type) = 0; + + virtual vector GetPortStates() = 0; virtual void SetPollCounter(uint32_t pollCounter) = 0; virtual uint32_t GetPollCounter() = 0; diff --git a/Core/Shared/Interfaces/IRenderingDevice.h b/Core/Shared/Interfaces/IRenderingDevice.h index 27b56914..fef37799 100644 --- a/Core/Shared/Interfaces/IRenderingDevice.h +++ b/Core/Shared/Interfaces/IRenderingDevice.h @@ -3,11 +3,13 @@ #include "stdafx.h" #include "Shared/SettingTypes.h" +struct RenderedFrame; + class IRenderingDevice { public: virtual ~IRenderingDevice() {} - virtual void UpdateFrame(RenderedFrame frame) = 0; + virtual void UpdateFrame(RenderedFrame& frame) = 0; virtual void Render(uint32_t* hudBuffer, uint32_t width, uint32_t height) = 0; virtual void Reset() = 0; virtual void SetFullscreenMode(bool fullscreen, void* windowHandle, uint32_t monitorWidth, uint32_t monitorHeight) = 0; diff --git a/Core/Shared/RenderedFrame.h b/Core/Shared/RenderedFrame.h new file mode 100644 index 00000000..39d7f6c2 --- /dev/null +++ b/Core/Shared/RenderedFrame.h @@ -0,0 +1,38 @@ +#pragma once +#include "stdafx.h" +#include "Shared/SettingTypes.h" +#include "Shared/ControlDeviceState.h" + +struct RenderedFrame +{ + void* FrameBuffer = nullptr; + void* Data = nullptr; //Used by HD packs + uint32_t Width = 256; + uint32_t Height = 240; + double Scale = 1.0; + uint32_t FrameNumber = 0; + vector InputData; + + RenderedFrame() + {} + + RenderedFrame(void* buffer, uint32_t width, uint32_t height, double scale = 1.0, uint32_t frameNumber = 0) : + FrameBuffer(buffer), + Width(width), + Height(height), + Scale(scale), + FrameNumber(frameNumber), + Data(nullptr), + InputData({}) + {} + + RenderedFrame(void* buffer, uint32_t width, uint32_t height, double scale, uint32_t frameNumber, vector inputData) : + FrameBuffer(buffer), + Width(width), + Height(height), + Scale(scale), + FrameNumber(frameNumber), + Data(nullptr), + InputData(inputData) + {} +}; diff --git a/Core/Shared/RewindManager.cpp b/Core/Shared/RewindManager.cpp index e3af75c3..b949a7ad 100644 --- a/Core/Shared/RewindManager.cpp +++ b/Core/Shared/RewindManager.cpp @@ -6,6 +6,7 @@ #include "Shared/Video/VideoRenderer.h" #include "Shared/Audio/SoundMixer.h" #include "Shared/BaseControlDevice.h" +#include "Shared/RenderedFrame.h" #include "Shared/Interfaces/IControlManager.h" RewindManager::RewindManager(Emulator* emu) @@ -220,7 +221,7 @@ void RewindManager::ProcessEndOfFrame() } } -void RewindManager::ProcessFrame(RenderedFrame frame, bool forRewind) +void RewindManager::ProcessFrame(RenderedFrame& frame, bool forRewind) { if(_rewindState == RewindState::Starting || _rewindState == RewindState::Started) { if(!forRewind) { @@ -354,7 +355,7 @@ bool RewindManager::HasHistory() return _hasHistory; } -void RewindManager::SendFrame(RenderedFrame frame, bool forRewind) +void RewindManager::SendFrame(RenderedFrame& frame, bool forRewind) { ProcessFrame(frame, forRewind); } diff --git a/Core/Shared/RewindManager.h b/Core/Shared/RewindManager.h index 463fe079..17cc6ef0 100644 --- a/Core/Shared/RewindManager.h +++ b/Core/Shared/RewindManager.h @@ -8,6 +8,7 @@ class Emulator; class EmuSettings; +struct RenderedFrame; enum class RewindState { @@ -55,7 +56,7 @@ private: void Stop(); void ForceStop(); - void ProcessFrame(RenderedFrame frame, bool forRewind); + void ProcessFrame(RenderedFrame& frame, bool forRewind); bool ProcessAudio(int16_t* soundBuffer, uint32_t sampleCount); void ClearBuffer(); @@ -78,6 +79,6 @@ public: bool HasHistory(); - void SendFrame(RenderedFrame frame, bool forRewind); + void SendFrame(RenderedFrame& frame, bool forRewind); bool SendAudio(int16_t *soundBuffer, uint32_t sampleCount); }; \ No newline at end of file diff --git a/Core/Shared/SaveStateManager.cpp b/Core/Shared/SaveStateManager.cpp index 9a2d6704..d20294ea 100644 --- a/Core/Shared/SaveStateManager.cpp +++ b/Core/Shared/SaveStateManager.cpp @@ -8,6 +8,7 @@ #include "Shared/Emulator.h" #include "Shared/EmuSettings.h" #include "Shared/Movies/MovieManager.h" +#include "Shared/RenderedFrame.h" #include "EventType.h" #include "Debugger/Debugger.h" #include "Netplay/GameClient.h" diff --git a/Core/Shared/SettingTypes.h b/Core/Shared/SettingTypes.h index 79413f1b..05445a10 100644 --- a/Core/Shared/SettingTypes.h +++ b/Core/Shared/SettingTypes.h @@ -505,30 +505,6 @@ struct OverscanDimensions uint32_t Bottom = 0; }; -struct RenderedFrame -{ - void* FrameBuffer = nullptr; - void* Data = nullptr; //Used by HD packs - uint32_t Width = 256; - uint32_t Height = 240; - double Scale = 1.0; - uint32_t FrameNumber = 0; - - RenderedFrame() - { - } - - RenderedFrame(void* buffer, uint32_t width, uint32_t height, double scale = 1.0, uint32_t frameNumber = 0) : - FrameBuffer(buffer), - Width(width), - Height(height), - Scale(scale), - FrameNumber(frameNumber), - Data(nullptr) - { - } -}; - struct FrameInfo { uint32_t Width; diff --git a/Core/Shared/Video/DrawCommand.h b/Core/Shared/Video/DrawCommand.h index 6ef5dcc7..442d2fd7 100644 --- a/Core/Shared/Video/DrawCommand.h +++ b/Core/Shared/Video/DrawCommand.h @@ -17,6 +17,22 @@ protected: int _yScale = 1; virtual void InternalDraw() = 0; + + void InternalDrawPixel(int32_t offset, int color, uint32_t alpha) + { + if(alpha != 0xFF000000) { + if(_argbBuffer[offset] == 0) { + //When drawing on an empty background, premultiply channels & preserve alpha value + //This is needed for hardware blending between the HUD and the game screen + BlendColors((uint8_t*)&_argbBuffer[offset], (uint8_t*)&color, true); + } else { + BlendColors((uint8_t*)&_argbBuffer[offset], (uint8_t*)&color); + } + } else { + _argbBuffer[offset] = color; + } + } + void DrawPixel(uint32_t x, uint32_t y, int color) { uint32_t alpha = (color & 0xFF000000); @@ -30,11 +46,7 @@ protected: return; } - if(alpha != 0xFF000000) { - BlendColors((uint8_t*)&_argbBuffer[offset], (uint8_t*)&color); - } else { - _argbBuffer[offset] = color; - } + InternalDrawPixel(offset, color, alpha); } else { int xPixelCount = _useIntegerScaling ? (int)std::floor(_xScale): (int)((x + 1)*_xScale) - (int)(x*_xScale); x = (int)(x * (_useIntegerScaling ? (int)std::floor(_xScale) : _xScale)); @@ -48,25 +60,25 @@ protected: continue; } - if(alpha != 0xFF000000) { - BlendColors((uint8_t*)&_argbBuffer[offset], (uint8_t*)&color); - } else { - _argbBuffer[offset] = color; - } + InternalDrawPixel(offset, color, alpha); } } } } } - __forceinline void BlendColors(uint8_t output[4], uint8_t input[4]) + __forceinline void BlendColors(uint8_t output[4], uint8_t input[4], bool keepAlpha = false) { uint8_t alpha = input[3] + 1; uint8_t invertedAlpha = 256 - input[3]; output[0] = (uint8_t)((alpha * input[0] + invertedAlpha * output[0]) >> 8); output[1] = (uint8_t)((alpha * input[1] + invertedAlpha * output[1]) >> 8); output[2] = (uint8_t)((alpha * input[2] + invertedAlpha * output[2]) >> 8); - output[3] = 0xFF; + if(keepAlpha) { + output[3] = input[3]; + } else { + output[3] = 0xFF; + } } public: diff --git a/Core/Shared/Video/VideoDecoder.cpp b/Core/Shared/Video/VideoDecoder.cpp index 0540dbfc..1fc6616b 100644 --- a/Core/Shared/Video/VideoDecoder.cpp +++ b/Core/Shared/Video/VideoDecoder.cpp @@ -11,6 +11,7 @@ #include "Shared/Video/ScaleFilter.h" #include "Shared/Video/DebugHud.h" #include "Shared/InputHud.h" +#include "Shared/RenderedFrame.h" #include "Shared/Video/SystemHud.h" #include "SNES/CartTypes.h" @@ -97,7 +98,7 @@ void VideoDecoder::DecodeFrame(bool forRewind) overscan.Bottom *= _scaleFilter->GetScale(); } - RenderedFrame convertedFrame((void*)outputBuffer, frameSize.Width, frameSize.Height, _frame.Scale, _frame.FrameNumber); + RenderedFrame convertedFrame((void*)outputBuffer, frameSize.Width, frameSize.Height, _frame.Scale, _frame.FrameNumber, _frame.InputData); _emu->GetDebugHud()->Draw(outputBuffer, frameSize, overscan, _frame.FrameNumber, true); diff --git a/Core/Shared/Video/VideoDecoder.h b/Core/Shared/Video/VideoDecoder.h index 606e1f71..993e20e0 100644 --- a/Core/Shared/Video/VideoDecoder.h +++ b/Core/Shared/Video/VideoDecoder.h @@ -3,6 +3,7 @@ #include "Utilities/SimpleLock.h" #include "Utilities/AutoResetEvent.h" #include "Shared/SettingTypes.h" +#include "Shared/RenderedFrame.h" class BaseVideoFilter; class ScaleFilter; diff --git a/Core/Shared/Video/VideoRenderer.cpp b/Core/Shared/Video/VideoRenderer.cpp index 7762f95b..e9ed5b09 100644 --- a/Core/Shared/Video/VideoRenderer.cpp +++ b/Core/Shared/Video/VideoRenderer.cpp @@ -19,6 +19,7 @@ VideoRenderer::VideoRenderer(Emulator* emu) _rendererHud.reset(new DebugHud()); _systemHud.reset(new SystemHud(_emu, _rendererHud.get())); + _inputHud.reset(new InputHud(emu, _rendererHud.get())); _hudSurface = new uint32_t[256*240]; _hudSize.Width = 256; @@ -89,7 +90,14 @@ void VideoRenderer::RenderThread() _hudSize = size; } + RenderedFrame frame; + { + _frameLock.AcquireSafe(); + frame = _lastFrame; + } + memset(_hudSurface, 0, _hudSize.Width * _hudSize.Height * sizeof(uint32_t)); + _inputHud->DrawControllers(_hudSize, frame.InputData); _systemHud->Draw(_hudSize.Width, _hudSize.Height); _rendererHud->Draw(_hudSurface, _hudSize, {}, 0, false); _renderer->Render(_hudSurface, _hudSize.Width, _hudSize.Height); @@ -97,13 +105,18 @@ void VideoRenderer::RenderThread() } } -void VideoRenderer::UpdateFrame(RenderedFrame frame) +void VideoRenderer::UpdateFrame(RenderedFrame& frame) { shared_ptr recorder = _recorder; if(recorder) { recorder->AddFrame(frame.FrameBuffer, frame.Width, frame.Height, _emu->GetFps()); } + { + _frameLock.AcquireSafe(); + _lastFrame = frame; + } + if(_renderer) { _renderer->UpdateFrame(frame); _waitForRender.Signal(); diff --git a/Core/Shared/Video/VideoRenderer.h b/Core/Shared/Video/VideoRenderer.h index 619182f9..937e19f9 100644 --- a/Core/Shared/Video/VideoRenderer.h +++ b/Core/Shared/Video/VideoRenderer.h @@ -2,6 +2,7 @@ #include "stdafx.h" #include #include "Shared/SettingTypes.h" +#include "Shared/RenderedFrame.h" #include "Utilities/AutoResetEvent.h" #include "Utilities/SimpleLock.h" @@ -9,6 +10,7 @@ class IRenderingDevice; class Emulator; class SystemHud; class DebugHud; +class InputHud; class IVideoRecorder; enum class VideoCodec; @@ -29,9 +31,13 @@ private: unique_ptr _rendererHud; unique_ptr _systemHud; + unique_ptr _inputHud; uint32_t* _hudSurface = nullptr; FrameInfo _hudSize = {}; + RenderedFrame _lastFrame; + SimpleLock _frameLock; + shared_ptr _recorder; void RenderThread(); @@ -46,7 +52,7 @@ public: void StartThread(); void StopThread(); - void UpdateFrame(RenderedFrame frame); + void UpdateFrame(RenderedFrame& frame); void RegisterRenderingDevice(IRenderingDevice *renderer); void UnregisterRenderingDevice(IRenderingDevice *renderer); diff --git a/NewUI/Debugger/Utilities/ContextMenuAction.cs b/NewUI/Debugger/Utilities/ContextMenuAction.cs index a56d918c..c44a49d9 100644 --- a/NewUI/Debugger/Utilities/ContextMenuAction.cs +++ b/NewUI/Debugger/Utilities/ContextMenuAction.cs @@ -527,6 +527,8 @@ namespace Mesen.Debugger.Utilities Audio, [IconFile("DipSwitches")] Emulation, + [IconFile("Controller")] + Input, [IconFile("VideoOptions")] Video, [IconFile("NesIcon")] diff --git a/NewUI/Localization/resources.en.xml b/NewUI/Localization/resources.en.xml index 637f62f2..195f295e 100644 --- a/NewUI/Localization/resources.en.xml +++ b/NewUI/Localization/resources.en.xml @@ -98,24 +98,29 @@ Setup Setup Setup + Warning: Your current configuration contains conflicting key bindings - some physical buttons on your keyboard or gamepad are mapped to multiple buttons on the NES controller. If this is not intentional, please review and correct your key bindings. + +
+ General + Small + Large + Controller axis deadzone size: - Advanced - Display Controller Input + Mouse speed: + Slow + Fast + + Display + Display ports: Port 1 Port 2 Port 3 Port 4 Port 5 - Display Position: - Display horizontally - - Small - Large - Controller axis deadzone size: + Display position: + Horizontal display Hide mouse pointer when using zapper - - Warning: Your current configuration contains conflicting key bindings - some physical buttons on your keyboard or gamepad are mapped to multiple buttons on the NES controller. If this is not intentional, please review and correct your key bindings.
Left @@ -359,6 +364,7 @@ Audio Emulation + Input Video NES SNES @@ -2160,6 +2166,7 @@ x == [$150] || y == [10] Audio Emulation + Input Video NES SNES diff --git a/NewUI/NewUI.csproj b/NewUI/NewUI.csproj index b19c2e04..76f08f5d 100644 --- a/NewUI/NewUI.csproj +++ b/NewUI/NewUI.csproj @@ -323,6 +323,9 @@ MemoryToolsWindow.axaml + + InputConfigView.axaml + MainMenuView.axaml diff --git a/NewUI/Utilities/ReactiveHelper.cs b/NewUI/Utilities/ReactiveHelper.cs index ae6ac31c..c449717b 100644 --- a/NewUI/Utilities/ReactiveHelper.cs +++ b/NewUI/Utilities/ReactiveHelper.cs @@ -1,6 +1,7 @@ using ReactiveUI; using ReactiveUI.Fody.Helpers; using System; +using System.Collections; using System.ComponentModel; using System.Reflection; @@ -12,8 +13,15 @@ namespace Mesen.Utilities { foreach(PropertyInfo prop in target.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)) { if(prop.GetCustomAttribute() != null) { - if(prop.GetValue(target) is ReactiveObject propValue) { + object? value = prop.GetValue(target); + if(value is ReactiveObject propValue) { ReactiveHelper.RegisterRecursiveObserver(propValue, handler); + } else if(value is IList list) { + foreach(object listValue in list) { + if(listValue is ReactiveObject) { + ReactiveHelper.RegisterRecursiveObserver((ReactiveObject)listValue, handler); + } + } } } } @@ -27,8 +35,15 @@ namespace Mesen.Utilities { foreach(PropertyInfo prop in target.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)) { if(prop.GetCustomAttribute() != null) { - if(prop.GetValue(target) is ReactiveObject propValue) { + object? value = prop.GetValue(target); + if(value is ReactiveObject propValue) { ReactiveHelper.UnregisterRecursiveObserver(propValue, handler); + } else if(value is IList list) { + foreach(object listValue in list) { + if(listValue is ReactiveObject) { + ReactiveHelper.UnregisterRecursiveObserver((ReactiveObject)listValue, handler); + } + } } } } diff --git a/NewUI/ViewModels/ConfigViewModel.cs b/NewUI/ViewModels/ConfigViewModel.cs index 4365381e..2c84ef1b 100644 --- a/NewUI/ViewModels/ConfigViewModel.cs +++ b/NewUI/ViewModels/ConfigViewModel.cs @@ -10,6 +10,7 @@ namespace Mesen.ViewModels public class ConfigViewModel : DisposableViewModel { [Reactive] public AudioConfigViewModel? Audio { get; set; } + [Reactive] public InputConfigViewModel? Input { get; set; } [Reactive] public VideoConfigViewModel? Video { get; set; } [Reactive] public PreferencesConfigViewModel? Preferences { get; set; } [Reactive] public EmulationConfigViewModel? Emulation { get; set; } @@ -38,6 +39,7 @@ namespace Mesen.ViewModels switch(tab) { case ConfigWindowTab.Audio: Audio ??= AddDisposable(new AudioConfigViewModel()); break; case ConfigWindowTab.Emulation: Emulation ??= AddDisposable(new EmulationConfigViewModel()); break; + case ConfigWindowTab.Input: Input ??= AddDisposable(new InputConfigViewModel()); break; case ConfigWindowTab.Video: Video ??= AddDisposable(new VideoConfigViewModel()); break; case ConfigWindowTab.Nes: @@ -63,6 +65,7 @@ namespace Mesen.ViewModels } ConfigManager.Config.Audio = Audio?.Config.Clone() ?? ConfigManager.Config.Audio; + ConfigManager.Config.Input = Input?.Config.Clone() ?? ConfigManager.Config.Input; ConfigManager.Config.Video = Video?.Config.Clone() ?? ConfigManager.Config.Video; ConfigManager.Config.Preferences = Preferences?.Config.Clone() ?? ConfigManager.Config.Preferences; ConfigManager.Config.Emulation = Emulation?.Config.Clone() ?? ConfigManager.Config.Emulation; @@ -80,12 +83,13 @@ namespace Mesen.ViewModels { Audio = 0, Emulation = 1, - Video = 2, - - Nes = 4, - Snes = 5, - Gameboy = 6, - - Preferences = 8 + Input = 2, + Video = 3, + //separator + Nes = 5, + Snes = 6, + Gameboy = 7, + //separator + Preferences = 9 } } diff --git a/NewUI/ViewModels/EmulationConfigViewModel.cs b/NewUI/ViewModels/EmulationConfigViewModel.cs index 346ff99b..95f407bb 100644 --- a/NewUI/ViewModels/EmulationConfigViewModel.cs +++ b/NewUI/ViewModels/EmulationConfigViewModel.cs @@ -8,7 +8,7 @@ namespace Mesen.ViewModels public class EmulationConfigViewModel : DisposableViewModel { [Reactive] public EmulationConfig Config { get; set; } - + public EmulationConfigViewModel() { Config = ConfigManager.Config.Emulation.Clone(); @@ -19,5 +19,5 @@ namespace Mesen.ViewModels AddDisposable(ReactiveHelper.RegisterRecursiveObserver(Config, (s, e) => { Config.ApplyConfig(); })); } - } + } } diff --git a/NewUI/ViewModels/InputConfigViewModel.cs b/NewUI/ViewModels/InputConfigViewModel.cs new file mode 100644 index 00000000..48600b8a --- /dev/null +++ b/NewUI/ViewModels/InputConfigViewModel.cs @@ -0,0 +1,23 @@ +using Avalonia.Controls; +using Mesen.Config; +using Mesen.Utilities; +using ReactiveUI.Fody.Helpers; + +namespace Mesen.ViewModels +{ + public class InputConfigViewModel : DisposableViewModel + { + [Reactive] public InputConfig Config { get; set; } + + public InputConfigViewModel() + { + Config = ConfigManager.Config.Input.Clone(); + + if(Design.IsDesignMode) { + return; + } + + AddDisposable(ReactiveHelper.RegisterRecursiveObserver(Config, (s, e) => { Config.ApplyConfig(); })); + } + } +} diff --git a/NewUI/ViewModels/MainMenuViewModel.cs b/NewUI/ViewModels/MainMenuViewModel.cs index 4072c6d7..23e200a5 100644 --- a/NewUI/ViewModels/MainMenuViewModel.cs +++ b/NewUI/ViewModels/MainMenuViewModel.cs @@ -374,6 +374,10 @@ namespace Mesen.ViewModels ActionType = ActionType.Emulation, OnClick = () => OpenConfig(wnd, ConfigWindowTab.Emulation) }, + new MainMenuAction() { + ActionType = ActionType.Input, + OnClick = () => OpenConfig(wnd, ConfigWindowTab.Input) + }, new MainMenuAction() { ActionType = ActionType.Video, OnClick = () => OpenConfig(wnd, ConfigWindowTab.Video) diff --git a/NewUI/Views/EmulationConfigView.axaml b/NewUI/Views/EmulationConfigView.axaml index ca574bef..6cfa7d05 100644 --- a/NewUI/Views/EmulationConfigView.axaml +++ b/NewUI/Views/EmulationConfigView.axaml @@ -22,15 +22,15 @@ - + - + - + diff --git a/NewUI/Views/InputConfigView.axaml b/NewUI/Views/InputConfigView.axaml new file mode 100644 index 00000000..38407810 --- /dev/null +++ b/NewUI/Views/InputConfigView.axaml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NewUI/Views/InputConfigView.axaml.cs b/NewUI/Views/InputConfigView.axaml.cs new file mode 100644 index 00000000..8cc538d7 --- /dev/null +++ b/NewUI/Views/InputConfigView.axaml.cs @@ -0,0 +1,21 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Mesen.Utilities; +using Mesen.Config; + +namespace Mesen.Views +{ + public class InputConfigView : UserControl + { + public InputConfigView() + { + InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + } +} diff --git a/NewUI/Windows/ConfigWindow.axaml b/NewUI/Windows/ConfigWindow.axaml index ff53d251..8673ca18 100644 --- a/NewUI/Windows/ConfigWindow.axaml +++ b/NewUI/Windows/ConfigWindow.axaml @@ -26,6 +26,9 @@ + + + @@ -71,6 +74,14 @@ + + + + + + + + diff --git a/Windows/Renderer.cpp b/Windows/Renderer.cpp index 975a1bd9..dd6e6f0d 100644 --- a/Windows/Renderer.cpp +++ b/Windows/Renderer.cpp @@ -120,14 +120,6 @@ void Renderer::CleanupDevice() { ResetNesBuffers(); ReleaseRenderTargetView(); - if(_pAlphaEnableBlendingState) { - _pAlphaEnableBlendingState->Release(); - _pAlphaEnableBlendingState = nullptr; - } - if(_pDepthDisabledStencilState) { - _pDepthDisabledStencilState->Release(); - _pDepthDisabledStencilState = nullptr; - } if(_pSwapChain) { _pSwapChain->SetFullscreenState(false, nullptr); _pSwapChain->Release(); @@ -318,61 +310,6 @@ HRESULT Renderer::InitDevice() if(FAILED(hr)) { return hr; } - - D3D11_DEPTH_STENCIL_DESC depthDisabledStencilDesc; - ZeroMemory(&depthDisabledStencilDesc, sizeof(depthDisabledStencilDesc)); - depthDisabledStencilDesc.DepthEnable = false; - depthDisabledStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; - depthDisabledStencilDesc.DepthFunc = D3D11_COMPARISON_LESS; - depthDisabledStencilDesc.StencilEnable = true; - depthDisabledStencilDesc.StencilReadMask = 0xFF; - depthDisabledStencilDesc.StencilWriteMask = 0xFF; - depthDisabledStencilDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; - depthDisabledStencilDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR; - depthDisabledStencilDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; - depthDisabledStencilDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; - depthDisabledStencilDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; - depthDisabledStencilDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR; - depthDisabledStencilDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; - depthDisabledStencilDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS; - - // Create the state using the device. - hr = _pd3dDevice->CreateDepthStencilState(&depthDisabledStencilDesc, &_pDepthDisabledStencilState); - if(FAILED(hr)) { - MessageManager::Log("D3DDevice::CreateDepthStencilState() failed - Error:" + std::to_string(hr)); - return hr; - } - - // Clear the blend state description. - D3D11_BLEND_DESC blendStateDescription; - ZeroMemory(&blendStateDescription, sizeof(D3D11_BLEND_DESC)); - - // Create an alpha enabled blend state description. - blendStateDescription.RenderTarget[0].BlendEnable = TRUE; - blendStateDescription.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; - blendStateDescription.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; - blendStateDescription.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; - blendStateDescription.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; - blendStateDescription.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; - blendStateDescription.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; - blendStateDescription.RenderTarget[0].RenderTargetWriteMask = 0x0f; - - // Create the blend state using the description. - hr = _pd3dDevice->CreateBlendState(&blendStateDescription, &_pAlphaEnableBlendingState); - if(FAILED(hr)) { - MessageManager::Log("D3DDevice::CreateBlendState() failed - Error:" + std::to_string(hr)); - return hr; - } - - float blendFactor[4]; - blendFactor[0] = 0.0f; - blendFactor[1] = 0.0f; - blendFactor[2] = 0.0f; - blendFactor[3] = 0.0f; - - _pDeviceContext->OMSetBlendState(_pAlphaEnableBlendingState, blendFactor, 0xffffffff); - _pDeviceContext->OMSetDepthStencilState(_pDepthDisabledStencilState, 1); - hr = CreateEmuTextureBuffers(); if(FAILED(hr)) { return hr; @@ -420,7 +357,7 @@ ID3D11ShaderResourceView* Renderer::GetShaderResourceView(ID3D11Texture2D* textu return shaderResourceView; } -void Renderer::UpdateFrame(RenderedFrame frame) +void Renderer::UpdateFrame(RenderedFrame& frame) { SetScreenSize(frame.Width, frame.Height); diff --git a/Windows/Renderer.h b/Windows/Renderer.h index d1f62cf8..7b6e5c19 100644 --- a/Windows/Renderer.h +++ b/Windows/Renderer.h @@ -25,8 +25,6 @@ private: ID3D11DeviceContext* _pDeviceContext = nullptr; IDXGISwapChain* _pSwapChain = nullptr; ID3D11RenderTargetView* _pRenderTargetView = nullptr; - ID3D11DepthStencilState* _pDepthDisabledStencilState = nullptr; - ID3D11BlendState* _pAlphaEnableBlendingState = nullptr; atomic _needFlip = false; uint8_t* _textureBuffer[2] = { nullptr, nullptr }; @@ -86,5 +84,5 @@ public: void Reset(); void Render(uint32_t* hudBuffer, uint32_t hudWidth, uint32_t hudHeight); - void UpdateFrame(RenderedFrame frame); + void UpdateFrame(RenderedFrame& frame); }; \ No newline at end of file