From 680ab5ec40fcc2078ed6ad4a48bf081e68b1c9a1 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sun, 30 Oct 2022 13:56:53 +0000 Subject: [PATCH] Overhaul backbuffer state handling When rendering directly to the output buffer, we need to maintain the state of what has been drawn and what needs redrawing per-buffer. We previously tried to do it implicitly by checking `SDL_DOUBLEBUF` and other flags. The previous implementation was broken in several ways, resulting in rendering issues on devices that support 8-bit output directly. Changes this mechanism to explicitly maintain buffer state per output buffer. The new mechanism doesn't require knowledge of the number of buffers, and thus also works correctly with triple-buffering. Fixes #5447 --- Source/CMakeLists.txt | 1 + Source/DiabloUI/progress.cpp | 13 ++- Source/capture.cpp | 3 +- Source/control.cpp | 32 ++---- Source/control.h | 3 - Source/cursor.cpp | 3 +- Source/debug.cpp | 7 +- Source/diablo.cpp | 34 +++--- Source/diablo.h | 1 - Source/engine/backbuffer_state.cpp | 98 +++++++++++++++++ Source/engine/backbuffer_state.hpp | 40 +++++++ Source/engine/dx.h | 2 + Source/engine/palette.cpp | 3 +- Source/engine/render/scrollrt.cpp | 170 ++++++++++++++--------------- Source/gamemenu.cpp | 11 +- Source/init.cpp | 7 +- Source/interfac.cpp | 12 +- Source/inv.cpp | 8 +- Source/inv.h | 1 - Source/items.cpp | 37 ++++--- Source/missiles.cpp | 15 +-- Source/movie.cpp | 5 +- Source/msg.cpp | 5 +- Source/objects.cpp | 45 ++++---- Source/panels/spell_book.cpp | 3 +- Source/panels/spell_list.cpp | 5 +- Source/player.cpp | 33 +++--- Source/spells.cpp | 13 ++- Source/stores.cpp | 5 +- Source/utils/display.cpp | 14 +-- Source/utils/display.h | 2 - 31 files changed, 386 insertions(+), 245 deletions(-) create mode 100644 Source/engine/backbuffer_state.cpp create mode 100644 Source/engine/backbuffer_state.hpp diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 739a783c2..9fcd48e4d 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -96,6 +96,7 @@ set(libdevilutionx_SRCS engine/actor_position.cpp engine/animationinfo.cpp engine/assets.cpp + engine/backbuffer_state.cpp engine/direction.cpp engine/dx.cpp engine/load_cel.cpp diff --git a/Source/DiabloUI/progress.cpp b/Source/DiabloUI/progress.cpp index 55fc19808..56d4d8cf4 100644 --- a/Source/DiabloUI/progress.cpp +++ b/Source/DiabloUI/progress.cpp @@ -96,14 +96,19 @@ bool UiProgressDialog(int (*fnfunc)()) // Blit the background once and then free it. ProgressLoadBackground(); + ProgressRenderBackground(); - if (RenderDirectlyToOutputSurface && IsDoubleBuffered()) { - // Blit twice for triple buffering. - for (unsigned i = 0; i < 2; ++i) { - UiFadeIn(); + + if (RenderDirectlyToOutputSurface && PalSurface != nullptr) { + // Render into all the backbuffers if there are multiple. + const void *initialPixels = PalSurface->pixels; + UiFadeIn(); + while (PalSurface->pixels != initialPixels) { ProgressRenderBackground(); + UiFadeIn(); } } + ProgressFreeBackground(); ProgressLoadForeground(); diff --git a/Source/capture.cpp b/Source/capture.cpp index 8e5021ab4..c5e855f02 100644 --- a/Source/capture.cpp +++ b/Source/capture.cpp @@ -8,6 +8,7 @@ #include #include "DiabloUI/diabloui.h" +#include "engine/backbuffer_state.hpp" #include "engine/dx.h" #include "engine/palette.h" #include "utils/file_util.h" @@ -195,7 +196,7 @@ void CaptureScreen() system_palette[i] = palette[i]; } palette_update(); - force_redraw = 255; + RedrawEverything(); } } // namespace devilution diff --git a/Source/control.cpp b/Source/control.cpp index a246f6449..041855df3 100644 --- a/Source/control.cpp +++ b/Source/control.cpp @@ -16,6 +16,7 @@ #include "controls/modifier_hints.h" #include "controls/plrctrls.h" #include "cursor.h" +#include "engine/backbuffer_state.hpp" #include "engine/clx_sprite.hpp" #include "engine/load_cel.hpp" #include "engine/render/clx_render.hpp" @@ -57,18 +58,10 @@ namespace devilution { -/** - * @brief Set if the life flask needs to be redrawn during next frame - */ -bool drawhpflag; bool dropGoldFlag; bool chrbtn[4]; bool lvlbtndown; int dropGoldValue; -/** - * @brief Set if the mana flask needs to be redrawn during the next frame - */ -bool drawmanaflag; bool chrbtnactive; UiFlags InfoColor; int sbooktab; @@ -76,7 +69,6 @@ int8_t initialDropGoldIndex; bool talkflag; bool sbookflag; bool chrflag; -bool drawbtnflag; StringOrView InfoString; bool panelflag; int initialDropGoldValue; @@ -251,7 +243,7 @@ void DrawFlaskLower(const Surface &out, const Surface &sourceBuffer, int offset, void SetButtonStateDown(int btnId) { PanelButtons[btnId] = true; - drawbtnflag = true; + RedrawComponent(PanelDrawComponent::ControlButtons); panbtndown = true; } @@ -703,8 +695,8 @@ void InitControlPan() buttonEnabled = false; chrbtnactive = false; InfoString = {}; - drawhpflag = true; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Health); + RedrawComponent(PanelDrawComponent::Mana); chrflag = false; spselflag = false; sbooktab = 0; @@ -758,7 +750,7 @@ void ClearPanBtn() { for (bool &panelButton : PanelButtons) panelButton = false; - drawbtnflag = true; + RedrawComponent(PanelDrawComponent::ControlButtons); panbtndown = false; } @@ -772,7 +764,7 @@ void DoPanBtn() if (MousePosition.x >= PanBtnPos[i].x + mainPanelPosition.x && MousePosition.x <= x) { if (MousePosition.y >= PanBtnPos[i].y + mainPanelPosition.y && MousePosition.y <= y) { PanelButtons[i] = true; - drawbtnflag = true; + RedrawComponent(PanelDrawComponent::ControlButtons); panbtndown = true; } } @@ -782,7 +774,7 @@ void DoPanBtn() Player &myPlayer = *MyPlayer; myPlayer._pRSpell = SPL_INVALID; myPlayer._pRSplType = RSPLTYPE_INVALID; - force_redraw = 255; + RedrawEverything(); return; } DoSpeedBook(); @@ -889,7 +881,7 @@ void CheckBtnUp() bool gamemenuOff = true; const Point mainPanelPosition = GetMainPanel().position; - drawbtnflag = true; + RedrawComponent(PanelDrawComponent::ControlButtons); panbtndown = false; for (int i = 0; i < 8; i++) { @@ -1223,8 +1215,6 @@ void DrawTalkPan(const Surface &out) if (!talkflag) return; - force_redraw = 255; - const Point mainPanelPosition = GetMainPanel().position; DrawPanelBox(out, MakeSdlRect(175, sgbPlrTalkTbl + 20, 294, 5), mainPanelPosition + Displacement { 175, 4 }); @@ -1329,7 +1319,7 @@ void control_type_message() if (!IsChatAvailable()) return; - talkflag = true; + RedrawComponent(PanelDrawComponent::ChatInput); SDL_Rect rect = MakeSdlRect(GetMainPanel().position.x + 200, GetMainPanel().position.y + 22, 250, 39); SDL_SetTextInputRect(&rect); TalkMessage[0] = '\0'; @@ -1337,7 +1327,7 @@ void control_type_message() talkButtonDown = false; } sgbPlrTalkTbl = GetMainPanel().size.height + 16; - force_redraw = 255; + RedrawEverything(); TalkSaveIndex = NextTalkSave; SDL_StartTextInput(); } @@ -1347,7 +1337,7 @@ void control_reset_talk() talkflag = false; SDL_StopTextInput(); sgbPlrTalkTbl = 0; - force_redraw = 255; + RedrawEverything(); } bool IsTalkActive() diff --git a/Source/control.h b/Source/control.h index f260336e7..d54973988 100644 --- a/Source/control.h +++ b/Source/control.h @@ -32,12 +32,10 @@ namespace devilution { constexpr Size SidePanelSize { 320, 352 }; -extern bool drawhpflag; extern bool dropGoldFlag; extern bool chrbtn[4]; extern bool lvlbtndown; extern int dropGoldValue; -extern bool drawmanaflag; extern bool chrbtnactive; extern UiFlags InfoColor; extern int sbooktab; @@ -45,7 +43,6 @@ extern int8_t initialDropGoldIndex; extern bool talkflag; extern bool sbookflag; extern bool chrflag; -extern bool drawbtnflag; extern StringOrView InfoString; extern bool panelflag; extern int initialDropGoldValue; diff --git a/Source/cursor.cpp b/Source/cursor.cpp index 9d85f74ce..23f23f4e0 100644 --- a/Source/cursor.cpp +++ b/Source/cursor.cpp @@ -12,6 +12,7 @@ #include "controls/plrctrls.h" #include "doom.h" #include "engine.h" +#include "engine/backbuffer_state.hpp" #include "engine/load_cel.hpp" #include "engine/point.hpp" #include "engine/render/clx_render.hpp" @@ -381,7 +382,7 @@ void CheckCursMove() ObjectUnderCursor = nullptr; pcursitem = -1; if (pcursinvitem != -1) { - drawsbarflag = true; + RedrawComponent(PanelDrawComponent::Belt); } pcursinvitem = -1; pcursstashitem = uint16_t(-1); diff --git a/Source/debug.cpp b/Source/debug.cpp index c2c1a315b..43d3323ca 100644 --- a/Source/debug.cpp +++ b/Source/debug.cpp @@ -14,6 +14,7 @@ #include "automap.h" #include "control.h" #include "cursor.h" +#include "engine/backbuffer_state.hpp" #include "engine/load_cel.hpp" #include "engine/point.hpp" #include "error.h" @@ -550,8 +551,8 @@ std::string DebugCmdRefillHealthMana(const string_view parameter) Player &myPlayer = *MyPlayer; myPlayer.RestoreFullLife(); myPlayer.RestoreFullMana(); - drawhpflag = true; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Health); + RedrawComponent(PanelDrawComponent::Mana); return "Ready for more."; } @@ -589,7 +590,7 @@ std::string DebugCmdChangeMana(const string_view parameter) int newMana = myPlayer._pMana + (change * 64); myPlayer._pMana = newMana; myPlayer._pManaBase = myPlayer._pMana + myPlayer._pMaxManaBase - myPlayer._pMaxMana; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); return "Mana has changed."; } diff --git a/Source/diablo.cpp b/Source/diablo.cpp index e9f138081..15e616dda 100644 --- a/Source/diablo.cpp +++ b/Source/diablo.cpp @@ -25,6 +25,7 @@ #include "discord/discord.h" #include "doom.h" #include "encrypt.h" +#include "engine/backbuffer_state.hpp" #include "engine/clx_sprite.hpp" #include "engine/demomode.h" #include "engine/dx.h" @@ -105,7 +106,6 @@ bool ReturnToMainMenu; bool gbProcessPlayers; bool gbLoadGame; bool cineflag; -int force_redraw; int PauseMode; bool gbBard; bool gbBarbarian; @@ -202,7 +202,7 @@ bool ProcessInput() plrctrls_every_frame(); if (!gbIsMultiplayer && gmenu_is_active()) { - force_redraw |= 1; + RedrawViewport(); return false; } @@ -704,7 +704,7 @@ void GameEventHandler(const SDL_Event &event, uint16_t modState) LastMouseButtonAction = MouseActionType::None; sgbMouseDown = CLICK_NONE; ShowProgress(GetCustomEvent(event.type)); - force_redraw = 255; + RedrawEverything(); DrawAndBlit(); LoadPWaterPalette(); if (gbRunGame) @@ -730,11 +730,20 @@ void RunGameLoop(interface_mode uMsg) gbRunGame = true; gbProcessPlayers = true; gbRunGameResult = true; - force_redraw = 255; - DrawAndBlit(); + + RedrawEverything(); + if (!HeadlessMode) { + while (IsRedrawEverything()) { + // In direct rendering mode with double/triple buffering, we need + // to prepare all buffers before fading in. + DrawAndBlit(); + } + } + LoadPWaterPalette(); PaletteFadeIn(8); - force_redraw = 255; + InitBackbufferState(); + RedrawEverything(); gbGameLoopStartup = true; nthread_ignore_mutex(false); @@ -780,7 +789,7 @@ void RunGameLoop(interface_mode uMsg) ProcessInput(); if (!drawGame) continue; - force_redraw |= 1; + RedrawViewport(); DrawAndBlit(); continue; } @@ -807,7 +816,7 @@ void RunGameLoop(interface_mode uMsg) PaletteFadeOut(8); NewCursor(CURSOR_NONE); ClearScreenBuffer(); - force_redraw = 255; + RedrawEverything(); scrollrt_draw_game_screen(); previousHandler = SetEventHandler(previousHandler); assert(HeadlessMode || previousHandler == GameEventHandler); @@ -1324,7 +1333,7 @@ void GameLogic() sound_update(); CheckTriggers(); CheckQuests(); - force_redraw |= 1; + RedrawViewport(); pfile_update(false); plrctrls_after_game_logic(); @@ -1340,7 +1349,7 @@ void TimeoutCursor(bool bTimeout) AddPanelString(_("-- Network timeout --")); AddPanelString(_("-- Waiting for players --")); NewCursor(CURSOR_HOURGLASS); - force_redraw = 255; + RedrawEverything(); } scrollrt_draw_game_screen(); } else if (sgnTimeoutCurs != CURSOR_NONE) { @@ -1351,7 +1360,7 @@ void TimeoutCursor(bool bTimeout) NewCursor(sgnTimeoutCurs); sgnTimeoutCurs = CURSOR_NONE; InfoString = {}; - force_redraw = 255; + RedrawEverything(); } } @@ -2427,7 +2436,7 @@ void diablo_pause_game() LastMouseButtonAction = MouseActionType::None; } - force_redraw = 255; + RedrawEverything(); } } @@ -2584,7 +2593,6 @@ void LoadGameLevel(bool firstflag, lvl_entry lvldir) if (firstflag) { CloseInventory(); - drawsbarflag = false; qtextflag = false; if (!HeadlessMode) { InitInv(); diff --git a/Source/diablo.h b/Source/diablo.h index d882008db..c1d1d130d 100644 --- a/Source/diablo.h +++ b/Source/diablo.h @@ -66,7 +66,6 @@ extern bool ReturnToMainMenu; extern bool gbProcessPlayers; extern DVL_API_FOR_TEST bool gbLoadGame; extern bool cineflag; -extern int force_redraw; /* These are defined in fonts.h */ extern void FontsCleanup(); extern DVL_API_FOR_TEST int PauseMode; diff --git a/Source/engine/backbuffer_state.cpp b/Source/engine/backbuffer_state.cpp new file mode 100644 index 000000000..631c3564a --- /dev/null +++ b/Source/engine/backbuffer_state.cpp @@ -0,0 +1,98 @@ +#include "engine/backbuffer_state.hpp" + +#include + +#include "engine/dx.h" +#include "utils/enum_traits.h" + +namespace devilution { +namespace { + +struct RedrawState { + enum { + RedrawNone, + RedrawViewportOnly, + RedrawAll + } Redraw; + std::array::value> redrawComponents; +}; + +struct BackbufferState { + RedrawState redrawState; + DrawnCursor cursor; +}; + +std::unordered_map States; + +BackbufferState &GetBackbufferState() +{ + // `PalSurface` is null in headless mode. + void *ptr = PalSurface != nullptr ? PalSurface->pixels : nullptr; + auto result = States.emplace(std::piecewise_construct, std::forward_as_tuple(ptr), std::forward_as_tuple()); + BackbufferState &state = result.first->second; + if (result.second) + state.redrawState.Redraw = RedrawState::RedrawAll; + return state; +} + +} // namespace + +bool IsRedrawEverything() +{ + return GetBackbufferState().redrawState.Redraw == RedrawState::RedrawAll; +} + +void RedrawViewport() +{ + for (auto &&it : States) { + if (it.second.redrawState.Redraw != RedrawState::RedrawAll) { + it.second.redrawState.Redraw = RedrawState::RedrawViewportOnly; + } + } +} + +bool IsRedrawViewport() +{ + return GetBackbufferState().redrawState.Redraw == RedrawState::RedrawViewportOnly; +} + +void RedrawComplete() +{ + GetBackbufferState().redrawState.Redraw = RedrawState::RedrawNone; +} + +void RedrawEverything() +{ + for (auto &&it : States) { + it.second.redrawState.Redraw = RedrawState::RedrawAll; + } +} + +void InitBackbufferState() +{ + States.clear(); +} + +void RedrawComponent(PanelDrawComponent component) +{ + for (auto &&it : States) { + it.second.redrawState.redrawComponents[static_cast(component)] = true; + } +} + +bool IsRedrawComponent(PanelDrawComponent component) +{ + return GetBackbufferState().redrawState.redrawComponents[static_cast(component)]; +} + +void RedrawComponentComplete(PanelDrawComponent component) +{ + GetBackbufferState().redrawState.redrawComponents[static_cast(component)] = false; +} + +DrawnCursor &GetDrawnCursor() +{ + return GetBackbufferState().cursor; +} + +} // namespace devilution diff --git a/Source/engine/backbuffer_state.hpp b/Source/engine/backbuffer_state.hpp new file mode 100644 index 000000000..2cb59bf19 --- /dev/null +++ b/Source/engine/backbuffer_state.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include + +#include "engine/rectangle.hpp" +#include "engine/surface.hpp" + +namespace devilution { + +enum class PanelDrawComponent { + Health, + Mana, + ControlButtons, + Belt, + ChatInput, + + FIRST = Health, + LAST = ChatInput +}; + +struct DrawnCursor { + Rectangle rect; + uint8_t behindBuffer[8192]; +}; + +void InitBackbufferState(); + +void RedrawEverything(); +bool IsRedrawEverything(); +void RedrawViewport(); +bool IsRedrawViewport(); +void RedrawComplete(); + +void RedrawComponent(PanelDrawComponent component); +bool IsRedrawComponent(PanelDrawComponent component); +void RedrawComponentComplete(PanelDrawComponent component); + +DrawnCursor &GetDrawnCursor(); + +} // namespace devilution diff --git a/Source/engine/dx.h b/Source/engine/dx.h index edca17ad1..05a9dc352 100644 --- a/Source/engine/dx.h +++ b/Source/engine/dx.h @@ -12,6 +12,8 @@ namespace devilution { /** Whether we render directly to the screen surface, i.e. `PalSurface == GetOutputSurface()` */ extern bool RenderDirectlyToOutputSurface; +extern SDL_Surface *PalSurface; + Surface GlobalBackBuffer(); void dx_init(); diff --git a/Source/engine/palette.cpp b/Source/engine/palette.cpp index 8506827f1..64b2cc228 100644 --- a/Source/engine/palette.cpp +++ b/Source/engine/palette.cpp @@ -7,6 +7,7 @@ #include +#include "engine/backbuffer_state.hpp" #include "engine/dx.h" #include "engine/load_file.hpp" #include "engine/random.hpp" @@ -195,7 +196,7 @@ void ApplyGamma(SDL_Color *dst, const SDL_Color *src, int n) dst[i].g = static_cast(pow(src[i].g / 256.0, g) * 256.0); dst[i].b = static_cast(pow(src[i].b / 256.0, g) * 256.0); } - force_redraw = 255; + RedrawEverything(); } void palette_init() diff --git a/Source/engine/render/scrollrt.cpp b/Source/engine/render/scrollrt.cpp index 75baa4034..bc46c6f5e 100644 --- a/Source/engine/render/scrollrt.cpp +++ b/Source/engine/render/scrollrt.cpp @@ -11,6 +11,7 @@ #include "cursor.h" #include "dead.h" #include "doom.h" +#include "engine/backbuffer_state.hpp" #include "engine/dx.h" #include "engine/render/clx_render.hpp" #include "engine/render/dun_render.hpp" @@ -91,6 +92,7 @@ extern void DrawControllerModifierHints(const Surface &out); bool frameflag; namespace { + /** * @brief Hash algorithm for point */ @@ -186,21 +188,6 @@ void UpdateMissilesRendererData() } } -uint32_t sgdwCursWdtOld; -int sgdwCursX; -int sgdwCursY; -/** - * Lower bound of back buffer. - */ -uint32_t sgdwCursHgt; - -int sgdwCursXOld; -int sgdwCursYOld; - -uint32_t sgdwCursWdt; -uint8_t sgSaveBack[8192]; -uint32_t sgdwCursHgtOld; - /** * @brief Keeps track of which tiles have been rendered already. */ @@ -223,10 +210,12 @@ const char *const PlayerModeNames[] = { "quitting" }; -void BlitCursor(uint8_t *dst, std::uint32_t dstPitch, uint8_t *src, std::uint32_t srcPitch) +Rectangle PrevCursorRect; + +void BlitCursor(uint8_t *dst, uint32_t dstPitch, uint8_t *src, uint32_t srcPitch, uint32_t srcWidth, uint32_t srcHeight) { - for (std::uint32_t i = 0; i < sgdwCursHgt; ++i, src += srcPitch, dst += dstPitch) { - memcpy(dst, src, sgdwCursWdt); + for (std::uint32_t i = 0; i < srcHeight; ++i, src += srcPitch, dst += dstPitch) { + memcpy(dst, src, srcWidth); } } @@ -235,17 +224,9 @@ void BlitCursor(uint8_t *dst, std::uint32_t dstPitch, uint8_t *src, std::uint32_ */ void UndrawCursor(const Surface &out) { - if (sgdwCursWdt == 0) { - return; - } - - BlitCursor(out.at(sgdwCursX, sgdwCursY), out.pitch(), sgSaveBack, sgdwCursWdt); - - sgdwCursXOld = sgdwCursX; - sgdwCursYOld = sgdwCursY; - sgdwCursWdtOld = sgdwCursWdt; - sgdwCursHgtOld = sgdwCursHgt; - sgdwCursWdt = 0; + DrawnCursor &cursor = GetDrawnCursor(); + BlitCursor(&out[cursor.rect.position], out.pitch(), cursor.behindBuffer, cursor.rect.size.width, cursor.rect.size.width, cursor.rect.size.height); + PrevCursorRect = cursor.rect; } bool ShouldShowCursor() @@ -267,23 +248,23 @@ bool ShouldShowCursor() */ void DrawCursor(const Surface &out) { + DrawnCursor &cursor = GetDrawnCursor(); if (pcurs <= CURSOR_NONE || !ShouldShowCursor()) { + cursor.rect.size.width = 0; return; } Size cursSize = GetInvItemSize(pcurs); if (cursSize.width == 0 || cursSize.height == 0) { + cursor.rect.size.width = 0; return; } - // Copy the buffer before the item cursor and its 1px outline are drawn to a temporary buffer. - const int outlineWidth = !MyPlayer->HoldItem.isEmpty() ? 1 : 0; - - if (MousePosition.x < -cursSize.width - outlineWidth || MousePosition.x - outlineWidth >= out.w() || MousePosition.y < -cursSize.height - outlineWidth || MousePosition.y - outlineWidth >= out.h()) - return; - - constexpr auto Clip = [](int &pos, std::uint32_t &length, std::uint32_t posEnd) { - if (pos < 0) { + constexpr auto Clip = [](int &pos, int &length, int posEnd) { + if (pos + length <= 0 || pos >= posEnd) { + pos = 0; + length = 0; + } else if (pos < 0) { length += pos; pos = 0; } else if (pos + length > posEnd) { @@ -291,15 +272,22 @@ void DrawCursor(const Surface &out) } }; - sgdwCursX = MousePosition.x - outlineWidth; - sgdwCursWdt = cursSize.width + 2 * outlineWidth; - Clip(sgdwCursX, sgdwCursWdt, out.w()); + // Copy the buffer before the item cursor and its 1px outline are drawn to a temporary buffer. + const int outlineWidth = !MyPlayer->HoldItem.isEmpty() ? 1 : 0; + + Rectangle &rect = cursor.rect; + rect.position.x = MousePosition.x - outlineWidth; + rect.size.width = cursSize.width + 2 * outlineWidth; + Clip(rect.position.x, rect.size.width, out.w()); - sgdwCursY = MousePosition.y - outlineWidth; - sgdwCursHgt = cursSize.height + 2 * outlineWidth; - Clip(sgdwCursY, sgdwCursHgt, out.h()); + rect.position.y = MousePosition.y - outlineWidth; + rect.size.height = cursSize.height + 2 * outlineWidth; + Clip(rect.position.y, rect.size.height, out.h()); - BlitCursor(sgSaveBack, sgdwCursWdt, out.at(sgdwCursX, sgdwCursY), out.pitch()); + if (rect.size.width == 0 || rect.size.height == 0) + return; + + BlitCursor(cursor.behindBuffer, rect.size.width, &out[rect.position], out.pitch(), rect.size.width, rect.size.height); DrawSoftwareCursor(out, MousePosition + Displacement { 0, cursSize.height - 1 }, pcurs); } @@ -1058,7 +1046,7 @@ void DrawView(const Surface &out, Point startPosition) bool debugGridTextNeeded = IsDebugGridTextNeeded(); if (debugGridTextNeeded || DebugGrid) { // force redrawing or debug stuff stays on panel on 640x480 resolution - force_redraw = 255; + RedrawEverything(); char debugGridTextBuffer[10]; bool megaTiles = IsDebugGridInMegatiles(); @@ -1225,7 +1213,8 @@ void DoBlitScreen(Sint16 dwX, Sint16 dwY, Uint16 dwWdt, Uint16 dwHgt) } /** - * @brief Check render pipeline and blit individual screen parts + * @brief Check render pipeline and update individual screen parts + * @param out Output surface. * @param dwHgt Section of screen to update from top to bottom * @param drawDesc Render info box * @param drawHp Render health bar @@ -1233,7 +1222,7 @@ void DoBlitScreen(Sint16 dwX, Sint16 dwY, Uint16 dwWdt, Uint16 dwHgt) * @param drawSbar Render belt * @param drawBtn Render panel buttons */ -void DrawMain(int dwHgt, bool drawDesc, bool drawHp, bool drawMana, bool drawSbar, bool drawBtn) +void DrawMain(const Surface &out, int dwHgt, bool drawDesc, bool drawHp, bool drawMana, bool drawSbar, bool drawBtn) { if (!gbActive || RenderDirectlyToOutputSurface) { return; @@ -1267,11 +1256,12 @@ void DrawMain(int dwHgt, bool drawDesc, bool drawHp, bool drawMana, bool drawSba DoBlitScreen(mainPanelPosition.x + 524, mainPanelPosition.y + 91, 36, 32); } } - if (sgdwCursWdtOld != 0) { - DoBlitScreen(sgdwCursXOld, sgdwCursYOld, sgdwCursWdtOld, sgdwCursHgtOld); + if (PrevCursorRect.size.width != 0 && PrevCursorRect.size.height != 0) { + DoBlitScreen(PrevCursorRect.position.x, PrevCursorRect.position.y, PrevCursorRect.size.width, PrevCursorRect.size.height); } - if (sgdwCursWdt != 0) { - DoBlitScreen(sgdwCursX, sgdwCursY, sgdwCursWdt, sgdwCursHgt); + Rectangle &cursorRect = GetDrawnCursor().rect; + if (cursorRect.size.width != 0 && cursorRect.size.height != 0) { + DoBlitScreen(cursorRect.position.x, cursorRect.position.y, cursorRect.size.width, cursorRect.size.height); } } } @@ -1301,8 +1291,7 @@ Displacement GetOffsetForWalking(const AnimationInfo &animationInfo, const Direc void ClearCursor() // CODE_FIX: this was supposed to be in cursor.cpp { - sgdwCursWdt = 0; - sgdwCursWdtOld = 0; + PrevCursorRect = {}; } void ShiftGrid(int *x, int *y, int horizontal, int vertical) @@ -1510,24 +1499,23 @@ void scrollrt_draw_game_screen() int hgt = 0; - if (force_redraw == 255) { - force_redraw = 0; + if (IsRedrawEverything()) { + RedrawComplete(); hgt = gnScreenHeight; } + const Surface &out = GlobalBackBuffer(); + UndrawCursor(out); + + DrawMain(out, hgt, false, false, false, false, false); + if (IsHardwareCursor()) { SetHardwareCursorVisible(ShouldShowCursor()); } else { - DrawCursor(GlobalBackBuffer()); + DrawCursor(out); } - DrawMain(hgt, false, false, false, false, false); - RenderPresent(); - - if (!IsHardwareCursor()) { - UndrawCursor(GlobalBackBuffer()); - } } void DrawAndBlit() @@ -1537,53 +1525,55 @@ void DrawAndBlit() } int hgt = 0; - bool ddsdesc = false; - bool ctrlPan = false; + bool drawHealth = IsRedrawComponent(PanelDrawComponent::Health); + bool drawMana = IsRedrawComponent(PanelDrawComponent::Mana); + bool drawControlButtons = IsRedrawComponent(PanelDrawComponent::ControlButtons); + bool drawBelt = IsRedrawComponent(PanelDrawComponent::Belt); + bool drawChatInput = IsRedrawComponent(PanelDrawComponent::ChatInput); + bool drawInfoBox = false; + bool drawCtrlPan = false; const Rectangle &mainPanel = GetMainPanel(); - if (gnScreenWidth > mainPanel.size.width || force_redraw == 255 || IsHighlightingLabelsEnabled()) { - drawhpflag = true; - drawmanaflag = true; - drawbtnflag = true; - drawsbarflag = true; - ddsdesc = false; - ctrlPan = true; + if (gnScreenWidth > mainPanel.size.width || IsRedrawEverything() || IsHighlightingLabelsEnabled()) { + drawHealth = true; + drawMana = true; + drawControlButtons = true; + drawBelt = true; + drawInfoBox = false; + drawCtrlPan = true; hgt = gnScreenHeight; - } else if (force_redraw == 1) { - ddsdesc = true; - ctrlPan = false; + } else if (IsRedrawViewport()) { + drawInfoBox = true; + drawCtrlPan = false; hgt = gnViewportHeight; } - force_redraw = 0; - const Surface &out = GlobalBackBuffer(); UndrawCursor(out); nthread_UpdateProgressToNextGameTick(); DrawView(out, ViewPosition); - if (ctrlPan) { + if (drawCtrlPan) { DrawCtrlPan(out); } - if (drawhpflag) { + if (drawHealth) { DrawLifeFlaskLower(out); } - if (drawmanaflag) { + if (drawMana) { DrawManaFlaskLower(out); DrawSpell(out); } - if (drawbtnflag) { + if (drawControlButtons) { DrawCtrlBtns(out); } - if (drawsbarflag) { + if (drawBelt) { DrawInvBelt(out); } - if (talkflag) { + if (drawChatInput) { DrawTalkPan(out); - hgt = gnScreenHeight; } DrawXPBar(out); if (*sgOptions.Graphics.showHealthValues) @@ -1599,14 +1589,16 @@ void DrawAndBlit() DrawFPS(out); - DrawMain(hgt, ddsdesc, drawhpflag, drawmanaflag, drawsbarflag, drawbtnflag); + DrawMain(out, hgt, drawInfoBox, drawHealth, drawMana, drawBelt, drawControlButtons); - RenderPresent(); + RedrawComplete(); + for (PanelDrawComponent component : enum_values()) { + if (IsRedrawComponent(component)) { + RedrawComponentComplete(component); + } + } - drawhpflag = false; - drawmanaflag = false; - drawbtnflag = false; - drawsbarflag = false; + RenderPresent(); } } // namespace devilution diff --git a/Source/gamemenu.cpp b/Source/gamemenu.cpp index f051e0ebd..acd6db70c 100644 --- a/Source/gamemenu.cpp +++ b/Source/gamemenu.cpp @@ -6,6 +6,7 @@ #include "gamemenu.h" #include "cursor.h" +#include "engine/backbuffer_state.hpp" #include "engine/sound.h" #include "engine/sound_defs.hpp" #include "error.h" @@ -102,7 +103,7 @@ void GamemenuNewGame(bool /*bActivate*/) MyPlayerIsDead = false; if (!HeadlessMode) { - force_redraw = 255; + RedrawEverything(); scrollrt_draw_game_screen(); } CornerStone.activated = false; @@ -293,14 +294,14 @@ void gamemenu_load_game(bool /*bActivate*/) gamemenu_off(); NewCursor(CURSOR_NONE); InitDiabloMsg(EMSG_LOADING); - force_redraw = 255; + RedrawEverything(); DrawAndBlit(); LoadGame(false); ClrDiabloMsg(); CornerStone.activated = false; PaletteFadeOut(8); MyPlayerIsDead = false; - force_redraw = 255; + RedrawEverything(); DrawAndBlit(); LoadPWaterPalette(); PaletteFadeIn(8); @@ -324,11 +325,11 @@ void gamemenu_save_game(bool /*bActivate*/) NewCursor(CURSOR_NONE); gamemenu_off(); InitDiabloMsg(EMSG_SAVING); - force_redraw = 255; + RedrawEverything(); DrawAndBlit(); SaveGame(); ClrDiabloMsg(); - force_redraw = 255; + RedrawEverything(); NewCursor(CURSOR_HAND); if (CornerStone.activated) { CornerstoneSave(); diff --git a/Source/init.cpp b/Source/init.cpp index 3e85bbd80..eed425fb2 100644 --- a/Source/init.cpp +++ b/Source/init.cpp @@ -14,6 +14,7 @@ #include "DiabloUI/diabloui.h" #include "engine/assets.hpp" +#include "engine/backbuffer_state.hpp" #include "engine/dx.h" #include "hwcursor.hpp" #include "miniwin/misc_msg.h" @@ -332,10 +333,10 @@ void MainWndProc(const SDL_Event &event) break; case SDL_WINDOWEVENT_SHOWN: gbActive = false; - force_redraw = 255; + RedrawEverything(); break; case SDL_WINDOWEVENT_EXPOSED: - force_redraw = 255; + RedrawEverything(); break; case SDL_WINDOWEVENT_SIZE_CHANGED: ReinitializeHardwareCursor(); @@ -343,7 +344,7 @@ void MainWndProc(const SDL_Event &event) case SDL_WINDOWEVENT_LEAVE: sgbMouseDown = CLICK_NONE; LastMouseButtonAction = MouseActionType::None; - force_redraw = 255; + RedrawEverything(); break; case SDL_WINDOWEVENT_CLOSE: diablo_quit(0); diff --git a/Source/interfac.cpp b/Source/interfac.cpp index ff003720e..d5203e396 100644 --- a/Source/interfac.cpp +++ b/Source/interfac.cpp @@ -293,13 +293,17 @@ void ShowProgress(interface_mode uMsg) // Blit the background once and then free it. LoadCutsceneBackground(uMsg); DrawCutsceneBackground(); - if (RenderDirectlyToOutputSurface && IsDoubleBuffered()) { - // Blit twice for triple buffering. - for (unsigned i = 0; i < 2; ++i) { + if (RenderDirectlyToOutputSurface && PalSurface != nullptr) { + // Render into all the backbuffers if there are multiple. + const void *initialPixels = PalSurface->pixels; + if (DiabloUiSurface() == PalSurface) + BltFast(nullptr, nullptr); + RenderPresent(); + while (PalSurface->pixels != initialPixels) { + DrawCutsceneBackground(); if (DiabloUiSurface() == PalSurface) BltFast(nullptr, nullptr); RenderPresent(); - DrawCutsceneBackground(); } } FreeCutsceneBackground(); diff --git a/Source/inv.cpp b/Source/inv.cpp index a97cea96e..f6f0f92ce 100644 --- a/Source/inv.cpp +++ b/Source/inv.cpp @@ -11,6 +11,7 @@ #include "DiabloUI/ui_flags.hpp" #include "controls/plrctrls.h" #include "cursor.h" +#include "engine/backbuffer_state.hpp" #include "engine/clx_sprite.hpp" #include "engine/load_cel.hpp" #include "engine/render/clx_render.hpp" @@ -37,7 +38,6 @@ namespace devilution { bool invflag; -bool drawsbarflag; /** * Maps from inventory slot to screen position. The inventory slots are @@ -566,7 +566,7 @@ void CheckInvPaste(Player &player, Point cursorPosition) if (&player == MyPlayer) { NetSendCmdChBeltItem(false, ii); } - drawsbarflag = true; + RedrawComponent(PanelDrawComponent::Belt); } break; case ILOC_NONE: case ILOC_INVALID: @@ -1235,7 +1235,7 @@ bool AutoPlaceItemInBelt(Player &player, const Item &item, bool persistItem) if (persistItem) { beltItem = item; player.CalcScrolls(); - drawsbarflag = true; + RedrawComponent(PanelDrawComponent::Belt); if (&player == MyPlayer) { size_t beltIndex = std::distance(&player.SpdList[0], &beltItem); NetSendCmdChBeltItem(false, beltIndex); @@ -1907,7 +1907,7 @@ int8_t CheckInvHLight() pi = &myPlayer.InvList[ii]; } else if (r >= SLOTXY_BELT_FIRST) { r -= SLOTXY_BELT_FIRST; - drawsbarflag = true; + RedrawComponent(PanelDrawComponent::Belt); pi = &myPlayer.SpdList[r]; if (pi->isEmpty()) return -1; diff --git a/Source/inv.h b/Source/inv.h index e18501bae..ded2b67dd 100644 --- a/Source/inv.h +++ b/Source/inv.h @@ -83,7 +83,6 @@ enum item_color : uint8_t { }; extern bool invflag; -extern bool drawsbarflag; extern const Point InvRect[73]; void InvDrawSlotBack(const Surface &out, Point targetPosition, Size size, item_quality itemQuality); diff --git a/Source/items.cpp b/Source/items.cpp index 2e6b7e918..7ca1fcb0a 100644 --- a/Source/items.cpp +++ b/Source/items.cpp @@ -20,6 +20,7 @@ #include "controls/plrctrls.h" #include "cursor.h" #include "doom.h" +#include "engine/backbuffer_state.hpp" #include "engine/clx_sprite.hpp" #include "engine/dx.h" #include "engine/load_cel.hpp" @@ -823,11 +824,11 @@ int SaveItemPower(const Player &player, Item &item, ItemPower &power) break; case IPL_MANA: item._iPLMana += r << 6; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); break; case IPL_MANA_CURSE: item._iPLMana -= r << 6; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); break; case IPL_DUR: { int bonus = r * item._iMaxDur / 100; @@ -883,7 +884,7 @@ int SaveItemPower(const Player &player, Item &item, ItemPower &power) break; case IPL_NOMANA: item._iFlags |= ItemSpecialEffect::NoMana; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); break; case IPL_ABSHALFTRAP: item._iFlags |= ItemSpecialEffect::HalfTrapDamage; @@ -902,14 +903,14 @@ int SaveItemPower(const Player &player, Item &item, ItemPower &power) item._iFlags |= ItemSpecialEffect::StealMana3; if (power.param1 == 5) item._iFlags |= ItemSpecialEffect::StealMana5; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); break; case IPL_STEALLIFE: if (power.param1 == 3) item._iFlags |= ItemSpecialEffect::StealLife3; if (power.param1 == 5) item._iFlags |= ItemSpecialEffect::StealLife5; - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); break; case IPL_TARGAC: if (gbIsHellfire) @@ -2719,8 +2720,8 @@ void CalcPlrItemVals(Player &player, bool loadgfx) } } - drawmanaflag = true; - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Mana); + RedrawComponent(PanelDrawComponent::Health); } void CalcPlrInv(Player &player, bool loadgfx) @@ -3813,25 +3814,25 @@ void UseItem(size_t pnum, item_misc_id mid, spell_id spl) case IMISC_HEAL: player.RestorePartialLife(); if (&player == MyPlayer) { - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); } break; case IMISC_FULLHEAL: player.RestoreFullLife(); if (&player == MyPlayer) { - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); } break; case IMISC_MANA: player.RestorePartialMana(); if (&player == MyPlayer) { - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); } break; case IMISC_FULLMANA: player.RestoreFullMana(); if (&player == MyPlayer) { - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); } break; case IMISC_ELIXSTR: @@ -3842,7 +3843,7 @@ void UseItem(size_t pnum, item_misc_id mid, spell_id spl) if (gbIsHellfire) { player.RestoreFullMana(); if (&player == MyPlayer) { - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); } } break; @@ -3854,7 +3855,7 @@ void UseItem(size_t pnum, item_misc_id mid, spell_id spl) if (gbIsHellfire) { player.RestoreFullLife(); if (&player == MyPlayer) { - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); } } break; @@ -3862,16 +3863,16 @@ void UseItem(size_t pnum, item_misc_id mid, spell_id spl) player.RestorePartialLife(); player.RestorePartialMana(); if (&player == MyPlayer) { - drawhpflag = true; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Health); + RedrawComponent(PanelDrawComponent::Mana); } } break; case IMISC_FULLREJUV: player.RestoreFullLife(); player.RestoreFullMana(); if (&player == MyPlayer) { - drawhpflag = true; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Health); + RedrawComponent(PanelDrawComponent::Mana); } break; case IMISC_SCROLL: @@ -3910,7 +3911,7 @@ void UseItem(size_t pnum, item_misc_id mid, spell_id spl) Stash.RefreshItemStatFlags(); } } - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); break; case IMISC_MAPOFDOOM: doom_init(); diff --git a/Source/missiles.cpp b/Source/missiles.cpp index a7f7a32da..57ebfcdf1 100644 --- a/Source/missiles.cpp +++ b/Source/missiles.cpp @@ -14,6 +14,7 @@ #ifdef _DEBUG #include "debug.h" #endif +#include "engine/backbuffer_state.hpp" #include "engine/load_file.hpp" #include "engine/points_in_rectangle_range.hpp" #include "engine/random.hpp" @@ -1323,7 +1324,7 @@ void AddStealPotions(Missile &missile, AddMissileParameter & /*parameter*/) hasPlayedSFX = true; } } - force_redraw = 255; + RedrawEverything(); return false; }); @@ -1344,7 +1345,7 @@ void AddManaTrap(Missile &missile, AddMissileParameter & /*parameter*/) player._pMana = 0; player._pManaBase = player._pMana + player._pMaxManaBase - player._pMaxMana; CalcPlrInv(player, false); - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); PlaySfxLoc(TSFX_COW7, *trappedPlayerPosition); } @@ -1535,7 +1536,7 @@ void AddMana(Missile &missile, AddMissileParameter & /*parameter*/) if (player._pManaBase > player._pMaxManaBase) player._pManaBase = player._pMaxManaBase; missile._miDelFlag = true; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); } void AddMagi(Missile &missile, AddMissileParameter & /*parameter*/) @@ -1545,7 +1546,7 @@ void AddMagi(Missile &missile, AddMissileParameter & /*parameter*/) player._pMana = player._pMaxMana; player._pManaBase = player._pMaxManaBase; missile._miDelFlag = true; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); } void AddRing(Missile &missile, AddMissileParameter & /*parameter*/) @@ -2264,7 +2265,7 @@ void AddHeal(Missile &missile, AddMissileParameter & /*parameter*/) player._pHPBase = std::min(player._pHPBase + hp, player._pMaxHPBase); missile._miDelFlag = true; - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); } void AddHealOther(Missile &missile, AddMissileParameter & /*parameter*/) @@ -2394,7 +2395,7 @@ void AddBlodboil(Missile &missile, AddMissileParameter ¶meter) int lvl = player._pLevel * 2; missile._mirange = lvl + 10 * missile._mispllvl + 245; CalcPlrItemVals(player, true); - force_redraw = 255; + RedrawEverything(); player.Say(HeroSpeech::Aaaaargh); } @@ -3788,7 +3789,7 @@ void MI_Blodboil(Missile &missile) CalcPlrItemVals(player, true); ApplyPlrDamage(player, 0, 1, hpdif); - force_redraw = 255; + RedrawEverything(); player.Say(HeroSpeech::HeavyBreathing); } diff --git a/Source/movie.cpp b/Source/movie.cpp index 6a4dbe2a1..2f1700deb 100644 --- a/Source/movie.cpp +++ b/Source/movie.cpp @@ -7,6 +7,7 @@ #include "controls/plrctrls.h" #include "diablo.h" #include "effects.h" +#include "engine/backbuffer_state.hpp" #include "engine/demomode.h" #include "engine/sound.h" #include "hwcursor.hpp" @@ -74,10 +75,10 @@ void PlayInGameMovie(const char *pszMovie) PaletteFadeOut(8); play_movie(pszMovie, false); ClearScreenBuffer(); - force_redraw = 255; + RedrawEverything(); scrollrt_draw_game_screen(); PaletteFadeIn(8); - force_redraw = 255; + RedrawEverything(); } } // namespace devilution diff --git a/Source/msg.cpp b/Source/msg.cpp index 5b948a985..771a7555a 100644 --- a/Source/msg.cpp +++ b/Source/msg.cpp @@ -16,6 +16,7 @@ #include "control.h" #include "dead.h" #include "encrypt.h" +#include "engine/backbuffer_state.hpp" #include "engine/random.hpp" #include "engine/world_tile.hpp" #include "gamemenu.h" @@ -2235,7 +2236,7 @@ size_t OnString(const TCmd *pCmd, Player &player) size_t OnFriendlyMode(const TCmd *pCmd, Player &player) // NOLINT(misc-unused-parameters) { player.friendlyMode = !player.friendlyMode; - force_redraw = 255; + RedrawEverything(); return sizeof(*pCmd); } @@ -2262,7 +2263,7 @@ size_t OnCheatExperience(const TCmd *pCmd, size_t pnum) // NOLINT(misc-unused-pa else if (Players[pnum]._pLevel < MaxCharacterLevel) { Players[pnum]._pExperience = Players[pnum]._pNextExper; if (*sgOptions.Gameplay.experienceBar) { - force_redraw = 255; + RedrawEverything(); } NextPlrLevel(Players[pnum]); } diff --git a/Source/objects.cpp b/Source/objects.cpp index 2685e9766..813254503 100644 --- a/Source/objects.cpp +++ b/Source/objects.cpp @@ -17,6 +17,7 @@ #ifdef _DEBUG #include "debug.h" #endif +#include "engine/backbuffer_state.hpp" #include "engine/load_cel.hpp" #include "engine/load_file.hpp" #include "engine/points_in_rectangle_range.hpp" @@ -2289,7 +2290,7 @@ void OperateShrineMysterious(Player &player) CheckStats(player); CalcPlrInv(player, true); - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_MYSTERIOUS); } @@ -2432,7 +2433,7 @@ void OperateShrineStone(Player &player) item._iCharges = item._iMaxCharges; } - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_STONE); } @@ -2530,7 +2531,7 @@ void OperateShrineCostOfWisdom(Player &player, spell_id spellId, diablo_message player._pMaxManaBase = 0; } - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(message); } @@ -2555,7 +2556,7 @@ void OperateShrineCryptic(Player &player) InitDiabloMsg(EMSG_SHRINE_CRYPTIC); - force_redraw = 255; + RedrawEverything(); } void OperateShrineEldritch(Player &player) @@ -2586,7 +2587,7 @@ void OperateShrineEldritch(Player &player) } } - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_ELDRITCH); } @@ -2599,7 +2600,7 @@ void OperateShrineEerie(Player &player) ModifyPlrMag(player, 2); CheckStats(player); CalcPlrInv(player, true); - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_EERIE); } @@ -2628,7 +2629,7 @@ void OperateShrineDivine(Player &player, Point spawnPosition) player._pHitPoints = player._pMaxHP; player._pHPBase = player._pMaxHPBase; - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_DIVINE); } @@ -2676,7 +2677,7 @@ void OperateShrineSpooky(const Player &player) myPlayer._pMana = myPlayer._pMaxMana; myPlayer._pManaBase = myPlayer._pMaxManaBase; - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_SPOOKY2); } @@ -2689,7 +2690,7 @@ void OperateShrineAbandoned(Player &player) ModifyPlrDex(player, 2); CheckStats(player); CalcPlrInv(player, true); - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_ABANDONED); } @@ -2702,7 +2703,7 @@ void OperateShrineCreepy(Player &player) ModifyPlrStr(player, 2); CheckStats(player); CalcPlrInv(player, true); - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_CREEPY); } @@ -2715,7 +2716,7 @@ void OperateShrineQuiet(Player &player) ModifyPlrVit(player, 2); CheckStats(player); CalcPlrInv(player, true); - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_QUIET); } @@ -2744,7 +2745,7 @@ void OperateShrineGlimmering(Player &player) } CalcPlrInv(player, true); - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_GLIMMERING); } @@ -2772,7 +2773,7 @@ void OperateShrineTainted(const Player &player) CheckStats(myPlayer); CalcPlrInv(myPlayer, true); - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_TAINTED2); } @@ -2813,7 +2814,7 @@ void OperateShrineOily(Player &player, Point spawnPosition) CheckStats(player); CalcPlrInv(player, true); - force_redraw = 255; + RedrawEverything(); AddMissile( spawnPosition, @@ -2843,7 +2844,7 @@ void OperateShrineGlowing(Player &player) player._pExperience = 0; CheckStats(player); - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_GLOWING); } @@ -2857,7 +2858,7 @@ void OperateShrineMendicant(Player &player) AddPlrExperience(player, player._pLevel, gold); TakePlrsMoney(gold); - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_MENDICANT); } @@ -2884,7 +2885,7 @@ void OperateShrineSparkling(Player &player, Point spawnPosition) 3 * currlevel + 2, 0); - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_SPARKLING); } @@ -2920,7 +2921,7 @@ void OperateShrineShimmering(Player &player) player._pMana = player._pMaxMana; player._pManaBase = player._pMaxManaBase; - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_SHIMMERING); } @@ -2949,7 +2950,7 @@ void OperateShrineSolar(Player &player) CheckStats(player); CalcPlrInv(player, true); - force_redraw = 255; + RedrawEverything(); } void OperateShrineMurphys(Player &player) @@ -3200,7 +3201,7 @@ void OperateGoatShrine(Player &player, Object &object, _sfx_id sType) object._oVar1 = FindValidShrine(); OperateShrine(player, object, sType); object._oAnimDelay = 2; - force_redraw = 255; + RedrawEverything(); } void OperateCauldron(Player &player, Object &object, _sfx_id sType) @@ -3210,7 +3211,7 @@ void OperateCauldron(Player &player, Object &object, _sfx_id sType) OperateShrine(player, object, sType); object._oAnimFrame = 3; object._oAnimFlag = false; - force_redraw = 255; + RedrawEverything(); } bool OperateFountains(Player &player, Object &fountain) @@ -3309,7 +3310,7 @@ bool OperateFountains(Player &player, Object &fountain) default: break; } - force_redraw = 255; + RedrawEverything(); return applied; } diff --git a/Source/panels/spell_book.cpp b/Source/panels/spell_book.cpp index 21510309b..99fcdabea 100644 --- a/Source/panels/spell_book.cpp +++ b/Source/panels/spell_book.cpp @@ -3,6 +3,7 @@ #include #include "control.h" +#include "engine/backbuffer_state.hpp" #include "engine/clx_sprite.hpp" #include "engine/load_cel.hpp" #include "engine/rectangle.hpp" @@ -202,7 +203,7 @@ void CheckSBook() } player._pRSpell = sn; player._pRSplType = st; - force_redraw = 255; + RedrawEverything(); } return; } diff --git a/Source/panels/spell_list.cpp b/Source/panels/spell_list.cpp index 4a3bd68f4..ff04a4fbf 100644 --- a/Source/panels/spell_list.cpp +++ b/Source/panels/spell_list.cpp @@ -4,6 +4,7 @@ #include "control.h" #include "engine.h" +#include "engine/backbuffer_state.hpp" #include "engine/palette.h" #include "engine/render/text_render.hpp" #include "inv_iterators.hpp" @@ -280,7 +281,7 @@ void SetSpell() myPlayer._pRSpell = pSpell; myPlayer._pRSplType = pSplType; - force_redraw = 255; + RedrawEverything(); } void SetSpeedSpell(size_t slot) @@ -331,7 +332,7 @@ void ToggleSpell(size_t slot) if ((spells & GetSpellBitmask(spellId)) != 0) { myPlayer._pRSpell = spellId; myPlayer._pRSplType = myPlayer._pSplTHotKey[slot]; - force_redraw = 255; + RedrawEverything(); } } diff --git a/Source/player.cpp b/Source/player.cpp index f01615229..c6498a2c7 100644 --- a/Source/player.cpp +++ b/Source/player.cpp @@ -16,6 +16,7 @@ #ifdef _DEBUG #include "debug.h" #endif +#include "engine/backbuffer_state.hpp" #include "engine/load_cl2.hpp" #include "engine/load_file.hpp" #include "engine/points_in_rectangle_range.hpp" @@ -841,7 +842,7 @@ bool PlrHitMonst(Player &player, Monster &monster, bool adjacentDamage = false) if (player._pHPBase > player._pMaxHPBase) { player._pHPBase = player._pMaxHPBase; } - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); } if (HasAnyOf(player._pIFlags, ItemSpecialEffect::StealMana3 | ItemSpecialEffect::StealMana5) && HasNoneOf(player._pIFlags, ItemSpecialEffect::NoMana)) { if (HasAnyOf(player._pIFlags, ItemSpecialEffect::StealMana3)) { @@ -858,7 +859,7 @@ bool PlrHitMonst(Player &player, Monster &monster, bool adjacentDamage = false) if (player._pManaBase > player._pMaxManaBase) { player._pManaBase = player._pMaxManaBase; } - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); } if (HasAnyOf(player._pIFlags, ItemSpecialEffect::StealLife3 | ItemSpecialEffect::StealLife5)) { if (HasAnyOf(player._pIFlags, ItemSpecialEffect::StealLife3)) { @@ -875,7 +876,7 @@ bool PlrHitMonst(Player &player, Monster &monster, bool adjacentDamage = false) if (player._pHPBase > player._pMaxHPBase) { player._pHPBase = player._pMaxHPBase; } - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); } #ifdef _DEBUG if (DebugGodMode) { @@ -947,7 +948,7 @@ bool PlrHitPlr(Player &attacker, Player &target) if (attacker._pHPBase > attacker._pMaxHPBase) { attacker._pHPBase = attacker._pMaxHPBase; } - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); } if (&attacker == MyPlayer) { NetSendCmdDamage(true, target.getId(), skdam); @@ -1630,7 +1631,7 @@ void ValidatePlayer() if (myPlayer._pExperience > myPlayer._pNextExper) { myPlayer._pExperience = myPlayer._pNextExper; if (*sgOptions.Gameplay.experienceBar) { - force_redraw = 255; + RedrawEverything(); } } @@ -1828,7 +1829,7 @@ void Player::RemoveSpdBarItem(int iv) SpdList[iv].clear(); CalcScrolls(); - force_redraw = 255; + RedrawEverything(); } [[nodiscard]] size_t Player::getId() const @@ -1974,7 +1975,7 @@ void Player::ReadySpellFromEquipment(inv_body_loc bodyLocation) if (item._itype == ItemType::Staff && IsValidSpell(item._iSpell) && item._iCharges > 0) { _pRSpell = item._iSpell; _pRSplType = RSPLTYPE_CHARGES; - force_redraw = 255; + RedrawEverything(); } } @@ -2629,7 +2630,7 @@ void NextPlrLevel(Player &player) player._pHPBase = player._pMaxHPBase; if (&player == MyPlayer) { - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); } int mana = 128; @@ -2647,7 +2648,7 @@ void NextPlrLevel(Player &player) } if (&player == MyPlayer) { - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); } if (ControlMode != ControlTypes::KeyboardAndMouse) @@ -2684,7 +2685,7 @@ void AddPlrExperience(Player &player, int lvl, int exp) player._pExperience = std::min(player._pExperience + clampedExp, MaxExperience); if (*sgOptions.Gameplay.experienceBar) { - force_redraw = 255; + RedrawEverything(); } if (player._pExperience >= ExpLvlsTbl[49]) { @@ -2904,7 +2905,7 @@ void StartPlrHit(Player &player, int dam, bool forcehit) player.Say(HeroSpeech::ArghClang); - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); if (player._pClass == HeroClass::Barbarian) { if (dam >> 6 < player._pLevel + player._pLevel / 4 && !forcehit) { return; @@ -2986,7 +2987,7 @@ StartPlayerKill(Player &player, int earflag) SetPlayerOld(player); if (&player == MyPlayer) { - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); if (!player.HoldItem.isEmpty()) { DeadItem(player, std::move(player.HoldItem), { 0, 0 }); @@ -3066,7 +3067,7 @@ void ApplyPlrDamage(Player &player, int dam, int minHP /*= 0*/, int frac /*= 0*/ totalDamage += totalDamage / -player.GetManaShieldDamageReduction(); } if (&player == MyPlayer) - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); if (player._pMana >= totalDamage) { player._pMana -= totalDamage; player._pManaBase -= totalDamage; @@ -3086,7 +3087,7 @@ void ApplyPlrDamage(Player &player, int dam, int minHP /*= 0*/, int frac /*= 0*/ if (totalDamage == 0) return; - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); player._pHitPoints -= totalDamage; player._pHPBase -= totalDamage; if (player._pHitPoints > player._pMaxHP) { @@ -3274,7 +3275,7 @@ void ProcessPlayers() if (HasAnyOf(player._pIFlags, ItemSpecialEffect::NoMana) && player._pManaBase > 0) { player._pManaBase -= player._pMana; player._pMana = 0; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); } } @@ -3633,7 +3634,7 @@ void SetPlayerHitPoints(Player &player, int val) player._pHPBase = val + player._pMaxHPBase - player._pMaxHP; if (&player == MyPlayer) { - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); } } diff --git a/Source/spells.cpp b/Source/spells.cpp index c0c623a0d..a02d75ad9 100644 --- a/Source/spells.cpp +++ b/Source/spells.cpp @@ -10,6 +10,7 @@ #ifdef _DEBUG #include "debug.h" #endif +#include "engine/backbuffer_state.hpp" #include "engine/point.hpp" #include "engine/random.hpp" #include "gamemenu.h" @@ -55,12 +56,12 @@ void ClearReadiedSpell(Player &player) { if (player._pRSpell != SPL_INVALID) { player._pRSpell = SPL_INVALID; - force_redraw = 255; + RedrawEverything(); } if (player._pRSplType != RSPLTYPE_INVALID) { player._pRSplType = RSPLTYPE_INVALID; - force_redraw = 255; + RedrawEverything(); } } @@ -191,7 +192,7 @@ void ConsumeSpell(Player &player, spell_id sn) int ma = GetManaAmount(player, sn); player._pMana -= ma; player._pManaBase -= ma; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); break; } if (sn == SPL_FLARE) { @@ -273,8 +274,8 @@ void DoResurrect(size_t pnum, Player &target) if (&target == MyPlayer) { MyPlayerIsDead = false; gamemenu_off(); - drawhpflag = true; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Health); + RedrawComponent(PanelDrawComponent::Mana); } ClrPlrPath(target); @@ -327,7 +328,7 @@ void DoHealOther(const Player &caster, Player &target) target._pHPBase = std::min(target._pHPBase + hp, target._pMaxHPBase); if (&target == MyPlayer) { - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); } } diff --git a/Source/stores.cpp b/Source/stores.cpp index 4fc7619a8..729ae2f5d 100644 --- a/Source/stores.cpp +++ b/Source/stores.cpp @@ -11,6 +11,7 @@ #include "controls/plrctrls.h" #include "cursor.h" +#include "engine/backbuffer_state.hpp" #include "engine/load_cel.hpp" #include "engine/random.hpp" #include "engine/render/clx_render.hpp" @@ -695,7 +696,7 @@ void FillManaPlayer() } myPlayer._pMana = myPlayer._pMaxMana; myPlayer._pManaBase = myPlayer._pMaxManaBase; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); } void StartWitch() @@ -1090,7 +1091,7 @@ void HealPlayer() } myPlayer._pHitPoints = myPlayer._pMaxHP; myPlayer._pHPBase = myPlayer._pMaxHPBase; - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); } void StartHealer() diff --git a/Source/utils/display.cpp b/Source/utils/display.cpp index 23a06aaac..31fc6abe5 100644 --- a/Source/utils/display.cpp +++ b/Source/utils/display.cpp @@ -24,6 +24,7 @@ #include "controls/devices/kbcontroller.h" #include "controls/game_controls.h" #include "controls/touch/gamepad.h" +#include "engine/backbuffer_state.hpp" #include "engine/dx.h" #include "options.h" #include "utils/log.hpp" @@ -449,7 +450,7 @@ void SetFullscreenMode() } InitializeVirtualGamepad(); #endif - force_redraw = 255; + RedrawEverything(); } void ResizeWindow() @@ -473,7 +474,7 @@ void ResizeWindow() #endif CreateBackBuffer(); - force_redraw = 255; + RedrawEverything(); } SDL_Surface *GetOutputSurface() @@ -493,15 +494,6 @@ SDL_Surface *GetOutputSurface() #endif } -bool IsDoubleBuffered() -{ -#ifdef USE_SDL1 - return (GetOutputSurface()->flags & SDL_DOUBLEBUF) == SDL_DOUBLEBUF; -#else - return true; -#endif -} - bool OutputRequiresScaling() { #ifdef USE_SDL1 diff --git a/Source/utils/display.h b/Source/utils/display.h index eb8b52acb..857cdb1ac 100644 --- a/Source/utils/display.h +++ b/Source/utils/display.h @@ -40,8 +40,6 @@ bool IsFullScreen(); // SDL2, upscale: Renderer texture surface. SDL_Surface *GetOutputSurface(); -bool IsDoubleBuffered(); - // Whether the output surface requires software scaling. // Always returns false on SDL2. bool OutputRequiresScaling();