Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add FloatingOriginPerf patch (by gotmachine) #266

Merged
merged 5 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions GameData/KSPCommunityFixes/Settings.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,10 @@ KSP_COMMUNITY_FIXES
// framerate in large part count situations.
FlightPerf = true

// General micro-optimization of floating origin shifts. Main benefit is in large particle count situations
// but this helps a bit in other cases as well.
FloatingOriginPerf = true

// ##########################
// Modding
// ##########################
Expand Down
2 changes: 2 additions & 0 deletions KSPCommunityFixes/KSPCommunityFixes.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
<Publicize Include="UnityEngine.CoreModule:UnityEngine.Object.m_CachedPtr" />
<Publicize Include="UnityEngine.CoreModule:UnityEngine.Object.GetOffsetOfInstanceIDInCPlusPlusObject" />
<Publicize Include="UnityEngine.IMGUIModule" />
<Publicize Include="UnityEngine.CoreModule:Unity.Collections.NativeArray`1.m_Buffer" />
<Publicize Include="mscorlib:System.IO.MonoIO" />
<Publicize Include="mscorlib:System.IO.MonoIOError" />
<Publicize Include="mscorlib:System.IO.MonoIOStat" />
Expand Down Expand Up @@ -160,6 +161,7 @@
<Compile Include="Performance\AsteroidAndCometDrillCache.cs" />
<Compile Include="BugFixes\DoubleCurvePreserveTangents.cs" />
<Compile Include="BugFixes\RestoreMaxPhysicsDT.cs" />
<Compile Include="Performance\FloatingOriginPerf.cs" />
<Compile Include="Performance\PartSystemsFastUpdate.cs" />
<Compile Include="Performance\CollisionEnhancerFastUpdate.cs" />
<Compile Include="Performance\CollisionManagerFastUpdate.cs" />
Expand Down
49 changes: 45 additions & 4 deletions KSPCommunityFixes/Library/Extensions.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine;

namespace KSPCommunityFixes
Expand All @@ -23,4 +22,46 @@ public static bool IsPAWOpen(this Part part)
return part.PartActionWindow.IsNotNullOrDestroyed() && part.PartActionWindow.isActiveAndEnabled;
}
}

