Skip to content

Commit

Permalink
- **FastLoader** : fixed the PNG loader behavior not being similiar a…
Browse files Browse the repository at this point in the history
…s in stock. It was wrongly generating mipmaps, notably resulting in NPOT textures not showing when texture quality wasn't set to full resolution ([see issue #224](#224)).

- **FastLoader** : fixed cached PNG textures loading not using the data loaded by the threaded reader, but instead reading the file again synchronously (!). Unsurprisingly, fixing that is massively improving texture loading time.
  • Loading branch information
gotmachine committed May 5, 2024
1 parent 28c174d commit 1d15409
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 51 deletions.
2 changes: 1 addition & 1 deletion GameData/KSPCommunityFixes/KSPCommunityFixes.version
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"NAME": "KSPCommunityFixes",
"URL": "https://raw.githubusercontent.com/KSPModdingLibs/KSPCommunityFixes/master/GameData/KSPCommunityFixes/KSPCommunityFixes.version",
"DOWNLOAD": "https://github.com/KSPModdingLibs/KSPCommunityFixes/releases",
"VERSION": {"MAJOR": 1, "MINOR": 35, "PATCH": 0, "BUILD": 0},
"VERSION": {"MAJOR": 1, "MINOR": 35, "PATCH": 1, "BUILD": 0},
"KSP_VERSION": {"MAJOR": 1, "MINOR": 12, "PATCH": 5},
"KSP_VERSION_MIN": {"MAJOR": 1, "MINOR": 8, "PATCH": 0},
"KSP_VERSION_MAX": {"MAJOR": 1, "MINOR": 12, "PATCH": 5}
Expand Down
13 changes: 13 additions & 0 deletions KSPCommunityFixes/Library/StaticHelpers.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using HarmonyLib;
using KSP.UI.TooltipTypes;
using UnityEngine;
Expand Down Expand Up @@ -78,5 +79,17 @@ public static bool EditPartModuleKSPFieldAttributes(Type partModuleType, string

return false;
}

/// <summary>
/// Evaluate whether a given integral value is a power of 2.
/// </summary>
/// <param name="value">The value.</param>
public static bool IsPowerOfTwo(int value) => (value & (value - 1)) == 0 && value > 0;

/// <summary>
/// Evaluate whether a given integral value is a power of 2.
/// </summary>
/// <param name="value">The value.</param>
public static bool IsPowerOfTwo(uint value) => (value & (value - 1)) == 0 && value != 0;
}
}
75 changes: 27 additions & 48 deletions KSPCommunityFixes/Performance/FastLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ internal class KSPCFFastLoader : MonoBehaviour
private static string ConfigPath => Path.Combine(ModPath, "PluginData", "PNGTextureCache.cfg");

private bool userOptInChoiceDone;
private const string textureCacheVersion = "V2";
private string configPath;
private string textureCachePath;
private string textureCacheDataPath;
Expand Down Expand Up @@ -1364,12 +1365,7 @@ private TextureInfo LoadMBM()
return new TextureInfo(file, texture2D, isNormalMap, false, true);
}

private static string[] noMipMapsPNGTexturePaths =
{
Path.DirectorySeparatorChar + "Icons" + Path.DirectorySeparatorChar,
Path.DirectorySeparatorChar + "Tutorials" + Path.DirectorySeparatorChar,
Path.DirectorySeparatorChar + "SimpleIcons" + Path.DirectorySeparatorChar
};
private static string mipMapsPNGTexturePath = Path.DirectorySeparatorChar + "Flags" + Path.DirectorySeparatorChar;

private TextureInfo LoadPNG()
{
Expand All @@ -1378,38 +1374,11 @@ private TextureInfo LoadPNG()
SetError("Invalid PNG file");
return null;
}

bool canCompress = width % 4 == 0 && height % 4 == 0;
bool isNormalMap = file.name.EndsWith("NRM");
bool nonReadable = false;
bool hasMipMaps = true;


if (isNormalMap)
{
hasMipMaps = false;
}
else
{
// KSPCF optimization : don't keep cargo icons in memory, don't generate mipmaps for them
if (file.fullPath.Contains("@thumbs"))
{
nonReadable = true;
hasMipMaps = false;
}
else
{
// stock behavior : don't generate mipmaps for a few special folders
for (int i = 0; i < noMipMapsPNGTexturePaths.Length; i++)
{
if (file.fullPath.Contains(noMipMapsPNGTexturePaths[i]))
{
hasMipMaps = false;
break;
}
}
}
}
bool isNormalMap = file.name.EndsWith("NRM");
bool nonReadable = file.fullPath.Contains("@thumbs"); // KSPCF optimization : don't keep cargo icons in memory
bool hasMipMaps = file.fullPath.Contains(mipMapsPNGTexturePath); // only generate mipmaps for flags (stock behavior)
bool canCompress = hasMipMaps ? StaticHelpers.IsPowerOfTwo(width) && StaticHelpers.IsPowerOfTwo(height) : width % 4 == 0 && height % 4 == 0;

// don't initially compress normal textures, as we need to swizzle the raw data first
TextureFormat textureFormat;
Expand Down Expand Up @@ -1460,7 +1429,7 @@ private TextureInfo LoadPNG()

