Scrolling/perf improvements to link graph overlays on viewport and smallmap

pull/59/head
Jonathan G Rennison 6 years ago
parent fe1e0a9d59
commit 0d0d55f81d

@ -17,6 +17,7 @@
#include "../date_func.h"
#include "../viewport_func.h"
#include "../smallmap_gui.h"
#include "../zoom_func.h"
#include "../core/geometry_func.hpp"
#include "../widgets/link_graph_legend_widget.h"
@ -24,6 +25,8 @@
#include "../3rdparty/cpp-btree/btree_map.h"
#include <algorithm>
#include "../safeguards.h"
/**
@ -40,25 +43,53 @@ const uint8 LinkGraphOverlay::LINK_COLOURS[] = {
* Get a DPI for the widget we will be drawing to.
* @param dpi DrawPixelInfo to fill with the desired dimensions.
*/
void LinkGraphOverlay::GetWidgetDpi(DrawPixelInfo *dpi) const
void LinkGraphOverlay::GetWidgetDpi(DrawPixelInfo *dpi, uint margin) const
{
const NWidgetBase *wi = this->window->GetWidget<NWidgetBase>(this->widget_id);
dpi->left = dpi->top = 0;
dpi->width = wi->current_x;
dpi->height = wi->current_y;
dpi->left = dpi->top = -margin;
dpi->width = wi->current_x + 2 * margin;
dpi->height = wi->current_y + 2 * margin;
}
bool LinkGraphOverlay::CacheStillValid() const
{
if (this->window->viewport) {
const ViewPort *vp = this->window->viewport;
Rect region { vp->virtual_left, vp->virtual_top,
vp->virtual_left + vp->virtual_width, vp->virtual_top + vp->virtual_height };
return (region.left >= this->cached_region.left &&
region.right <= this->cached_region.right &&
region.top >= this->cached_region.top &&
region.bottom <= this->cached_region.bottom);
} else {
return true;
}
}
/**
* Rebuild the cache and recalculate which links and stations to be shown.
*/
void LinkGraphOverlay::RebuildCache()
void LinkGraphOverlay::RebuildCache(bool incremental)
{
this->cached_links.clear();
this->cached_stations.clear();
if (!incremental) {
this->cached_links.clear();
this->cached_stations.clear();
}
if (this->company_mask == 0) return;
DrawPixelInfo dpi;
this->GetWidgetDpi(&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);
this->GetWidgetDpi(&dpi, pixel_margin);
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 {
this->GetWidgetDpi(&dpi);
cache_all = true;
}
struct LinkCacheItem {
Point from_pt;
@ -66,8 +97,23 @@ void LinkGraphOverlay::RebuildCache()
LinkProperties prop;
};
btree::btree_map<std::pair<StationID, StationID>, LinkCacheItem> link_cache_map;
std::vector<StationID> incremental_station_exclude;
std::vector<std::pair<StationID, StationID>> incremental_link_exclude;
auto AddLinks = [&](const Station *from, const Station *to, Point from_pt, Point to_pt) {
if (incremental) {
incremental_station_exclude.reserve(this->cached_stations.size());
for (StationSupplyList::iterator i(this->cached_stations.begin()); i != this->cached_stations.end(); ++i) {
incremental_station_exclude.push_back(i->id);
}
std::sort(incremental_station_exclude.begin(), incremental_station_exclude.end());
incremental_link_exclude.reserve(this->cached_links.size());
for (LinkList::iterator i(this->cached_links.begin()); i != this->cached_links.end(); ++i) {
incremental_link_exclude.push_back(std::make_pair(i->from_id, i->to_id));
}
std::sort(incremental_link_exclude.begin(), incremental_link_exclude.end());
}
auto AddLinks = [&](const Station *from, const Station *to, Point from_pt, Point to_pt, btree::btree_map<std::pair<StationID, StationID>, LinkCacheItem>::iterator insert_iter) {
LinkCacheItem *item = nullptr;
CargoID c;
FOR_EACH_SET_CARGO_ID(c, this->cargo_mask) {
@ -81,11 +127,10 @@ void LinkGraphOverlay::RebuildCache()
ConstEdge edge = lg[ge.node][to->goods[c].node];
if (edge.Capacity() > 0) {
if (!item) {
item = &link_cache_map[std::make_pair(from->index, to->index)];
if (item->prop.capacity == 0) {
item->from_pt = from_pt;
item->to_pt = to_pt;
}
auto iter = link_cache_map.insert(insert_iter, std::make_pair(std::make_pair(from->index, to->index), LinkCacheItem()));
item = &(iter->second);
item->from_pt = from_pt;
item->to_pt = to_pt;
}
this->AddStats(lg.Monthly(edge.Capacity()), lg.Monthly(edge.Usage()),
ge.flows.GetFlowVia(to->index), from->owner == OWNER_NONE || to->owner == OWNER_NONE,
@ -98,6 +143,8 @@ void LinkGraphOverlay::RebuildCache()
FOR_ALL_STATIONS(sta) {
if (sta->rect.IsEmpty()) continue;
if (incremental && std::binary_search(incremental_station_exclude.begin(), incremental_station_exclude.end(), sta->index)) continue;
Point pta = this->GetStationMiddle(sta);
StationID from = sta->index;
@ -114,9 +161,8 @@ void LinkGraphOverlay::RebuildCache()
for (ConstEdgeIterator i = from_node.Begin(); i != from_node.End(); ++i) {
StationID to = lg[i->first].Station();
assert(from != to);
if (!Station::IsValidID(to) || link_cache_map.count(std::make_pair(from, to))) {
continue;
}
if (!Station::IsValidID(to)) continue;
const Station *stb = Station::Get(to);
assert(sta != stb);
@ -124,19 +170,28 @@ void LinkGraphOverlay::RebuildCache()
if (stb->owner != OWNER_NONE && sta->owner != OWNER_NONE && !HasBit(this->company_mask, stb->owner)) continue;
if (stb->rect.IsEmpty()) continue;
if (incremental && std::binary_search(incremental_station_exclude.begin(), incremental_station_exclude.end(), to)) continue;
if (incremental && std::binary_search(incremental_link_exclude.begin(), incremental_link_exclude.end(), std::make_pair(from, to))) continue;
auto key = std::make_pair(from, to);
auto iter = link_cache_map.lower_bound(key);
if (iter != link_cache_map.end() && !(link_cache_map.key_comp()(key, iter->first))) {
continue;
}
Point ptb = this->GetStationMiddle(stb);
if (!this->IsLinkVisible(pta, ptb, &dpi)) continue;
if (!cache_all && !this->IsLinkVisible(pta, ptb, &dpi)) continue;
AddLinks(sta, stb, pta, ptb);
AddLinks(sta, stb, pta, ptb, iter);
}
}
if (this->IsPointVisible(pta, &dpi)) {
if (cache_all || this->IsPointVisible(pta, &dpi)) {
this->cached_stations.push_back({ from, supply, pta });
}
}
this->cached_links.reserve(link_cache_map.size());
this->cached_links.reserve(this->cached_links.size() + link_cache_map.size());
for (auto &iter : link_cache_map) {
this->cached_links.push_back({ iter.first.first, iter.first.second, iter.second.from_pt, iter.second.to_pt, iter.second.prop });
}

@ -68,7 +68,8 @@ public:
window(w), widget_id(wid), cargo_mask(cargo_mask), company_mask(company_mask), scale(scale)
{}
void RebuildCache();
void RebuildCache(bool incremental = false);
bool CacheStillValid() const;
void Draw(const DrawPixelInfo *dpi);
void SetCargoMask(CargoTypes cargo_mask);
void SetCompanyMask(uint32 company_mask);
@ -86,6 +87,7 @@ protected:
uint32 company_mask; ///< Bitmask of companies to be displayed.
LinkList cached_links; ///< Cache for links to reduce recalculation.
StationSupplyList cached_stations; ///< Cache for stations to be drawn.
Rect cached_region; ///< Region covered by cached_links and cached_stations.
uint scale; ///< Width of link lines.
uint64 last_update_number = 0; ///< Last window update number
@ -97,7 +99,7 @@ protected:
void DrawContent(Point pta, Point ptb, const LinkProperties &cargo) const;
bool IsLinkVisible(Point pta, Point ptb, const DrawPixelInfo *dpi, int padding = 0) const;
bool IsPointVisible(Point pt, const DrawPixelInfo *dpi, int padding = 0) const;
void GetWidgetDpi(DrawPixelInfo *dpi) const;
void GetWidgetDpi(DrawPixelInfo *dpi, uint margin = 0) const;
static void AddStats(uint new_cap, uint new_usg, uint new_flow, bool new_shared, LinkProperties &cargo);
static void DrawVertex(int x, int y, int size, int colour, int border_colour);

@ -156,7 +156,7 @@ bool DoZoomInOutWindow(ZoomStateChange how, Window *w)
/* Update the windows that have zoom-buttons to perhaps disable their buttons */
w->InvalidateData();
if (how != ZOOM_NONE) {
RebuildViewportOverlay(w);
RebuildViewportOverlay(w, false);
}
return true;
}

@ -617,8 +617,6 @@ void SmallMapWindow::SetZoomLevel(ZoomLevelChange change, const Point *zoom_pt)
Point new_tile = this->PixelToTile(zoom_pt->x, zoom_pt->y, &sub);
this->SetNewScroll(this->scroll_x + (tile.x - new_tile.x) * TILE_SIZE,
this->scroll_y + (tile.y - new_tile.y) * TILE_SIZE, sub);
} else if (this->map_type == SMT_LINKSTATS) {
this->overlay->RebuildCache();
}
this->SetWidgetDisabledState(WID_SM_ZOOM_IN, this->zoom == zoomlevels[MIN_ZOOM_INDEX]);
this->SetWidgetDisabledState(WID_SM_ZOOM_OUT, this->zoom == zoomlevels[MAX_ZOOM_INDEX]);
@ -1538,7 +1536,6 @@ void SmallMapWindow::SetNewScroll(int sx, int sy, int sub)
this->scroll_x = sx;
this->scroll_y = sy;
this->subscroll = sub;
if (this->map_type == SMT_LINKSTATS) this->overlay->RebuildCache();
}
/* virtual */ void SmallMapWindow::OnScroll(Point delta)

@ -91,7 +91,7 @@ protected:
static const uint LEGEND_BLOB_WIDTH = 8; ///< Width of the coloured blob in front of a line text in the #WID_SM_LEGEND widget.
static const uint INDUSTRY_MIN_NUMBER_OF_COLUMNS = 2; ///< Minimal number of columns in the #WID_SM_LEGEND widget for the #SMT_INDUSTRY legend.
static const uint FORCE_REFRESH_PERIOD = 0x1F; ///< map is redrawn after that many ticks
static const uint FORCE_REFRESH_PERIOD = 0x5F; ///< map is redrawn after that many ticks
static const uint BLINK_PERIOD = 0x0F; ///< highlight blinking interval
uint min_number_of_columns; ///< Minimal number of columns in legends.

@ -429,7 +429,7 @@ static void DoSetViewportPosition(const Window *w, int left, int top, int width,
}
}
static void SetViewportPosition(Window *w, int x, int y)
static void SetViewportPosition(Window *w, int x, int y, bool force_update_overlay)
{
ViewPort *vp = w->viewport;
int old_left = vp->virtual_left;
@ -440,6 +440,8 @@ static void SetViewportPosition(Window *w, int x, int y)
vp->virtual_left = x;
vp->virtual_top = y;
if (force_update_overlay || IsViewportOverlayOutsideCachedRegion(w)) 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)
*/
@ -2823,7 +2825,7 @@ void UpdateViewportPosition(Window *w)
w->viewport->scrollpos_x = pt.x;
w->viewport->scrollpos_y = pt.y;
SetViewportPosition(w, pt.x, pt.y);
SetViewportPosition(w, pt.x, pt.y, false);
} else {
/* Ensure the destination location is within the map */
ClampViewportToMap(vp, w->viewport->dest_scrollpos_x, w->viewport->dest_scrollpos_y);
@ -2850,8 +2852,7 @@ void UpdateViewportPosition(Window *w)
if (_scrolling_viewport == w) UpdateActiveScrollingViewport(w);
SetViewportPosition(w, w->viewport->scrollpos_x, w->viewport->scrollpos_y);
if (update_overlay) RebuildViewportOverlay(w);
SetViewportPosition(w, w->viewport->scrollpos_x, w->viewport->scrollpos_y, update_overlay);
}
}
@ -3438,13 +3439,24 @@ bool HandleViewportClicked(const ViewPort *vp, int x, int y, bool double_click)
return result;
}
void RebuildViewportOverlay(Window *w)
void RebuildViewportOverlay(Window *w, bool incremental)
{
if (w->viewport->overlay != NULL &&
w->viewport->overlay->GetCompanyMask() != 0 &&
w->viewport->overlay->GetCargoMask() != 0) {
w->viewport->overlay->RebuildCache(incremental);
if (!incremental) w->SetDirty();
}
}
bool IsViewportOverlayOutsideCachedRegion(Window *w)
{
if (w->viewport->overlay != NULL &&
w->viewport->overlay->GetCompanyMask() != 0 &&
w->viewport->overlay->GetCargoMask() != 0) {
w->viewport->overlay->RebuildCache();
w->SetDirty();
return !w->viewport->overlay->CacheStillValid();
} else {
return false;
}
}
@ -3477,7 +3489,7 @@ bool ScrollWindowTo(int x, int y, int z, Window *w, bool instant)
if (instant) {
w->viewport->scrollpos_x = pt.x;
w->viewport->scrollpos_y = pt.y;
RebuildViewportOverlay(w);
RebuildViewportOverlay(w, true);
}
w->viewport->dest_scrollpos_x = pt.x;

@ -80,7 +80,8 @@ bool ScrollWindowTo(int x, int y, int z, Window *w, bool instant = false);
void UpdateActiveScrollingViewport(Window *w);
void RebuildViewportOverlay(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);

Loading…
Cancel
Save