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:
@@ -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)
|
||||
------------------------------------------------------------------------
|
||||
|
||||
@@ -366,8 +366,6 @@ public:
|
||||
|
||||
void PaintWindows() override
|
||||
{
|
||||
WindowResetVisibilities();
|
||||
|
||||
if (ClimateHasWeatherEffect())
|
||||
{
|
||||
WindowUpdateAllViewports();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user