Skip to content

Latest commit

 

History

History
137 lines (96 loc) · 8.09 KB

File metadata and controls

137 lines (96 loc) · 8.09 KB

Performance Review Baseline

This document lists the runtime areas most likely to matter in the upcoming performance review. It is not a finding list yet; it is a map of where to measure and what behavior must be preserved.

Primary Hot Paths

DynamicDrawManager.DrawDynamicThings postfix:

  • Calls DotDrawer.DrawDots(map) during dynamic drawing.
  • Enumerates map.mapPawns.AllPawnsSpawned.
  • Performs hidden/fog filtering, rule lookup, color lookup, material lookup, edge marker math, and mesh draw calls.
  • This is the first place to profile on large colonies, animal-heavy maps, raids, and vehicle-heavy maps.

Pawn and label suppression prefixes:

  • PawnRenderer.RenderPawnAt
  • Vehicles.VehicleRenderer:RenderPawnAt
  • SelectionDrawer.DrawSelectionBracketFor
  • PawnUIOverlay.DrawPawnGUIOverlay
  • SilhouetteUtility.ShouldDrawSilhouette
  • GenMapUI.DrawPawnLabel
  • GenMapUI.DrawThingLabel

These run as RimWorld tries to draw or label things. Keep prefix decisions cheap and avoid allocations.

Camera patches:

  • CameraDriver.Update
  • CameraDriver.ApplyPositionToGameObject
  • CameraDriver.CurrentViewRect
  • CameraDriver.CurrentZoom

These are lower volume than per-pawn rendering but central to user feel. Regressions show up immediately as zoom drift, clipping, culling, wrong mouse anchoring, or odd movement speed.

Settings and editor UI:

  • Dialog_Customization.DoWindowContents() clears Caches.dotConfigCache every tick while the rule editor is open.
  • The editor performs per-row layout and color/mode UI work.
  • This is less critical during gameplay, but it can matter for users with many custom rules.

Current Cache Behavior

FastUI is frame-scoped and avoids repeating RimWorld UI coordinate calls in the same frame.

MarkerDecisionCache is frame-scoped and stores one marker decision per pawn thingIDNumber. It shares the expensive decision work between the dynamic draw postfix and vanilla-rendering suppression prefixes.

Caches.dotConfigCache and Caches.shouldShowLabelCache are quota-based. Each cached entry is refreshed after 60 retrievals, not by tick or frame. Entries are mutable so repeated hits do not replace dictionary values just to increment the retrieval count. This can reduce repeated rule scans but can also keep stale rule decisions briefly after state changes.

MarkerCache holds per-pawn Unity materials and refreshes entries only when the marker mode, custom marker name, or outline factor no longer matches the current rule/settings state. It destroys old materials through MaterialAllocator.Destroy(). Better-silhouette textures are copied into cutout-mask textures before they are passed into the Camera+ bordered shader, because RimWorld's silhouette path relies on alpha cutout behavior that Camera+ otherwise loses when it reuses only material.mainTexture.

cachedMainColors stores sampled texture colors by pawn runtime type and body graphic path. This avoids repeated texture readback/downsampling after the first sample for a graphic.

cachedCameraDelegates stores reflection-discovered optional integration methods by pawn runtime type.

Allocation And Expensive Work Candidates

Likely candidates to verify with profiling:

  • LINQ in DotDrawer.DrawDots() and rule matching paths.
  • DotConfig.conditions.All(...) for every uncached rule lookup.
  • Tools.GetMainColor(), especially texture downsampling and pixel grouping on cache misses.
  • MarkerCache.MaterialFor(), especially material creation/destruction cadence and cache key lifetime.
  • CameraDelegates reflection for new pawn runtime types.
  • Repeated UI.MapToUIPosition() and UI.UIToMapPosition() calls in edge-marker calculations.
  • File watcher reload behavior for custom marker PNGs.

Verified First-Pass Fixes

