From 22526c1b90ca2b8de53475a9bb74558e4a4d8d70 Mon Sep 17 00:00:00 2001 From: Pablo Rodriguez Date: Fri, 14 Mar 2025 17:35:31 -0400 Subject: [PATCH] display now only draws chipxels that have changed --- DisplaySDL.cpp | 72 +++++++++++++++++++++++++++++++++----------------- DisplaySDL.hpp | 4 ++- 2 files changed, 51 insertions(+), 25 deletions(-) diff --git a/DisplaySDL.cpp b/DisplaySDL.cpp index 4aff847..5692ac1 100644 --- a/DisplaySDL.cpp +++ b/DisplaySDL.cpp @@ -1,10 +1,13 @@ #include "DisplaySDL.hpp" #include "Peripherals.hpp" +#include #include DisplaySDL::DisplaySDL(int w, int h, uint32_t fgColor, uint32_t bgColor): - mpFramebuffer{std::make_unique()} { + mpFramebuffer{std::make_unique()}, + mpDisplayState{std::make_unique()}, + mDoClear{true} { // Create SDL Window mpWindow = SDL_CreateWindow( "ChocoChip-8", @@ -41,44 +44,65 @@ void DisplaySDL::clear() { for(auto &scanline : *mpFramebuffer) { scanline.reset(); } - SDL_FillRect(SDL_GetWindowSurface(mpWindow), nullptr, mBgColor); + mDoClear = true; } int DisplaySDL::blit(const chocochip8::Scanline &spriteScanline, int y) { using chocochip8::Scanline; Scanline &targetScanline = mpFramebuffer->at(y); - Scanline bitsToSet = spriteScanline & ~targetScanline; - Scanline bitsToReset = spriteScanline & targetScanline; + bool collision = (spriteScanline & targetScanline).any(); targetScanline ^= spriteScanline; + return collision; +} +void DisplaySDL::updateWindow(bool doWindowUpdate) const { + using chocochip8::Scanline; SDL_Surface *pSurface = SDL_GetWindowSurface(mpWindow); - SDL_Rect rect; - // Map framebuffer pixel to SDL surface rectangle - auto mapChipxelToPixel = [&rect, pSurface](int x, int y) { - // Note that the MSB of an scanline's bitset is the leftmost pixel. - x = (chocochip8::gcWidth - 1) - x; - int x1 = ( x * pSurface->w) / chocochip8::gcWidth; - int x2 = ((x + 1) * pSurface->w) / chocochip8::gcWidth; + if(mDoClear) { + for(auto &scanline : *mpDisplayState) { + scanline.reset(); + } + SDL_FillRect(pSurface, NULL, mBgColor); + mDoClear = false; + doWindowUpdate = true; + } + + for(int y = 0; y < mpFramebuffer->size(); y++) { + Scanline &rNewScanline = (*mpFramebuffer)[y]; + Scanline &rOldScanline = (*mpDisplayState)[y]; + if(rNewScanline == rOldScanline) { + // Skip scanlines that haven't changed since the last update + continue; + } + + for(int x = 0; x < rNewScanline.size(); x++) { + bool isSet = rNewScanline._Unchecked_test(x); + bool wasSet = rOldScanline._Unchecked_test(x); + if(isSet == wasSet) { + continue; + } + + // Map framebuffer pixel to SDL surface rectangle, + // note that the MSB of an scanline's bitset is the leftmost pixel. + int z = (chocochip8::gcWidth - 1) - x; + int x1 = ( z * pSurface->w) / chocochip8::gcWidth; + int x2 = ((z + 1) * pSurface->w) / chocochip8::gcWidth; int y1 = ( y * pSurface->h) / chocochip8::gcHeight; int y2 = ((y + 1) * pSurface->h) / chocochip8::gcHeight; + SDL_Rect rect; rect.x = x1; rect.y = y1; rect.w = x2 - x1; rect.h = y2 - y1; - }; - for(int x = bitsToReset._Find_first(); x < bitsToReset.size(); x = bitsToReset._Find_next(x)) { - mapChipxelToPixel(x, y); - SDL_FillRect(pSurface, &rect, mBgColor); - } - for(int x = bitsToSet._Find_first(); x < bitsToSet.size(); x = bitsToSet._Find_next(x)) { - mapChipxelToPixel(x, y); - SDL_FillRect(pSurface, &rect, mFgColor); + Uint32 color = (isSet ? mFgColor : mBgColor); + SDL_FillRect(pSurface, &rect, color); + doWindowUpdate = true; + } } - return bitsToReset.any(); -} - -void DisplaySDL::updateWindow() const { - SDL_UpdateWindowSurface(mpWindow); + if(doWindowUpdate) { + *mpDisplayState = *mpFramebuffer; + SDL_UpdateWindowSurface(mpWindow); + } } diff --git a/DisplaySDL.hpp b/DisplaySDL.hpp index ad47384..9c7f366 100644 --- a/DisplaySDL.hpp +++ b/DisplaySDL.hpp @@ -13,12 +13,14 @@ public: ~DisplaySDL() override; void clear() override; int blit(const chocochip8::Scanline &scanline, int y) override; - void updateWindow() const; + void updateWindow(bool forceWindowUpdate = false) const; private: using Framebuffer = std::array; std::unique_ptr mpFramebuffer; + mutable std::unique_ptr mpDisplayState; SDL_Window *mpWindow; Uint32 mFgColor; Uint32 mBgColor; + mutable bool mDoClear; };