Refactor rendering projects
This commit is contained in:
@@ -52,7 +52,7 @@ public static class AssetHandlerExtensions
|
||||
return await handler.ExportAsync(assetStream, targetStream, options, token);
|
||||
}
|
||||
|
||||
public static async ValueTask<Result<Asset>> ReadAsync(this IAssetHandler handler, string assetFilePath, IAssetRegistry assetDatabase, CancellationToken token = default)
|
||||
public static async ValueTask<Result<Asset>> LoadAsync(this IAssetHandler handler, string assetFilePath, IAssetRegistry assetDatabase, CancellationToken token = default)
|
||||
{
|
||||
await using var sourceStream = new FileStream(assetFilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
return await handler.LoadAsync(sourceStream, assetDatabase, token);
|
||||
|
||||
@@ -4,8 +4,11 @@ using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.Image;
|
||||
using System.Buffers;
|
||||
using System.Configuration;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using TerraFX.Interop.Windows;
|
||||
using static Ghost.Editor.Core.AssetHandler.TextureAssetSettings;
|
||||
|
||||
namespace Ghost.Editor.Core.AssetHandler;
|
||||
|
||||
@@ -211,8 +214,14 @@ public class TextureAssetSettings : IAssetSettings
|
||||
{
|
||||
get; set;
|
||||
} = new SamplerSettings();
|
||||
}
|
||||
|
||||
public async ValueTask<Result<long>> WriteToStreamAsync(Stream stream, CancellationToken token = default)
|
||||
[CustomAssetHandler(ID = TextureAsset._TYPE_ID, SupportedExtensions = new[] { ".png", ".jpg", ".jpeg", ".tga", ".bmp", ".hdr" })]
|
||||
internal class TextureAssetHandler : IImportableAssetHandler
|
||||
{
|
||||
private const int _CURRENT_VERSION = 1;
|
||||
|
||||
private static async ValueTask<Result<long>> WriteSettingsToStreamAsync(TextureAssetSettings settings, Stream stream, CancellationToken token = default)
|
||||
{
|
||||
var size = Unsafe.SizeOf<BasicSettings>() + Unsafe.SizeOf<AdvancedSettings>() + Unsafe.SizeOf<SamplerSettings>();
|
||||
var tempArray = ArrayPool<byte>.Shared.Rent(size);
|
||||
@@ -220,9 +229,9 @@ public class TextureAssetSettings : IAssetSettings
|
||||
try
|
||||
{
|
||||
ref byte address = ref MemoryMarshal.GetReference(tempArray);
|
||||
Unsafe.WriteUnaligned(ref address, Basic);
|
||||
Unsafe.WriteUnaligned(ref Unsafe.Add(ref address, Unsafe.SizeOf<BasicSettings>()), Advanced);
|
||||
Unsafe.WriteUnaligned(ref Unsafe.Add(ref address, Unsafe.SizeOf<BasicSettings>() + Unsafe.SizeOf<AdvancedSettings>()), Sampler);
|
||||
Unsafe.WriteUnaligned(ref address, settings.Basic);
|
||||
Unsafe.WriteUnaligned(ref Unsafe.Add(ref address, Unsafe.SizeOf<BasicSettings>()), settings.Advanced);
|
||||
Unsafe.WriteUnaligned(ref Unsafe.Add(ref address, Unsafe.SizeOf<BasicSettings>() + Unsafe.SizeOf<AdvancedSettings>()), settings.Sampler);
|
||||
|
||||
await stream.WriteAsync(tempArray.AsMemory(0, size), token).ConfigureAwait(false);
|
||||
|
||||
@@ -238,7 +247,7 @@ public class TextureAssetSettings : IAssetSettings
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask<Result<IAssetSettings>> ReadFromStreamAsync(Stream stream, CancellationToken token = default)
|
||||
private static async ValueTask<Result<IAssetSettings>> ReadSettingsFromStreamAsync(Stream stream, CancellationToken token = default)
|
||||
{
|
||||
var size = Unsafe.SizeOf<BasicSettings>() + Unsafe.SizeOf<AdvancedSettings>() + Unsafe.SizeOf<SamplerSettings>();
|
||||
var tempArray = ArrayPool<byte>.Shared.Rent(size);
|
||||
@@ -248,9 +257,9 @@ public class TextureAssetSettings : IAssetSettings
|
||||
await stream.ReadAsync(tempArray.AsMemory(0, size), token).ConfigureAwait(false);
|
||||
|
||||
// Use index-based reads after the await to avoid 'ref across await' errors.
|
||||
var basic = Unsafe.ReadUnaligned<BasicSettings>(ref tempArray[0]);
|
||||
var basic = Unsafe.ReadUnaligned<BasicSettings>(ref tempArray[0]);
|
||||
var advanced = Unsafe.ReadUnaligned<AdvancedSettings>(ref tempArray[Unsafe.SizeOf<BasicSettings>()]);
|
||||
var sampler = Unsafe.ReadUnaligned<SamplerSettings>(ref tempArray[Unsafe.SizeOf<BasicSettings>() + Unsafe.SizeOf<AdvancedSettings>()]);
|
||||
var sampler = Unsafe.ReadUnaligned<SamplerSettings>(ref tempArray[Unsafe.SizeOf<BasicSettings>() + Unsafe.SizeOf<AdvancedSettings>()]);
|
||||
|
||||
var settings = new TextureAssetSettings
|
||||
{
|
||||
@@ -270,12 +279,6 @@ public class TextureAssetSettings : IAssetSettings
|
||||
ArrayPool<byte>.Shared.Return(tempArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[CustomAssetHandler(ID = TextureAsset._TYPE_ID, SupportedExtensions = new[] { ".png", ".jpg", ".jpeg", ".tga", ".bmp", ".hdr" })]
|
||||
internal class TextureAssetHandler : IImportableAssetHandler
|
||||
{
|
||||
private const int _CURRENT_VERSION = 1;
|
||||
|
||||
public ValueTask<Result> ExportAsync(Stream assetStream, Stream targetStream, IAssetExportOptions? options, CancellationToken token = default)
|
||||
{
|
||||
@@ -287,7 +290,9 @@ internal class TextureAssetHandler : IImportableAssetHandler
|
||||
// ---- 1. Probe image info -----------------------------------------------
|
||||
var info = ImageInfo.FromStream(sourceStream);
|
||||
if (info.BitsPerChannel <= 0)
|
||||
{
|
||||
return Result.Failure($"Unsupported image format with {info.BitsPerChannel} bits per channel.");
|
||||
}
|
||||
|
||||
var isFloat = info.BitsPerChannel > 8;
|
||||
var width = info.Width;
|
||||
@@ -329,14 +334,6 @@ internal class TextureAssetHandler : IImportableAssetHandler
|
||||
token).ConfigureAwait(false);
|
||||
|
||||
// ---- 4. Write asset file: header + settings + raw image data -----------
|
||||
// Content layout (all little-endian):
|
||||
// int32 width
|
||||
// int32 height
|
||||
// byte isFloat (0 = byte, 1 = float)
|
||||
// int32 colorComponents (cast of ColorComponents enum)
|
||||
// byte[] pixelBytes
|
||||
const int _CONTENT_HEADER_SIZE = 4 + 4 + 1 + 4; // 13 bytes
|
||||
var contentSize = _CONTENT_HEADER_SIZE + pixelBytes.Length;
|
||||
|
||||
var header = new AssetMetadata(id, TextureAsset.s_typeGuid)
|
||||
{
|
||||
@@ -344,18 +341,24 @@ internal class TextureAssetHandler : IImportableAssetHandler
|
||||
SettingsOffset = AssetMetadata.SIZE,
|
||||
};
|
||||
|
||||
// Reserve space for the header, then write settings
|
||||
targetStream.Seek(0, SeekOrigin.Begin);
|
||||
AssetMetadata.WriteToStream(targetStream, ref header);
|
||||
|
||||
targetStream.Seek(header.SettingsOffset, SeekOrigin.Begin);
|
||||
var sizeResult = await settings.WriteToStreamAsync(targetStream, token).ConfigureAwait(false);
|
||||
var sizeResult = await WriteSettingsToStreamAsync(settings, targetStream, token).ConfigureAwait(false);
|
||||
if (sizeResult.IsFailure)
|
||||
{
|
||||
return Result.Failure($"Failed to write texture asset settings: {sizeResult.Message}");
|
||||
}
|
||||
|
||||
// Content layout (all little-endian):
|
||||
// int32 width
|
||||
// int32 height
|
||||
// byte isFloat (0 = byte, 1 = float)
|
||||
// int32 colorComponents (cast of ColorComponents enum)
|
||||
// byte[] pixelBytes
|
||||
const int _CONTENT_HEADER_SIZE = 4 + 4 + 1 + 4; // 13 bytes
|
||||
|
||||
header.SettingsSize = sizeResult.Value;
|
||||
header.ContentOffset = header.SettingsOffset + sizeResult.Value;
|
||||
header.ContentSize = contentSize;
|
||||
header.ContentSize = _CONTENT_HEADER_SIZE + pixelBytes.Length;
|
||||
|
||||
// Write raw image content
|
||||
targetStream.Seek(header.ContentOffset, SeekOrigin.Begin);
|
||||
@@ -367,6 +370,7 @@ internal class TextureAssetHandler : IImportableAssetHandler
|
||||
BitConverter.TryWriteBytes(contentHeader.AsSpan(4, 4), height);
|
||||
contentHeader[8] = isFloat ? (byte)1 : (byte)0;
|
||||
BitConverter.TryWriteBytes(contentHeader.AsSpan(9, 4), (int)colorComponents);
|
||||
|
||||
await targetStream.WriteAsync(contentHeader.AsMemory(0, _CONTENT_HEADER_SIZE), token).ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
|
||||
@@ -22,10 +22,6 @@ internal static unsafe class TextureProcessor
|
||||
{
|
||||
private const string _TEXTURE_CACHE_SUBFOLDER = "TextureCache";
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public entry point
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Compresses <paramref name="pixelData"/> according to <paramref name="settings"/>
|
||||
/// and writes the result to the texture cache.
|
||||
@@ -69,10 +65,6 @@ internal static unsafe class TextureProcessor
|
||||
return cachePath;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// NVTT pipeline
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private static void RunNvttPipeline(
|
||||
string outputPath,
|
||||
ReadOnlySpan<byte> pixelData,
|
||||
@@ -110,7 +102,7 @@ internal static unsafe class TextureProcessor
|
||||
if (settings.Advanced.StretchToPowerOfTwo)
|
||||
{
|
||||
surface.ResizeMakeSquare(maxExtent,
|
||||
NvttRoundMode.NVTT_RoundMode_ToPreviousPowerOfTwo,
|
||||
NvttRoundMode.NVTT_RoundMode_ToNearestPowerOfTwo,
|
||||
NvttResizeFilter.NVTT_ResizeFilter_Box);
|
||||
}
|
||||
else if (surface.Width > maxExtent || surface.Height > maxExtent)
|
||||
@@ -177,7 +169,7 @@ internal static unsafe class TextureProcessor
|
||||
}
|
||||
|
||||
// ---- 8. enable CUDA if available ---------------------------------------
|
||||
ctx.SetCudaAcceleration(Ghost.Nvtt.NvttGlobal.IsCudaSupported);
|
||||
ctx.SetCudaAcceleration(NvttGlobal.IsCudaSupported);
|
||||
|
||||
// ---- 9. write DDS header -----------------------------------------------
|
||||
ctx.OutputHeader(surface, mipmapCount, compOpts, outOpts);
|
||||
@@ -185,18 +177,18 @@ internal static unsafe class TextureProcessor
|
||||
// ---- 10. compress mip chain using a working clone ----------------------
|
||||
using var mip = surface.Clone();
|
||||
|
||||
for (int level = 0; level < mipmapCount; level++)
|
||||
for (var level = 0; level < mipmapCount; level++)
|
||||
{
|
||||
// Scale alpha for coverage on each mip (if requested)
|
||||
if (settings.Advanced.ScaleAlphaForMipCoverage && level > 0)
|
||||
{
|
||||
float refCoverage = mip.AlphaTestCoverage(
|
||||
var refCoverage = mip.AlphaTestCoverage(
|
||||
settings.Advanced.ScaleAlphaForMipCoverageThreshold / 255f);
|
||||
mip.ScaleAlphaToCoverage(refCoverage,
|
||||
settings.Advanced.ScaleAlphaForMipCoverageThreshold / 255f);
|
||||
}
|
||||
|
||||
ctx.Compress(mip, face: 0, mipmap: level, compOpts, outOpts);
|
||||
ctx.Compress(mip, 0, level, compOpts, outOpts);
|
||||
|
||||
if (level + 1 < mipmapCount)
|
||||
{
|
||||
@@ -205,53 +197,45 @@ internal static unsafe class TextureProcessor
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private static NvttFormat SelectFormat(TextureAssetSettings settings)
|
||||
=> settings.Basic.TextureType switch
|
||||
{
|
||||
TextureType.Normal => NvttFormat.NVTT_Format_BC5, // RG normal map
|
||||
TextureType.Normal => NvttFormat.NVTT_Format_BC5, // RG normal map
|
||||
TextureType.SingleChannel => NvttFormat.NVTT_Format_BC4, // single channel
|
||||
TextureType.Lightmap => NvttFormat.NVTT_Format_BC6U, // HDR lightmap (unsigned)
|
||||
_ => NvttFormat.NVTT_Format_BC7, // default colour
|
||||
TextureType.Lightmap => NvttFormat.NVTT_Format_BC6U, // HDR lightmap (unsigned)
|
||||
_ => NvttFormat.NVTT_Format_BC7, // default colour
|
||||
};
|
||||
|
||||
private static NvttQuality SelectQuality(TextureCompressionLevel level)
|
||||
=> level switch
|
||||
{
|
||||
TextureCompressionLevel.Low => NvttQuality.NVTT_Quality_Fastest,
|
||||
TextureCompressionLevel.Low => NvttQuality.NVTT_Quality_Fastest,
|
||||
TextureCompressionLevel.High => NvttQuality.NVTT_Quality_Production,
|
||||
_ => NvttQuality.NVTT_Quality_Normal,
|
||||
_ => NvttQuality.NVTT_Quality_Normal,
|
||||
};
|
||||
|
||||
private static NvttMipmapFilter SelectMipmapFilter(MipmapFilter filter)
|
||||
=> filter switch
|
||||
{
|
||||
MipmapFilter.Box => NvttMipmapFilter.NVTT_MipmapFilter_Box,
|
||||
MipmapFilter.Triangle => NvttMipmapFilter.NVTT_MipmapFilter_Triangle,
|
||||
MipmapFilter.Box => NvttMipmapFilter.NVTT_MipmapFilter_Box,
|
||||
MipmapFilter.Triangle => NvttMipmapFilter.NVTT_MipmapFilter_Triangle,
|
||||
MipmapFilter.MitchellNetravali => NvttMipmapFilter.NVTT_MipmapFilter_Mitchell,
|
||||
_ => NvttMipmapFilter.NVTT_MipmapFilter_Kaiser,
|
||||
_ => NvttMipmapFilter.NVTT_MipmapFilter_Kaiser,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Produces a stable 64-bit hash of the settings structs so the cache file
|
||||
/// name changes whenever any setting changes.
|
||||
/// </summary>
|
||||
private static ulong ComputeSettingsHash(TextureAssetSettings s)
|
||||
{
|
||||
var basicSize = Unsafe.SizeOf<TextureAssetSettings.BasicSettings>();
|
||||
var basicSize = Unsafe.SizeOf<TextureAssetSettings.BasicSettings>();
|
||||
var advancedSize = Unsafe.SizeOf<TextureAssetSettings.AdvancedSettings>();
|
||||
var samplerSize = Unsafe.SizeOf<TextureAssetSettings.SamplerSettings>();
|
||||
var total = basicSize + advancedSize + samplerSize;
|
||||
var samplerSize = Unsafe.SizeOf<TextureAssetSettings.SamplerSettings>();
|
||||
var total = basicSize + advancedSize + samplerSize;
|
||||
|
||||
Span<byte> buf = stackalloc byte[total];
|
||||
var basic = s.Basic;
|
||||
var basic = s.Basic;
|
||||
var advanced = s.Advanced;
|
||||
var sampler = s.Sampler;
|
||||
MemoryMarshal.Write(buf, in basic);
|
||||
MemoryMarshal.Write(buf.Slice(basicSize), in advanced);
|
||||
var sampler = s.Sampler;
|
||||
MemoryMarshal.Write(buf, in basic);
|
||||
MemoryMarshal.Write(buf.Slice(basicSize), in advanced);
|
||||
MemoryMarshal.Write(buf.Slice(basicSize + advancedSize), in sampler);
|
||||
|
||||
return XxHash64.HashToUInt64(buf);
|
||||
|
||||
Reference in New Issue
Block a user