From 4f7083d7c782db056b39b825f6e558e9c0267c64 Mon Sep 17 00:00:00 2001 From: Pablo Rodriguez Date: Fri, 14 Mar 2025 02:50:01 -0400 Subject: [PATCH] optimized display update region --- DisplaySDL.cpp | 16 ++++++++++++++-- Interpreter.cpp | 6 ++---- Peripherals.hpp | 25 +++++++++++++++++++++++-- main.cpp | 10 ++++++++-- 4 files changed, 47 insertions(+), 10 deletions(-) diff --git a/DisplaySDL.cpp b/DisplaySDL.cpp index 9303a6e..0949998 100644 --- a/DisplaySDL.cpp +++ b/DisplaySDL.cpp @@ -36,6 +36,11 @@ DisplaySDL::~DisplaySDL() { } void DisplaySDL::updateWindow() const { + if(miTopDirtyScanline > miBottomDirtyScanline) { + // No changes since the last update + return; + } + SDL_Surface *pSurface = SDL_GetWindowSurface(mpWindow); SDL_LockSurface(pSurface); @@ -48,10 +53,15 @@ void DisplaySDL::updateWindow() const { lx = -1; ly = -1; for(sy = 0; sy < pSurface->h; sy++) { + // Map SDL surface coordinates to Chocochip8 Framebuffer coordinates + fy = sy * (double(chocochip8::gcHeight) / pSurface->h); + if(fy < miTopDirtyScanline || miBottomDirtyScanline < fy) { + // Skip scanlines that haven't changed since the last update + continue; + } + for(sx = 0; sx < pSurface->w; sx++) { - // Map SDL surface coordinates to Chocochip8 Framebuffer coordinates fx = sx * (double(chocochip8::gcWidth) / pSurface->w); - fy = sy * (double(chocochip8::gcHeight) / pSurface->h); // Reuse color if this screen pixel maps to the same ChocoChip8 pixel as the last if(fx != lx || fy != ly) { @@ -72,6 +82,8 @@ void DisplaySDL::updateWindow() const { } } + miTopDirtyScanline = chocochip8::gcHeight - 1; + miBottomDirtyScanline = 0; SDL_UnlockSurface(pSurface); SDL_UpdateWindowSurface(mpWindow); } diff --git a/Interpreter.cpp b/Interpreter.cpp index 213a162..ba54010 100644 --- a/Interpreter.cpp +++ b/Interpreter.cpp @@ -87,9 +87,7 @@ void Interpreter::tick() { case 0x0000: // 0NNN - call machine language routine switch(inst) { case 0x00E0: // clear display - for(auto &scanline : *mrDisplay.mpFramebuffer) { - scanline.reset(); - } + mrDisplay.clear(); break; case 0x00EE: // return from subroutine mvSpecialReg[SR_PC] = mCallStack.top(); @@ -292,7 +290,7 @@ void Interpreter::executeDraw(uint8_t x, uint8_t y, uint8_t n) { // we draw one scanline per sprite row in high-res mode, but twice in low-res mode for(int j = 0; j < (mIsHighResMode ? 1 : 2); j++) { - Scanline &targetScanline = mrDisplay.mpFramebuffer->at(y + i * (mIsHighResMode ? 1 : 2) + j); + Scanline &targetScanline = mrDisplay.getModifyableScanline(y + i * (mIsHighResMode ? 1 : 2) + j); if((targetScanline & spriteScanline) != 0) { collisionCount += 1; } diff --git a/Peripherals.hpp b/Peripherals.hpp index 84b5ecb..b776271 100644 --- a/Peripherals.hpp +++ b/Peripherals.hpp @@ -20,11 +20,32 @@ namespace chocochip8 { class Display { public: - friend class Interpreter; - Display(): mpFramebuffer{std::make_unique()} {} + Display(): + mpFramebuffer{std::make_unique()}, + miTopDirtyScanline{0}, + miBottomDirtyScanline{gcHeight - 1} {} + virtual ~Display() = default; + + virtual void clear() { + for(auto &scanline : *mpFramebuffer) { + scanline.reset(); + } + miTopDirtyScanline = 0; + miBottomDirtyScanline = gcHeight - 1; + } + + virtual Scanline& getModifyableScanline(size_t y) { + Scanline& res = mpFramebuffer->at(y); + miTopDirtyScanline = std::min(miTopDirtyScanline, y); + miBottomDirtyScanline = std::max(miBottomDirtyScanline, y); + return res; + } + protected: std::unique_ptr mpFramebuffer; + mutable size_t miTopDirtyScanline; + mutable size_t miBottomDirtyScanline; }; class Buzzer { diff --git a/main.cpp b/main.cpp index 5e6b5d0..2f8d0e6 100644 --- a/main.cpp +++ b/main.cpp @@ -25,7 +25,7 @@ int main(int argc, char* argv[]) { BuzzerSDL buzzer(440); DisplaySDL display(1280, 640); TestKeypad keypad; - chocochip8::Interpreter chip8(90, display, buzzer, keypad); + chocochip8::Interpreter chip8(1000, display, buzzer, keypad); auto rom = std::vector(); auto romfile = std::ifstream(argv[1] != NULL ? argv[1] : "/dev/stdin", std::ios_base::in | std::ios_base::binary); @@ -38,6 +38,8 @@ int main(int argc, char* argv[]) { SDL_Event event; bool done = false; + Uint64 start, end; + start = SDL_GetTicks64(); while(!done) { while(SDL_PollEvent(&event)) { if(event.type == SDL_QUIT) { @@ -46,7 +48,11 @@ int main(int argc, char* argv[]) { } chip8.tick(); display.updateWindow(); - SDL_Delay(1000.0 / 60); + SDL_Delay(1); + end = SDL_GetTicks64(); + if(end - start > 3) + std::cout << "Frame took too long: " << end - start << "\n"; + start = end; } buzzer.off();