Skip to content

Commit

Permalink
Potential fix for issue #216
Browse files Browse the repository at this point in the history
Optimization of raycasts in ModuleEngines.EngineExhaustDamage() and ModuleDeployableSolarPanel.CalculateTrackingLOS() :
- Only synchronize transforms on the first raycast from any module, mainly relevant when something else is moving transforms in between calls, which is often the case for active engines with gimbals.
- Cached ScaledSpace raycast results for solar panels : call time is divided by between 4 (when blocked by a scaled space object) and 2 (when not blocked)
  • Loading branch information
gotmachine committed Mar 25, 2024
1 parent e7cd641 commit 84652da
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 0 deletions.
2 changes: 2 additions & 0 deletions GameData/KSPCommunityFixes/Settings.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,8 @@ KSP_COMMUNITY_FIXES
// Allow a min value of 0.02 instead of 0.03 for the "Max Physics Delta-Time Per Frame" main menu setting.
LowerMinPhysicsDTPerFrame = true
OptimizedModuleRaycasts = true
// ##########################
// Modding
// ##########################
Expand Down
1 change: 1 addition & 0 deletions KSPCommunityFixes/KSPCommunityFixes.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@
<Compile Include="Modding\ReflectionTypeLoadExceptionHandler.cs" />
<Compile Include="Performance\FewerSaves.cs" />
<Compile Include="Performance\ConfigNodePerf.cs" />
<Compile Include="Performance\OptimizedModuleRaycasts.cs" />
<Compile Include="Performance\PQSCoroutineLeak.cs" />
<Compile Include="Performance\PQSUpdateNoMemoryAlloc.cs" />
<Compile Include="Performance\ProgressTrackingSpeedBoost.cs" />
Expand Down
152 changes: 152 additions & 0 deletions KSPCommunityFixes/Performance/OptimizedModuleRaycasts.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
using HarmonyLib;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;

namespace KSPCommunityFixes.Performance
{
internal class OptimizedModuleRaycasts : BasePatch
{
private static readonly WaitForFixedUpdate waitForFixedUpdate = new WaitForFixedUpdate();
private static bool partModulesSyncedOnceInFixedUpdate = false;

protected override void ApplyPatches(List<PatchInfo> patches)
{
patches.Add(
new PatchInfo(PatchMethodType.Transpiler,
AccessTools.Method(typeof(ModuleEngines), nameof(ModuleEngines.EngineExhaustDamage)),
this));

patches.Add(
new PatchInfo(PatchMethodType.Prefix,
AccessTools.Method(typeof(ModuleDeployableSolarPanel), nameof(ModuleDeployableSolarPanel.CalculateTrackingLOS)),
this));

KSPCommunityFixes.Instance.StartCoroutine(ResetSyncOnFixedEnd());
}

static IEnumerator ResetSyncOnFixedEnd()
{
while (true)
{
partModulesSyncedOnceInFixedUpdate = false;
lastVesselId = 0;
lastTrackingTransformId = 0;
yield return waitForFixedUpdate;
}
}

static IEnumerable<CodeInstruction> ModuleEngines_EngineExhaustDamage_Transpiler(IEnumerable<CodeInstruction> instructions)
{
MethodInfo m_Physics_RayCast = AccessTools.Method(typeof(Physics), nameof(Physics.Raycast), new[] { typeof(Vector3), typeof(Vector3), typeof(RaycastHit).MakeByRefType(), typeof(float), typeof(int) });
MethodInfo m_RaycastNoSync = AccessTools.Method(typeof(OptimizedModuleRaycasts), nameof(RaycastNoSync));

foreach (CodeInstruction instruction in instructions)
{
if (instruction.Calls(m_Physics_RayCast))
{
instruction.operand = m_RaycastNoSync;
}

yield return instruction;
}
}

private static int lastVesselId;
private static int lastTrackingTransformId;
private static bool lastHasLoS;
private static string lastBlocker;

private static bool ModuleDeployableSolarPanel_CalculateTrackingLOS_Prefix(ModuleDeployableSolarPanel __instance, Vector3 trackingDirection, ref string blocker, out bool __result)
{
if (__instance.part.ShieldedFromAirstream && __instance.applyShielding)
{
blocker = "aero shielding";
__result = false;
return false;
}

int trackingTransformId = __instance.trackingTransformLocal.GetInstanceID();
int vesselId = __instance.vessel.GetInstanceID();
if (lastTrackingTransformId == trackingTransformId && lastVesselId == vesselId)
{
if (!lastHasLoS)
{
__result = false;
blocker = lastBlocker;
return false;
}
}
else
{
lastTrackingTransformId = trackingTransformId;
lastVesselId = vesselId;

Vector3 scaledVesselPos = ScaledSpace.LocalToScaledSpace(__instance.vessel.transform.position);
Vector3 scaledDirection = (ScaledSpace.LocalToScaledSpace(__instance.trackingTransformLocal.position) - scaledVesselPos).normalized;

if (Physics.Raycast(scaledVesselPos, scaledDirection, out RaycastHit scaledHit, float.MaxValue, __instance.planetLayerMask) && scaledHit.transform.NotDestroyedRefNotEquals(__instance.trackingTransformScaled))
{
__instance.hit = scaledHit; // just to ensure this is populated
lastBlocker = scaledHit.transform.gameObject.name; // allocates a string
blocker = lastBlocker;
lastHasLoS = false;
__result = false;
return false;
}

lastHasLoS = true;
lastBlocker = null;
}

Vector3 localPanelPos = __instance.secondaryTransform.position + trackingDirection * __instance.raycastOffset;
__result = !RaycastNoSync(localPanelPos, trackingDirection, out RaycastHit localhit, float.MaxValue, __instance.defaultLayerMask);
__instance.hit = localhit; // just to ensure this is populated

if (!__result
&& UIPartActionController.Instance.IsNotNullOrDestroyed()
&& UIPartActionController.Instance.ItemListContains(__instance.part, false)
&& localhit.transform.gameObject.IsNotNullOrDestroyed())
{
GameObject hitObject = localhit.transform.gameObject;
if (!ReferenceEquals(hitObject.GetComponent<PQ>(), null))
{
blocker = ModuleDeployableSolarPanel.cacheAutoLOC_438839;
}
else
{
Part partUpwardsCached = FlightGlobals.GetPartUpwardsCached(hitObject);
if (partUpwardsCached.IsNotNullOrDestroyed())
{
blocker = partUpwardsCached.partInfo.title;
}
else
{
string tag = hitObject.tag; // allocates a string
if (tag.Contains("KSC"))
blocker = ResearchAndDevelopment.GetMiniBiomedisplayNameByUnityTag(tag, true);
else
blocker = hitObject.name;
}
}
}

return false;
}

public static bool RaycastNoSync(Vector3 origin, Vector3 direction, out RaycastHit hitInfo, float maxDistance, int layerMask)
{
if (!partModulesSyncedOnceInFixedUpdate)
{
Physics.SyncTransforms();
partModulesSyncedOnceInFixedUpdate = true;
}

Physics.autoSyncTransforms = false;
bool result = Physics.defaultPhysicsScene.Raycast(origin, direction, out hitInfo, maxDistance, layerMask);
Physics.autoSyncTransforms = true;
return result;
}
}
}

0 comments on commit 84652da

Please sign in to comment.