diff --git a/Gaming/inc/Game.h b/Gaming/inc/Game.h index 304df41..1ab6b58 100644 --- a/Gaming/inc/Game.h +++ b/Gaming/inc/Game.h @@ -19,11 +19,17 @@ namespace Gaming { class Game : public EightBit::Device { public: - Game(bool verbose = false); + Game(SDL_LogPriority logging = SDL_LOG_PRIORITY_WARN); virtual ~Game(); - virtual void runLoop(); + virtual SDL_AppResult runFrame(); + virtual SDL_AppResult handleEvent(SDL_Event& e); + void raisePOWER() noexcept override; + void lowerPOWER() noexcept override; + + virtual void initialise(); + virtual void terminate(); protected: [[nodiscard]] virtual float fps() const noexcept = 0; @@ -39,11 +45,8 @@ namespace Gaming { [[nodiscard]] virtual std::string title() const noexcept = 0; - virtual void handleEvents(); virtual void update(); virtual void draw(); - virtual bool maybeSynchronise(); // true, if manual synchronisation required - virtual void synchronise(); virtual void runRasterLines() {}; virtual void runVerticalBlank() {} @@ -88,10 +91,6 @@ namespace Gaming { SDL_PixelFormat m_pixelType = SDL_PIXELFORMAT_ARGB8888; bool m_vsync = false; - Uint64 m_performanceFrequency = 0; - double m_targetFrameTime = 0.0; // in seconds - Uint64 m_frameStartTime = 0; // in performance frequency - Uint64 m_frameEndTime = 0; // in performance frequency std::map> m_gameControllers; diff --git a/Gaming/inc/Wrapper.h b/Gaming/inc/Wrapper.h index 96c7fd2..4e2faa9 100644 --- a/Gaming/inc/Wrapper.h +++ b/Gaming/inc/Wrapper.h @@ -5,10 +5,19 @@ #include namespace Gaming { - class Wrapper final { + class Wrapper final : public EightBit::Device { + + using base = Device; + public: - Wrapper(bool verbose = false); - ~Wrapper(); + Wrapper(SDL_LogPriority logging = SDL_LOG_PRIORITY_WARN); + ~Wrapper() = default; + + void raisePOWER() noexcept final; + void lowerPOWER() noexcept final; + + void initialise(); + void terminate(); static void throwException(const std::string& failure) { std::ostringstream output; @@ -24,5 +33,8 @@ namespace Gaming { static void maybeThrowException(void* handle, const std::string& failure) { maybeThrowException(handle != nullptr, failure); } + + private: + SDL_LogPriority m_logging; }; } \ No newline at end of file diff --git a/Gaming/src/Game.cpp b/Gaming/src/Game.cpp index 34350c6..c1244bd 100644 --- a/Gaming/src/Game.cpp +++ b/Gaming/src/Game.cpp @@ -3,10 +3,12 @@ #include "../inc/GameController.h" +#include + namespace Gaming { -Game::Game(bool verbose /* = false */) -: m_wrapper(verbose) {} +Game::Game(SDL_LogPriority logging /* = SDL_LOG_PRIORITY_WARN */) +: m_wrapper(logging) {} Game::~Game() {} @@ -27,8 +29,18 @@ int Game::displayHeight() const noexcept { } void Game::raisePOWER() noexcept { - Device::raisePOWER(); + initialise(); +} + +void Game::lowerPOWER() noexcept { + terminate(); + Device::lowerPOWER(); +} + +void Game::initialise() { + + m_wrapper.raisePOWER(); m_window.reset(::SDL_CreateWindow(title().c_str(), windowWidth(), windowHeight(), 0), ::SDL_DestroyWindow); Wrapper::maybeThrowException(m_window.get(), "Unable to create window"); @@ -59,14 +71,24 @@ void Game::raisePOWER() noexcept { } } + if (!m_vsync) { + SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "Setting callback rate hint"); + const auto success = SDL_SetHint(SDL_HINT_MAIN_CALLBACK_RATE, std::format("{}", fps()).c_str()); + Wrapper::maybeThrowException(success, "Unable to set event loop callback rate hint"); + } + m_pixelFormat = SDL_GetPixelFormatDetails(m_pixelType); Wrapper::maybeThrowException(m_pixelFormat, "Unable to obtain pixel format details"); configureBackground(); createBitmapTexture(); +} - m_performanceFrequency = ::SDL_GetPerformanceFrequency(); - m_targetFrameTime = 1.0 / fps(); +void Game::terminate() { + m_bitmapTexture.reset(); + m_renderer.reset(); + m_window.reset(); + m_wrapper.lowerPOWER(); } void Game::configureBackground() const { @@ -79,54 +101,48 @@ void Game::createBitmapTexture() { Wrapper::maybeThrowException(m_bitmapTexture.get(), "Unable to create bitmap texture"); } -void Game::runLoop() { - while (powered()) { - update(); - draw(); - maybeSynchronise(); - } +SDL_AppResult Game::runFrame() { + update(); + draw(); + return SDL_APP_CONTINUE; } void Game::update() { - m_frameStartTime = ::SDL_GetPerformanceCounter(); - handleEvents(); runVerticalBlank(); runRasterLines(); } -void Game::handleEvents() { - ::SDL_Event e; - while (::SDL_PollEvent(&e)) { - switch (e.type) { - case SDL_EVENT_QUIT: - lowerPOWER(); - break; - case SDL_EVENT_KEY_DOWN: - handleKeyDown(e.key.key); - break; - case SDL_EVENT_KEY_UP: - handleKeyUp(e.key.key); - break; - case SDL_EVENT_JOYSTICK_BUTTON_DOWN: - handleJoyButtonDown(e.jbutton); - break; - case SDL_EVENT_JOYSTICK_BUTTON_UP: - handleJoyButtonUp(e.jbutton); - break; - case SDL_EVENT_GAMEPAD_BUTTON_DOWN: - handleGamepadButtonDown(e.gbutton); - break; - case SDL_EVENT_GAMEPAD_BUTTON_UP: - handleGamepadButtonUp(e.gbutton); - break; - case SDL_EVENT_GAMEPAD_ADDED: - addGamepad(e); - break; - case SDL_EVENT_GAMEPAD_REMOVED: - removeGamepad(e); - break; - } +SDL_AppResult Game::handleEvent(SDL_Event& e) { + switch (e.type) { + case SDL_EVENT_QUIT: + return SDL_APP_SUCCESS; + break; + case SDL_EVENT_KEY_DOWN: + handleKeyDown(e.key.key); + break; + case SDL_EVENT_KEY_UP: + handleKeyUp(e.key.key); + break; + case SDL_EVENT_JOYSTICK_BUTTON_DOWN: + handleJoyButtonDown(e.jbutton); + break; + case SDL_EVENT_JOYSTICK_BUTTON_UP: + handleJoyButtonUp(e.jbutton); + break; + case SDL_EVENT_GAMEPAD_BUTTON_DOWN: + handleGamepadButtonDown(e.gbutton); + break; + case SDL_EVENT_GAMEPAD_BUTTON_UP: + handleGamepadButtonUp(e.gbutton); + break; + case SDL_EVENT_GAMEPAD_ADDED: + addGamepad(e); + break; + case SDL_EVENT_GAMEPAD_REMOVED: + removeGamepad(e); + break; } + return SDL_APP_CONTINUE; } void Game::draw() { @@ -135,37 +151,6 @@ void Game::draw() { displayTexture(); } -bool Game::maybeSynchronise() { - const bool synchronising = !m_vsync; - if (synchronising) - synchronise(); - return synchronising; -} - -void Game::synchronise() { - - m_frameEndTime = ::SDL_GetPerformanceCounter(); - - const auto frameTime = m_frameEndTime - m_frameStartTime; // In performance frequency - ::SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "Frame time (ticks): %ld", frameTime); - - const auto elapsedFrameTime = double(frameTime) / m_performanceFrequency; // In seconds - ::SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "Frame time (seconds): %f", elapsedFrameTime); - - const auto gap = m_targetFrameTime - elapsedFrameTime; // in seconds - ::SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "Timing gap (seconds): %f", gap); - - if (gap > 0) { - const auto delay = Uint32(gap * 1000.0); - ::SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "Delay (ticks): %d", delay); - SDL_Delay(delay); - } - - if (gap < 0) { - ::SDL_LogWarn(SDL_LOG_CATEGORY_RENDER, "Running slowly"); - } -} - void Game::removeGamepad(SDL_Event& e) { const auto which = e.gdevice.which; const auto found = m_gameControllers.find(which); diff --git a/Gaming/src/Wrapper.cpp b/Gaming/src/Wrapper.cpp index f6d44c9..59248f3 100644 --- a/Gaming/src/Wrapper.cpp +++ b/Gaming/src/Wrapper.cpp @@ -3,13 +3,27 @@ using namespace Gaming; -Wrapper::Wrapper(bool verbose /* = false */) { - const auto success = ::SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC); - maybeThrowException(success, "Unable to initialise SDL library"); - const auto priority = verbose ? SDL_LogPriority::SDL_LOG_PRIORITY_VERBOSE : SDL_LogPriority::SDL_LOG_PRIORITY_ERROR; - ::SDL_SetLogPriorities(priority); +Wrapper::Wrapper(SDL_LogPriority logging /* = SDL_LOG_PRIORITY_WARN */) +: m_logging(logging) {} + +void Wrapper::raisePOWER() noexcept { + base::raisePOWER(); + initialise(); } -Wrapper::~Wrapper() { - ::SDL_Quit(); -} \ No newline at end of file +void Wrapper::lowerPOWER() noexcept { + terminate(); + base::lowerPOWER(); +} + +void Wrapper::initialise() { + const auto success = ::SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC); + maybeThrowException(success, "Unable to initialise SDL library"); + ::SDL_SetLogPriorities(m_logging); + ::SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "SDL library initialised"); +} + +void Wrapper::terminate() { + ::SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "SDL library terminating"); + ::SDL_Quit(); +}