private TextureInfo LoadPNGCached()
{
if (cachedTextureInfo.TryCreateTexture(out Texture2D texture))
if (cachedTextureInfo.TryCreateTexture(buffer, out Texture2D texture))
return new TextureInfo(file, texture, cachedTextureInfo.normal, cachedTextureInfo.readable, true);

buffer = System.IO.File.ReadAllBytes(file.fullPath);
Expand Down Expand Up @@ -1687,9 +1656,9 @@ private static Texture2D BitmapToCompressedNormalMapFast(Texture2D original, boo
normalMap.SetPixels32(pixels);
}

if (normalMap.width % 4 == 0 && normalMap.height % 4 == 0)
// Unity can't convert NPOT textures to DXT5 with mipmaps
if (StaticHelpers.IsPowerOfTwo(normalMap.width) && StaticHelpers.IsPowerOfTwo(normalMap.height))
{
normalMap.Apply(true, false);
normalMap.Compress(false);
normalMap.Apply(true, makeNoLongerReadable);
}
Expand Down Expand Up @@ -2014,11 +1983,21 @@ private void SetupTextureCache()
else if (File.Exists(textureCacheDataPath))
{
string[] textureCacheDataContent = File.ReadAllLines(textureCacheDataPath);
foreach (string json in textureCacheDataContent)

if (textureCacheDataContent.Length > 0 && textureCacheDataContent[0].StartsWith(textureCacheVersion))
{
for (int i = 1; i < textureCacheDataContent.Length; i++)
{
string json = textureCacheDataContent[i];
CachedTextureInfo cachedTextureInfo = JsonUtility.FromJson<CachedTextureInfo>(json);
textureCacheData.Add(cachedTextureInfo.name, cachedTextureInfo);
textureDataIds.Add(cachedTextureInfo.id);
}
}
else
{
CachedTextureInfo cachedTextureInfo = JsonUtility.FromJson<CachedTextureInfo>(json);
textureCacheData.Add(cachedTextureInfo.name, cachedTextureInfo);
textureDataIds.Add(cachedTextureInfo.id);
Directory.Delete(textureCachePath, true);
Directory.CreateDirectory(textureCachePath);
}
}
}
Expand Down Expand Up @@ -2052,7 +2031,8 @@ private void WriteTextureCache()
{
File.Delete(textureCacheDataPath);

List<string> textureCacheDataContent = new List<string>(textureCacheData.Count);
List<string> textureCacheDataContent = new List<string>(textureCacheData.Count + 1);
textureCacheDataContent.Add(textureCacheVersion);

foreach (CachedTextureInfo cachedTextureInfo in textureCacheData.Values)
if (cachedTextureInfo.loaded)
Expand Down Expand Up @@ -2113,13 +2093,12 @@ public void SaveRawTextureData(Texture2D texture)
File.WriteAllBytes(Path.Combine(loader.textureCachePath, id.ToString()), rawData);
}

public bool TryCreateTexture(out Texture2D texture)
public bool TryCreateTexture(byte[] buffer, out Texture2D texture)
{
try
{
texture = new Texture2D(width, height, GraphicsFormat.RGBA_DXT5_UNorm, mipCount, mipCount == 1 ? TextureCreationFlags.None : TextureCreationFlags.MipChain);
byte[] rawData = File.ReadAllBytes(Path.Combine(loader.textureCachePath, id.ToString()));
texture.LoadRawTextureData(rawData);
texture.LoadRawTextureData(buffer);
texture.Apply(false, !readable);
loaded = true;
return true;
Expand Down
4 changes: 2 additions & 2 deletions KSPCommunityFixes/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
// Revision
//
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.35.0.0")]
[assembly: AssemblyFileVersion("1.35.1.0")]

[assembly: KSPAssembly("KSPCommunityFixes", 1, 35, 0)]
[assembly: KSPAssembly("KSPCommunityFixes", 1, 35, 1)]
[assembly: KSPAssemblyDependency("MultipleModulePartAPI", 1, 0, 0)]
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,10 @@ If doing so in the `Debug` configuration and if your KSP install is modified to

### Changelog

##### 1.35.1
- **FastLoader** : fixed the PNG loader behavior not being similiar as in stock. It was wrongly generating mipmaps, notably resulting in NPOT textures not showing when texture quality wasn't set to full resolution ([see issue #224](https://github.com/KSPModdingLibs/KSPCommunityFixes/issues/224)).
- **FastLoader** : fixed cached PNG textures loading not using the data loaded by the threaded reader, but instead reading the file again synchronously (!). Unsurprisingly, fixing that is massively improving texture loading time.

##### 1.35.0
- New KSP performance patch : [**OptimizedModuleRaycasts**](https://github.com/KSPModdingLibs/KSPCommunityFixes/issues/216) [KSP 1.12.3 - 1.12.5] : Improve engine exhaust damage and solar panel line of sight raycasts performance by avoiding extra physics state synchronization and caching solar panels scaled space raycasts results.
- New KSP QoL/performance patch : [**OptionalMakingHistoryDLCFeatures**](https://github.com/KSPModdingLibs/KSPCommunityFixes/issues/218) [KSP 1.12.3 - 1.12.5] : Allow to disable the Making History DLC mission editor and additional launch sites features to decrease memory usage and increase loading speed. The Making History parts will still be available. Can be toggled from the KSPCF in-game settings (requires a restart), or from a MM patch (see `Settings.cfg`)
Expand Down

0 comments on commit 1d15409

Please sign in to comment.