static class ParticleBuffer
{
private static NativeArray<ParticleSystem.Particle> particleBuffer = new NativeArray<ParticleSystem.Particle>(1000, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
private static long particleSize = UnsafeUtility.SizeOf<ParticleSystem.Particle>();

/// <summary>
/// Get a native array of active Particle in this ParticleSystem
/// </summary>
/// <param name="particleCount">The amount of particles in the system, usually ParticleSystem.particleCount. After returning, this will be the amount of active particles, which might be lower.</param>
/// <returns></returns>
public static NativeArray<ParticleSystem.Particle> GetParticlesNativeArray(this ParticleSystem particleSystem, ref int particleCount)
{
if (particleBuffer.Length < particleCount)
{
particleBuffer.Dispose();
particleBuffer = new NativeArray<ParticleSystem.Particle>(particleCount * 2, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
}
particleCount = particleSystem.GetParticles(particleBuffer);
return particleBuffer;
}

/// <summary>
/// Get the position of the particle at the specified index, avoiding to have to make copies of the (huge) particle struct
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe Vector3 GetParticlePosition(this NativeArray<ParticleSystem.Particle> buffer, int particleIndex)
{
// note : the position Vector3 is the first field of the struct
return *(Vector3*)((byte*)buffer.m_Buffer + particleIndex * particleSize);
}

/// <summary>
/// Set the position of the particle at the specified index, avoiding to have to make copies of the (huge) particle struct
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void SetParticlePosition(this NativeArray<ParticleSystem.Particle> buffer, int particleIndex, Vector3 position)
{
// note : the position Vector3 is the first field of the struct
*(Vector3*)((byte*)buffer.m_Buffer + particleIndex * particleSize) = position;
}
}
}
8 changes: 4 additions & 4 deletions KSPCommunityFixes/Performance/FlightPerf.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ internal class FlightPerf : BasePatch
{
protected override void ApplyPatches()
{
AddPatch(PatchType.Override, typeof(FlightIntegrator), nameof(FlightIntegrator.UpdateOcclusionSolar));
AddPatch(PatchType.Override, typeof(FlightIntegrator), nameof(FlightIntegrator.UpdateOcclusionBody));
AddPatch(PatchType.Override, typeof(FlightIntegrator), nameof(FlightIntegrator.UpdateMassStats));
AddPatch(PatchType.Override, typeof(VesselPrecalculate), nameof(VesselPrecalculate.CalculatePhysicsStats));
AddPatch(PatchType.Prefix, typeof(FlightIntegrator), nameof(FlightIntegrator.UpdateOcclusionSolar));
AddPatch(PatchType.Prefix, typeof(FlightIntegrator), nameof(FlightIntegrator.UpdateOcclusionBody));
AddPatch(PatchType.Prefix, typeof(FlightIntegrator), nameof(FlightIntegrator.UpdateMassStats));
AddPatch(PatchType.Prefix, typeof(VesselPrecalculate), nameof(VesselPrecalculate.CalculatePhysicsStats));

// other offenders, in aero situations :

Expand Down
265 changes: 265 additions & 0 deletions KSPCommunityFixes/Performance/FloatingOriginPerf.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
using KSPCommunityFixes.Library;
using System;
using System.Collections.Generic;
using Unity.Collections;
using UnityEngine;

namespace KSPCommunityFixes.Performance
{
internal class FloatingOriginPerf : BasePatch
{
protected override Version VersionMin => new Version(1, 12, 3);

protected override void ApplyPatches()
{
AddPatch(PatchType.Override, typeof(FloatingOrigin), nameof(FloatingOrigin.setOffset));
}

private static HashSet<int> activePS = new HashSet<int>(200);

private static void FloatingOrigin_setOffset_Override(FloatingOrigin fo, Vector3d refPos, Vector3d nonFrame)
{
if (refPos.IsInvalid())
return;

if (double.IsInfinity(refPos.sqrMagnitude))
return;

fo.SetOffsetThisFrame = true;
fo.offset = refPos;
fo.reverseoffset = new Vector3d(0.0 - refPos.x, 0.0 - refPos.y, 0.0 - refPos.z);
fo.offsetNonKrakensbane = fo.offset + nonFrame;

Vector3 offsetF = fo.offset;
Vector3 offsetNonKrakensbaneF = fo.offsetNonKrakensbane;
float deltaTime = Time.deltaTime;
Vector3 frameVelocity = Krakensbane.GetFrameVelocity();

activePS.Clear();

List<CelestialBody> bodies = FlightGlobals.Bodies;
for (int i = bodies.Count; i-- > 0;)
bodies[i].position -= fo.offsetNonKrakensbane;

bool needCoMRecalc = fo.offset.sqrMagnitude > fo.CoMRecalcOffsetMaxSqr;
List<Vessel> vessels = FlightGlobals.Vessels;

for (int i = vessels.Count; i-- > 0;)
{
Vessel vessel = vessels[i];

if (vessel.state == Vessel.State.DEAD)
continue;

Vector3d vesselOffset = (!vessel.loaded || vessel.packed || vessel.LandedOrSplashed) ? fo.offsetNonKrakensbane : fo.offset;
vessel.SetPosition((Vector3d)vessel.transform.position - vesselOffset);

if (needCoMRecalc && vessel.packed)
{
vessel.precalc.CalculatePhysicsStats();
}
else
{
vessel.CoMD -= vesselOffset;
vessel.CoM = vessel.CoMD;
}

// Update legacy (?) particle system
for (int j = vessel.parts.Count; j-- > 0;)
{
Part part = vessel.parts[j];

if (part.fxGroups.Count == 0)
continue;

bool partDataComputed = false;
bool hasRigidbody = false;
Vector3 partVelocity = Vector3.zero;

for (int k = part.fxGroups.Count; k-- > 0;)
{
FXGroup fXGroup = part.fxGroups[k];
for (int l = fXGroup.fxEmittersNewSystem.Count; l-- > 0;)
{
ParticleSystem particleSystem = fXGroup.fxEmittersNewSystem[l];

if (particleSystem.IsNullOrDestroyed())
continue;

int particleCount = particleSystem.particleCount;
if (particleCount == 0 || particleSystem.main.simulationSpace != ParticleSystemSimulationSpace.World)
continue;

activePS.Add(particleSystem.GetInstanceIDFast());

if (!partDataComputed)
{
partDataComputed = true;
Rigidbody partRB = part.Rigidbody;
if (partRB.IsNotNullOrDestroyed())
{
hasRigidbody = true;
partVelocity = partRB.velocity + frameVelocity;
}
}

NativeArray<ParticleSystem.Particle> particleBuffer = particleSystem.GetParticlesNativeArray(ref particleCount);
for (int pIdx = particleCount; pIdx-- > 0;)
{
Vector3 particlePos = particleBuffer.GetParticlePosition(pIdx);

if (hasRigidbody)
{
float scalar = UnityEngine.Random.value * deltaTime;
particlePos.Substract(partVelocity.x * scalar, partVelocity.y * scalar, partVelocity.z * scalar);
}

particlePos.Substract(offsetNonKrakensbaneF);
particleBuffer.SetParticlePosition(pIdx, particlePos);
}
particleSystem.SetParticles(particleBuffer, particleCount);
}
}
}
}

// update "new" (but just as shitty) particle system (this replicate a call to EffectBehaviour.OffsetParticles())
Vector3 systemVelocity = Vector3.zero;

List<ParticleSystem> pSystems = EffectBehaviour.emitters;
for (int i = pSystems.Count; i-- > 0;)
{
ParticleSystem particleSystem = pSystems[i];

if (particleSystem.IsNullOrDestroyed())
{
pSystems.RemoveAt(i);
continue;
}

int particleCount = particleSystem.particleCount;
if (particleCount == 0 || particleSystem.main.simulationSpace != ParticleSystemSimulationSpace.World)
continue;

activePS.Add(particleSystem.GetInstanceIDFast());

bool hasRigidbody = false;
Rigidbody rb = particleSystem.GetComponentInParent<Rigidbody>();
if (rb.IsNotNullRef())
{
hasRigidbody = true;
systemVelocity = rb.velocity + frameVelocity;
}

NativeArray<ParticleSystem.Particle> particleBuffer = particleSystem.GetParticlesNativeArray(ref particleCount);
for (int pIdx = particleCount; pIdx-- > 0;)
{
Vector3 particlePos = particleBuffer.GetParticlePosition(pIdx);

if (hasRigidbody)
{
float scalar = UnityEngine.Random.value * deltaTime;
particlePos.Substract(systemVelocity.x * scalar, systemVelocity.y * scalar, systemVelocity.z * scalar);
}

particlePos.Substract(offsetNonKrakensbaneF);
particleBuffer.SetParticlePosition(pIdx, particlePos);
}
particleSystem.SetParticles(particleBuffer, particleCount);
}

List<KSPParticleEmitter> pSystemsKSP = EffectBehaviour.kspEmitters;
for (int i = pSystemsKSP.Count; i-- > 0;)
{
KSPParticleEmitter particleSystemKSP = pSystemsKSP[i];

if (particleSystemKSP.IsNullOrDestroyed())
{
pSystemsKSP.RemoveAt(i);
continue;
}

int particleCount = particleSystemKSP.ps.particleCount;
if (particleCount == 0 || !particleSystemKSP.useWorldSpace)
continue;

activePS.Add(particleSystemKSP.ps.GetInstanceIDFast());

bool hasRigidbody = false;
Rigidbody rb = particleSystemKSP.GetComponentInParent<Rigidbody>();
if (rb.IsNotNullRef())
{
hasRigidbody = true;
systemVelocity = rb.velocity + frameVelocity;
}

NativeArray<ParticleSystem.Particle> particleBuffer = particleSystemKSP.ps.GetParticlesNativeArray(ref particleCount);
for (int pIdx = particleCount; pIdx-- > 0;)
{
Vector3 particlePos = particleBuffer.GetParticlePosition(pIdx);

if (hasRigidbody)
{
float scalar = UnityEngine.Random.value * deltaTime;
particlePos.Substract(systemVelocity.x * scalar, systemVelocity.y * scalar, systemVelocity.z * scalar);
}

particlePos.Substract(offsetNonKrakensbaneF);
particleBuffer.SetParticlePosition(pIdx, particlePos);
}
particleSystemKSP.ps.SetParticles(particleBuffer, particleCount);
}

// Just have another handling of the same stuff, sometimes overlapping, sometimes not, because why not ?
for (int i = fo.particleSystems.Count; i-- > 0;)
{
ParticleSystem particleSystem = fo.particleSystems[i];
if (particleSystem.IsNullOrDestroyed() || activePS.Contains(particleSystem.GetInstanceIDFast()))
{
fo.particleSystems.RemoveAt(i);
continue;
}

int particleCount = particleSystem.particleCount;
if (particleCount == 0)
continue;

if (particleSystem.main.simulationSpace != ParticleSystemSimulationSpace.World)
continue;

if (activePS.Contains(particleSystem.GetInstanceIDFast()))
{
fo.particleSystems.RemoveAt(i);
continue;
}

NativeArray<ParticleSystem.Particle> particleBuffer = particleSystem.GetParticlesNativeArray(ref particleCount);
for (int pIdx = particleCount; pIdx-- > 0;)
{
Vector3 particlePos = particleBuffer.GetParticlePosition(pIdx);
particlePos.Substract(offsetNonKrakensbaneF);
particleBuffer.SetParticlePosition(pIdx, particlePos);
}

particleSystem.SetParticles(particleBuffer, particleCount);
}

// more particle system (explosions, fireworks...) moving in here, but this is getting silly, I don't care anymore...
if (FlightGlobals.ActiveVessel != null && FlightGlobals.ActiveVessel.radarAltitude < fo.altToStopMovingExplosions)
FXMonger.OffsetPositions(-fo.offsetNonKrakensbane);

for (int i = FlightGlobals.physicalObjects.Count; i-- > 0;)
{
physicalObject physicalObject = FlightGlobals.physicalObjects[i];
if (physicalObject.IsNotNullOrDestroyed())
{
Transform obj = physicalObject.transform;
obj.position -= offsetF;
}
}

FloatingOrigin.TerrainShaderOffset += fo.offsetNonKrakensbane;
GameEvents.onFloatingOriginShift.Fire(fo.offset, nonFrame);
}
}
}
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ User options are available from the "ESC" in-game settings menu :<br/><img src="
- [**CollisionEnhancerFastUpdate**](https://github.com/KSPModdingLibs/KSPCommunityFixes/pull/257) [KSP 1.12.3 - 1.12.5]<br/>Optimization of the `CollisionEnhancer` component (responsible for part to terrain collision detection).
- [**PartSystemsFastUpdate**](https://github.com/KSPModdingLibs/KSPCommunityFixes/pull/257) [KSP 1.12.3 - 1.12.5]<br/>Optimization of various flight scene auxiliary subsystems : temperature gauges, highlighter, strut position tracking...
- [**MinorPerfTweaks**](https://github.com/KSPModdingLibs/KSPCommunityFixes/pull/257) [KSP 1.12.3 - 1.12.5]<br/>Various small performance patches (volume normalizer, eva module checks)
- [**FloatingOriginPerf**](https://github.com/KSPModdingLibs/KSPCommunityFixes/pull/257) [KSP 1.12.3 - 1.12.5]<br/>General micro-optimization of floating origin shifts. Main benefit is in large particle count situations (ie, launches with many engines) but this helps a bit in other cases as well.
- [**FasterPartFindTransform**](https://github.com/KSPModdingLibs/KSPCommunityFixes/pull/255) [KSP 1.12.3 - 1.12.5]<br/>Faster, and minimal GC alloc relacements for the Part FindModelTransform* and FindHeirarchyTransform* methods.

#### API and modding tools
Expand Down Expand Up @@ -209,6 +210,7 @@ If doing so in the `Debug` configuration and if your KSP install is modified to
- **CollisionEnhancerFastUpdate** : Optimization of the `CollisionEnhancer` component (responsible for part to terrain collision detection).
- **PartSystemsFastUpdate** : Optimization of various flight scene auxiliary subsystems : temperature gauges, highlighter, strut position tracking...
- **MinorPerfTweaks** : Various small performance patches (volume normalizer, eva module checks)
- **FloatingOriginPerf** : General micro-optimization of floating origin shifts. Main benefit is in large particle count situations (ie, launches with many engines) but this helps a bit in other cases as well.
- New KSP bufix : [**DragCubeLoadException**](https://github.com/KSPModdingLibs/KSPCommunityFixes/pull/232) [KSP 1.8.0 - 1.12.5] : Fix loading of drag cubes without a name failing with an IndexOutOfRangeException (contributed by @Nazfib).
- New KSP bufix : [**TimeWarpBodyCollision**](https://github.com/KSPModdingLibs/KSPCommunityFixes/pull/259) [KSP 1.12.0 - 1.12.5] : Fix timewarp rate not always being limited on SOI transistions, sometimes resulting in failure to detect an encounter/collision with the body in the next SOI (contributed by @JonnyOThan).
- New modding API improvement : [**KSPFieldEnumDesc**](https://github.com/KSPModdingLibs/KSPCommunityFixes/pull/243) [KSP 1.12.2 - 1.12.5] : Disabled by default, you can enable it with a MM patch. Adds display name and localization support for enum KSPFields. To use add `Description` attribute to the field (contributed by @siimav).
Expand Down
Loading