From 2e7d565b05ab368c273f1a4c0b39c93589022853 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sat, 15 Jun 2024 14:47:50 +0100 Subject: [PATCH] CLX renderer: Cache the last drawn outline --- Source/diablo.cpp | 2 ++ Source/engine/render/clx_render.cpp | 35 ++++++++++++++++++++++++----- Source/engine/render/clx_render.hpp | 7 ++++++ 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/Source/diablo.cpp b/Source/diablo.cpp index eebe60ba1..b1b784a43 100644 --- a/Source/diablo.cpp +++ b/Source/diablo.cpp @@ -36,6 +36,7 @@ #include "engine/load_cel.hpp" #include "engine/load_file.hpp" #include "engine/random.hpp" +#include "engine/render/clx_render.hpp" #include "engine/sound.h" #include "gamemenu.h" #include "gmenu.h" @@ -2860,6 +2861,7 @@ void LoadGameLevel(bool firstflag, lvl_entry lvldir) MakeLightTable(); SetDungeonMicros(); LoadLvlGFX(); + ClearClxDrawCache(); IncProgress(); if (firstflag) { diff --git a/Source/engine/render/clx_render.cpp b/Source/engine/render/clx_render.cpp index cf068911e..aadbf6b57 100644 --- a/Source/engine/render/clx_render.cpp +++ b/Source/engine/render/clx_render.cpp @@ -259,6 +259,13 @@ constexpr size_t MaxOutlineSpriteWidth = 256; using OutlinePixels = StaticVector, MaxOutlinePixels>; using OutlineRowSolidRuns = StaticVector, MaxOutlineSpriteWidth / 2 + 1>; +struct OutlinePixelsCacheEntry { + OutlinePixels outlinePixels; + const void *spriteData = nullptr; + bool skipColorIndexZero; +}; +OutlinePixelsCacheEntry OutlinePixelsCache; + void PopulateOutlinePixelsForRow( const StaticBitVector &rowBelow, const StaticBitVector &rowAbove, @@ -289,11 +296,10 @@ void AppendOutlineRowSolidRuns(int x, int w, OutlineRowSolidRuns &solidRuns) } template -OutlinePixels GetOutline(ClxSprite sprite) // NOLINT(readability-function-cognitive-complexity) +void GetOutline(ClxSprite sprite, OutlinePixels &result) // NOLINT(readability-function-cognitive-complexity) { const unsigned width = sprite.width(); assert(width < MaxOutlineSpriteWidth); - OutlinePixels result; StaticBitVector rows[3] = { StaticBitVector(width), StaticBitVector(width), @@ -387,21 +393,33 @@ OutlinePixels GetOutline(ClxSprite sprite) // NOLINT(readability-function-cognit } rowAbove->reset(); PopulateOutlinePixelsForRow(*rowBelow, *rowAbove, *solidRun, y + 1, result); - return result; +} + +template +void UpdateOutlinePixelsCache(ClxSprite sprite) +{ + if (OutlinePixelsCache.spriteData == sprite.pixelData() + && OutlinePixelsCache.skipColorIndexZero == SkipColorIndexZero) { + return; + } + OutlinePixelsCache.skipColorIndexZero = SkipColorIndexZero; + OutlinePixelsCache.spriteData = sprite.pixelData(); + OutlinePixelsCache.outlinePixels.clear(); + GetOutline(sprite, OutlinePixelsCache.outlinePixels); } template void RenderClxOutline(const Surface &out, Point position, ClxSprite sprite, uint8_t color) { - const OutlinePixels pixels = GetOutline(sprite); + UpdateOutlinePixelsCache(sprite); position.y -= sprite.height() - 1; if (position.x > 0 && position.x + sprite.width() < out.w() && position.y > 0 && position.y + sprite.height() < out.h()) { - for (auto [x, y] : pixels) { + for (const auto &[x, y] : OutlinePixelsCache.outlinePixels) { *out.at(position.x + x, position.y + y) = color; } } else { - for (auto [x, y] : pixels) { + for (const auto &[x, y] : OutlinePixelsCache.outlinePixels) { out.SetPixel(Point(position.x + x, position.y + y), color); } } @@ -591,4 +609,9 @@ void ClxDrawOutlineSkipColorZero(const Surface &out, uint8_t col, Point position RenderClxOutline(out, position, clx, col); } +void ClearClxDrawCache() +{ + OutlinePixelsCache.spriteData = nullptr; +} + } // namespace devilution diff --git a/Source/engine/render/clx_render.hpp b/Source/engine/render/clx_render.hpp index d7546815a..9444ff189 100644 --- a/Source/engine/render/clx_render.hpp +++ b/Source/engine/render/clx_render.hpp @@ -121,6 +121,13 @@ bool IsPointWithinClx(Point position, ClxSprite clx); */ std::pair ClxMeasureSolidHorizontalBounds(ClxSprite clx); +/** + * @brief Clears the CLX draw cache. + * + * Must be called whenever CLX sprites are freed. + */ +void ClearClxDrawCache(); + #ifdef DEBUG_CLX std::string ClxDescribe(ClxSprite clx); #endif