Improve texture export performance

This commit is contained in:
ds5678 2025-10-06 02:59:42 -07:00
parent 58d0496f3c
commit 39f9ea7592
3 changed files with 112 additions and 139 deletions

View File

@ -3,7 +3,6 @@
<PropertyGroup>
<OutputPath>..\0Bins\Other\AssetRipper.Export.Modules.Textures\$(Configuration)\</OutputPath>
<IntermediateOutputPath>..\0Bins\obj\AssetRipper.Export.Modules.Textures\$(Configuration)\</IntermediateOutputPath>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
@ -14,7 +13,7 @@
<PackageReference Include="AssetRipper.Conversions.Crunch" Version="1.0.3" />
<PackageReference Include="AssetRipper.Conversions.FastPng" Version="1.1.0" />
<PackageReference Include="AssetRipper.Conversions.UnityCrunch" Version="1.0.3" />
<PackageReference Include="AssetRipper.TextureDecoder" Version="2.3.0" />
<PackageReference Include="AssetRipper.TextureDecoder" Version="2.4.0" />
<PackageReference Include="AssetRipper.Tpk" Version="1.1.0" />
<PackageReference Include="StbImageWriteSharp" Version="1.16.7" />
</ItemGroup>

View File

@ -9,25 +9,25 @@ public static class TerrainHeatmap
{
public static DirectBitmap GetBitmap(ITerrainData terrain)
{
DirectBitmap bitmap = new DirectBitmap<ColorBGRA32, byte>(
DirectBitmap bitmap = new DirectBitmap<ColorRGBA<byte>, byte>(
Math.Max(terrain.Heightmap.Width, terrain.Heightmap.Resolution),
Math.Max(terrain.Heightmap.Height, terrain.Heightmap.Resolution),
1,
GetBGRA32Data(terrain));
GetRGBA32Data(terrain));
bitmap.FlipY();
return bitmap;
}
public static byte[] GetBGRA32Data(ITerrainData terrain)
public static byte[] GetRGBA32Data(ITerrainData terrain)
{
AssetList<short> heights = terrain.Heightmap.Heights;
byte[] result = new byte[heights.Count * 4];
for (int y = 0; y < heights.Count; y++)
{
Color32 color = (Color32)ConvertToColor((float)heights[y] / short.MaxValue);
result[4 * y] = color.B;
result[4 * y] = color.R;
result[4 * y + 1] = color.G;
result[4 * y + 2] = color.R;
result[4 * y + 2] = color.B;
result[4 * y + 3] = byte.MaxValue; //small optimization
}
return result;

View File

@ -224,7 +224,7 @@ public static class TextureConverter
TextureFormat.RG32_SIGNED => TryConvertToBitmap<ColorRG<short>, short>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
TextureFormat.RGB48_SIGNED => TryConvertToBitmap<ColorRGB<short>, short>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
TextureFormat.RGBA64_SIGNED => TryConvertToBitmap<ColorRGBA<short>, short>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
_ => TryConvertToBitmap<ColorBGRA32, byte>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
_ => TryConvertToBitmap<ColorRGBA<byte>, byte>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
};
}
@ -289,149 +289,75 @@ public static class TextureConverter
ReadOnlySpan<byte> inputSpan = uncompressedSpan.Slice(i * bytesPerLayer, bytesPerLayer);
Span<byte> outputSpan = bitmap.Bits.Slice(i * outputSize, outputSize);
if (typeof(TColor) == typeof(ColorBGRA32))
if (!TryDecodeTexture<TColor, TChannelValue>(textureFormat, width, height, inputSpan, outputSpan))
{
if (!TryDecodeTexture(textureFormat, width, height, inputSpan, outputSpan))
{
bitmap = DirectBitmap.Empty;
return false;
}
}
else
{
if (!TryDecodeTexture<TColor, TChannelValue>(textureFormat, width, height, inputSpan, outputSpan))
{
bitmap = DirectBitmap.Empty;
return false;
}
bitmap = DirectBitmap.Empty;
return false;
}
}
return true;
}
private static bool TryDecodeTexture(TextureFormat textureFormat, int width, int height, ReadOnlySpan<byte> inputSpan, Span<byte> outputSpan)
{
switch (textureFormat)
{
//ASTC
case TextureFormat.ASTC_RGB_4x4:
case TextureFormat.ASTC_RGBA_4x4:
AstcDecoder.DecodeASTC(inputSpan, width, height, 4, 4, outputSpan);
return true;
case TextureFormat.ASTC_RGB_5x5:
case TextureFormat.ASTC_RGBA_5x5:
AstcDecoder.DecodeASTC(inputSpan, width, height, 5, 5, outputSpan);
return true;
case TextureFormat.ASTC_RGB_6x6:
case TextureFormat.ASTC_RGBA_6x6:
AstcDecoder.DecodeASTC(inputSpan, width, height, 6, 6, outputSpan);
return true;
case TextureFormat.ASTC_RGB_8x8:
case TextureFormat.ASTC_RGBA_8x8:
AstcDecoder.DecodeASTC(inputSpan, width, height, 8, 8, outputSpan);
return true;
case TextureFormat.ASTC_RGB_10x10:
case TextureFormat.ASTC_RGBA_10x10:
AstcDecoder.DecodeASTC(inputSpan, width, height, 10, 10, outputSpan);
return true;
case TextureFormat.ASTC_RGB_12x12:
case TextureFormat.ASTC_RGBA_12x12:
AstcDecoder.DecodeASTC(inputSpan, width, height, 12, 12, outputSpan);
return true;
//ATC
case TextureFormat.ATC_RGB4:
AtcDecoder.DecompressAtcRgb4(inputSpan, width, height, outputSpan);
return true;
case TextureFormat.ATC_RGBA8:
AtcDecoder.DecompressAtcRgba8(inputSpan, width, height, outputSpan);
return true;
//BC
case TextureFormat.BC4:
Bc4.Decompress(inputSpan, width, height, outputSpan);
return true;
case TextureFormat.BC5:
Bc5.Decompress(inputSpan, width, height, outputSpan);
return true;
case TextureFormat.BC6H:
Bc6h.Decompress(inputSpan, width, height, false, outputSpan);
return true;
case TextureFormat.BC7:
Bc7.Decompress(inputSpan, width, height, outputSpan);
return true;
//ETC
case TextureFormat.ETC_RGB4:
case TextureFormat.ETC_RGB4_3DS:
case TextureFormat.ETC_RGB4Crunched:
EtcDecoder.DecompressETC(inputSpan, width, height, outputSpan);
return true;
case TextureFormat.EAC_R:
EtcDecoder.DecompressEACRUnsigned(inputSpan, width, height, outputSpan);
return true;
case TextureFormat.EAC_R_SIGNED:
EtcDecoder.DecompressEACRSigned(inputSpan, width, height, outputSpan);
return true;
case TextureFormat.EAC_RG:
EtcDecoder.DecompressEACRGUnsigned(inputSpan, width, height, outputSpan);
return true;
case TextureFormat.EAC_RG_SIGNED:
EtcDecoder.DecompressEACRGSigned(inputSpan, width, height, outputSpan);
return true;
case TextureFormat.ETC2_RGB:
EtcDecoder.DecompressETC2(inputSpan, width, height, outputSpan);
return true;
case TextureFormat.ETC2_RGBA1:
EtcDecoder.DecompressETC2A1(inputSpan, width, height, outputSpan);
return true;
case TextureFormat.ETC2_RGBA8:
case TextureFormat.ETC_RGBA8_3DS:
case TextureFormat.ETC2_RGBA8Crunched:
EtcDecoder.DecompressETC2A8(inputSpan, width, height, outputSpan);
return true;
//PVRTC
case TextureFormat.PVRTC_RGB2:
case TextureFormat.PVRTC_RGBA2:
PvrtcDecoder.DecompressPVRTC(inputSpan, width, height, true, outputSpan);
return true;
case TextureFormat.PVRTC_RGB4:
case TextureFormat.PVRTC_RGBA4:
PvrtcDecoder.DecompressPVRTC(inputSpan, width, height, false, outputSpan);
return true;
case TextureFormat.BGRA32_14:
case TextureFormat.BGRA32_37:
//This needs sliced because the inputSpan can have mips.
inputSpan[..outputSpan.Length].CopyTo(outputSpan);
return true;
default:
return TryDecodeTexture<ColorBGRA32, byte>(textureFormat, width, height, inputSpan, outputSpan);
}
}
private static bool TryDecodeTexture<TColor, TChannelValue>(TextureFormat textureFormat, int width, int height, ReadOnlySpan<byte> inputSpan, Span<byte> outputSpan)
where TColor : unmanaged, IColor<TChannelValue>
where TChannelValue : unmanaged
{
switch (textureFormat)
{
//ASTC
case TextureFormat.ASTC_RGB_4x4:
case TextureFormat.ASTC_RGBA_4x4:
AstcDecoder.DecodeASTC<TColor, TChannelValue>(inputSpan, width, height, 4, 4, outputSpan);
return true;
case TextureFormat.ASTC_RGB_5x5:
case TextureFormat.ASTC_RGBA_5x5:
AstcDecoder.DecodeASTC<TColor, TChannelValue>(inputSpan, width, height, 5, 5, outputSpan);
return true;
case TextureFormat.ASTC_RGB_6x6:
case TextureFormat.ASTC_RGBA_6x6:
AstcDecoder.DecodeASTC<TColor, TChannelValue>(inputSpan, width, height, 6, 6, outputSpan);
return true;
case TextureFormat.ASTC_RGB_8x8:
case TextureFormat.ASTC_RGBA_8x8:
AstcDecoder.DecodeASTC<TColor, TChannelValue>(inputSpan, width, height, 8, 8, outputSpan);
return true;
case TextureFormat.ASTC_RGB_10x10:
case TextureFormat.ASTC_RGBA_10x10:
AstcDecoder.DecodeASTC<TColor, TChannelValue>(inputSpan, width, height, 10, 10, outputSpan);
return true;
case TextureFormat.ASTC_RGB_12x12:
case TextureFormat.ASTC_RGBA_12x12:
AstcDecoder.DecodeASTC<TColor, TChannelValue>(inputSpan, width, height, 12, 12, outputSpan);
return true;
//ATC
case TextureFormat.ATC_RGB4:
AtcDecoder.DecompressAtcRgb4<TColor, TChannelValue>(inputSpan, width, height, outputSpan);
return true;
case TextureFormat.ATC_RGBA8:
AtcDecoder.DecompressAtcRgba8<TColor, TChannelValue>(inputSpan, width, height, outputSpan);
return true;
//BC
case TextureFormat.BC4:
Bc4.Decompress<TColor, TChannelValue>(inputSpan, width, height, outputSpan);
return true;
case TextureFormat.BC5:
Bc5.Decompress<TColor, TChannelValue>(inputSpan, width, height, outputSpan);
return true;
case TextureFormat.BC6H:
Bc6h.Decompress<TColor, TChannelValue>(inputSpan, width, height, false, outputSpan);
return true;
case TextureFormat.BC7:
Bc7.Decompress<TColor, TChannelValue>(inputSpan, width, height, outputSpan);
return true;
//DXT
case TextureFormat.DXT1:
case TextureFormat.DXT1Crunched:
@ -447,6 +373,54 @@ public static class TextureConverter
DxtDecoder.DecompressDXT5<TColor, TChannelValue>(inputSpan, width, height, outputSpan);
return true;
//ETC
case TextureFormat.ETC_RGB4:
case TextureFormat.ETC_RGB4_3DS:
case TextureFormat.ETC_RGB4Crunched:
EtcDecoder.DecompressETC<TColor, TChannelValue>(inputSpan, width, height, outputSpan);
return true;
case TextureFormat.EAC_R:
EtcDecoder.DecompressEACRUnsigned<TColor, TChannelValue>(inputSpan, width, height, outputSpan);
return true;
case TextureFormat.EAC_R_SIGNED:
EtcDecoder.DecompressEACRSigned<TColor, TChannelValue>(inputSpan, width, height, outputSpan);
return true;
case TextureFormat.EAC_RG:
EtcDecoder.DecompressEACRGUnsigned<TColor, TChannelValue>(inputSpan, width, height, outputSpan);
return true;
case TextureFormat.EAC_RG_SIGNED:
EtcDecoder.DecompressEACRGSigned<TColor, TChannelValue>(inputSpan, width, height, outputSpan);
return true;
case TextureFormat.ETC2_RGB:
EtcDecoder.DecompressETC2<TColor, TChannelValue>(inputSpan, width, height, outputSpan);
return true;
case TextureFormat.ETC2_RGBA1:
EtcDecoder.DecompressETC2A1<TColor, TChannelValue>(inputSpan, width, height, outputSpan);
return true;
case TextureFormat.ETC2_RGBA8:
case TextureFormat.ETC_RGBA8_3DS:
case TextureFormat.ETC2_RGBA8Crunched:
EtcDecoder.DecompressETC2A8<TColor, TChannelValue>(inputSpan, width, height, outputSpan);
return true;
//PVRTC
case TextureFormat.PVRTC_RGB2:
case TextureFormat.PVRTC_RGBA2:
PvrtcDecoder.DecompressPVRTC<TColor, TChannelValue>(inputSpan, width, height, true, outputSpan);
return true;
case TextureFormat.PVRTC_RGB4:
case TextureFormat.PVRTC_RGBA4:
PvrtcDecoder.DecompressPVRTC<TColor, TChannelValue>(inputSpan, width, height, false, outputSpan);
return true;
//YUY2
case TextureFormat.YUY2:
Yuy2Decoder.DecompressYUY2<TColor, TChannelValue>(inputSpan, width, height, outputSpan);