diff --git a/src/gfx.cpp b/src/gfx.cpp index 81b43e79e9..0c2a3708c0 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -1423,7 +1423,7 @@ void RedrawScreenRect(int left, int top, int right, int bottom) } static std::vector _dirty_viewport_occlusions; -static const ViewPort *_dirty_viewport; +static ViewPort *_dirty_viewport; static NWidgetDisplay _dirty_viewport_disp_flags; static void DrawDirtyViewport(uint occlusion, int left, int top, int right, int bottom) @@ -1677,6 +1677,9 @@ void DrawDirtyBlocks() _dirty_blocks.clear(); ++_dirty_block_colour; + + extern void ClearViewPortCaches(); + ClearViewPortCaches(); } void UnsetDirtyBlocks(int left, int top, int right, int bottom) diff --git a/src/screenshot.cpp b/src/screenshot.cpp index 9daa4f746a..ad0e096332 100644 --- a/src/screenshot.cpp +++ b/src/screenshot.cpp @@ -672,6 +672,8 @@ static void LargeWorldCallback(void *userdata, void *buf, uint y, uint pitch, ui /* Switch back to rendering to the screen */ _screen = old_screen; _screen_disable_anim = old_disable_anim; + + ClearViewPortCache(vp); } /** diff --git a/src/vehicle.cpp b/src/vehicle.cpp index e701b54970..7260feab26 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -1593,13 +1593,13 @@ void ViewportAddVehicles(DrawPixelInfo *dpi) } } -void ViewportMapDrawVehicles(DrawPixelInfo *dpi) +void ViewportMapDrawVehicles(DrawPixelInfo *dpi, ViewPort *vp) { - /* The bounding rectangle */ - const int l = dpi->left; - const int r = dpi->left + dpi->width; - const int t = dpi->top; - const int b = dpi->top + dpi->height; + /* The save rectangle */ + const int l = vp->virtual_left; + const int r = vp->virtual_left + vp->virtual_width; + const int t = vp->virtual_top; + const int b = vp->virtual_top + vp->virtual_height; /* The hash area to scan */ const ViewportHashBound vhb = GetViewportHashBound(l, r, t, b); @@ -1607,18 +1607,21 @@ void ViewportMapDrawVehicles(DrawPixelInfo *dpi) Blitter *blitter = BlitterFactory::GetCurrentBlitter(); for (int y = vhb.yl;; y = (y + (1 << 6)) & (0x3F << 6)) { for (int x = vhb.xl;; x = (x + 1) & 0x3F) { - const Vehicle *v = _vehicle_viewport_hash[x + y]; // already masked & 0xFFF + if (!HasBit(vp->map_draw_vehicles_cache.done_hash_bits[y >> 6], x)) { + SetBit(vp->map_draw_vehicles_cache.done_hash_bits[y >> 6], x); + const Vehicle *v = _vehicle_viewport_hash[x + y]; // already masked & 0xFFF - while (v != nullptr) { - if (!(v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) && (v->type != VEH_EFFECT)) { - Point pt = RemapCoords(v->x_pos, v->y_pos, v->z_pos); - if (pt.x >= l && pt.x < r && pt.y >= t && pt.y < b) { - const int pixel_x = UnScaleByZoomLower(pt.x - dpi->left, dpi->zoom); - const int pixel_y = UnScaleByZoomLower(pt.y - dpi->top, dpi->zoom); - blitter->SetPixel(dpi->dst_ptr, pixel_x, pixel_y, PC_WHITE); + while (v != nullptr) { + if (!(v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) && (v->type != VEH_EFFECT)) { + Point pt = RemapCoords(v->x_pos, v->y_pos, v->z_pos); + if (pt.x >= l && pt.x < r && pt.y >= t && pt.y < b) { + const int pixel_x = UnScaleByZoomLower(pt.x - l, dpi->zoom); + const int pixel_y = UnScaleByZoomLower(pt.y - t, dpi->zoom); + vp->map_draw_vehicles_cache.vehicle_pixels[pixel_x + (pixel_y) * vp->width] = true; + } } + v = v->hash_viewport_next; } - v = v->hash_viewport_next; } if (x == vhb.xu) break; @@ -1626,6 +1629,21 @@ void ViewportMapDrawVehicles(DrawPixelInfo *dpi) if (y == vhb.yu) break; } + + /* The drawing rectangle */ + int mask = ScaleByZoom(-1, vp->zoom); + const int dl = UnScaleByZoomLower(dpi->left - (vp->virtual_left & mask), dpi->zoom); + const int dr = UnScaleByZoomLower(dpi->left + dpi->width - (vp->virtual_left & mask), dpi->zoom); + const int dt = UnScaleByZoomLower(dpi->top - (vp->virtual_top & mask), dpi->zoom); + const int db = UnScaleByZoomLower(dpi->top + dpi->height - (vp->virtual_top & mask), dpi->zoom); + int y_ptr = vp->width * dt; + for (int y = dt; y < db; y++, y_ptr += vp->width) { + for (int x = dl; x < dr; x++) { + if (vp->map_draw_vehicles_cache.vehicle_pixels[y_ptr + x]) { + blitter->SetPixel(dpi->dst_ptr, x - dl, y - dt, PC_WHITE); + } + } + } } /** diff --git a/src/vehicle_func.h b/src/vehicle_func.h index 4927edfdfe..b8eab7103a 100644 --- a/src/vehicle_func.h +++ b/src/vehicle_func.h @@ -27,6 +27,8 @@ static const int VEHICLE_PROFIT_MIN_AGE = DAYS_IN_YEAR * 2; ///< Only vehicles older than this have a meaningful profit. static const Money VEHICLE_PROFIT_THRESHOLD = 10000; ///< Threshold for a vehicle to be considered making good profit. +struct ViewPort; + /** * Helper to check whether an image index is valid for a particular vehicle. * @tparam T The type of vehicle. @@ -126,7 +128,7 @@ void ResetVehicleColourMap(); byte GetBestFittingSubType(Vehicle *v_from, Vehicle *v_for, CargoID dest_cargo_type); void ViewportAddVehicles(DrawPixelInfo *dpi); -void ViewportMapDrawVehicles(DrawPixelInfo *dpi); +void ViewportMapDrawVehicles(DrawPixelInfo *dpi, ViewPort *vp); void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical); CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore = nullptr, bool across_only = false); diff --git a/src/viewport.cpp b/src/viewport.cpp index 8a4ce2752a..bf2fc49f78 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -334,6 +334,21 @@ static Point MapXYZToViewport(const ViewPort *vp, int x, int y, int z) return p; } +void ClearViewPortCache(ViewPort *vp) +{ + if (vp->zoom >= ZOOM_LVL_DRAW_MAP) { + memset(vp->map_draw_vehicles_cache.done_hash_bits, 0, sizeof(vp->map_draw_vehicles_cache.done_hash_bits)); + vp->map_draw_vehicles_cache.vehicle_pixels.assign(vp->map_draw_vehicles_cache.vehicle_pixels.size(), false); + } +} + +void ClearViewPortCaches() +{ + for (ViewPort *vp : _viewport_window_cache) { + ClearViewPortCache(vp); + } +} + void DeleteWindowViewport(Window *w) { if (w->viewport == nullptr) return; @@ -512,11 +527,10 @@ static void DoSetViewportPositionFillRegion(int left, int top, int width, int he DrawOverlappedWindowForAll(left, top, left + width, top + height); }; -static void DoSetViewportPosition(const Window *w, const int left, const int top, const int width, const int height) +static void DoSetViewportPosition(Window *w, const int left, const int top, const int width, const int height) { const int xo = _vp_move_offs.x; const int yo = _vp_move_offs.y; - if (xo == 0 && yo == 0) return; IncrementWindowUpdateNumber(); @@ -648,7 +662,10 @@ static void SetViewportPosition(Window *w, int x, int y, bool force_update_overl i = top + height - _screen.height; if (i >= 0) height -= i; - if (height > 0) DoSetViewportPosition((const Window *) w->z_front, left, top, width, height); + if (height > 0 && (_vp_move_offs.x != 0 || _vp_move_offs.y != 0)) { + DoSetViewportPosition((Window *) w->z_front, left, top, width, height); + ClearViewPortCache(w->viewport); + } } } @@ -3009,7 +3026,7 @@ static void ViewportProcessParentSprites() } } -void ViewportDoDraw(const ViewPort *vp, int left, int top, int right, int bottom) +void ViewportDoDraw(ViewPort *vp, int left, int top, int right, int bottom) { DrawPixelInfo *old_dpi = _cur_dpi; _cur_dpi = &_vd.dpi; @@ -3048,7 +3065,7 @@ void ViewportDoDraw(const ViewPort *vp, int left, int top, int right, int bottom if (_settings_client.gui.show_slopes_on_viewport_map) ViewportMapDraw(vp); else ViewportMapDraw(vp); } - ViewportMapDrawVehicles(&_vd.dpi); + ViewportMapDrawVehicles(&_vd.dpi, vp); if (_scrolling_viewport && _settings_client.gui.show_scrolling_viewport_on_map) ViewportMapDrawScrollingViewportBox(vp); if (vp->zoom < ZOOM_LVL_OUT_256X) ViewportAddKdtreeSigns(&_vd.dpi, true); } else { @@ -3124,7 +3141,7 @@ void ViewportDoDraw(const ViewPort *vp, int left, int top, int right, int bottom * Make sure we don't draw a too big area at a time. * If we do, the sprite sorter will run into major performance problems and the sprite memory may overflow. */ -void ViewportDrawChk(const ViewPort *vp, int left, int top, int right, int bottom) +void ViewportDrawChk(ViewPort *vp, int left, int top, int right, int bottom) { if ((vp->zoom < ZOOM_LVL_DRAW_MAP) && ((int64)ScaleByZoom(bottom - top, vp->zoom) * (int64)ScaleByZoom(right - left, vp->zoom) > (int64)(1000000 * ZOOM_LVL_BASE * ZOOM_LVL_BASE))) { if ((bottom - top) > (right - left)) { @@ -3265,6 +3282,12 @@ void UpdateViewportSizeZoom(ViewPort *vp) uint size = vp->dirty_blocks_per_row * vp->dirty_blocks_per_column; vp->dirty_blocks.assign(size, false); UpdateViewportDirtyBlockLeftMargin(vp); + if (vp->zoom >= ZOOM_LVL_DRAW_MAP) { + memset(vp->map_draw_vehicles_cache.done_hash_bits, 0, sizeof(vp->map_draw_vehicles_cache.done_hash_bits)); + vp->map_draw_vehicles_cache.vehicle_pixels.assign(vp->width * vp->height, false); + } else { + vp->map_draw_vehicles_cache.vehicle_pixels.clear(); + } } void UpdateActiveScrollingViewport(Window *w) diff --git a/src/viewport_func.h b/src/viewport_func.h index bf134b119b..9419746b12 100644 --- a/src/viewport_func.h +++ b/src/viewport_func.h @@ -23,6 +23,8 @@ static const int TILE_HEIGHT_STEP = 50; ///< One Z unit tile height difference i void SetSelectionRed(bool); +void ClearViewPortCache(ViewPort *vp); +void ClearViewPortCaches(); void DeleteWindowViewport(Window *w); void InitializeWindowViewport(Window *w, int x, int y, int width, int height, uint32 follow_flags, ZoomLevel zoom); ViewPort *IsPtInWindowViewport(const Window *w, int x, int y); @@ -73,7 +75,7 @@ void SetRedErrorSquare(TileIndex tile); void SetTileSelectSize(int w, int h); void SetTileSelectBigSize(int ox, int oy, int sx, int sy); -void ViewportDoDraw(const ViewPort *vp, int left, int top, int right, int bottom); +void ViewportDoDraw(ViewPort *vp, int left, int top, int right, int bottom); bool ScrollWindowToTile(TileIndex tile, Window *w, bool instant = false); bool ScrollWindowTo(int x, int y, int z, Window *w, bool instant = false); diff --git a/src/viewport_type.h b/src/viewport_type.h index 6209de1068..69e8fea386 100644 --- a/src/viewport_type.h +++ b/src/viewport_type.h @@ -29,6 +29,11 @@ enum ViewportMapType { VPMT_MAX = VPMT_INDUSTRY, }; +struct ViewPortMapDrawVehiclesCache { + uint64 done_hash_bits[64]; + std::vector vehicle_pixels; +}; + /** * Data structure for viewport, display of a part of the world */ @@ -54,6 +59,7 @@ struct ViewPort { uint8 dirty_block_left_margin; bool is_dirty = false; bool is_drawn = false; + ViewPortMapDrawVehiclesCache map_draw_vehicles_cache; uint GetDirtyBlockWidthShift() const { return this->GetDirtyBlockShift(); } uint GetDirtyBlockHeightShift() const { return this->GetDirtyBlockShift(); }