Refactor namespaces and improve resource handling

- Updated namespaces from `Ghost.UnitTest` to `Ghost.Graphics.Test` across multiple files.
- Refactored `GraphicsTestWindow` to use a new `RenderSystem` configuration.
- Removed deprecated `Logger` and `SerializationTest` classes.
- Improved memory management in D3D12 components, including resource allocation and cleanup.
- Added `[SupportedOSPlatform]` attributes to specify Windows version compatibility.
- Updated `.editorconfig` settings and project references for consistency.
- Enabled `nativeDebugging` in `launchSettings.json`.
This commit is contained in:
2025-11-11 21:30:47 +09:00
parent fb003da26a
commit 6f786a0698
35 changed files with 334 additions and 401 deletions

View File

@@ -5,6 +5,7 @@ using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.LowLevel.Utilities;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows;
@@ -119,6 +120,7 @@ internal readonly struct ShaderReflectionData
}
}
[SupportedOSPlatform(Win32Utility.OS_SUPPORTED_VERSION)]
internal static unsafe class D3D12ShaderCompiler
{
private static string GetProfileString(ShaderStage stage, CompilerTier version)
@@ -196,94 +198,109 @@ internal static unsafe class D3D12ShaderCompiler
public static Result<CompileResult> Compile(ref readonly CompilerConfig config, Allocator allocator, IDxcBlob** ppReflectionBlob)
{
// NOTE: Should we cache the compiler and utils instances for better performance?
using ComPtr<IDxcCompiler3> compiler = default;
using ComPtr<IDxcUtils> utils = default;
using ComPtr<IDxcIncludeHandler> includeHandler = default;
// Create DXC compiler and utils
var dxccID = CLSID.CLSID_DxcCompiler;
var dxcuID = CLSID.CLSID_DxcUtils;
ThrowIfFailed(DxcCreateInstance(&dxccID, compiler.IID(), compiler.PPV()));
ThrowIfFailed(DxcCreateInstance(&dxcuID, utils.IID(), utils.PPV()));
//includeHandler.Get()->LoadSource();
utils.Get()->CreateDefaultIncludeHandler(includeHandler.GetAddressOf());
// Create source blob
using ComPtr<IDxcBlobEncoding> sourceBlob = default;
if (utils.Get()->LoadFile(config.shaderPath.AsSpan().GetUnsafePtr(), null, sourceBlob.GetAddressOf()).FAILED)
{
return Result.Fail($"Failed to load shader file: {config.shaderPath}");
}
var argsArray = GetCompilerArguments(in config);
var argPtrs = stackalloc char*[argsArray.Count];
for (var i = 0; i < argsArray.Count; i++)
{
argPtrs[i] = (char*)Marshal.StringToHGlobalUni(argsArray[i]);
}
// NOTE: Should we cache the pCompiler and pUtils instances for better performance?
IDxcCompiler3* pCompiler = default;
IDxcUtils* pUtils = default;
IDxcIncludeHandler* pIncludeHandler = default;
try
{
// Compile shader
using ComPtr<IDxcResult> result = default;
var buffer = new DxcBuffer
{
Ptr = sourceBlob.Get()->GetBufferPointer(),
Size = sourceBlob.Get()->GetBufferSize(),
Encoding = DXC.DXC_CP_UTF8
};
// Create DXC pCompiler and pUtils
var dxccID = CLSID.CLSID_DxcCompiler;
var dxcuID = CLSID.CLSID_DxcUtils;
ThrowIfFailed(compiler.Get()->Compile(&buffer, argPtrs, (uint)argsArray.Count, includeHandler.Get(), result.IID(), result.PPV()));
ThrowIfFailed(DxcCreateInstance(&dxccID, pCompiler->IID(), (void**)&pCompiler));
ThrowIfFailed(DxcCreateInstance(&dxcuID, pUtils->IID(), (void**)&pUtils));
// Check compilation result
HRESULT hrStatus;
result.Get()->GetStatus(&hrStatus);
if (hrStatus.FAILED)
{
// Get error messages
using ComPtr<IDxcBlobEncoding> errorBlob = default;
result.Get()->GetErrorBuffer(errorBlob.GetAddressOf());
//pIncludeHandler.Get()->LoadSource();
pUtils->CreateDefaultIncludeHandler(&pIncludeHandler);
if (errorBlob.Get() != null)
// Create source blob
using ComPtr<IDxcBlobEncoding> sourceBlob = default;
fixed (char* pPath = config.shaderPath)
{
if (pUtils->LoadFile(pPath, null, sourceBlob.GetAddressOf()).FAILED)
{
var errorMessage = Marshal.PtrToStringUni((IntPtr)errorBlob.Get()->GetBufferPointer());
return Result.Fail($"DXC shader compilation failed:\n{errorMessage}");
}
else
{
return Result.Fail("DXC shader compilation failed with unknown error.");
return Result.Fail($"Failed to load shader file: {config.shaderPath}");
}
}
// Get compiled bytecode
using ComPtr<IDxcBlob> bytecodeBlob = default;
ThrowIfFailed(result.Get()->GetResult(bytecodeBlob.GetAddressOf()));
// Get reflection data using DXC API
if (ppReflectionBlob != null)
var argsArray = GetCompilerArguments(in config);
var argPtrs = stackalloc char*[argsArray.Count];
for (var i = 0; i < argsArray.Count; i++)
{
ThrowIfFailed(result.Get()->GetOutput(DXC_OUT_KIND.DXC_OUT_REFLECTION, __uuidof<IDxcBlob>(), (void**)ppReflectionBlob, null));
argPtrs[i] = (char*)Marshal.StringToHGlobalUni(argsArray[i]);
}
var bytecodeSize = bytecodeBlob.Get()->GetBufferSize();
var bytecode = new UnsafeArray<byte>((int)bytecodeSize, allocator);
IDxcResult* pResult = default;
NativeMemory.Copy(bytecodeBlob.Get()->GetBufferPointer(), bytecode.GetUnsafePtr(), bytecodeSize);
return new CompileResult
try
{
bytecode = bytecode,
};
// Compile shader
var buffer = new DxcBuffer
{
Ptr = sourceBlob.Get()->GetBufferPointer(),
Size = sourceBlob.Get()->GetBufferSize(),
Encoding = DXC.DXC_CP_UTF8
};
ThrowIfFailed(pCompiler->Compile(&buffer, argPtrs, (uint)argsArray.Count, pIncludeHandler, pResult->IID(), (void**)&pResult));
// Check compilation pResult
HRESULT hrStatus;
pResult->GetStatus(&hrStatus);
if (hrStatus.FAILED)
{
// Get error messages
using ComPtr<IDxcBlobEncoding> errorBlob = default;
pResult->GetErrorBuffer(errorBlob.GetAddressOf());
if (errorBlob.Get() != null)
{
var errorMessage = Marshal.PtrToStringUni((IntPtr)errorBlob.Get()->GetBufferPointer());
return Result.Fail($"DXC shader compilation failed:\n{errorMessage}");
}
else
{
return Result.Fail("DXC shader compilation failed with unknown error.");
}
}
// Get compiled bytecode
using ComPtr<IDxcBlob> bytecodeBlob = default;
ThrowIfFailed(pResult->GetResult(bytecodeBlob.GetAddressOf()));
// Get reflection data using DXC API
if (ppReflectionBlob != null)
{
ThrowIfFailed(pResult->GetOutput(DXC_OUT_KIND.DXC_OUT_REFLECTION, __uuidof<IDxcBlob>(), (void**)ppReflectionBlob, null));
}
var bytecodeSize = bytecodeBlob.Get()->GetBufferSize();
var bytecode = new UnsafeArray<byte>((int)bytecodeSize, allocator);
NativeMemory.Copy(bytecodeBlob.Get()->GetBufferPointer(), bytecode.GetUnsafePtr(), bytecodeSize);
return new CompileResult
{
bytecode = bytecode,
};
}
finally
{
for (var i = 0; i < argsArray.Count; i++)
{
Marshal.FreeHGlobal((nint)argPtrs[i]);
}
pResult->Release();
}
}
finally
{
for (var i = 0; i < argsArray.Count; i++)
{
Marshal.FreeHGlobal((nint)argPtrs[i]);
}
pCompiler->Release();
pUtils->Release();
pIncludeHandler->Release();
}
}
@@ -296,98 +313,108 @@ internal static unsafe class D3D12ShaderCompiler
return Result<ShaderReflectionData>.Fail("Reflection blob is null.");
}
// Create DXC utils to parse reflection data
var dxcuID = CLSID.CLSID_DxcUtils;
using ComPtr<IDxcUtils> utils = default;
ThrowIfFailed(DxcCreateInstance(&dxcuID, utils.IID(), utils.PPV()));
ComPtr<IDxcUtils> utils = default;
ComPtr<ID3D12ShaderReflection> reflection = default;
// Create reflection interface from blob
var reflectionBuffer = new DxcBuffer
try
{
Ptr = reflectionBlob->GetBufferPointer(),
Size = reflectionBlob->GetBufferSize(),
Encoding = DXC.DXC_CP_ACP
};
// Create DXC pUtils to parse reflection data
var dxcuID = CLSID.CLSID_DxcUtils;
ThrowIfFailed(DxcCreateInstance(&dxcuID, utils.IID(), utils.PPV()));
using ComPtr<ID3D12ShaderReflection> reflection = default;
ThrowIfFailed(utils.Get()->CreateReflection(&reflectionBuffer, reflection.IID(), reflection.PPV()));
D3D12_SHADER_DESC shaderDesc;
ThrowIfFailed(reflection.Get()->GetDesc(&shaderDesc));
var reflectionData = new ShaderReflectionData();
for (uint i = 0; i < shaderDesc.BoundResources; i++)
{
D3D12_SHADER_INPUT_BIND_DESC bindDesc;
ThrowIfFailed(reflection.Get()->GetResourceBindingDesc(i, &bindDesc));
var resourceName = Marshal.PtrToStringUTF8((IntPtr)bindDesc.Name);
if (resourceName == null)
// Create reflection interface from blob
var reflectionBuffer = new DxcBuffer
{
return Result.Fail("Failed to get resource name from reflection data.");
}
Ptr = reflectionBlob->GetBufferPointer(),
Size = reflectionBlob->GetBufferSize(),
Encoding = DXC.DXC_CP_ACP
};
switch (bindDesc.Type)
ThrowIfFailed(utils.Get()->CreateReflection(&reflectionBuffer, reflection.IID(), reflection.PPV()));
D3D12_SHADER_DESC shaderDesc;
ThrowIfFailed(reflection.Get()->GetDesc(&shaderDesc));
var reflectionData = new ShaderReflectionData();
for (uint i = 0; i < shaderDesc.BoundResources; i++)
{
case D3D_SHADER_INPUT_TYPE.D3D_SIT_CBUFFER:
D3D12_SHADER_INPUT_BIND_DESC bindDesc;
ThrowIfFailed(reflection.Get()->GetResourceBindingDesc(i, &bindDesc));
var resourceName = Marshal.PtrToStringUTF8((IntPtr)bindDesc.Name);
if (resourceName == null)
{
var cbuffer = reflection.Get()->GetConstantBufferByName(bindDesc.Name);
D3D12_SHADER_BUFFER_DESC cbufferDesc;
ThrowIfFailed(cbuffer->GetDesc(&cbufferDesc));
return Result.Fail("Failed to get resource name from reflection data.");
}
var variables = new List<CBufferVariableInfo>((int)cbufferDesc.Variables);
// Now we iterate all variables for *every* cbuffer, not just b3
for (uint j = 0; j < cbufferDesc.Variables; j++)
switch (bindDesc.Type)
{
case D3D_SHADER_INPUT_TYPE.D3D_SIT_CBUFFER:
{
var variable = cbuffer->GetVariableByIndex(j);
D3D12_SHADER_VARIABLE_DESC varDesc;
variable->GetDesc(&varDesc);
var cbuffer = reflection.Get()->GetConstantBufferByName(bindDesc.Name);
D3D12_SHADER_BUFFER_DESC cbufferDesc;
ThrowIfFailed(cbuffer->GetDesc(&cbufferDesc));
var variableName = Marshal.PtrToStringUTF8((IntPtr)varDesc.Name);
if (variableName == null)
var variables = new List<CBufferVariableInfo>((int)cbufferDesc.Variables);
// Now we iterate all variables for *every* cbuffer, not just b3
for (uint j = 0; j < cbufferDesc.Variables; j++)
{
continue;
var variable = cbuffer->GetVariableByIndex(j);
D3D12_SHADER_VARIABLE_DESC varDesc;
variable->GetDesc(&varDesc);
var variableName = Marshal.PtrToStringUTF8((IntPtr)varDesc.Name);
if (variableName == null)
{
continue;
}
variables.Add(new CBufferVariableInfo
{
Name = variableName,
StartOffset = varDesc.StartOffset,
Size = varDesc.Size
});
}
variables.Add(new CBufferVariableInfo
reflectionData.ConstantBuffers.Add(new CBufferInfo
{
Name = variableName,
StartOffset = varDesc.StartOffset,
Size = varDesc.Size
Name = resourceName,
RegisterSlot = bindDesc.BindPoint,
RegisterSpace = bindDesc.Space,
SizeInBytes = cbufferDesc.Size,
Variables = variables
});
break;
}
reflectionData.ConstantBuffers.Add(new CBufferInfo
// NOTE: Currently we do not support resource bindings yet, everything access through bindless heaps.
default:
{
Name = resourceName,
RegisterSlot = bindDesc.BindPoint,
RegisterSpace = bindDesc.Space,
SizeInBytes = cbufferDesc.Size,
Variables = variables
});
reflectionData.OtherResources.Add(new ResourceBindingInfo
{
Name = resourceName,
Type = bindDesc.Type,
BindPoint = bindDesc.BindPoint,
BindCount = bindDesc.BindCount,
Space = bindDesc.Space
});
break;
}
// NOTE: Currently we do not support resource bindings yet, everything access through bindless heaps.
default:
{
reflectionData.OtherResources.Add(new ResourceBindingInfo
{
Name = resourceName,
Type = bindDesc.Type,
BindPoint = bindDesc.BindPoint,
BindCount = bindDesc.BindCount,
Space = bindDesc.Space
});
break;
break;
}
}
}
}
return reflectionData;
return reflectionData;
}
finally
{
utils.Dispose();
reflection.Dispose();
}
}
}