Linkgraph: Scroll overlay pixel cache instead of clearing it on scroll
Reduce cost of preparing/drawing cache Improve missing station checks in RefreshDrawCache Remove need for checks in DrawLinks Don't rebase overlay cache coordinate to screen in viewport map mode
This commit is contained in:
@@ -116,16 +116,25 @@ void LinkGraphOverlay::RebuildCache(bool incremental)
|
||||
this->cached_links.clear();
|
||||
this->cached_stations.clear();
|
||||
this->last_update_number = GetWindowUpdateNumber();
|
||||
this->rebuild_counter++;
|
||||
}
|
||||
if (this->company_mask == 0) return;
|
||||
|
||||
const Rect old_cached_region = this->cached_region;
|
||||
DrawPixelInfo dpi;
|
||||
bool cache_all = false;
|
||||
if (this->window->viewport) {
|
||||
const Viewport *vp = this->window->viewport;
|
||||
const int pixel_margin = 256;
|
||||
const int vp_margin = ScaleByZoom(pixel_margin, vp->zoom);
|
||||
if (vp->zoom < ZOOM_LVL_DRAW_MAP) {
|
||||
this->GetWidgetDpi(&dpi, pixel_margin);
|
||||
} else {
|
||||
dpi.left = UnScaleByZoomLower(vp->virtual_left - vp_margin, vp->zoom);
|
||||
dpi.top = UnScaleByZoomLower(vp->virtual_top - vp_margin, vp->zoom);
|
||||
dpi.width = UnScaleByZoom(vp->virtual_width + vp_margin * 2, vp->zoom);
|
||||
dpi.height = UnScaleByZoom(vp->virtual_height + vp_margin * 2, vp->zoom);
|
||||
}
|
||||
this->cached_region = Rect({ vp->virtual_left - vp_margin, vp->virtual_top - vp_margin,
|
||||
vp->virtual_left + vp->virtual_width + vp_margin, vp->virtual_top + vp->virtual_height + vp_margin });
|
||||
} else {
|
||||
@@ -237,6 +246,26 @@ void LinkGraphOverlay::RebuildCache(bool incremental)
|
||||
this->cached_links.push_back({ iter.first.first, iter.first.second, iter.second.from_pt, iter.second.to_pt, iter.second.prop });
|
||||
}
|
||||
|
||||
if (incremental && (this->cached_stations.size() > previous_cached_stations_count || this->cached_links.size() > previous_cached_links_count)) {
|
||||
/* Check if newly added stations/links are visible in previous cached area */
|
||||
DrawPixelInfo old_dpi;
|
||||
old_dpi.left = old_cached_region.left;
|
||||
old_dpi.top = old_cached_region.top;
|
||||
old_dpi.width = old_cached_region.right - old_cached_region.left;
|
||||
old_dpi.height = old_cached_region.bottom - old_cached_region.top;
|
||||
|
||||
auto check_found = [&]() -> bool {
|
||||
for (size_t i = previous_cached_stations_count; i < this->cached_stations.size(); i++) {
|
||||
if (this->IsPointVisible(this->cached_stations[i].pt, &old_dpi)) return true;
|
||||
}
|
||||
for (size_t i = previous_cached_links_count; i < this->cached_links.size(); i++) {
|
||||
if (this->IsLinkVisible(this->cached_links[i].from_pt, this->cached_links[i].to_pt, &old_dpi)) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
if (check_found()) this->rebuild_counter++;
|
||||
}
|
||||
|
||||
if (previous_cached_stations_count > 0) {
|
||||
std::inplace_merge(this->cached_stations.begin(), this->cached_stations.begin() + previous_cached_stations_count, this->cached_stations.end(),
|
||||
[](const StationSupplyInfo &a, const StationSupplyInfo &b) {
|
||||
@@ -364,17 +393,32 @@ inline bool LinkGraphOverlay::IsLinkVisible(Point pta, Point ptb, const DrawPixe
|
||||
|
||||
void LinkGraphOverlay::RefreshDrawCache()
|
||||
{
|
||||
static const Point INVALID_POINT = Point{ INT_MIN / 2, INT_MIN / 2 };
|
||||
|
||||
for (StationSupplyList::iterator i(this->cached_stations.begin()); i != this->cached_stations.end(); ++i) {
|
||||
const Station *st = Station::GetIfValid(i->id);
|
||||
if (st == nullptr) continue;
|
||||
|
||||
i->pt = this->GetStationMiddle(st);
|
||||
if (st == nullptr) {
|
||||
i->pt = INVALID_POINT;
|
||||
continue;
|
||||
}
|
||||
|
||||
Point new_pt = this->GetStationMiddle(st);
|
||||
if (i->pt.x != new_pt.x || i->pt.y != new_pt.y) {
|
||||
i->pt = new_pt;
|
||||
}
|
||||
}
|
||||
|
||||
for (LinkList::iterator i(this->cached_links.begin()); i != this->cached_links.end(); ++i) {
|
||||
const Station *sta = Station::GetIfValid(i->from_id);
|
||||
if (sta == nullptr) continue;
|
||||
if (sta == nullptr) {
|
||||
i->from_pt = i->to_pt = INVALID_POINT;
|
||||
continue;
|
||||
}
|
||||
const Station *stb = Station::GetIfValid(i->to_id);
|
||||
if (stb == nullptr) continue;
|
||||
if (stb == nullptr) {
|
||||
i->from_pt = i->to_pt = INVALID_POINT;
|
||||
continue;
|
||||
}
|
||||
|
||||
i->from_pt = this->GetStationMiddle(sta);
|
||||
i->to_pt = this->GetStationMiddle(stb);
|
||||
@@ -389,7 +433,7 @@ void LinkGraphOverlay::PrepareDraw()
|
||||
if (this->dirty) {
|
||||
this->RebuildCache();
|
||||
}
|
||||
if (this->last_update_number != GetWindowUpdateNumber()) {
|
||||
if (this->last_update_number != GetWindowUpdateNumber() && this->window->viewport->zoom < ZOOM_LVL_DRAW_MAP) {
|
||||
this->last_update_number = GetWindowUpdateNumber();
|
||||
this->RefreshDrawCache();
|
||||
}
|
||||
@@ -414,8 +458,6 @@ void LinkGraphOverlay::DrawLinks(const DrawPixelInfo *dpi) const
|
||||
int width = ScaleGUITrad(this->scale);
|
||||
for (const auto &i : this->cached_links) {
|
||||
if (!this->IsLinkVisible(i.from_pt, i.to_pt, dpi, width + 2)) continue;
|
||||
if (!Station::IsValidID(i.from_id)) continue;
|
||||
if (!Station::IsValidID(i.to_id)) continue;
|
||||
this->DrawContent(dpi, i.from_pt, i.to_pt, i.prop);
|
||||
}
|
||||
}
|
||||
|
@@ -87,10 +87,12 @@ public:
|
||||
void SetDirty() { this->dirty = true; }
|
||||
|
||||
/** Get a bitmask of the currently shown cargoes. */
|
||||
CargoTypes GetCargoMask() { return this->cargo_mask; }
|
||||
CargoTypes GetCargoMask() const { return this->cargo_mask; }
|
||||
|
||||
/** Get a bitmask of the currently shown companies. */
|
||||
CompanyMask GetCompanyMask() { return this->company_mask; }
|
||||
CompanyMask GetCompanyMask() const { return this->company_mask; }
|
||||
|
||||
uint64_t GetRebuildCounter() const { return this->rebuild_counter; }
|
||||
|
||||
protected:
|
||||
Window *window; ///< Window to be drawn into.
|
||||
@@ -103,6 +105,7 @@ protected:
|
||||
uint scale; ///< Width of link lines.
|
||||
bool dirty; ///< Set if overlay should be rebuilt.
|
||||
uint64_t last_update_number = 0; ///< Last window update number
|
||||
uint64_t rebuild_counter = 0; ///< Rebuild counter
|
||||
|
||||
Point GetStationMiddle(const Station *st) const;
|
||||
|
||||
|
@@ -566,6 +566,32 @@ static void ScrollPlanPixelCache(Viewport *vp, int offset_x, int offset_y)
|
||||
if (clear) ClearViewportPlanPixelCache(vp);
|
||||
}
|
||||
|
||||
static void ScrollOrInvalidateOverlayPixelCache(Viewport *vp, int offset_x, int offset_y)
|
||||
{
|
||||
if (vp->overlay_pixel_cache.empty()) return;
|
||||
|
||||
if (vp->zoom < ZOOM_LVL_DRAW_MAP || vp->last_overlay_rebuild_counter != vp->overlay->GetRebuildCounter()) {
|
||||
vp->overlay_pixel_cache.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
bool clear = ScrollViewportPixelCacheGeneric(vp, vp->overlay_pixel_cache, offset_x, offset_y, 1, [](Viewport *vp, int x, int y, int width, int height) {
|
||||
DrawPixelInfo overlay_dpi;
|
||||
overlay_dpi.dst_ptr = vp->overlay_pixel_cache.data() + x + (y * vp->width);
|
||||
overlay_dpi.height = height;
|
||||
overlay_dpi.width = width;
|
||||
overlay_dpi.pitch = vp->width;
|
||||
overlay_dpi.zoom = ZOOM_LVL_NORMAL;
|
||||
overlay_dpi.left = UnScaleByZoomLower(vp->virtual_left, vp->zoom) + x;
|
||||
overlay_dpi.top = UnScaleByZoomLower(vp->virtual_top, vp->zoom) + y;
|
||||
|
||||
Blitter_8bppDrawing blitter;
|
||||
BlitterFactory::TemporaryCurrentBlitterOverride current_blitter(&blitter);
|
||||
vp->overlay->Draw(&overlay_dpi);
|
||||
});
|
||||
if (clear) vp->overlay_pixel_cache.clear();
|
||||
}
|
||||
|
||||
void ClearViewportCache(Viewport *vp)
|
||||
{
|
||||
if (vp->zoom >= ZOOM_LVL_DRAW_MAP) {
|
||||
@@ -870,7 +896,11 @@ static void SetViewportPosition(Window *w, int x, int y, bool force_update_overl
|
||||
vp->virtual_top = y;
|
||||
UpdateViewportDirtyBlockLeftMargin(vp);
|
||||
|
||||
if (force_update_overlay || IsViewportOverlayOutsideCachedRegion(w)) RebuildViewportOverlay(w, true);
|
||||
bool have_overlay = w->viewport->overlay != nullptr &&
|
||||
w->viewport->overlay->GetCompanyMask() != 0 &&
|
||||
w->viewport->overlay->GetCargoMask() != 0;
|
||||
|
||||
if (have_overlay && (force_update_overlay || !w->viewport->overlay->CacheStillValid())) RebuildViewportOverlay(w, true);
|
||||
|
||||
/* Viewport is bound to its left top corner, so it must be rounded down (UnScaleByZoomLower)
|
||||
* else glitch described in FS#1412 will happen (offset by 1 pixel with zoom level > NORMAL)
|
||||
@@ -913,6 +943,7 @@ static void SetViewportPosition(Window *w, int x, int y, bool force_update_overl
|
||||
SCOPE_INFO_FMT([&], "DoSetViewportPosition: %d, %d, %d, %d, %d, %d, %s", left, top, width, height, move_offset.x, move_offset.y, scope_dumper().WindowInfo(w));
|
||||
ScrollViewportLandPixelCache(vp, move_offset.x, move_offset.y);
|
||||
ScrollPlanPixelCache(vp, move_offset.x, move_offset.y);
|
||||
if (have_overlay) ScrollOrInvalidateOverlayPixelCache(vp, move_offset.x, move_offset.y);
|
||||
w->viewport->update_vehicles = true;
|
||||
DoSetViewportPosition((Window *) w->z_front, move_offset, left, top, width, height);
|
||||
ClearViewportCache(w->viewport);
|
||||
@@ -3816,16 +3847,6 @@ static void ViewportProcessParentSprites(ViewportDrawerDynamic *vdd, uint data_i
|
||||
}
|
||||
}
|
||||
|
||||
static bool CheckViewportOverlayPixelRefresh(Viewport *vp)
|
||||
{
|
||||
size_t screen_area = vp->ScreenArea();
|
||||
if (vp->last_overlay_update_number == GetWindowUpdateNumber() && vp->overlay_pixel_cache.size() == screen_area) return false;
|
||||
|
||||
vp->last_overlay_update_number = GetWindowUpdateNumber();
|
||||
vp->overlay_pixel_cache.assign(screen_area, 0xD7);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ViewportDoDrawPhase2(Viewport *vp, ViewportDrawerDynamic *vdd);
|
||||
static void ViewportDoDrawPhase3(Viewport *vp);
|
||||
static void ViewportDoDrawRenderJob(Viewport *vp, ViewportDrawerDynamic *vdd);
|
||||
@@ -3868,15 +3889,19 @@ void ViewportDoDraw(Viewport *vp, int left, int top, int right, int bottom, uint
|
||||
if (vp->overlay != nullptr && vp->overlay->GetCargoMask() != 0 && vp->overlay->GetCompanyMask() != 0) {
|
||||
vp->overlay->PrepareDraw();
|
||||
|
||||
if (vp->zoom >= ZOOM_LVL_DRAW_MAP && CheckViewportOverlayPixelRefresh(vp)) {
|
||||
if (vp->zoom >= ZOOM_LVL_DRAW_MAP && (vp->overlay_pixel_cache.empty() || vp->last_overlay_rebuild_counter != vp->overlay->GetRebuildCounter())) {
|
||||
vp->last_overlay_rebuild_counter = vp->overlay->GetRebuildCounter();
|
||||
|
||||
vp->overlay_pixel_cache.assign(vp->ScreenArea(), 0xD7);
|
||||
|
||||
DrawPixelInfo overlay_dpi;
|
||||
overlay_dpi.dst_ptr = vp->overlay_pixel_cache.data();
|
||||
overlay_dpi.height = vp->height;
|
||||
overlay_dpi.width = vp->width;
|
||||
overlay_dpi.pitch = vp->width;
|
||||
overlay_dpi.zoom = ZOOM_LVL_NORMAL;
|
||||
overlay_dpi.left = vp->left;
|
||||
overlay_dpi.top = vp->top;
|
||||
overlay_dpi.left = UnScaleByZoomLower(vp->virtual_left, vp->zoom);
|
||||
overlay_dpi.top = UnScaleByZoomLower(vp->virtual_top, vp->zoom);
|
||||
|
||||
Blitter_8bppDrawing blitter;
|
||||
BlitterFactory::TemporaryCurrentBlitterOverride current_blitter(&blitter);
|
||||
@@ -4283,6 +4308,7 @@ void UpdateViewportSizeZoom(Viewport *vp)
|
||||
} else {
|
||||
vp->land_pixel_cache.assign(vp->ScreenArea(), 0xD7);
|
||||
}
|
||||
vp->overlay_pixel_cache.clear();
|
||||
vp->plan_pixel_cache.clear();
|
||||
} else {
|
||||
vp->map_draw_vehicles_cache.vehicle_pixels.clear();
|
||||
@@ -5154,17 +5180,6 @@ void RebuildViewportOverlay(Window *w, bool incremental)
|
||||
}
|
||||
}
|
||||
|
||||
bool IsViewportOverlayOutsideCachedRegion(Window *w)
|
||||
{
|
||||
if (w->viewport->overlay != nullptr &&
|
||||
w->viewport->overlay->GetCompanyMask() != 0 &&
|
||||
w->viewport->overlay->GetCargoMask() != 0) {
|
||||
return !w->viewport->overlay->CacheStillValid();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scrolls the viewport in a window to a given location.
|
||||
* @param x Desired x location of the map to scroll to (world coordinate).
|
||||
@@ -6567,12 +6582,23 @@ Point GetViewportStationMiddle(const Viewport *vp, const Station *st)
|
||||
{
|
||||
int x = TileX(st->xy) * TILE_SIZE;
|
||||
int y = TileY(st->xy) * TILE_SIZE;
|
||||
int z = GetSlopePixelZ(Clamp(x, 0, MapSizeX() * TILE_SIZE - 1), Clamp(y, 0, MapSizeY() * TILE_SIZE - 1));
|
||||
|
||||
/* Be faster/less precise in viewport map mode, sub-pixel precision is not needed.
|
||||
* Don't rebase point into screen coordinates in viewport map mode.
|
||||
*/
|
||||
if (vp->zoom < ZOOM_LVL_DRAW_MAP) {
|
||||
int z = GetSlopePixelZ(Clamp(x, 0, MapSizeX() * TILE_SIZE - 1), Clamp(y, 0, MapSizeY() * TILE_SIZE - 1));
|
||||
Point p = RemapCoords(x, y, z);
|
||||
p.x = UnScaleByZoom(p.x - vp->virtual_left, vp->zoom) + vp->left;
|
||||
p.y = UnScaleByZoom(p.y - vp->virtual_top, vp->zoom) + vp->top;
|
||||
return p;
|
||||
} else {
|
||||
int z = st->xy < MapSize() ? TILE_HEIGHT * TileHeight(st->xy) : 0;
|
||||
Point p = RemapCoords(x, y, z);
|
||||
p.x = UnScaleByZoomLower(p.x, vp->zoom);
|
||||
p.y = UnScaleByZoomLower(p.y, vp->zoom);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
/** Helper class for getting the best sprite sorter. */
|
||||
|
@@ -103,7 +103,6 @@ bool ScrollWindowTo(int x, int y, int z, Window *w, bool instant = false);
|
||||
void UpdateActiveScrollingViewport(Window *w);
|
||||
|
||||
void RebuildViewportOverlay(Window *w, bool incremental);
|
||||
bool IsViewportOverlayOutsideCachedRegion(Window *w);
|
||||
|
||||
bool ScrollMainWindowToTile(TileIndex tile, bool instant = false);
|
||||
bool ScrollMainWindowTo(int x, int y, int z = -1, bool instant = false);
|
||||
|
@@ -61,7 +61,7 @@ struct Viewport {
|
||||
bool is_dirty = false;
|
||||
bool is_drawn = false;
|
||||
bool update_vehicles = false;
|
||||
uint64_t last_overlay_update_number = 0;
|
||||
uint64_t last_overlay_rebuild_counter = 0;
|
||||
uint64_t last_plan_update_number = 0;
|
||||
ViewPortMapDrawVehiclesCache map_draw_vehicles_cache;
|
||||
std::vector<byte> land_pixel_cache;
|
||||
|
Reference in New Issue
Block a user