Verified during the 2026-05-15 prep pass:

  • Removed LINQ allocation from DotDrawer.DrawDots() by iterating map.mapPawns.AllPawnsSpawned directly.
  • Reused the already-fetched DotConfig through marker color/material paths.
  • Changed quota-cache hits to mutate cache entries instead of replacing dictionary values every request.
  • Cached per-material marker colors so _FillColor and _OutlineColor are only set when the colors change.
  • Replaced per-edge UI conversion scale work with one per-frame clipped-marker map scale.
  • Replaced normalized edge ray intersection with direct rectangle scale math.
  • Preserved render state in Tools.DownsampleTexture() by restoring the previous active RenderTexture.
  • Converted raw RimWorld silhouette textures into cached cutout-mask textures before drawing them with the Camera+ bordered shader.

The primary 962-pawn scenario improved from 3629.618 us to 2363.312 us average DotDrawer.DrawDots time at the 600-draw snapshot. Keep both the performance CSV and the visual closeup screenshots when evaluating later refactors; the earlier broad color corruption and the later silhouette texture-frame artifact were real regressions.

Verified Second-Pass Experiments

Verified during the follow-up performance pass:

  • Added MarkerDecisionCache so marker draw code and vanilla-suppression patches share the same per-frame decision result.
  • Deferred marker color and material lookup in DotDrawer.DrawDots() until an edge marker or in-map marker is actually needed.
  • Replaced retrieval-count material refreshes with state-based invalidation for marker mode, custom marker name, and outline factor.
  • Cleared marker materials when custom marker PNGs are reloaded.
  • Added a CAMERAPLUS_PERF-only PawnRenderer.DynamicDrawPhaseAt prefix that can measure the effect of skipping vanilla renderer phases for marker-replaced pawns.

The perf-gated run on CameraPlusPerf_962Pawns_EdgeDots reached the 600-draw snapshot with 962 visible pawns, 962 marker draws, and active edge dots. DotDrawer.DrawDots averaged 2342.038 us and DynamicDrawManager.DrawDynamicThings.Postfix averaged 2343.504 us in that snapshot. The production build still does not include the renderer-phase skip.

Correctness Constraints For Optimization

Do not break these behaviors while optimizing:

  • Zoom-to-mouse should keep the map point under the cursor stable unless Shift is held or the setting is off.
  • Effective zoom limits come from zoomedInPercent, zoomedOutPercent, and exponentiality.
  • Marker display is controlled by the first matching DotConfig rule.
  • Rule matching is an AND across all conditions in a rule.
  • Mouse proximity can reveal labels and suppress markers.
  • Edge indicators are separate from in-map markers.
  • Dead pawn hiding affects body rendering and corpse forbidden overlays.
  • Animal display must respect LabelStyle and includeNotTamedAnimals.
  • skipCustomRendering must allow other mods to bypass CameraPlus custom drawing.
  • Custom marker PNG files in the CameraPlus save-data folder must be reloadable without restarting RimWorld.
  • Save-specific marker rules live in CameraSettings; default rules for new games live in CameraPlusDefaultRules.xml.

Suggested Measurement Passes

Use at least these scenarios before and after performance changes:

  • Empty colony, default settings, close/middle/far/furthest zoom.
  • Large late-game colony with many colonists, animals, mechs, corpses, and items.
  • Raid/combat scene with drafted colonists, downed pawns, mental states, and many labels.
  • Many custom marker rules, including text predicates for hediffs, apparel, inventory, equipment, weapon, pawn name, and faction name.
  • Custom PNG marker modes.
  • Long session with map changes to check material/cache cleanup.
  • Optional compat pass with Vehicle Framework, Save Our Ship 2, and Dubs Performance Analyzer when available.

Build Verification

Minimum verification after source changes:

dotnet build Source/CameraPlus.csproj -c Release

Runtime verification should include:

  • open game to main menu and confirm asset bundle load has no errors.
  • load a save and test zoom-to-mouse, camera shake setting, zoom limits, and edge scrolling.
  • check map markers and edge markers at several zoom levels.
  • open marker-rule editor, change a rule, and verify it takes effect immediately.
  • save/load a customization preset.
  • save/load camera views with the configured shortcuts.