feat(rendering): add GPU scene updates and optimizations
Added a new `code-executor` agent with strict TDD and performance focus. Refactored `TextureProcessor` and `TextureAssetHandler` to use `Magick.NET` for image processing. Enhanced `GPUScene` with `InstanceCounterBuffer` and improved instance management. Introduced a compute shader for GPU scene updates. Updated `GhostRenderPipeline` to handle add/remove instance buffers. BREAKING CHANGE: Removed `x86` platform support and replaced `CachesFolderPath` with `LibraryFolderPath`. Updated project dependencies and removed unused utility classes.
This commit is contained in:
@@ -1,11 +1,7 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Core.Graphics;
|
||||
using Ghost.Core.Utilities;
|
||||
using Ghost.DSL.ShaderParser;
|
||||
using Misaki.HighPerformance.Utilities;
|
||||
using System.IO.Hashing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Ghost.DSL.ShaderCompiler;
|
||||
@@ -24,17 +20,6 @@ public struct DSLShaderError
|
||||
|
||||
internal static class DSLShaderCompiler
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static ulong GetUniqueId(string code)
|
||||
{
|
||||
if (string.IsNullOrEmpty(code))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return XxHash64.HashToUInt64(MemoryMarshal.AsBytes(code.AsSpan()));
|
||||
}
|
||||
|
||||
private static PipelineState MeragePipeline(PipelineSemantic? semantic, PipelineState parent)
|
||||
{
|
||||
if (semantic == null)
|
||||
@@ -163,7 +148,7 @@ internal static class DSLShaderCompiler
|
||||
passes = passes
|
||||
};
|
||||
|
||||
for (int i = 0; i < descriptor.passes.Length; i++)
|
||||
for (var i = 0; i < descriptor.passes.Length; i++)
|
||||
{
|
||||
descriptor.passes[i].shader = descriptor;
|
||||
}
|
||||
@@ -283,7 +268,7 @@ internal static class DSLShaderCompiler
|
||||
}
|
||||
|
||||
var shaderCodes = new ShaderCode[semantics.entryPoints.Count];
|
||||
for (int i = 0; i < shaderCodes.Length; i++)
|
||||
for (var i = 0; i < shaderCodes.Length; i++)
|
||||
{
|
||||
var result = BuildFinalShaderCode(semantics.entryPoints[i].shaderPath, semantics.includes.AsSpan(), semantics.hlsl, propertyInfo.code);
|
||||
if (result.IsFailure)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.Image;
|
||||
using ImageMagick;
|
||||
using System.Buffers;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
@@ -218,6 +218,14 @@ internal class TextureAssetHandler : IImportableAssetHandler
|
||||
{
|
||||
private const int _CURRENT_VERSION = 1;
|
||||
|
||||
private struct ImageContentHeader
|
||||
{
|
||||
public uint width;
|
||||
public uint height;
|
||||
public uint depth;
|
||||
public uint colorComponents;
|
||||
}
|
||||
|
||||
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>();
|
||||
@@ -284,105 +292,60 @@ internal class TextureAssetHandler : IImportableAssetHandler
|
||||
|
||||
public async ValueTask<Result> ImportAsync(Stream sourceStream, Stream targetStream, Guid id, CancellationToken token = default)
|
||||
{
|
||||
var info = ImageInfo.FromStream(sourceStream);
|
||||
if (info.BitsPerChannel <= 0)
|
||||
using var image = new MagickImage(sourceStream);
|
||||
var bytes = image.ToByteArray();
|
||||
|
||||
var settings = new TextureAssetSettings();
|
||||
await TextureProcessor.CompressToCacheAsync(EditorApplication.LibraryFolderPath, id, bytes, image.Width, image.Height, image.Depth, settings, token).ConfigureAwait(false);
|
||||
|
||||
var header = new AssetMetadata(id, TextureAsset.s_typeGuid)
|
||||
{
|
||||
return Result.Failure($"Unsupported image format with {info.BitsPerChannel} bits per channel.");
|
||||
HandlerVersion = _CURRENT_VERSION,
|
||||
SettingsOffset = AssetMetadata.SIZE,
|
||||
};
|
||||
|
||||
targetStream.Seek(header.SettingsOffset, SeekOrigin.Begin);
|
||||
var sizeResult = await WriteSettingsToStreamAsync(settings, targetStream, token).ConfigureAwait(false);
|
||||
if (sizeResult.IsFailure)
|
||||
{
|
||||
return Result.Failure($"Failed to write texture asset settings: {sizeResult.Message}");
|
||||
}
|
||||
|
||||
var isFloat = info.BitsPerChannel > 8;
|
||||
var width = info.Width;
|
||||
var height = info.Height;
|
||||
var colorComponents = info.ColorComponents;
|
||||
// Content layout (all little-endian):
|
||||
// uint32 width
|
||||
// uint32 height
|
||||
// uint32 depth
|
||||
// uint32 colorComponents
|
||||
// byte[] pixelBytes
|
||||
|
||||
byte[] pixelBytes;
|
||||
|
||||
if (isFloat)
|
||||
header.SettingsSize = sizeResult.Value;
|
||||
header.ContentOffset = header.SettingsOffset + sizeResult.Value;
|
||||
unsafe
|
||||
{
|
||||
using var image = ImageResultFloat.FromStream(sourceStream, colorComponents);
|
||||
var span = MemoryMarshal.AsBytes(image.AsSpan());
|
||||
pixelBytes = ArrayPool<byte>.Shared.Rent(span.Length);
|
||||
span.CopyTo(pixelBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
using var image = ImageResult.FromStream(sourceStream, colorComponents);
|
||||
var span = image.AsSpan();
|
||||
pixelBytes = ArrayPool<byte>.Shared.Rent(span.Length);
|
||||
span.CopyTo(pixelBytes);
|
||||
header.ContentSize = sizeof(ImageContentHeader) + image.Width * image.Height * (image.Depth / 8) * image.ChannelCount;
|
||||
}
|
||||
|
||||
try
|
||||
// Write raw image content
|
||||
targetStream.Seek(header.ContentOffset, SeekOrigin.Begin);
|
||||
|
||||
var contentHeader = new ImageContentHeader
|
||||
{
|
||||
var settings = new TextureAssetSettings();
|
||||
await Task.Run(() =>
|
||||
TextureProcessor.CompressToCache(
|
||||
EditorApplication.CachesFolderPath,
|
||||
id,
|
||||
pixelBytes,
|
||||
width,
|
||||
height,
|
||||
isFloat,
|
||||
colorComponents,
|
||||
settings),
|
||||
token).ConfigureAwait(false);
|
||||
width = image.Width,
|
||||
height = image.Height,
|
||||
depth = image.Depth,
|
||||
colorComponents = image.ChannelCount
|
||||
};
|
||||
|
||||
var header = new AssetMetadata(id, TextureAsset.s_typeGuid)
|
||||
{
|
||||
HandlerVersion = _CURRENT_VERSION,
|
||||
SettingsOffset = AssetMetadata.SIZE,
|
||||
};
|
||||
targetStream.Write(MemoryMarshal.AsBytes(new Span<ImageContentHeader>(ref contentHeader)));
|
||||
|
||||
targetStream.Seek(header.SettingsOffset, SeekOrigin.Begin);
|
||||
var sizeResult = await WriteSettingsToStreamAsync(settings, targetStream, token).ConfigureAwait(false);
|
||||
if (sizeResult.IsFailure)
|
||||
{
|
||||
return Result.Failure($"Failed to write texture asset settings: {sizeResult.Message}");
|
||||
}
|
||||
await targetStream.WriteAsync(bytes, token).ConfigureAwait(false);
|
||||
await targetStream.FlushAsync(token).ConfigureAwait(false);
|
||||
|
||||
// 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
|
||||
// Patch header now that all sizes are known
|
||||
targetStream.Seek(0, SeekOrigin.Begin);
|
||||
AssetMetadata.WriteToStream(targetStream, ref header);
|
||||
|
||||
header.SettingsSize = sizeResult.Value;
|
||||
header.ContentOffset = header.SettingsOffset + sizeResult.Value;
|
||||
header.ContentSize = _CONTENT_HEADER_SIZE + pixelBytes.Length;
|
||||
|
||||
// Write raw image content
|
||||
targetStream.Seek(header.ContentOffset, SeekOrigin.Begin);
|
||||
|
||||
var contentHeader = ArrayPool<byte>.Shared.Rent(_CONTENT_HEADER_SIZE);
|
||||
try
|
||||
{
|
||||
BitConverter.TryWriteBytes(contentHeader.AsSpan(0, 4), width);
|
||||
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
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(contentHeader);
|
||||
}
|
||||
|
||||
await targetStream.WriteAsync(pixelBytes, token).ConfigureAwait(false);
|
||||
await targetStream.FlushAsync(token).ConfigureAwait(false);
|
||||
|
||||
// Patch header now that all sizes are known
|
||||
targetStream.Seek(0, SeekOrigin.Begin);
|
||||
AssetMetadata.WriteToStream(targetStream, ref header);
|
||||
|
||||
return Result.Success();
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(pixelBytes);
|
||||
}
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
public ValueTask<Result<Asset>> LoadAsync(Stream sourceStream, IAssetRegistry assetRegistry, CancellationToken token = default)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using Ghost.Nvtt;
|
||||
using Misaki.HighPerformance.Image;
|
||||
using ImageMagick;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using System.IO.Hashing;
|
||||
using System.Runtime.CompilerServices;
|
||||
@@ -19,26 +19,152 @@ namespace Ghost.Editor.Core.AssetHandler;
|
||||
///
|
||||
/// The caller owns opening/closing all streams; this class only takes spans and paths.
|
||||
/// </summary>
|
||||
internal static unsafe class TextureProcessor
|
||||
internal static class TextureProcessor
|
||||
{
|
||||
private class NvttPipelineTask : IThreadPoolWorkItem
|
||||
{
|
||||
private readonly string _outputPath;
|
||||
|
||||
private readonly byte[] _image;
|
||||
private readonly uint _depth;
|
||||
private readonly uint _width;
|
||||
private readonly uint _height;
|
||||
|
||||
private readonly TextureAssetSettings _settings;
|
||||
private readonly TaskCompletionSource _completionSource;
|
||||
|
||||
public Task Task => _completionSource.Task;
|
||||
|
||||
public NvttPipelineTask(string outputPath, byte[] image, uint width, uint height, uint depth, TextureAssetSettings settings)
|
||||
{
|
||||
_outputPath = outputPath;
|
||||
_image = image;
|
||||
_width = width;
|
||||
_height = height;
|
||||
_depth = depth;
|
||||
_settings = settings;
|
||||
_completionSource = new TaskCompletionSource();
|
||||
}
|
||||
|
||||
public unsafe void Execute()
|
||||
{
|
||||
using var pSurface = new DisposablePtr<NvttSurface>(NvttSurface.Create());
|
||||
using var pCompOpts = new DisposablePtr<NvttCompressionOptions>(NvttCompressionOptions.Create());
|
||||
using var pOutOpts = new DisposablePtr<NvttOutputOptions>(NvttOutputOptions.Create());
|
||||
using var pCtx = new DisposablePtr<NvttContext>(NvttContext.Create());
|
||||
|
||||
var inputFormat = _depth > 8
|
||||
? NvttInputFormat.NVTT_InputFormat_RGBA_32F
|
||||
: NvttInputFormat.NVTT_InputFormat_BGRA_8UB; // we'll swizzle RB below
|
||||
|
||||
fixed (void* pData = _image)
|
||||
{
|
||||
pSurface.Get()->SetImageData(inputFormat, (int)_width, (int)_height, 1, pData, NvttBoolean.NVTT_True, null);
|
||||
}
|
||||
|
||||
// stb gives us RGBA byte order; NVTT BGRA_8UB reads it as BGRA,
|
||||
// so channels R and B are swapped — fix with swizzle(2,1,0,3).
|
||||
if (_depth <= 8)
|
||||
{
|
||||
pSurface.Get()->Swizzle(2, 1, 0, 3, null);
|
||||
}
|
||||
|
||||
var maxExtent = (int)_settings.Sampler.MaxSize;
|
||||
if (_settings.Advanced.StretchToPowerOfTwo)
|
||||
{
|
||||
pSurface.Get()->ResizeMakeSquare(maxExtent,
|
||||
NvttRoundMode.NVTT_RoundMode_ToNearestPowerOfTwo,
|
||||
NvttResizeFilter.NVTT_ResizeFilter_Box, null);
|
||||
}
|
||||
else if (pSurface.Get()->Width() > maxExtent || pSurface.Get()->Height() > maxExtent)
|
||||
{
|
||||
pSurface.Get()->ResizeMax(maxExtent,
|
||||
NvttRoundMode.NVTT_RoundMode_None,
|
||||
NvttResizeFilter.NVTT_ResizeFilter_Box, null);
|
||||
}
|
||||
|
||||
if (_settings.Advanced.UseBorderColor)
|
||||
{
|
||||
var c = _settings.Advanced.BorderColor;
|
||||
pSurface.Get()->SetBorder(c.r, c.g, c.b, c.a, null);
|
||||
}
|
||||
else if (_settings.Advanced.ZeroAlphaBorder)
|
||||
{
|
||||
pSurface.Get()->SetBorder(0f, 0f, 0f, 0f, null);
|
||||
}
|
||||
|
||||
if (_settings.Basic.IsSRGB && _settings.Advanced.GammaCorrection)
|
||||
{
|
||||
pSurface.Get()->ToLinearFromSrgb(null);
|
||||
}
|
||||
|
||||
if (_settings.Advanced.PremultiplyAlpha)
|
||||
{
|
||||
pSurface.Get()->PremultiplyAlpha(null);
|
||||
}
|
||||
|
||||
pCompOpts.Get()->SetFormat(SelectFormat(_settings));
|
||||
pCompOpts.Get()->SetQuality(SelectQuality(_settings.Advanced.CompressionLevel));
|
||||
|
||||
if (_settings.Advanced.CutoutAlpha)
|
||||
{
|
||||
pCompOpts.Get()->SetQuantization(false, false, true,
|
||||
_settings.Advanced.CutoutAlphaThreshold);
|
||||
}
|
||||
|
||||
pOutOpts.Get()->SetOutputHeader(true);
|
||||
pOutOpts.Get()->SetSrgbFlag(_settings.Basic.IsSRGB);
|
||||
pOutOpts.Get()->SetContainer(NvttContainer.NVTT_Container_DDS10);
|
||||
pOutOpts.Get()->SetFileName(Encoding.UTF8.GetBytes(_outputPath));
|
||||
|
||||
var nvttFilter = SelectMipmapFilter(_settings.Advanced.MipmapFilter);
|
||||
|
||||
int mipmapCount;
|
||||
if (!_settings.Advanced.GenerateMipmaps)
|
||||
{
|
||||
mipmapCount = 1;
|
||||
}
|
||||
else if (_settings.Advanced.MipmapLevelCount == 0)
|
||||
{
|
||||
mipmapCount = pSurface.Get()->CountMipmaps(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
mipmapCount = (int)_settings.Advanced.MipmapLevelCount;
|
||||
}
|
||||
|
||||
pCtx.Get()->SetCudaAcceleration(NvttApi.IsCudaSupported());
|
||||
|
||||
pCtx.Get()->OutputHeader(pSurface.Get(), mipmapCount, pCompOpts.Get(), pOutOpts.Get());
|
||||
|
||||
using var pMip = new DisposablePtr<NvttSurface>(pSurface.Get()->Clone());
|
||||
|
||||
for (var level = 0; level < mipmapCount; level++)
|
||||
{
|
||||
// Scale alpha for coverage on each pMip (if requested)
|
||||
if (_settings.Advanced.ScaleAlphaForMipCoverage && level > 0)
|
||||
{
|
||||
var refCoverage = pMip.Get()->AlphaTestCoverage(
|
||||
_settings.Advanced.ScaleAlphaForMipCoverageThreshold / 255f, 3);
|
||||
pMip.Get()->ScaleAlphaToCoverage(refCoverage,
|
||||
_settings.Advanced.ScaleAlphaForMipCoverageThreshold / 255f, 3, null);
|
||||
}
|
||||
|
||||
pCtx.Get()->Compress(pMip.Get(), 0, level, pCompOpts.Get(), pOutOpts.Get());
|
||||
|
||||
if (level + 1 < mipmapCount)
|
||||
{
|
||||
pMip.Get()->BuildNextMipmapDefaults(nvttFilter, 1, null);
|
||||
}
|
||||
}
|
||||
|
||||
_completionSource.SetResult();
|
||||
}
|
||||
}
|
||||
|
||||
private const string _TEXTURE_CACHE_SUBFOLDER = "TextureCache";
|
||||
|
||||
/// <summary>
|
||||
/// Compresses <paramref name="pixelData"/> according to <paramref name="settings"/>
|
||||
/// and writes the result to the texture cache.
|
||||
///
|
||||
/// Returns the absolute path of the cache file on success.
|
||||
/// The cache file is skipped if it already exists with a matching content hash.
|
||||
/// </summary>
|
||||
public static string CompressToCache(
|
||||
string cachesFolderPath,
|
||||
Guid assetId,
|
||||
ReadOnlySpan<byte> pixelData,
|
||||
int width,
|
||||
int height,
|
||||
bool isFloat,
|
||||
ColorComponents colorComponents,
|
||||
TextureAssetSettings settings)
|
||||
public static async ValueTask<string> CompressToCacheAsync(string cachesFolderPath, Guid assetId, byte[] image, uint width, uint height, uint depth, TextureAssetSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
var cacheDir = Path.Combine(cachesFolderPath, _TEXTURE_CACHE_SUBFOLDER);
|
||||
Directory.CreateDirectory(cacheDir);
|
||||
@@ -57,129 +183,11 @@ internal static unsafe class TextureProcessor
|
||||
File.Delete(stale);
|
||||
}
|
||||
|
||||
RunNvttPipeline(cachePath, pixelData, width, height, isFloat, colorComponents, settings);
|
||||
|
||||
return cachePath;
|
||||
}
|
||||
|
||||
private static void RunNvttPipeline(
|
||||
string outputPath,
|
||||
ReadOnlySpan<byte> pixelData,
|
||||
int width,
|
||||
int height,
|
||||
bool isFloat,
|
||||
ColorComponents colorComponents,
|
||||
TextureAssetSettings settings)
|
||||
{
|
||||
using var pSurface = new DisposablePtr<NvttSurface>(NvttSurface.Create());
|
||||
using var pCompOpts = new DisposablePtr<NvttCompressionOptions>(NvttCompressionOptions.Create());
|
||||
using var pOutOpts = new DisposablePtr<NvttOutputOptions>(NvttOutputOptions.Create());
|
||||
using var pCtx = new DisposablePtr<NvttContext>(NvttContext.Create());
|
||||
|
||||
var inputFormat = isFloat
|
||||
? NvttInputFormat.NVTT_InputFormat_RGBA_32F
|
||||
: NvttInputFormat.NVTT_InputFormat_BGRA_8UB; // we'll swizzle RB below
|
||||
|
||||
fixed (void* pData = pixelData)
|
||||
{
|
||||
pSurface.Get()->SetImageData(inputFormat, width, height, 1, pData, NvttBoolean.NVTT_True, null);
|
||||
}
|
||||
|
||||
// stb gives us RGBA byte order; NVTT BGRA_8UB reads it as BGRA,
|
||||
// so channels R and B are swapped — fix with swizzle(2,1,0,3).
|
||||
if (!isFloat)
|
||||
{
|
||||
pSurface.Get()->Swizzle(2, 1, 0, 3, null);
|
||||
}
|
||||
|
||||
var maxExtent = (int)settings.Sampler.MaxSize;
|
||||
if (settings.Advanced.StretchToPowerOfTwo)
|
||||
{
|
||||
pSurface.Get()->ResizeMakeSquare(maxExtent,
|
||||
NvttRoundMode.NVTT_RoundMode_ToNearestPowerOfTwo,
|
||||
NvttResizeFilter.NVTT_ResizeFilter_Box, null);
|
||||
}
|
||||
else if (pSurface.Get()->Width() > maxExtent || pSurface.Get()->Height() > maxExtent)
|
||||
{
|
||||
pSurface.Get()->ResizeMax(maxExtent,
|
||||
NvttRoundMode.NVTT_RoundMode_None,
|
||||
NvttResizeFilter.NVTT_ResizeFilter_Box, null);
|
||||
}
|
||||
|
||||
if (settings.Advanced.UseBorderColor)
|
||||
{
|
||||
var c = settings.Advanced.BorderColor;
|
||||
pSurface.Get()->SetBorder(c.r, c.g, c.b, c.a, null);
|
||||
}
|
||||
else if (settings.Advanced.ZeroAlphaBorder)
|
||||
{
|
||||
pSurface.Get()->SetBorder(0f, 0f, 0f, 0f, null);
|
||||
}
|
||||
var workItem = new NvttPipelineTask(cachePath, image, width, height, depth, settings);
|
||||
ThreadPool.UnsafeQueueUserWorkItem(workItem, true);
|
||||
await workItem.Task.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (settings.Basic.IsSRGB && settings.Advanced.GammaCorrection)
|
||||
{
|
||||
pSurface.Get()->ToLinearFromSrgb(null);
|
||||
}
|
||||
|
||||
if (settings.Advanced.PremultiplyAlpha)
|
||||
{
|
||||
pSurface.Get()->PremultiplyAlpha(null);
|
||||
}
|
||||
|
||||
pCompOpts.Get()->SetFormat(SelectFormat(settings));
|
||||
pCompOpts.Get()->SetQuality(SelectQuality(settings.Advanced.CompressionLevel));
|
||||
|
||||
if (settings.Advanced.CutoutAlpha)
|
||||
{
|
||||
pCompOpts.Get()->SetQuantization(false, false, true,
|
||||
settings.Advanced.CutoutAlphaThreshold);
|
||||
}
|
||||
|
||||
pOutOpts.Get()->SetOutputHeader(true);
|
||||
pOutOpts.Get()->SetSrgbFlag(settings.Basic.IsSRGB);
|
||||
pOutOpts.Get()->SetContainer(NvttContainer.NVTT_Container_DDS10);
|
||||
pOutOpts.Get()->SetFileName(Encoding.UTF8.GetBytes(outputPath));
|
||||
|
||||
var nvttFilter = SelectMipmapFilter(settings.Advanced.MipmapFilter);
|
||||
|
||||
int mipmapCount;
|
||||
if (!settings.Advanced.GenerateMipmaps)
|
||||
{
|
||||
mipmapCount = 1;
|
||||
}
|
||||
else if (settings.Advanced.MipmapLevelCount == 0)
|
||||
{
|
||||
mipmapCount = pSurface.Get()->CountMipmaps(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
mipmapCount = (int)settings.Advanced.MipmapLevelCount;
|
||||
}
|
||||
|
||||
pCtx.Get()->SetCudaAcceleration(NvttApi.IsCudaSupported());
|
||||
|
||||
pCtx.Get()->OutputHeader(pSurface.Get(), mipmapCount, pCompOpts.Get(), pOutOpts.Get());
|
||||
|
||||
using var pMip = new DisposablePtr<NvttSurface>(pSurface.Get()->Clone());
|
||||
|
||||
for (var level = 0; level < mipmapCount; level++)
|
||||
{
|
||||
// Scale alpha for coverage on each pMip (if requested)
|
||||
if (settings.Advanced.ScaleAlphaForMipCoverage && level > 0)
|
||||
{
|
||||
var refCoverage = pMip.Get()->AlphaTestCoverage(
|
||||
settings.Advanced.ScaleAlphaForMipCoverageThreshold / 255f, 3);
|
||||
pMip.Get()->ScaleAlphaToCoverage(refCoverage,
|
||||
settings.Advanced.ScaleAlphaForMipCoverageThreshold / 255f, 3, null);
|
||||
}
|
||||
|
||||
pCtx.Get()->Compress(pMip.Get(), 0, level, pCompOpts.Get(), pOutOpts.Get());
|
||||
|
||||
if (level + 1 < mipmapCount)
|
||||
{
|
||||
pMip.Get()->BuildNextMipmapDefaults(nvttFilter, 1, null);
|
||||
}
|
||||
}
|
||||
return cachePath;
|
||||
}
|
||||
|
||||
private static NvttFormat SelectFormat(TextureAssetSettings settings)
|
||||
@@ -208,7 +216,7 @@ internal static unsafe class TextureProcessor
|
||||
_ => NvttMipmapFilter.NVTT_MipmapFilter_Kaiser,
|
||||
};
|
||||
|
||||
private static ulong ComputeSettingsHash(TextureAssetSettings s)
|
||||
private static ulong ComputeSettingsHash(TextureAssetSettings settings)
|
||||
{
|
||||
var basicSize = Unsafe.SizeOf<TextureAssetSettings.BasicSettings>();
|
||||
var advancedSize = Unsafe.SizeOf<TextureAssetSettings.AdvancedSettings>();
|
||||
@@ -216,9 +224,9 @@ internal static unsafe class TextureProcessor
|
||||
var total = basicSize + advancedSize + samplerSize;
|
||||
|
||||
Span<byte> buf = stackalloc byte[total];
|
||||
var basic = s.Basic;
|
||||
var advanced = s.Advanced;
|
||||
var sampler = s.Sampler;
|
||||
var basic = settings.Basic;
|
||||
var advanced = settings.Advanced;
|
||||
var sampler = settings.Sampler;
|
||||
MemoryMarshal.Write(buf, in basic);
|
||||
MemoryMarshal.Write(buf.Slice(basicSize), in advanced);
|
||||
MemoryMarshal.Write(buf.Slice(basicSize + advancedSize), in sampler);
|
||||
|
||||
@@ -8,7 +8,7 @@ public static class EditorApplication
|
||||
public const string ASSETS_FOLDER_NAME = "Assets";
|
||||
public const string SOURCES_FOLDER_NAME = "Sources";
|
||||
public const string PACKAGES_FOLDER_NAME = "Packages";
|
||||
public const string CACHES_FOLDER_NAME = "Caches";
|
||||
public const string LIBRARY_FOLDER_NAME = "Library";
|
||||
public const string CONFIG_FOLDER_NAME = "Config";
|
||||
|
||||
private static IServiceProvider? s_serviceProvider;
|
||||
@@ -25,7 +25,7 @@ public static class EditorApplication
|
||||
public static string AssetsFolderPath => Path.Combine(ProjectPath, ASSETS_FOLDER_NAME);
|
||||
public static string SourcesFolderPath => Path.Combine(ProjectPath, SOURCES_FOLDER_NAME);
|
||||
public static string PackagesFolderPath => Path.Combine(ProjectPath, PACKAGES_FOLDER_NAME);
|
||||
public static string CachesFolderPath => Path.Combine(ProjectPath, CACHES_FOLDER_NAME);
|
||||
public static string LibraryFolderPath => Path.Combine(ProjectPath, LIBRARY_FOLDER_NAME);
|
||||
public static string ConfigFolderPath => Path.Combine(ProjectPath, CONFIG_FOLDER_NAME);
|
||||
|
||||
public static DispatcherQueue DispatcherQueue
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
<TargetFramework>net10.0-windows10.0.22621.0</TargetFramework>
|
||||
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
|
||||
<RootNamespace>Ghost.Editor.Core</RootNamespace>
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
|
||||
<Platforms>x64;ARM64</Platforms>
|
||||
<RuntimeIdentifiers>win-x64;win-arm64</RuntimeIdentifiers>
|
||||
<UseWinUI>true</UseWinUI>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<SupportedOSPlatformVersion>10.0.20348.0</SupportedOSPlatformVersion>
|
||||
@@ -13,8 +14,9 @@
|
||||
<langversion>preview</langversion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Magick.NET-Q16-HDRI-OpenMP-x64" Version="14.12.0" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="10.0.5" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.7705" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.28000.1721" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.260317003" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Behaviors" Version="8.2.251219" />
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net10.0-windows10.0.22621.0</TargetFramework>
|
||||
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
|
||||
<Platforms>x86;x64;ARM64</Platforms>
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
|
||||
<Platforms>x64;ARM64</Platforms>
|
||||
<RuntimeIdentifiers>win-x64;win-arm64</RuntimeIdentifiers>
|
||||
<PublishProfile>win-$(Platform).pubxml</PublishProfile>
|
||||
<UseWinUI>true</UseWinUI>
|
||||
<EnableMsixTooling>true</EnableMsixTooling>
|
||||
@@ -41,7 +41,7 @@
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.TabbedCommandBar" Version="8.2.251219" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.5" />
|
||||
<PackageReference Include="Microsoft.Windows.CsWinRT" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.7705" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.28000.1721" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.260317003" />
|
||||
<PackageReference Include="WinUIEx" Version="2.9.0" />
|
||||
</ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user