DSprtn / GTFO_VR_Plugin

A plugin to add full roomscale Virtual Reality support to your favorite game!
MIT License
143 stars 13 forks source link

Fix NavMarker performance creep #35

Closed Nordskog closed 2 years ago

Nordskog commented 2 years ago

What?

NavMarkers are processed every frame to orient them in world-space, replacing the original logic of NavMarkerLayer.AfterCameraUpdate() in UpdateAllNavMarkers() In order to do this we iterate through NavMarkerLayer.m_markersActive, which has two problems:

  1. Accessing the elements of this particular List is very expensive for some reason.
  2. GTFO is broken and never removes destroyed markers from this list.

By their powers combined, performance slowly tanks as the number of null elements grows into the hundreds and probably eventually thousands. You can hit 100 markers within a few minutes if you wake a couple of rooms with a biotracker, and by the time you pass 200 the performance penalty is up to 2ms.

Changes

Maintain our own list of markers by patching a few of NavMarkerLayer's functions for adding and removing them. Accessing this has no performance penalty, and we can clean the nulls entries too.

New patches: NavMarkerLayer.PrepareMarker() - Called on creation of all markers, add to our list here. This includes Locator makers that we don't want to mess with so... NavMarkerLayer.PlaceLocatorMarker() - Gets a reference to Locator markers on creation and removes them from our list NavMarkerLayer.RemoveMarker() - Remove markers from list. Value passed to this is always null, so we also prune our list of null values here. NavMarkerLayer.RemoveMarkerForGO() - Find marker with m_trackingObj matching input and remove from list. Not used as far as I can tell, but implemented anyway.

The function that does all work, UpdateAllNavMarkers has also been modified to improve performance ( by you ). Notably some expensive calculations are cached, and any logic checking/setting the text shader has been removed. Still seems to work all the same.

R6 / R7 code also includes NavMarker.UpdateTrackingDimension() and UpdateEnabledPerDimensionState() and the end of NavMarker.AfterCameraUpdate()'s original logic. These two mostly only modify NavMarker.m_trackingPosAtLastDimensionUpdate.x/y/z, as well as toggling the marker GO depending on the dimension. Their absence is likely responsible for player name/info vanishing when outside ( different dimension ), and adding them doesn't seem like it would nor has it broken anything, so I've added them too.

DSprtn commented 2 years ago

Curious that accessing lists with interop kills performance so much. Nice job!