1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2025-12-10 09:32:29 +01:00

Merge pull request #24464 from mixiate/fix-window-visibility-cache

Refactor window visibility cache and fix viewport visibility
This commit is contained in:
Matt
2025-05-22 21:31:33 +03:00
committed by GitHub
8 changed files with 49 additions and 95 deletions

View File

@@ -23,6 +23,7 @@
- Fix: [#24406] The network status window uses an undefined string for its title.
- Fix: [#24444] In the object load error window, the guide text overlaps when the title bar is enlarged.
- Fix: [#24448] Shortcuts involving the Caps Lock key are wrongly localised to NumPad Dot.
- Fix: [#24464] Window and viewport visibility is not calculated correctly causing minor performance issues.
0.4.22 (2025-05-04)
------------------------------------------------------------------------

View File

@@ -366,8 +366,6 @@ public:
void PaintWindows() override
{
WindowResetVisibilities();
if (ClimateHasWeatherEffect())
{
WindowUpdateAllViewports();

View File

@@ -184,8 +184,6 @@ void X8DrawingEngine::EndDraw()
void X8DrawingEngine::PaintWindows()
{
WindowResetVisibilities();
// Redraw dirty regions before updating the viewports, otherwise
// when viewports get panned, they copy dirty pixels
DrawAllDirtyBlocks();

View File

@@ -203,6 +203,7 @@ namespace OpenRCT2
if (Config::Get().general.AlwaysShowGridlines)
viewport->flags |= VIEWPORT_FLAG_GRIDLINES;
w->viewport = viewport;
viewport->isVisible = w->isVisible;
CoordsXYZ centrePos = focus.GetPos();
w->viewport_target_sprite = std::visit(
@@ -250,7 +251,7 @@ namespace OpenRCT2
{
for (auto& vp : _viewports)
{
if (maxZoom == ZoomLevel{ -1 } || vp.zoom <= ZoomLevel{ maxZoom })
if (vp.isVisible && (maxZoom == ZoomLevel{ -1 } || vp.zoom <= ZoomLevel{ maxZoom }))
{
int32_t x1, y1, x2, y2;
@@ -272,7 +273,7 @@ namespace OpenRCT2
{
for (auto& vp : _viewports)
{
if (maxZoom == ZoomLevel{ -1 } || vp.zoom <= ZoomLevel{ maxZoom })
if (vp.isVisible && (maxZoom == ZoomLevel{ -1 } || vp.zoom <= ZoomLevel{ maxZoom }))
{
auto screenCoords = Translate3DTo2DWithZ(vp.rotation, pos);
auto screenPos = ScreenRect(
@@ -287,7 +288,7 @@ namespace OpenRCT2
{
for (auto& vp : _viewports)
{
if (maxZoom == ZoomLevel{ -1 } || vp.zoom <= ZoomLevel{ maxZoom })
if (vp.isVisible && (maxZoom == ZoomLevel{ -1 } || vp.zoom <= ZoomLevel{ maxZoom }))
{
ViewportInvalidate(&vp, screenRect);
}
@@ -1841,24 +1842,6 @@ namespace OpenRCT2
{
PROFILED_FUNCTION();
// if unknown viewport visibility, use the containing window to discover the status
if (viewport->visibility == VisibilityCache::unknown)
{
auto windowManager = Ui::GetWindowManager();
auto owner = windowManager->GetOwner(viewport);
if (owner != nullptr && owner->classification != WindowClass::MainWindow)
{
// note, window_is_visible will update viewport->visibility, so this should have a low hit count
if (!WindowIsVisible(*owner))
{
return;
}
}
}
if (viewport->visibility == VisibilityCache::covered)
return;
auto zoom = viewport->zoom;
auto viewPos = viewport->viewPos;
auto viewportScreenPos = viewport->pos;

View File

@@ -38,7 +38,7 @@ namespace OpenRCT2
uint32_t flags{};
ZoomLevel zoom{};
uint8_t rotation{};
VisibilityCache visibility{};
bool isVisible = false;
[[nodiscard]] constexpr int32_t ViewWidth() const
{

View File

@@ -115,13 +115,50 @@ static constexpr float kWindowScrollLocations[][2] = {
void WindowUpdateAllViewports()
{
WindowVisitEach([&](WindowBase* w) {
if (w->viewport != nullptr && WindowIsVisible(*w))
if (w->viewport != nullptr && w->isVisible)
{
ViewportUpdatePosition(w);
}
});
}
static void WindowUpdateVisibilities()
{
const auto itEnd = g_window_list.end();
for (auto it = g_window_list.begin(); it != itEnd; ++it)
{
auto& window = *(*it);
if (window.viewport == nullptr)
{
window.isVisible = true;
continue;
}
if (window.classification == WindowClass::MainWindow)
{
window.isVisible = true;
window.viewport->isVisible = true;
continue;
}
window.isVisible = true;
window.viewport->isVisible = true;
for (auto itOther = std::next(it); itOther != itEnd; ++itOther)
{
const auto& otherWindow = *(*itOther);
if (otherWindow.flags & WF_DEAD)
continue;
if (otherWindow.windowPos.x <= window.windowPos.x && otherWindow.windowPos.y <= window.windowPos.y
&& otherWindow.windowPos.x + otherWindow.width >= window.windowPos.x + window.width
&& otherWindow.windowPos.y + otherWindow.height >= window.windowPos.y + window.height)
{
window.isVisible = false;
window.viewport->isVisible = false;
break;
}
}
}
}
/**
*
* rct2: 0x006E77A1
@@ -155,6 +192,8 @@ static constexpr float kWindowScrollLocations[][2] = {
auto windowManager = Ui::GetWindowManager();
windowManager->UpdateMouseWheel();
WindowUpdateVisibilities();
}
void WindowNotifyLanguageChange()
@@ -496,7 +535,7 @@ static constexpr float kWindowScrollLocations[][2] = {
*/
void WindowDraw(RenderTarget& rt, WindowBase& w, int32_t left, int32_t top, int32_t right, int32_t bottom)
{
if (!WindowIsVisible(w))
if (!w.isVisible)
return;
// Divide the draws up for only the visible regions of the window recursively
@@ -569,7 +608,7 @@ static constexpr float kWindowScrollLocations[][2] = {
auto* v = (*it).get();
if (v->flags & WF_DEAD)
continue;
if ((&w == v || (v->flags & WF_TRANSPARENT)) && WindowIsVisible(*v))
if ((&w == v || (v->flags & WF_TRANSPARENT)) && v->isVisible)
{
WindowDrawSingle(rt, *v, left, top, right, bottom);
}
@@ -855,48 +894,6 @@ static constexpr float kWindowScrollLocations[][2] = {
windowMgr->CloseByClass(WindowClass::Textinput);
}
bool WindowIsVisible(WindowBase& w)
{
// w->visibility is used to prevent repeat calculations within an iteration by caching the result
if (w.visibility == VisibilityCache::visible)
return true;
if (w.visibility == VisibilityCache::covered)
return false;
// only consider viewports, consider the main window always visible
if (w.viewport == nullptr || w.classification == WindowClass::MainWindow)
{
// default to previous behaviour
w.visibility = VisibilityCache::visible;
return true;
}
// start from the window above the current
auto itPos = WindowGetIterator(&w);
for (auto it = std::next(itPos); it != g_window_list.end(); it++)
{
auto& w_other = *(*it);
if (w_other.flags & WF_DEAD)
continue;
// if covered by a higher window, no rendering needed
if (w_other.windowPos.x <= w.windowPos.x && w_other.windowPos.y <= w.windowPos.y
&& w_other.windowPos.x + w_other.width >= w.windowPos.x + w.width
&& w_other.windowPos.y + w_other.height >= w.windowPos.y + w.height)
{
w.visibility = VisibilityCache::covered;
w.viewport->visibility = VisibilityCache::covered;
return false;
}
}
// default to previous behaviour
w.visibility = VisibilityCache::visible;
w.viewport->visibility = VisibilityCache::visible;
return true;
}
/**
*
* rct2: 0x006E7499
@@ -942,18 +939,6 @@ static constexpr float kWindowScrollLocations[][2] = {
return nullptr;
}
void WindowResetVisibilities()
{
// reset window visibility status to unknown
WindowVisitEach([](WindowBase* w) {
w->visibility = VisibilityCache::unknown;
if (w->viewport != nullptr)
{
w->viewport->visibility = VisibilityCache::unknown;
}
});
}
void WindowInitAll()
{
auto* windowMgr = Ui::GetWindowManager();

View File

@@ -26,7 +26,6 @@ struct RenderTarget;
struct TrackDesignFileRef;
struct ScenarioIndexEntry;
enum class VisibilityCache : uint8_t;
enum class CursorID : uint8_t;
enum class CloseWindowModifier : uint8_t;
@@ -232,13 +231,6 @@ enum class ModalResult : int8_t
ok,
};
enum class VisibilityCache : uint8_t
{
unknown,
visible,
covered
};
enum class CloseWindowModifier : uint8_t
{
none,
@@ -341,10 +333,7 @@ namespace OpenRCT2
void TextinputCancel();
bool WindowIsVisible(WindowBase& w);
Viewport* WindowGetPreviousViewport(Viewport* current);
void WindowResetVisibilities();
void WindowInitAll();
void WindowFollowSprite(WindowBase& w, EntityId spriteIndex);

View File

@@ -106,7 +106,7 @@ namespace OpenRCT2
ScreenCoordsXY savedViewPos{};
WindowClass classification{};
ColourWithFlags colours[6]{};
VisibilityCache visibility{};
bool isVisible = true;
EntityId viewport_smart_follow_sprite{ EntityId::GetNull() }; // Handles setting viewport target sprite etc
void SetViewportLocation(const CoordsXYZ& coords);