diff --git a/src/linkgraph/linkgraph_gui.cpp b/src/linkgraph/linkgraph_gui.cpp index fbfc1041ca..ad794e5d76 100644 --- a/src/linkgraph/linkgraph_gui.cpp +++ b/src/linkgraph/linkgraph_gui.cpp @@ -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); - this->GetWidgetDpi(&dpi, pixel_margin); + 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; + if (st == nullptr) { + i->pt = INVALID_POINT; + continue; + } - i->pt = this->GetStationMiddle(st); + 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); } } diff --git a/src/linkgraph/linkgraph_gui.h b/src/linkgraph/linkgraph_gui.h index 08c64242c4..2fa5c44f97 100644 --- a/src/linkgraph/linkgraph_gui.h +++ b/src/linkgraph/linkgraph_gui.h @@ -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. @@ -102,7 +104,8 @@ protected: Rect cached_region; ///< Region covered by cached_links and cached_stations. 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 last_update_number = 0; ///< Last window update number + uint64_t rebuild_counter = 0; ///< Rebuild counter Point GetStationMiddle(const Station *st) const; diff --git a/src/viewport.cpp b/src/viewport.cpp index db076d67bb..35c1a9ab5e 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -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)); - 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; + /* 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. */ diff --git a/src/viewport_func.h b/src/viewport_func.h index 801ee209c0..6f8c336949 100644 --- a/src/viewport_func.h +++ b/src/viewport_func.h @@ -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); diff --git a/src/viewport_type.h b/src/viewport_type.h index e2302ddc33..d2476b9722 100644 --- a/src/viewport_type.h +++ b/src/viewport_type.h @@ -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 land_pixel_cache;