Refactor and optimize rendering pipeline

- Added `<IsTrimmable>` property in project files for trimming.
- Replaced bindless texture types with non-bindless equivalents.
- Refactored `ShaderDescriptor` and `ShaderPass` for better modularity.
- Introduced `ShaderDescriptorExtensions` for property size calculations.
- Simplified constant buffer handling in `Material.cs`.
- Improved resource management in `D3D12` components.
- Added support for static meshes and optimized resource barriers.
- Refactored shader code generation and property merging in `SDLCompiler`.
- Removed unused or redundant code (e.g., `IncludesBlock` parser).
- Updated comments, documentation, and error handling for clarity.
This commit is contained in:
2025-11-28 18:58:50 +09:00
parent 0720444c2c
commit bd97d233cb
49 changed files with 842 additions and 1025 deletions

View File

@@ -10,17 +10,18 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<IsAotCompatible>True</IsAotCompatible> <IsAotCompatible>True</IsAotCompatible>
<DefineConstants>$(DefineConstants);PLATEFORME_WIN64</DefineConstants> <DefineConstants>$(DefineConstants);PLATEFORME_WIN64</DefineConstants>
<IsTrimmable>True</IsTrimmable>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<IsAotCompatible>True</IsAotCompatible> <IsAotCompatible>True</IsAotCompatible>
<DefineConstants>$(DefineConstants);PLATEFORME_WIN64</DefineConstants> <DefineConstants>$(DefineConstants);PLATEFORME_WIN64</DefineConstants>
<IsTrimmable>True</IsTrimmable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Misaki.HighPerformance" Version="1.0.1" /> <PackageReference Include="Misaki.HighPerformance" Version="1.0.1" />
<PackageReference Include="Misaki.HighPerformance.Jobs" Version="1.1.0" /> <PackageReference Include="Misaki.HighPerformance.LowLevel" Version="1.2.5" />
<PackageReference Include="Misaki.HighPerformance.LowLevel" Version="1.2.1" />
<PackageReference Include="Misaki.HighPerformance.Mathematics" Version="1.2.6" /> <PackageReference Include="Misaki.HighPerformance.Mathematics" Version="1.2.6" />
<PackageReference Include="System.IO.Hashing" Version="10.0.0" /> <PackageReference Include="System.IO.Hashing" Version="10.0.0" />
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.26100.5" /> <PackageReference Include="TerraFX.Interop.Windows" Version="10.0.26100.5" />

View File

@@ -10,11 +10,12 @@ public enum ShaderPropertyType
{ {
None, None,
Float, Float2, Float3, Float4, Float, Float2, Float3, Float4,
Float4x4,
Int, Int2, Int3, Int4, Int, Int2, Int3, Int4,
UInt, UInt2, UInt3, UInt4, UInt, UInt2, UInt3, UInt4,
Bool, Bool2, Bool3, Bool4, Bool, Bool2, Bool3, Bool4,
Texture2DBindless, Texture3DBindless, TextureCubeBindless, Texture2D, Texture3D, TextureCube,
Texture2DArrayBindless, TextureCubeArrayBindless, Texture2DArray, TextureCubeArray,
} }
public struct ShaderEntryPoint public struct ShaderEntryPoint
@@ -77,11 +78,8 @@ public class FullPassDescriptor : IPassDescriptor
public ShaderEntryPoint taskShader; public ShaderEntryPoint taskShader;
public ShaderEntryPoint meshShader; public ShaderEntryPoint meshShader;
public ShaderEntryPoint pixelShader; public ShaderEntryPoint pixelShader;
public string? generatedCodePath;
public List<string>? defines; public List<string>? defines;
public List<string>? includes;
public List<KeywordsGroup>? keywords; public List<KeywordsGroup>? keywords;
public List<PropertyDescriptor>? properties;
public PipelineDescriptor localPipeline; public PipelineDescriptor localPipeline;
public string Identifier => uniqueIdentifier; public string Identifier => uniqueIdentifier;
@@ -100,6 +98,42 @@ public class FallbackPassDescriptor : IPassDescriptor
public class ShaderDescriptor public class ShaderDescriptor
{ {
public string name = string.Empty; public string name = string.Empty;
public string? generatedCodePath;
public uint cbufferSize;
public List<PropertyDescriptor> globalProperties = new(); public List<PropertyDescriptor> globalProperties = new();
public List<PropertyDescriptor> properties = new();
public List<IPassDescriptor> passes = new(); public List<IPassDescriptor> passes = new();
} }
public static class ShaderDescriptorExtensions
{
public static uint GetSize(this ShaderPropertyType type)
{
return type switch
{
ShaderPropertyType.Float => 4,
ShaderPropertyType.Float2 => 8,
ShaderPropertyType.Float3 => 12,
ShaderPropertyType.Float4 => 16,
ShaderPropertyType.Float4x4 => 64,
ShaderPropertyType.Int => 4,
ShaderPropertyType.Int2 => 8,
ShaderPropertyType.Int3 => 12,
ShaderPropertyType.Int4 => 16,
ShaderPropertyType.UInt => 4,
ShaderPropertyType.UInt2 => 8,
ShaderPropertyType.UInt3 => 12,
ShaderPropertyType.UInt4 => 16,
ShaderPropertyType.Bool => 4,
ShaderPropertyType.Bool2 => 8,
ShaderPropertyType.Bool3 => 12,
ShaderPropertyType.Bool4 => 16,
ShaderPropertyType.Texture2D => 4, // Bindless resource use uint32
ShaderPropertyType.Texture3D => 4,
ShaderPropertyType.TextureCube => 4,
ShaderPropertyType.Texture2DArray => 4,
ShaderPropertyType.TextureCubeArray => 4,
_ => 0,
};
}
}

View File

@@ -1,4 +1,5 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Diagnostics;
namespace Ghost.Core; namespace Ghost.Core;

View File

@@ -1,5 +1,4 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using TerraFX.Interop.Windows;
namespace Ghost.Core; namespace Ghost.Core;
@@ -44,6 +43,11 @@ public readonly struct Result
return new Result<T, S>(value, status); return new Result<T, S>(value, status);
} }
public static RefResult<T, S> CreateRef<T, S>(ref T value, S status)
{
return new RefResult<T, S>(ref value, status);
}
public override string ToString() => IsSuccess ? "OK" : $"Error: {Message}"; public override string ToString() => IsSuccess ? "OK" : $"Error: {Message}";
public static implicit operator bool(Result result) => result.IsSuccess; public static implicit operator bool(Result result) => result.IsSuccess;
@@ -68,16 +72,6 @@ public readonly struct Result<T>
_message = message; _message = message;
} }
public ref readonly T GetValueRef()
{
if (!IsSuccess)
{
throw new InvalidOperationException("Cannot get value from a failed Result.");
}
return ref Unsafe.AsRef(in _value);
}
public static Result<T> Success(T value) public static Result<T> Success(T value)
{ {
return new Result<T>(true, value); return new Result<T>(true, value);
@@ -92,9 +86,7 @@ public readonly struct Result<T>
public static implicit operator Result<T>(T? data) => data is not null ? Success(data) : Failure(null); public static implicit operator Result<T>(T? data) => data is not null ? Success(data) : Failure(null);
public static implicit operator Result<T>(Result result) => result.IsSuccess ? Success(default!) : Failure(result.Message); public static implicit operator Result<T>(Result result) => result.IsSuccess ? Success(default!) : Failure(result.Message);
public static implicit operator bool(Result<T> result) => result.IsSuccess; public static implicit operator bool(Result<T> result) => result.IsSuccess;
public static implicit operator Result(Result<T> result) => result.IsSuccess ? Result.Success() : Result.Failure(result.Message);
} }
public enum ResultStatus : byte public enum ResultStatus : byte
@@ -126,11 +118,6 @@ public readonly struct Result<T, S>
_status = status; _status = status;
} }
public ref readonly T GetValueRef()
{
return ref Unsafe.AsRef(in _value);
}
public static Result<T, S> Create(T value, S status) public static Result<T, S> Create(T value, S status)
{ {
return new Result<T, S>(value, status); return new Result<T, S>(value, status);
@@ -139,6 +126,28 @@ public readonly struct Result<T, S>
public override string ToString() => $"Value: {_value}, Status: {_status}"; public override string ToString() => $"Value: {_value}, Status: {_status}";
} }
public readonly ref struct RefResult<T, S>
{
private readonly ref T _value;
private readonly S _status;
public ref T Value => ref _value;
public S Status => _status;
public RefResult(ref T value, S status)
{
_value = ref value;
_status = status;
}
public static RefResult<T, S> Create(ref T value, S status)
{
return new RefResult<T, S>(ref value, status);
}
public override string ToString() => $"Value: {_value}, Status: {_status}";
}
public static class ResultExtensions public static class ResultExtensions
{ {
public static void ThrowIfFailed(this Result result) public static void ThrowIfFailed(this Result result)

View File

@@ -32,14 +32,14 @@ internal static unsafe partial class Win32Utility
public static Guid* IID_NULL public static Guid* IID_NULL
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get => (Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in TerraFX.Interop.Windows.IID.IID_NULL)); get => (Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in IID.IID_NULL));
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IID_PPV IID_PPV_ARGS<T>(ComPtr<T> comPtr) public static IID_PPV IID_PPV_ARGS<T>(ComPtr<T>* comPtr)
where T : unmanaged, IUnknown.Interface where T : unmanaged, IUnknown.Interface
{ {
return new IID_PPV(Windows.__uuidof<T>(), comPtr.PPV()); return new IID_PPV(Windows.__uuidof<T>(), (void**)comPtr);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -80,27 +80,6 @@ internal static unsafe partial class Win32Utility
return Result.Failure($"{op} failed with code {hr}"); return Result.Failure($"{op} failed with code {hr}");
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Guid* IID<T>(this ComPtr<T> comPtr)
where T : unmanaged, IUnknown.Interface
{
return Windows.__uuidof<T>();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Guid* IID<T>(this T ptr)
where T : unmanaged, IUnknown.Interface
{
return Windows.__uuidof<T>();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void** PPV<T>(ref this ComPtr<T> comPtr)
where T : unmanaged, IUnknown.Interface
{
return (void**)comPtr.GetAddressOf();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void** ReleaseAndGetVoidAddressOf<T>(ref this ComPtr<T> comPtr) public static void** ReleaseAndGetVoidAddressOf<T>(ref this ComPtr<T> comPtr)
where T : unmanaged, IUnknown.Interface where T : unmanaged, IUnknown.Interface

View File

@@ -9,6 +9,7 @@
<ResourceDictionary> <ResourceDictionary>
<ResourceDictionary.MergedDictionaries> <ResourceDictionary.MergedDictionaries>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" /> <XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
<ResourceDictionary Source="ms-appx:///Microsoft.UI.Xaml/DensityStyles/Compact.xaml" />
<core:ControlsDictionary /> <core:ControlsDictionary />
<ResourceDictionary Source="/Themes/Override.xaml" /> <ResourceDictionary Source="/Themes/Override.xaml" />
</ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries>

View File

@@ -55,7 +55,6 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Ghost.Editor.Core\Ghost.Editor.Core.csproj" /> <ProjectReference Include="..\Ghost.Editor.Core\Ghost.Editor.Core.csproj" />
<ProjectReference Include="..\Ghost.Engine\Ghost.Engine.csproj" /> <ProjectReference Include="..\Ghost.Engine\Ghost.Engine.csproj" />
<ProjectReference Include="..\Ghost.Entities\Ghost.Entities.csproj" />
<ProjectReference Include="..\Ghost.Test.Core\Ghost.Test.Core.csproj" /> <ProjectReference Include="..\Ghost.Test.Core\Ghost.Test.Core.csproj" />
</ItemGroup> </ItemGroup>
@@ -84,9 +83,14 @@
<PropertyGroup> <PropertyGroup>
<PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun> <PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun>
<PublishReadyToRun Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRun> <PublishReadyToRun Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRun>
<PublishTrimmed>False</PublishTrimmed>
<SupportedOSPlatformVersion>10.0.20348.0</SupportedOSPlatformVersion> <SupportedOSPlatformVersion>10.0.20348.0</SupportedOSPlatformVersion>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" />
</Project> </Project>

View File

@@ -2,7 +2,7 @@
"profiles": { "profiles": {
"Ghost.Graphics.Test (Package)": { "Ghost.Graphics.Test (Package)": {
"commandName": "MsixPackage", "commandName": "MsixPackage",
"nativeDebugging": true "nativeDebugging": false
}, },
"Ghost.Graphics.Test (Unpackaged)": { "Ghost.Graphics.Test (Unpackaged)": {
"commandName": "Project" "commandName": "Project"

View File

@@ -1,15 +1,14 @@
using Ghost.Graphics;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Microsoft.UI.Xaml; using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Media;
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using TerraFX.Interop.WinRT;
using WinRT;
namespace Ghost.Graphics.Test.Windows; namespace Ghost.Graphics.Test.Windows;
public sealed partial class GraphicsTestWindow : Window public sealed partial class GraphicsTestWindow : Window
{ {
private bool _isFirstActivationHandled = false;
private IRenderSystem? _renderSystem; private IRenderSystem? _renderSystem;
private IRenderer? _renderer; private IRenderer? _renderer;
private ISwapChain? _swapChain; private ISwapChain? _swapChain;
@@ -18,33 +17,46 @@ public sealed partial class GraphicsTestWindow : Window
{ {
InitializeComponent(); InitializeComponent();
Panel.Loaded += SwapChainPanel_Loaded; Activated += GraphicsTestWindow_Activated;
Panel.Unloaded += SwapChainPanel_Unloaded; Closed += GraphicsTestWindow_Closed;
Panel.SizeChanged += SwapChainPanel_SizeChanged; Panel.SizeChanged += SwapChainPanel_SizeChanged;
} }
private void SwapChainPanel_Loaded(object sender, RoutedEventArgs e) private void GraphicsTestWindow_Activated(object sender, WindowActivatedEventArgs e)
{ {
if (_isFirstActivationHandled)
{
return;
}
#if DEBUG #if DEBUG
AllocationManager.EnableDebugLayer(); AllocationManager.EnableDebugLayer();
#endif #endif
_renderSystem = new RenderSystem(new() _renderSystem = new RenderSystem(new RenderingConfig()
{ {
FrameBufferCount = 2, FrameBufferCount = 2,
GraphicsAPI = GraphicsAPI.Direct3D12 GraphicsAPI = GraphicsAPI.Direct3D12
}); });
_renderer = _renderSystem.GraphicsEngine.CreateRenderer(); _renderer = _renderSystem.GraphicsEngine.CreateRenderer();
_swapChain = _renderSystem.GraphicsEngine.CreateSwapChain(new SwapChainDesc((uint)AppWindow.Size.Width, (uint)AppWindow.Size.Height, SwapChainTarget.FromCompositionSurface(Panel))); _swapChain = _renderSystem.GraphicsEngine.CreateSwapChain(new SwapChainDesc
{
Width = (uint)AppWindow.Size.Width,
Height = (uint)AppWindow.Size.Height,
Target = SwapChainTarget.FromCompositionSurface(Panel)
});
_renderer.SetSwapChain(_swapChain); _renderer.SetSwapChain(_swapChain);
_renderSystem.Start(); _renderSystem.Start();
CompositionTarget.Rendering += OnRendering; CompositionTarget.Rendering += OnRendering;
e.Handled = true;
_isFirstActivationHandled = true;
} }
private void SwapChainPanel_Unloaded(object sender, RoutedEventArgs e) private void GraphicsTestWindow_Closed(object sender, WindowEventArgs e)
{ {
CompositionTarget.Rendering -= OnRendering; CompositionTarget.Rendering -= OnRendering;
_renderSystem?.Stop(); _renderSystem?.Stop();
@@ -52,6 +64,10 @@ public sealed partial class GraphicsTestWindow : Window
_renderer?.Dispose(); _renderer?.Dispose();
_swapChain?.Dispose(); _swapChain?.Dispose();
_renderSystem?.Dispose(); _renderSystem?.Dispose();
#if DEBUG
AllocationManager.Dispose();
#endif
} }
private void SwapChainPanel_SizeChanged(object sender, SizeChangedEventArgs e) private void SwapChainPanel_SizeChanged(object sender, SizeChangedEventArgs e)
@@ -69,7 +85,7 @@ public sealed partial class GraphicsTestWindow : Window
return; return;
} }
if (_renderSystem.CPUFenceValue < _renderSystem.GPUFenceValue + _renderSystem.Config.FrameBufferCount) if (_renderSystem.CPUFenceValue < _renderSystem.GPUFenceValue + _renderSystem.MaxFrameLatency)
{ {
DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.High, () => DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.High, () =>
{ {

View File

@@ -6,7 +6,7 @@ using Misaki.HighPerformance.LowLevel.Collections;
namespace Ghost.Graphics.Contracts; namespace Ghost.Graphics.Contracts;
public struct CompileResult : IDisposable public struct ShaderCompileResult : IDisposable
{ {
public UnsafeArray<byte> bytecode; public UnsafeArray<byte> bytecode;
public ShaderReflectionData reflectionData; public ShaderReflectionData reflectionData;
@@ -21,9 +21,9 @@ public struct CompileResult : IDisposable
public struct GraphicsCompiledResult : IDisposable public struct GraphicsCompiledResult : IDisposable
{ {
public CompileResult tsResult; public ShaderCompileResult tsResult;
public CompileResult msResult; public ShaderCompileResult msResult;
public CompileResult psResult; public ShaderCompileResult psResult;
public void Dispose() public void Dispose()
{ {
@@ -143,7 +143,7 @@ public readonly struct ShaderReflectionData
public interface IShaderCompiler : IDisposable public interface IShaderCompiler : IDisposable
{ {
Result<CompileResult> Compile(ref readonly CompilerConfig config, Allocator allocator); Result<ShaderCompileResult> Compile(ref readonly CompilerConfig config, Allocator allocator);
Result<GraphicsCompiledResult> CompilePass(IPassDescriptor descriptor); Result<GraphicsCompiledResult> CompilePass(IPassDescriptor descriptor, string? generatedCodePath);
Result<GraphicsCompiledResult> LoadCompiledCache(ShaderPassKey key); Result<GraphicsCompiledResult> LoadCompiledCache(ShaderPassKey key);
} }

View File

@@ -2,8 +2,6 @@ using Ghost.Core;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.LowLevel.Utilities;
using Misaki.HighPerformance.Mathematics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace Ghost.Graphics.Core; namespace Ghost.Graphics.Core;
@@ -12,29 +10,38 @@ internal struct CBufferCache : IResourceReleasable
{ {
private UnsafeArray<byte> _cpuData; private UnsafeArray<byte> _cpuData;
private Handle<GraphicsBuffer> _gpuResource; private Handle<GraphicsBuffer> _gpuResource;
private uint _size;
private uint _alignedSize; private uint _alignedSize;
public readonly UnsafeArray<byte> CpuData => _cpuData; public readonly UnsafeArray<byte> CpuData => _cpuData;
public readonly Handle<GraphicsBuffer> GpuResource => _gpuResource; public readonly Handle<GraphicsBuffer> GpuResource => _gpuResource;
public readonly uint Size => _size;
public readonly uint AlignedSize => _alignedSize; public readonly uint AlignedSize => _alignedSize;
public readonly bool IsCreated => _gpuResource.IsValid && _cpuData.IsCreated; public readonly bool IsCreated => _size != 0 && _gpuResource.IsValid && _cpuData.IsCreated;
public CBufferCache(Handle<GraphicsBuffer> buffer, uint bufferSize) public CBufferCache(Handle<GraphicsBuffer> buffer, uint bufferSize)
{ {
_size = bufferSize;
_alignedSize = (bufferSize + 255u) & ~255u; _alignedSize = (bufferSize + 255u) & ~255u;
_cpuData = new((int)AlignedSize, Allocator.Persistent); _cpuData = new UnsafeArray<byte>((int)AlignedSize, Allocator.Persistent);
_gpuResource = buffer; _gpuResource = buffer;
} }
public void ReleaseResource(IResourceDatabase database) public void ReleaseResource(IResourceDatabase database)
{ {
if (!IsCreated)
{
return;
}
_cpuData.Dispose(); _cpuData.Dispose();
database.ReleaseResource(GpuResource.AsResource()); database.ReleaseResource(GpuResource.AsResource());
_gpuResource = Handle<GraphicsBuffer>.Invalid; _gpuResource = Handle<GraphicsBuffer>.Invalid;
_size = 0;
_alignedSize = 0; _alignedSize = 0;
} }
} }
@@ -42,183 +49,96 @@ internal struct CBufferCache : IResourceReleasable
public struct Material : IResourceReleasable, IHandleType public struct Material : IResourceReleasable, IHandleType
{ {
private Identifier<Shader> _shader; private Identifier<Shader> _shader;
private UnsafeArray<CBufferCache> _materialPropertiesCache; // One per shader pass private CBufferCache _cBufferCache;
internal readonly CBufferCache CBufferCache => _cBufferCache;
public readonly Identifier<Shader> Shader => _shader; public readonly Identifier<Shader> Shader => _shader;
internal ref CBufferCache GetPassCache(int passIndex) public Result SetShader(Identifier<Shader> shaderId, IResourceAllocator allocator, IResourceDatabase database)
{
return ref _materialPropertiesCache[passIndex];
}
public void SetShader(Identifier<Shader> shaderId, IResourceAllocator allocator, IResourceDatabase database)
{ {
if (!shaderId.IsValid) if (!shaderId.IsValid)
{ {
throw new ArgumentException("Shader ID is invalid."); return Result.Failure("Shader ID is invalid.");
} }
_cBufferCache.ReleaseResource(database);
_shader = shaderId; _shader = shaderId;
var shader = database.GetShaderReference(shaderId); var shader = database.GetShaderReference(shaderId);
_materialPropertiesCache = new UnsafeArray<CBufferCache>(shader.PassCount, Allocator.Persistent); if (shader.CBufferSize != 0)
for (var i = 0; i < shader.PassCount; i++)
{ {
var pass = database.GetShaderPass(shader.GetPassKey(i));
var cbufferInfo = pass.CBuffer;
if (cbufferInfo.SizeInBytes == 0)
{
continue;
}
var desc = new BufferDesc var desc = new BufferDesc
{ {
Size = cbufferInfo.SizeInBytes, Size = shader.CBufferSize,
Usage = BufferUsage.Constant, Usage = BufferUsage.Constant,
MemoryType = ResourceMemoryType.Default, MemoryType = ResourceMemoryType.Default,
}; };
var buffer = allocator.CreateBuffer(ref desc); var buffer = allocator.CreateBuffer(ref desc);
_materialPropertiesCache[i] = new CBufferCache(buffer, cbufferInfo.SizeInBytes); _cBufferCache = new CBufferCache(buffer, shader.CBufferSize);
}
}
void IResourceReleasable.ReleaseResource(IResourceDatabase database)
{
foreach (var cache in _materialPropertiesCache)
{
cache.ReleaseResource(database);
} }
_materialPropertiesCache.Dispose(); return Result.Success();
}
}
public ref struct MaterialAccessor
{
private ref Material _materialData;
private readonly Shader _shader;
private readonly IResourceDatabase _resourceDatabase;
public MaterialAccessor(Handle<Material> material, IResourceDatabase resourceDatabase)
{
_resourceDatabase = resourceDatabase;
_materialData = ref resourceDatabase.GetMaterialReference(material);
_shader = resourceDatabase.GetShaderReference(_materialData.Shader);
} }
private readonly unsafe void WriteToCache<T>(string propertyName, in T value) [MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly unsafe Result<T, ResultStatus> GetPropertyCache<T>()
where T : unmanaged where T : unmanaged
{ {
foreach (var index in _shader.GetPropertyPassIndices(propertyName)) if (sizeof(T) != _cBufferCache.Size)
{ {
var passKey = _shader.GetPassKey(index); return Result.Create(default(T), ResultStatus.InvalidArgument);
var pass = _resourceDatabase.GetShaderPass(passKey);
var propertyInfo = pass.GetPropertyInfo(propertyName);
if (propertyInfo.Size != sizeof(T))
{
throw new ArgumentException($"Property '{propertyName}' has a size mismatch. Expected {propertyInfo.Size} bytes, but got {sizeof(T)} bytes.");
}
ref var cache = ref _materialData.GetPassCache(index);
Unsafe.WriteUnaligned(ref cache.CpuData[propertyInfo.StartOffset], value);
}
}
/// <summary>
/// Sets a float property in the material's constant buffer.
/// </summary>
/// <param name="propertyName">The name of the property to set.</param>
/// <param name="value">The Value to set for the property.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly void SetFloat(string propertyName, in float value)
{
WriteToCache(propertyName, in value);
}
/// <summary>
/// Sets a uint property in the material's constant buffer (useful for texture indices).
/// </summary>
/// <param name="propertyName">The name of the property to set.</param>
/// <param name="value">The Value to set for the property.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly void SetUInt(string propertyName, in uint value)
{
WriteToCache(propertyName, in value);
}
/// <summary>
/// Sets a Vector property in the material's constant buffer.
/// </summary>
/// <param name="propertyName">The name of the property to set.</param>
/// <param name="value">The Value to set for the property.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly void SetVector(string propertyName, in float4 value)
{
WriteToCache(propertyName, in value);
}
/// <summary>
/// Sets a Matrix property in the material's constant buffer.
/// </summary>
/// <param name="propertyName">The name of the property to set.</param>
/// <param name="value">The Value to set for the property.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly void SetMatrix(string propertyName, in float4x4 value)
{
WriteToCache(propertyName, in value);
}
/// <summary>
/// Sets a texture index for a shader property (for bindless texture access)
/// </summary>
/// <param name="propertyName">The name of the shader property (e.g., "_TextureIndex1")</param>
/// <param name="texture">The bindless texture to reference</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly void SetTextureBindless(string propertyName, Handle<Texture> texture)
{
var bindlessIndex = _resourceDatabase.GetBindlessIndex(texture.AsResource());
if (bindlessIndex == -1)
{
throw new ArgumentException("The provided texture does not have a valid bindless index. Ensure the texture is created with bindless support.");
} }
SetUInt(propertyName, (uint)bindlessIndex); return Result.Create(*(T*)_cBufferCache.CpuData.GetUnsafePtr(), ResultStatus.Success);
} }
/// <summary>
/// Sets the mesh buffer indices for bindless vertex and index buffer access
/// </summary>
/// <param name="mesh">The mesh whose buffer indices to set</param>
/// <param name="vertexBufferIndexProperty">The name of the vertex buffer index property</param>
/// <param name="indexBufferIndexProperty">The name of the index buffer index property</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly void SetBufferBindless(string propertyName, Handle<GraphicsBuffer> buffer) public readonly Span<byte> GetRawPropertyCache()
{ {
var bindlessIndex = _resourceDatabase.GetBindlessIndex(buffer.AsResource()); if (_cBufferCache.Size == 0)
if (bindlessIndex == -1)
{ {
throw new ArgumentException("The provided buffer does not have a valid bindless index. Ensure the buffer is created with bindless support."); return Span<byte>.Empty;
} }
SetUInt(propertyName, (uint)bindlessIndex); return _cBufferCache.CpuData.AsSpan(0, (int)_cBufferCache.Size);
} }
/// <summary>
/// Uploads all cached material data to the GPU using the specified command buffer.
/// </summary>
/// <param name="cmb">The command buffer used to perform the upload operations to the GPU. Cannot be null.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly void UploadMaterialData(ICommandBuffer cmb) public readonly unsafe Result<ResultStatus> SetPropertyCache<T>(ref readonly T data)
where T : unmanaged
{ {
for (var i = 0; i < _shader.PassCount; i++) if (sizeof(T) != _cBufferCache.Size)
{ {
ref var cache = ref _materialData.GetPassCache(i); return new Result<ResultStatus>(false, ResultStatus.InvalidArgument);
cmb.UploadBuffer<byte>(cache.GpuResource, cache.CpuData.AsSpan());
} }
Unsafe.WriteUnaligned(_cBufferCache.CpuData.GetUnsafePtr(), data);
return Result.Success(ResultStatus.Success);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly unsafe Result<ResultStatus> SetRawPropertyCache(ReadOnlySpan<byte> data)
{
if (data.Length != _cBufferCache.Size)
{
return new Result<ResultStatus>(false, ResultStatus.InvalidArgument);
}
Unsafe.WriteUnaligned(_cBufferCache.CpuData.GetUnsafePtr(), data);
return Result.Success(ResultStatus.Success);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly void UploadData(ICommandBuffer cmb)
{
cmb.UploadBuffer(_cBufferCache.GpuResource, _cBufferCache.CpuData.AsSpan());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void IResourceReleasable.ReleaseResource(IResourceDatabase database)
{
_cBufferCache.ReleaseResource(database);
} }
} }

View File

@@ -11,6 +11,9 @@ namespace Ghost.Graphics.Core;
public struct Mesh : IResourceReleasable, IHandleType public struct Mesh : IResourceReleasable, IHandleType
{ {
private UnsafeList<Vertex> _vertices;
private UnsafeList<uint> _indices;
internal bool IsMeshDataDirty internal bool IsMeshDataDirty
{ {
get; private set; get; private set;
@@ -21,10 +24,10 @@ public struct Mesh : IResourceReleasable, IHandleType
/// </summary> /// </summary>
public UnsafeList<Vertex> Vertices public UnsafeList<Vertex> Vertices
{ {
readonly get => field; readonly get => _vertices;
set set
{ {
field = value; _vertices = value;
VertexCount = value.Count; VertexCount = value.Count;
IsMeshDataDirty = true; IsMeshDataDirty = true;
} }
@@ -35,10 +38,10 @@ public struct Mesh : IResourceReleasable, IHandleType
/// </summary> /// </summary>
public UnsafeList<uint> Indices public UnsafeList<uint> Indices
{ {
readonly get => field; readonly get => _indices;
set set
{ {
field = value; _indices = value;
IndexCount = value.Count; IndexCount = value.Count;
IsMeshDataDirty = true; IsMeshDataDirty = true;
} }
@@ -100,8 +103,8 @@ public struct Mesh : IResourceReleasable, IHandleType
internal Mesh(ReadOnlySpan<Vertex> vertices, ReadOnlySpan<uint> indices, Handle<GraphicsBuffer> vertexBuffer, Handle<GraphicsBuffer> indexBuffer) internal Mesh(ReadOnlySpan<Vertex> vertices, ReadOnlySpan<uint> indices, Handle<GraphicsBuffer> vertexBuffer, Handle<GraphicsBuffer> indexBuffer)
{ {
Vertices = new(vertices.Length, Allocator.Persistent); Vertices = new UnsafeList<Vertex>(vertices.Length, Allocator.Persistent);
Indices = new(indices.Length, Allocator.Persistent); Indices = new UnsafeList<uint>(indices.Length, Allocator.Persistent);
Vertices.CopyFrom(vertices); Vertices.CopyFrom(vertices);
Indices.CopyFrom(indices); Indices.CopyFrom(indices);
VertexBuffer = vertexBuffer; VertexBuffer = vertexBuffer;
@@ -112,8 +115,8 @@ public struct Mesh : IResourceReleasable, IHandleType
public readonly void ReleaseCpuResources() public readonly void ReleaseCpuResources()
{ {
Vertices.Dispose(); _vertices.Dispose();
Indices.Dispose(); _indices.Dispose();
} }
void IResourceReleasable.ReleaseResource(IResourceDatabase database) void IResourceReleasable.ReleaseResource(IResourceDatabase database)

View File

@@ -56,13 +56,32 @@ public unsafe readonly ref struct RenderingContext
queue.WaitIdle(); queue.WaitIdle();
} }
public Handle<Mesh> CreateMesh(UnsafeList<Vertex> vertices, UnsafeList<uint> indices) public Handle<Mesh> CreateMesh(UnsafeList<Vertex> vertices, UnsafeList<uint> indices, bool staticMesh)
{ {
var mesh = ResourceAllocator.CreateMesh(vertices, indices); var mesh = ResourceAllocator.CreateMesh(vertices, indices);
ref var meshData = ref ResourceDatabase.GetMeshReference(mesh);
var vertexHandle = meshData.VertexBuffer.AsResource();
var indexHandle = meshData.IndexBuffer.AsResource();
_directCmd.ResourceBarrier(vertexHandle, ResourceState.Common, ResourceState.CopyDest);
_directCmd.ResourceBarrier(indexHandle, ResourceState.Common, ResourceState.CopyDest);
_directCmd.UploadBuffer(meshData.VertexBuffer, meshData.Vertices.AsSpan());
_directCmd.UploadBuffer(meshData.IndexBuffer, meshData.Indices.AsSpan());
_directCmd.ResourceBarrier(vertexHandle, ResourceState.CopyDest, ResourceState.VertexAndConstantBuffer);
_directCmd.ResourceBarrier(indexHandle, ResourceState.CopyDest, ResourceState.IndexBuffer);
if (staticMesh)
{
meshData.ReleaseCpuResources();
}
return mesh; return mesh;
} }
public Handle<Mesh> CreateMesh(ReadOnlySpan<Vertex> vertices, ReadOnlySpan<uint> indices) public Handle<Mesh> CreateMesh(ReadOnlySpan<Vertex> vertices, ReadOnlySpan<uint> indices, bool staticMesh)
{ {
var vertexList = new UnsafeList<Vertex>(vertices.Length, Allocator.Persistent); var vertexList = new UnsafeList<Vertex>(vertices.Length, Allocator.Persistent);
var indexList = new UnsafeList<uint>(indices.Length, Allocator.Persistent); var indexList = new UnsafeList<uint>(indices.Length, Allocator.Persistent);
@@ -70,12 +89,7 @@ public unsafe readonly ref struct RenderingContext
vertexList.CopyFrom(vertices); vertexList.CopyFrom(vertices);
indexList.CopyFrom(indices); indexList.CopyFrom(indices);
return CreateMesh(vertexList, indexList); return CreateMesh(vertexList, indexList, staticMesh);
}
public MaterialAccessor GetMaterialAccessor(Handle<Material> material)
{
return new MaterialAccessor(material, ResourceDatabase);
} }
// TODO: Make one memory pool for upload. // TODO: Make one memory pool for upload.
@@ -108,12 +122,12 @@ public unsafe readonly ref struct RenderingContext
if (needVertexTransition) if (needVertexTransition)
{ {
_directCmd.ResourceBarrier(meshData.VertexBuffer.AsResource(), ResourceState.CopyDest, ResourceState.VertexAndConstantBuffer); _directCmd.ResourceBarrier(meshData.VertexBuffer.AsResource(), ResourceState.CopyDest, vertexState);
} }
if (needIndexTransition) if (needIndexTransition)
{ {
_directCmd.ResourceBarrier(meshData.IndexBuffer.AsResource(), ResourceState.CopyDest, ResourceState.IndexBuffer); _directCmd.ResourceBarrier(meshData.IndexBuffer.AsResource(), ResourceState.CopyDest, indexState);
} }
if (markMeshStatic) if (markMeshStatic)
@@ -177,7 +191,7 @@ public unsafe readonly ref struct RenderingContext
slicePitch = slicePitch slicePitch = slicePitch
}; };
_directCmd.UploadTexture(texture, subresourceData); _directCmd.UploadTexture(texture, [subresourceData]);
} }
if (needTransition) if (needTransition)
@@ -194,10 +208,15 @@ public unsafe readonly ref struct RenderingContext
ref var materialRef = ref ResourceDatabase.GetMaterialReference(material); ref var materialRef = ref ResourceDatabase.GetMaterialReference(material);
var shader = ResourceDatabase.GetShaderReference(materialRef.Shader); var shader = ResourceDatabase.GetShaderReference(materialRef.Shader);
shader.TryGetPassKey(passName, out var passIndex, out var passKey); var keyResult = shader.TryGetPassKey(passName, out var passIndex);
if (keyResult.Status != ResultStatus.Success)
{
throw new Exception(keyResult.ToString());
}
var hash = new GraphicsPipelineHash var hash = new GraphicsPipelineHash
{ {
Id = passKey, Id = keyResult.Value.Identifier,
RtvCount = 1, RtvCount = 1,
DsvFormat = TextureFormat.Unknown, DsvFormat = TextureFormat.Unknown,
}; };
@@ -209,20 +228,13 @@ public unsafe readonly ref struct RenderingContext
_directCmd.SetConstantBufferView(RootSignatureLayout.PER_OBJECT_BUFFER_SLOT, meshRef.ObjectDataBuffer); _directCmd.SetConstantBufferView(RootSignatureLayout.PER_OBJECT_BUFFER_SLOT, meshRef.ObjectDataBuffer);
// NOTE: We use fixed root signature layout for bindless rendering. // NOTE: We use fixed root signature layout for bindless rendering.
ref var cache = ref materialRef.GetPassCache(passIndex); var cache = materialRef.CBufferCache;
if (cache.IsCreated) if (cache.IsCreated)
{ {
_directCmd.SetConstantBufferView(RootSignatureLayout.PER_MATERIAL_BUFFER_SLOT, cache.GpuResource); _directCmd.SetConstantBufferView(RootSignatureLayout.PER_MATERIAL_BUFFER_SLOT, cache.GpuResource);
} }
// NOTE: Since we are using true bindless resources, we only need to set the descriptor heaps, not individual tables. var threadGroupCountX = ((uint)meshRef.IndexCount + numThreadsX - 1) / numThreadsX;
// TODO: Maybe handle the traditional bindless model? _directCmd.DispatchMesh(threadGroupCountX, 1, 1);
#if false
var samplerGpuHandle = _descriptorAllocator.GetSamplerHeap()->GetGPUDescriptorHandleForHeapStart();
_commandList.Get()->SetGraphicsRootDescriptorTable(rootParamIndex, samplerGpuHandle);
#endif
//var threadGroupCountX = ((uint)meshRef.IndexCount + numThreadsX - 1) / numThreadsX;
_directCmd.DispatchMesh(1, 1, 1);
} }
} }

View File

@@ -3,45 +3,44 @@ using Ghost.Core.Graphics;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Collections;
using System.Runtime.InteropServices; using System.Runtime.CompilerServices;
namespace Ghost.Graphics.Core; namespace Ghost.Graphics.Core;
public class ShaderPass : IResourceReleasable public struct ShaderPass : IResourceReleasable
{ {
private CBufferInfo _cbufferInfo; public ShaderPassKey Identifier
// NOTE: This is for per pass cbuffer only. Global, per view, and per mesh cbuffers are fixed.
private readonly Dictionary<string, int> _propertyLookup;
public CBufferInfo CBuffer => _cbufferInfo;
public ShaderPass(CBufferInfo info)
{ {
_cbufferInfo = info; get; init;
var capacity = info.Properties?.Count ?? 0;
_propertyLookup = new Dictionary<string, int>(capacity);
for (var i = 0; i < capacity; i++)
{
_propertyLookup[info.Properties![i].Name] = i;
}
} }
public int GetPropertyId(string propertyName) public ZTestOptions ZTest
{ {
return _propertyLookup.TryGetValue(propertyName, out var id) ? id : -1; get; set;
} }
public CBufferPropertyInfo GetPropertyInfo(int id) public ZWriteOptions ZWrite
{ {
return _cbufferInfo.Properties[id]; get; set;
} }
public CBufferPropertyInfo GetPropertyInfo(string propertyName) public CullOptions Cull
{ {
return _cbufferInfo.Properties[GetPropertyId(propertyName)]; get; set;
} }
public BlendOptions Blend
{
get; set;
}
public uint ColorMask
{
get; set;
}
// TODO: Shader variant.
void IResourceReleasable.ReleaseResource(IResourceDatabase database) void IResourceReleasable.ReleaseResource(IResourceDatabase database)
{ {
} }
@@ -52,46 +51,43 @@ public class ShaderPass : IResourceReleasable
/// </summary> /// </summary>
public class Shader : IResourceReleasable, IIdentifierType public class Shader : IResourceReleasable, IIdentifierType
{ {
private UnsafeArray<ShaderPassKey> _passIDs; private readonly uint _cbufferSize;
private UnsafeArray<ShaderPass> _passes;
// TODO: Optmize lookups with a better data structure if needed // TODO: Optmize lookups with a better data structure if needed
private readonly Dictionary<string, int> _passLookup; // pass name to index private readonly Dictionary<string, int> _passLookup; // pass name to index
private readonly Dictionary<string, List<int>> _propertyLookup; // property name to pass index (property name to list of pass indices that contain the property)
public int PassCount => _passIDs.Count; public int PassCount => _passes.Count;
public uint CBufferSize => _cbufferSize;
internal Shader(ShaderDescriptor descriptor) internal Shader(ShaderDescriptor descriptor)
{ {
_passIDs = new UnsafeArray<ShaderPassKey>(descriptor.passes.Count, Allocator.Persistent); _cbufferSize = descriptor.cbufferSize;
_passLookup = new(descriptor.passes.Count); _passes = new UnsafeArray<ShaderPass>(descriptor.passes.Count, Allocator.Persistent);
_propertyLookup = new(descriptor.passes.Count); _passLookup = new Dictionary<string, int>(descriptor.passes.Count);
for (var i = 0; i < descriptor.passes.Count; i++) for (var i = 0; i < descriptor.passes.Count; i++)
{ {
var pass = descriptor.passes[i]; var pass = descriptor.passes[i];
// TODO: Handle inherited passes
if (pass is not FullPassDescriptor fullPass)
{
continue;
}
var passKey = new ShaderPassKey(pass.Identifier); var passKey = new ShaderPassKey(pass.Identifier);
_passIDs[i] = passKey; _passes[i] = new ShaderPass
_passLookup[pass.Name] = i;
if (pass is FullPassDescriptor fullPass)
{ {
if (fullPass.properties == null) Identifier = passKey,
{ ZTest = fullPass.localPipeline.zTest,
continue; ZWrite = fullPass.localPipeline.zWrite,
} Cull = fullPass.localPipeline.cull,
Blend = fullPass.localPipeline.blend,
ColorMask = fullPass.localPipeline.colorMask
};
foreach (var property in fullPass.properties) _passLookup[pass.Name] = i;
{
ref var passIndices = ref CollectionsMarshal.GetValueRefOrAddDefault(_propertyLookup, property.name, out var exists);
if (!exists || passIndices == null)
{
passIndices = new List<int>();
}
passIndices.Add(i);
}
}
// TODO: handle inherited passes
} }
} }
@@ -100,38 +96,26 @@ public class Shader : IResourceReleasable, IIdentifierType
return _passLookup.GetValueOrDefault(passName, -1); return _passLookup.GetValueOrDefault(passName, -1);
} }
public ShaderPassKey GetPassKey(int index) public ref ShaderPass GetPassReference(int index)
{ {
return _passIDs[index]; return ref _passes[index];
} }
public bool TryGetPassKey(string passName, out int passIndex, out ShaderPassKey passID) public RefResult<ShaderPass, ResultStatus> TryGetPassKey(string passName, out int passIndex)
{ {
var index = _passLookup.GetValueOrDefault(passName, -1); var index = _passLookup.GetValueOrDefault(passName, -1);
if (index == -1) if (index == -1)
{ {
passIndex = -1; passIndex = -1;
passID = new(0); return Result.CreateRef(ref Unsafe.NullRef<ShaderPass>(), ResultStatus.NotFound);
return false;
} }
passIndex = index; passIndex = index;
passID = _passIDs[index]; return Result.CreateRef(ref _passes[index], ResultStatus.Success);
return true;
}
public IReadOnlyCollection<int> GetPropertyPassIndices(string propertyName)
{
if (_propertyLookup.TryGetValue(propertyName, out var passIndices))
{
return passIndices;
}
return Array.Empty<int>();
} }
void IResourceReleasable.ReleaseResource(IResourceDatabase database) void IResourceReleasable.ReleaseResource(IResourceDatabase database)
{ {
_passIDs.Dispose(); _passes.Dispose();
} }
} }

View File

@@ -9,7 +9,7 @@ namespace Ghost.Graphics.Core;
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct Vertex public struct Vertex
{ {
public unsafe static class Semantic public static class Semantic
{ {
public const DXGI_FORMAT ALIGNED_FORMAT = DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT; public const DXGI_FORMAT ALIGNED_FORMAT = DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT;
public const int COUNT = 5; public const int COUNT = 5;

View File

@@ -173,17 +173,65 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
_commandList.Get()->RSSetScissorRects(1, &d3d12Rect); _commandList.Get()->RSSetScissorRects(1, &d3d12Rect);
} }
public void ResourceBarrier(Handle<GPUResource> resource, ResourceState before, ResourceState after) public void ResourceBarrier(ReadOnlySpan<BarrierDesc> barrierDescs)
{ {
ThrowIfDisposed(); ThrowIfDisposed();
ThrowIfNotRecording(); ThrowIfNotRecording();
IncrementCommandCount(); IncrementCommandCount();
var d3d12Resource = _resourceDatabase.GetResource(resource); var count = 0u;
var barrier = D3D12_RESOURCE_BARRIER.InitTransition(d3d12Resource, var pBarriers = stackalloc D3D12_RESOURCE_BARRIER[barrierDescs.Length];
before.ToD3D12States(), after.ToD3D12States());
for (int i = 0; i < barrierDescs.Length; i++)
{
var desc = barrierDescs[i];
if (desc.StateBefore == desc.StateAfter)
{
continue;
}
if (!desc.Resource.IsValid)
{
throw new ArgumentException($"Barrier resource at index {i} is not a valid resource handle");
}
ref var resourceRecord = ref _resourceDatabase.GetResourceRecord(desc.Resource.AsResource());
if (resourceRecord.state != desc.StateBefore)
{
throw new InvalidOperationException($"Resource state mismatch: expected {desc.StateBefore}, actual {resourceRecord.state}");
}
var barrier = D3D12_RESOURCE_BARRIER.InitTransition(resourceRecord.ResourcePtr,
desc.StateBefore.ToD3D12States(), desc.StateAfter.ToD3D12States());
pBarriers[count] = barrier;
count++;
// Update the resource state in the database
resourceRecord.state = desc.StateAfter;
}
_commandList.Get()->ResourceBarrier(count, pBarriers);
}
public void ResourceBarrier(Handle<GPUResource> resource, ResourceState stateBefore, ResourceState stateAfter)
{
if (stateBefore == stateAfter)
{
return;
}
ref var resourceRecord = ref _resourceDatabase.GetResourceRecord(resource);
if (resourceRecord.state != stateBefore)
{
throw new InvalidOperationException($"Resource state mismatch: expected {stateBefore}, actual {resourceRecord.state}");
}
var barrier = D3D12_RESOURCE_BARRIER.InitTransition(resourceRecord.ResourcePtr,
stateBefore.ToD3D12States(), stateAfter.ToD3D12States());
_commandList.Get()->ResourceBarrier(1, &barrier); _commandList.Get()->ResourceBarrier(1, &barrier);
resourceRecord.state = stateAfter;
} }
public void SetRenderTargets(ReadOnlySpan<Handle<Texture>> renderTargets, Handle<Texture> depthTarget) public void SetRenderTargets(ReadOnlySpan<Handle<Texture>> renderTargets, Handle<Texture> depthTarget)
@@ -465,7 +513,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
_commandList.Get()->CopyBufferRegion(pResource, 0, uploadResource, 0, sizeInBytes); _commandList.Get()->CopyBufferRegion(pResource, 0, uploadResource, 0, sizeInBytes);
} }
public void UploadTexture(Handle<Texture> texture, params ReadOnlySpan<SubResourceData> subresources) public void UploadTexture(Handle<Texture> texture, ReadOnlySpan<SubResourceData> subresources)
{ {
ThrowIfDisposed(); ThrowIfDisposed();
ThrowIfNotRecording(); ThrowIfNotRecording();

View File

@@ -19,7 +19,7 @@ internal unsafe class D3D12DescriptorAllocator : IDisposable
private bool _disposed; private bool _disposed;
public unsafe D3D12DescriptorAllocator(D3D12RenderDevice device, int initialRtvCount = 256, int initialDsvCount = 256, int initialSrvCount = 200_000, int initialSamplerCount = 256) public D3D12DescriptorAllocator(D3D12RenderDevice device, int initialRtvCount = 256, int initialDsvCount = 256, int initialSrvCount = 200_000, int initialSamplerCount = 256)
{ {
_rtvHeap = new D3D12DescriptorHeap("rtv", device, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, initialRtvCount, initialRtvCount / 2); _rtvHeap = new D3D12DescriptorHeap("rtv", device, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, initialRtvCount, initialRtvCount / 2);
_dsvHeap = new D3D12DescriptorHeap("dsv", device, D3D12_DESCRIPTOR_HEAP_TYPE_DSV, initialDsvCount, initialDsvCount / 2); _dsvHeap = new D3D12DescriptorHeap("dsv", device, D3D12_DESCRIPTOR_HEAP_TYPE_DSV, initialDsvCount, initialDsvCount / 2);

View File

@@ -6,7 +6,7 @@ using System.Runtime.CompilerServices;
namespace Ghost.Graphics.D3D12; namespace Ghost.Graphics.D3D12;
internal unsafe class D3D12GraphicsEngine : IGraphicsEngine internal class D3D12GraphicsEngine : IGraphicsEngine
{ {
#if DEBUG #if DEBUG
private readonly D3D12DebugLayer _debugLayer; private readonly D3D12DebugLayer _debugLayer;
@@ -34,17 +34,17 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
public D3D12GraphicsEngine(IRenderSystem renderSystem) public D3D12GraphicsEngine(IRenderSystem renderSystem)
{ {
#if DEBUG #if DEBUG
_debugLayer = new(); _debugLayer = new D3D12DebugLayer();
#endif #endif
_device = new(); _device = new D3D12RenderDevice();
_shaderCompiler = new(); _shaderCompiler = new DxcShaderCompiler();
_descriptorAllocator = new(_device); _descriptorAllocator = new D3D12DescriptorAllocator(_device);
_resourceDatabase = new(_descriptorAllocator); _resourceDatabase = new D3D12ResourceDatabase(_descriptorAllocator);
_pipelineLibrary = new(_device, _resourceDatabase); _pipelineLibrary = new D3D12PipelineLibrary(_device, _resourceDatabase);
_resourceAllocator = new(renderSystem, _device, _descriptorAllocator, _resourceDatabase, _pipelineLibrary); _resourceAllocator = new D3D12ResourceAllocator(renderSystem, _device, _descriptorAllocator, _resourceDatabase, _pipelineLibrary);
_copyCommandBuffer = new( _copyCommandBuffer = new D3D12CommandBuffer(
_device, _device,
_pipelineLibrary, _pipelineLibrary,
_resourceDatabase, _resourceDatabase,
@@ -136,6 +136,9 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
_copyCommandBuffer.End(); _copyCommandBuffer.End();
_resourceAllocator.ReleaseTempResources(); _resourceAllocator.ReleaseTempResources();
_descriptorAllocator.ResetCbvSrvUavDynamicHeap();
_descriptorAllocator.ResetDSVDynamicHeap();
_descriptorAllocator.ResetRTVDynamicHeap();
} }
public void Dispose() public void Dispose()

View File

@@ -8,8 +8,6 @@ using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel; using Misaki.HighPerformance.LowLevel;
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.LowLevel.Utilities;
using Misaki.HighPerformance.Utilities;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using TerraFX.Interop.DirectX; using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows; using TerraFX.Interop.Windows;
@@ -40,7 +38,6 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
private UniquePtr<ID3D12RootSignature> _defaultRootSignature; private UniquePtr<ID3D12RootSignature> _defaultRootSignature;
private readonly Dictionary<GraphicsPipelineKey, D3D12PipelineState> _pipelineCache; private readonly Dictionary<GraphicsPipelineKey, D3D12PipelineState> _pipelineCache;
private readonly Dictionary<ShaderPassKey, CBufferInfo> _cbufferInfoCache;
public ID3D12RootSignature* DefaultRootSignature => _defaultRootSignature.Get(); public ID3D12RootSignature* DefaultRootSignature => _defaultRootSignature.Get();
@@ -50,9 +47,8 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
_resourceDatabase = resourceDatabase; _resourceDatabase = resourceDatabase;
_pipelineCache = new Dictionary<GraphicsPipelineKey, D3D12PipelineState>(); _pipelineCache = new Dictionary<GraphicsPipelineKey, D3D12PipelineState>();
_cbufferInfoCache = new Dictionary<ShaderPassKey, CBufferInfo>();
CreateDefaultRootSignature(); CreateDefaultRootSignature().ThrowIfFailed();
} }
private Result CreateDefaultRootSignature() private Result CreateDefaultRootSignature()
@@ -140,36 +136,21 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
Desc_1_1 = rootSignatureDesc Desc_1_1 = rootSignatureDesc
}; };
ID3DBlob* pSignature = default; using ComPtr<ID3DBlob> pSignature = default;
ID3DBlob* pError = default; using ComPtr<ID3DBlob> pError = default;
try var serializeResult = D3D12SerializeVersionedRootSignature(&versionedDesc, pSignature.GetAddressOf(), pError.GetAddressOf());
if (serializeResult.FAILED)
{ {
var serializeResult = D3D12SerializeVersionedRootSignature(&versionedDesc, &pSignature, &pError); var errorMsg = pError.Get() != null ? Marshal.PtrToStringUTF8((nint)pError.Get()->GetBufferPointer()) : "Unknown error";
if (serializeResult.FAILED) return Result.Failure($"Failed to serialize default root signature: {errorMsg}");
{
var errorMsg = pError != null ? Marshal.PtrToStringUTF8((nint)pError->GetBufferPointer()) : "Unknown error";
return Result.Failure($"Failed to serialize default root signature: {errorMsg}");
}
ID3D12RootSignature* pRootSignature = default;
ThrowIfFailed(_device.NativeDevice.Get()->CreateRootSignature(0, pSignature->GetBufferPointer(), pSignature->GetBufferSize(),
__uuidof(pRootSignature), (void**)&pRootSignature));
_defaultRootSignature.Attach(pRootSignature);
} }
finally
{
if (pSignature != null)
{
pSignature->Release();
}
if (pError != null) ID3D12RootSignature* pRootSignature = default;
{ ThrowIfFailed(_device.NativeDevice.Get()->CreateRootSignature(0, pSignature.Get()->GetBufferPointer(), pSignature.Get()->GetBufferSize(),
pError->Release(); __uuidof(pRootSignature), (void**)&pRootSignature));
}
} _defaultRootSignature.Attach(pRootSignature);
return Result.Success(); return Result.Success();
} }
@@ -337,8 +318,6 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
return Result.Failure(result.Message); return Result.Failure(result.Message);
} }
_cbufferInfoCache[descriptor.PassId] = result.Value;
var desc = new D3DX12_MESH_SHADER_PIPELINE_STATE_DESC var desc = new D3DX12_MESH_SHADER_PIPELINE_STATE_DESC
{ {
pRootSignature = _defaultRootSignature.Get(), pRootSignature = _defaultRootSignature.Get(),
@@ -422,16 +401,6 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
return key; return key;
} }
public Result<CBufferInfo, ResultStatus> GetCBufferInfo(ShaderPassKey passId)
{
if (_cbufferInfoCache.TryGetValue(passId, out var cbufferInfo))
{
return Result.Create(cbufferInfo, ResultStatus.Success);
}
return Result.Create(default(CBufferInfo), ResultStatus.NotFound);
}
public Result<SharedPtr<ID3D12PipelineState>, ResultStatus> GetGraphicsPSO(GraphicsPipelineKey key) public Result<SharedPtr<ID3D12PipelineState>, ResultStatus> GetGraphicsPSO(GraphicsPipelineKey key)
{ {
if (_pipelineCache.TryGetValue(key, out var cacheEntry)) if (_pipelineCache.TryGetValue(key, out var cacheEntry))

View File

@@ -31,7 +31,6 @@ internal class D3D12Renderer : IRenderer
} }
private readonly D3D12GraphicsEngine _graphicsEngine; private readonly D3D12GraphicsEngine _graphicsEngine;
private readonly D3D12CommandQueue _commandQueue;
private readonly FrameResource[] _frameResources; private readonly FrameResource[] _frameResources;
private uint _frameIndex; private uint _frameIndex;
@@ -60,7 +59,6 @@ internal class D3D12Renderer : IRenderer
public D3D12Renderer(D3D12GraphicsEngine graphicsEngine, D3D12ResourceAllocator resourceAllocator, D3D12ResourceDatabase resourceDatabase) public D3D12Renderer(D3D12GraphicsEngine graphicsEngine, D3D12ResourceAllocator resourceAllocator, D3D12ResourceDatabase resourceDatabase)
{ {
_graphicsEngine = graphicsEngine; _graphicsEngine = graphicsEngine;
_commandQueue = (D3D12CommandQueue)graphicsEngine.Device.GraphicsQueue;
_resourceAllocator = resourceAllocator; _resourceAllocator = resourceAllocator;
_resourceDatabase = resourceDatabase; _resourceDatabase = resourceDatabase;
@@ -153,7 +151,7 @@ internal class D3D12Renderer : IRenderer
_swapChain?.Resize(newSize.x, newSize.y); _swapChain?.Resize(newSize.x, newSize.y);
_currentSize = newSize; _currentSize = newSize;
// Update off-screen render target size // Update off-screen render Target size
if (_swapChain != null) if (_swapChain != null)
{ {
_resourceDatabase.ReleaseResource(_renderTarget.AsResource()); _resourceDatabase.ReleaseResource(_renderTarget.AsResource());
@@ -170,7 +168,7 @@ internal class D3D12Renderer : IRenderer
if (frame.fenceValue > 0) if (frame.fenceValue > 0)
{ {
_commandQueue.WaitForValue(frame.fenceValue); _graphicsEngine.Device.GraphicsQueue.WaitForValue(frame.fenceValue);
} }
if (_renderTarget.IsValid) if (_renderTarget.IsValid)
@@ -202,12 +200,12 @@ internal class D3D12Renderer : IRenderer
frame.commandBuffer.End(); frame.commandBuffer.End();
_commandQueue.Submit(frame.commandBuffer); _graphicsEngine.Device.GraphicsQueue.Submit(frame.commandBuffer);
_swapChain?.Present(); _swapChain?.Present();
} }
frame.fenceValue = _commandQueue.Signal(_frameIndex); frame.fenceValue = _graphicsEngine.Device.GraphicsQueue.Signal(_frameIndex);
_frameIndex++; _frameIndex++;
} }
@@ -257,7 +255,7 @@ internal class D3D12Renderer : IRenderer
// Handle swap chain back buffer transitions if needed // Handle swap chain back buffer transitions if needed
if (_swapChain != null) if (_swapChain != null)
{ {
// Transition back buffer to render target // Transition back buffer to render Target
cmd.ResourceBarrier(destination.AsResource(), ResourceState.Present, ResourceState.RenderTarget); cmd.ResourceBarrier(destination.AsResource(), ResourceState.Present, ResourceState.RenderTarget);
} }
@@ -266,7 +264,7 @@ internal class D3D12Renderer : IRenderer
// FIX: Implement proper blit operation with shader // FIX: Implement proper blit operation with shader
// This is a placeholder - in D3D12, you would typically: // This is a placeholder - in D3D12, you would typically:
// 1. Set render target to the destination // 1. Set render Target to the destination
// 2. Use a full-screen quad/triangle with a shader that samples from the source // 2. Use a full-screen quad/triangle with a shader that samples from the source
// Handle swap chain back buffer transitions if needed // Handle swap chain back buffer transitions if needed
@@ -284,7 +282,7 @@ internal class D3D12Renderer : IRenderer
{ {
if (frame.fenceValue > 0) if (frame.fenceValue > 0)
{ {
_commandQueue.WaitForValue(frame.fenceValue); _graphicsEngine.Device.GraphicsQueue.WaitForValue(frame.fenceValue);
} }
} }
} }
@@ -301,8 +299,8 @@ internal class D3D12Renderer : IRenderer
// NOTE: Testing only. // NOTE: Testing only.
_pass.Cleanup(_resourceDatabase); _pass.Cleanup(_resourceDatabase);
// If using a swap chain, release the off-screen render target. // If using a swap chain, release the off-screen render Target.
// Otherwise, the render target is managed externally. // Otherwise, the render Target is managed externally.
if (_swapChain != null) if (_swapChain != null)
{ {
_resourceDatabase.ReleaseResource(_renderTarget.AsResource()); _resourceDatabase.ReleaseResource(_renderTarget.AsResource());

View File

@@ -5,7 +5,7 @@ using Ghost.Graphics.Core;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel; using Misaki.HighPerformance.LowLevel;
using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.Mathematics; using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using TerraFX.Interop.DirectX; using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows; using TerraFX.Interop.Windows;
@@ -522,10 +522,11 @@ internal sealed unsafe partial class D3D12ResourceAllocator
return D3D12_RESOURCE_STATE_COPY_DEST; return D3D12_RESOURCE_STATE_COPY_DEST;
} }
// Default to Common, but check for specific roles
var state = D3D12_RESOURCE_STATE_COMMON; var state = D3D12_RESOURCE_STATE_COMMON;
#if true
return state; return state;
#else
// D3D12 does not support state other than COMMON for buffers at creation.
if (usage.HasFlag(BufferUsage.Vertex) || usage.HasFlag(BufferUsage.Constant)) if (usage.HasFlag(BufferUsage.Vertex) || usage.HasFlag(BufferUsage.Constant))
{ {
// Vertex and Constant buffers can share this state // Vertex and Constant buffers can share this state
@@ -564,6 +565,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator
// If it's a mix, start in common and let the user barrier // If it's a mix, start in common and let the user barrier
return D3D12_RESOURCE_STATE_COMMON; return D3D12_RESOURCE_STATE_COMMON;
#endif
} }
private static ResourceState D3D12StatesToRHIState(D3D12_RESOURCE_STATES states) private static ResourceState D3D12StatesToRHIState(D3D12_RESOURCE_STATES states)
@@ -605,7 +607,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
private readonly D3D12ResourceDatabase _resourceDatabase; private readonly D3D12ResourceDatabase _resourceDatabase;
private readonly D3D12PipelineLibrary _pipelineLibrary; private readonly D3D12PipelineLibrary _pipelineLibrary;
private UnsafeQueue<Handle<GPUResource>> _temResources; private UnsafeQueue<Handle<GPUResource>> _tempResources;
private bool _disposed; private bool _disposed;
@@ -633,7 +635,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
_resourceDatabase = resourceDatabase; _resourceDatabase = resourceDatabase;
_pipelineLibrary = pipelineLibrary; _pipelineLibrary = pipelineLibrary;
_temResources = new(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent); _tempResources = new UnsafeQueue<Handle<GPUResource>>(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
} }
~D3D12ResourceAllocator() ~D3D12ResourceAllocator()
@@ -648,7 +650,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
if (isTemp) if (isTemp)
{ {
_temResources.Enqueue(handle); _tempResources.Enqueue(handle);
} }
return handle; return handle;
@@ -714,13 +716,15 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
var initialState = DetermineInitialTextureState(desc.Usage); var initialState = DetermineInitialTextureState(desc.Usage);
D3D12MA_Allocation* pAllocation = default; D3D12MA_Allocation* pAllocation = default;
ThrowIfFailed(_d3d12MA.Get()->CreateResource(&allocationDesc, &resourceDesc, initialState, null, &pAllocation, Win32Utility.IID_NULL, null)); var iid = IID.IID_NULL;
ThrowIfFailed(_d3d12MA.Get()->CreateResource(&allocationDesc, &resourceDesc, initialState, null, &pAllocation, &iid, null));
var resourceDescriptor = ResourceViewGroup.Invalid; var resourceDescriptor = ResourceViewGroup.Invalid;
if (desc.Usage.HasFlag(TextureUsage.ShaderResource)) if (desc.Usage.HasFlag(TextureUsage.ShaderResource))
{ {
resourceDescriptor.srv = _descriptorAllocator.AllocateCbvSrvUav(isTemp); resourceDescriptor.srv = _descriptorAllocator.AllocateCbvSrvUav(isTemp);
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.srv); // TODO: Maybe use non-shader-visible descriptor first then batch copy to shader-visible heap later?
var cpuHandle = _descriptorAllocator.GetCpuHandleShaderVisible(resourceDescriptor.srv);
var isCubeMap = desc.Dimension == TextureDimension.TextureCube || desc.Dimension == TextureDimension.TextureCubeArray; var isCubeMap = desc.Dimension == TextureDimension.TextureCube || desc.Dimension == TextureDimension.TextureCubeArray;
var srvDesc = CreateTextureSrvDesc(pAllocation->GetResource(), mipLevels, desc.Slice, isCubeMap); var srvDesc = CreateTextureSrvDesc(pAllocation->GetResource(), mipLevels, desc.Slice, isCubeMap);
@@ -749,7 +753,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
if (desc.Usage.HasFlag(TextureUsage.UnorderedAccess)) if (desc.Usage.HasFlag(TextureUsage.UnorderedAccess))
{ {
resourceDescriptor.uav = _descriptorAllocator.AllocateCbvSrvUav(isTemp); resourceDescriptor.uav = _descriptorAllocator.AllocateCbvSrvUav(isTemp);
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.uav); var cpuHandle = _descriptorAllocator.GetCpuHandleShaderVisible(resourceDescriptor.uav);
var uavDesc = CreateTextureUavDesc(pAllocation->GetResource()); var uavDesc = CreateTextureUavDesc(pAllocation->GetResource());
_device.NativeDevice.Get()->CreateUnorderedAccessView(pAllocation->GetResource(), null, &uavDesc, cpuHandle); _device.NativeDevice.Get()->CreateUnorderedAccessView(pAllocation->GetResource(), null, &uavDesc, cpuHandle);
@@ -906,24 +910,6 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
var shader = new Shader(descriptor); var shader = new Shader(descriptor);
foreach (var pass in descriptor.passes)
{
if (pass is not FullPassDescriptor fullPass)
{
continue;
}
// TODO: Cache the pass key because we hash it multiple times right now.
var passKey = new ShaderPassKey(fullPass.Identifier);
var cbr = _pipelineLibrary.GetCBufferInfo(passKey);
if (cbr.Status != ResultStatus.Success)
{
return Identifier<Shader>.Invalid;
}
_resourceDatabase.AddShaderPass(passKey, new ShaderPass(cbr.Value));
}
return _resourceDatabase.AddShader(shader); return _resourceDatabase.AddShader(shader);
} }
@@ -931,14 +917,14 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
while (_temResources.Count > 0) while (_tempResources.Count > 0)
{ {
var handle = _temResources.Peek(); var handle = _tempResources.Peek();
ref var info = ref _resourceDatabase.GetResourceInfo(handle, out var exist); ref var info = ref _resourceDatabase.GetResourceRecord(handle, out var exist);
if (!exist || !info.Allocated) if (!exist || !info.Allocated)
{ {
// Resource already released or invalid, just dequeue // Resource already released or invalid, just dequeue
_temResources.Dequeue(); _tempResources.Dequeue();
continue; continue;
} }
@@ -950,7 +936,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
} }
_resourceDatabase.ReleaseResource(handle); _resourceDatabase.ReleaseResource(handle);
_temResources.Dequeue(); _tempResources.Dequeue();
} }
} }
@@ -961,20 +947,15 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
return; return;
} }
#if DEBUG || GHOST_EDITOR Debug.Assert(_tempResources.Count == 0, "Temporary resources should be released before disposing the allocator.");
if (_temResources.Count > 0)
{
throw new InvalidOperationException($"ResourceAllocator is being disposed with {_temResources.Count} temp allocations still registered. Ensure all resources are released before disposing.");
}
#endif
foreach (var handle in _temResources) foreach (var handle in _tempResources)
{ {
_resourceDatabase.ReleaseResource(handle); _resourceDatabase.ReleaseResource(handle);
} }
_d3d12MA.Dispose(); _d3d12MA.Dispose();
_temResources.Dispose(); _tempResources.Dispose();
_disposed = true; _disposed = true;
GC.SuppressFinalize(this); GC.SuppressFinalize(this);

View File

@@ -1,11 +1,11 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Graphics.Core; using Ghost.Graphics.Core;
using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Misaki.HighPerformance.Collections; using Misaki.HighPerformance.Collections;
using Misaki.HighPerformance.LowLevel; using Misaki.HighPerformance.LowLevel;
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Collections;
using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using TerraFX.Interop.DirectX; using TerraFX.Interop.DirectX;
@@ -42,7 +42,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
public readonly bool isExternal; public readonly bool isExternal;
public readonly bool Allocated => isExternal ? resourceUnion.resource.Get() != null : resourceUnion.allocation.Get() != null; public readonly bool Allocated => isExternal ? resourceUnion.resource.Get() != null : resourceUnion.allocation.Get() != null;
public readonly ID3D12Resource* ResourcePtr => isExternal ? resourceUnion.resource.Get() : resourceUnion.allocation.Get()->GetResource(); public readonly SharedPtr<ID3D12Resource> ResourcePtr => isExternal ? resourceUnion.resource.Get() : resourceUnion.allocation.Get()->GetResource();
public ResourceRecord(D3D12MA_Allocation* allocation, uint cpuFenceValue, ResourceState state, ResourceViewGroup resourceDescriptor, ResourceDesc desc) public ResourceRecord(D3D12MA_Allocation* allocation, uint cpuFenceValue, ResourceState state, ResourceViewGroup resourceDescriptor, ResourceDesc desc)
{ {
@@ -131,7 +131,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
resource = default!; resource = default!;
} }
public unsafe Handle<GPUResource> ImportExternalResource(ID3D12Resource* pResource, ResourceState initialState, ResourceViewGroup viewGroup, string? name = null) public unsafe Handle<GPUResource> ImportExternalResource(ID3D12Resource* pResource, ResourceState initialState, ResourceViewGroup viewGroup, string name = "")
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
@@ -139,16 +139,14 @@ internal class D3D12ResourceDatabase : IResourceDatabase
var handle = new Handle<GPUResource>(id, generation); var handle = new Handle<GPUResource>(id, generation);
#if DEBUG || GHOST_EDITOR #if DEBUG || GHOST_EDITOR
if (name != null) pResource->SetName(name);
{ _resourceName[handle] = name;
_resourceName[handle] = name;
}
#endif #endif
return handle; return handle;
} }
public unsafe Handle<GPUResource> AddResource(D3D12MA_Allocation* allocation, uint cpuFenceValue, ResourceState initialState, ResourceViewGroup resourceDescriptor, ResourceDesc desc, string? name = null) public unsafe Handle<GPUResource> AddResource(D3D12MA_Allocation* allocation, uint cpuFenceValue, ResourceState initialState, ResourceViewGroup resourceDescriptor, ResourceDesc desc, string name = "")
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
@@ -156,10 +154,8 @@ internal class D3D12ResourceDatabase : IResourceDatabase
var handle = new Handle<GPUResource>(id, generation); var handle = new Handle<GPUResource>(id, generation);
#if DEBUG || GHOST_EDITOR #if DEBUG || GHOST_EDITOR
if (name != null) allocation->SetName(name);
{ _resourceName[handle] = name;
_resourceName[handle] = name;
}
#endif #endif
return handle; return handle;
@@ -184,7 +180,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
return ref info; return ref info;
} }
public ref ResourceRecord GetResourceInfo(Handle<GPUResource> handle, out bool exist) public ref ResourceRecord GetResourceRecord(Handle<GPUResource> handle, out bool exist)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
return ref _resources.GetElementReferenceAt(handle.id, handle.generation, out exist); return ref _resources.GetElementReferenceAt(handle.id, handle.generation, out exist);
@@ -232,7 +228,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
ref var info = ref GetResourceInfo(handle, out var exist); ref var info = ref GetResourceRecord(handle, out var exist);
if (!exist || !info.Allocated) if (!exist || !info.Allocated)
{ {
return -1; return -1;
@@ -254,7 +250,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
return null; return null;
} }
public unsafe void ReleaseResource(Handle<GPUResource> handle) public void ReleaseResource(Handle<GPUResource> handle)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
@@ -269,14 +265,10 @@ internal class D3D12ResourceDatabase : IResourceDatabase
return; return;
} }
var refCount = info.Release(_descriptorAllocator); info.Release(_descriptorAllocator);
//Debug.Assert(info.Release(_descriptorAllocator) == 0);
#if DEBUG || GHOST_EDITOR #if DEBUG || GHOST_EDITOR
_resourceName.Remove(handle, out var name); _resourceName.Remove(handle, out var name);
//if (refCount > 0)
//{
// throw new GPUResourceLeakException(refCount, info.ResourcePtr, name ?? "Unknown Resource");
//}
//Debug.Assert(refCount == 0, "Resource released with non-zero reference count.");
#endif #endif
_resources.Remove(handle.id, handle.generation); _resources.Remove(handle.id, handle.generation);
@@ -405,34 +397,6 @@ internal class D3D12ResourceDatabase : IResourceDatabase
ReleaseResource(ref shader); ReleaseResource(ref shader);
} }
public void AddShaderPass(ShaderPassKey passKey, ShaderPass pass)
{
ObjectDisposedException.ThrowIf(_disposed, this);
_shaderPasses.Add(passKey, pass);
}
public ShaderPass GetShaderPass(ShaderPassKey passKey)
{
ObjectDisposedException.ThrowIf(_disposed, this);
if (!_shaderPasses.TryGetValue(passKey, out var pass))
{
throw new KeyNotFoundException($"Shader pass '{passKey}' not found.");
}
return pass;
}
public void RemoveShaderPass(ShaderPassKey passKey)
{
ObjectDisposedException.ThrowIf(_disposed, this);
if (_shaderPasses.Remove(passKey, out var pass))
{
ReleaseResource(ref pass);
}
}
public void Dispose() public void Dispose()
{ {
static void ThrowMemoryLeakException(string resourceType, int count) static void ThrowMemoryLeakException(string resourceType, int count)

View File

@@ -7,6 +7,7 @@ using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel; using Misaki.HighPerformance.LowLevel;
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Collections;
using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using TerraFX.Interop.DirectX; using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows; using TerraFX.Interop.Windows;
@@ -27,7 +28,7 @@ internal unsafe class D3D12SwapChain : ISwapChain
private UniquePtr<IDXGISwapChain4> _swapChain; private UniquePtr<IDXGISwapChain4> _swapChain;
private UnsafeArray<Handle<Texture>> _backBuffers; private UnsafeArray<Handle<Texture>> _backBuffers;
private object? _compositionSurface; private readonly object? _compositionSurface;
private bool _disposed; private bool _disposed;
@@ -48,20 +49,22 @@ internal unsafe class D3D12SwapChain : ISwapChain
public D3D12SwapChain(D3D12ResourceDatabase resourceDatabase, D3D12DescriptorAllocator descriptorAllocator, D3D12RenderDevice device, SwapChainDesc desc) public D3D12SwapChain(D3D12ResourceDatabase resourceDatabase, D3D12DescriptorAllocator descriptorAllocator, D3D12RenderDevice device, SwapChainDesc desc)
{ {
Debug.Assert(desc.BufferCount >= 2);
_resourceDatabase = resourceDatabase; _resourceDatabase = resourceDatabase;
_descriptorAllocator = descriptorAllocator; _descriptorAllocator = descriptorAllocator;
_renderDevice = device; _renderDevice = device;
_backBuffers = new UnsafeArray<Handle<Texture>>(D3D12PipelineResource.BACK_BUFFER_COUNT, Allocator.Persistent); _backBuffers = new UnsafeArray<Handle<Texture>>((int)desc.BufferCount, Allocator.Persistent);
Width = desc.width; Width = desc.Width;
Height = desc.height; Height = desc.Height;
BufferCount = D3D12PipelineResource.BACK_BUFFER_COUNT; BufferCount = desc.BufferCount;
CreateSwapChain(desc); CreateSwapChain(desc);
CreateBackBuffers(); CreateBackBuffers();
_compositionSurface = desc.target.compositionSurface; _compositionSurface = desc.Target.CompositionSurface;
} }
~D3D12SwapChain() ~D3D12SwapChain()
@@ -73,9 +76,9 @@ internal unsafe class D3D12SwapChain : ISwapChain
{ {
var swapChainDesc = new DXGI_SWAP_CHAIN_DESC1 var swapChainDesc = new DXGI_SWAP_CHAIN_DESC1
{ {
Width = desc.width, Width = desc.Width,
Height = desc.height, Height = desc.Height,
Format = desc.format.ToDXGIFormat(), Format = desc.Format.ToDXGIFormat(),
SampleDesc = new DXGI_SAMPLE_DESC(1, 0), SampleDesc = new DXGI_SAMPLE_DESC(1, 0),
BufferUsage = DXGI_USAGE_BACK_BUFFER | DXGI_USAGE_RENDER_TARGET_OUTPUT, BufferUsage = DXGI_USAGE_BACK_BUFFER | DXGI_USAGE_RENDER_TARGET_OUTPUT,
BufferCount = D3D12PipelineResource.BACK_BUFFER_COUNT, BufferCount = D3D12PipelineResource.BACK_BUFFER_COUNT,
@@ -91,15 +94,15 @@ internal unsafe class D3D12SwapChain : ISwapChain
var pFactory = _renderDevice.DXGIFactory.Get(); var pFactory = _renderDevice.DXGIFactory.Get();
var pCommandQueue = _renderDevice.NativeGraphicsQueue.Get(); var pCommandQueue = _renderDevice.NativeGraphicsQueue.Get();
switch (desc.target.type) switch (desc.Target.Type)
{ {
case SwapChainTargetType.Composition: case SwapChainTargetType.Composition:
ThrowIfFailed(pFactory->CreateSwapChainForComposition((IUnknown*)pCommandQueue, &swapChainDesc, null, &pTempSwapChain)); ThrowIfFailed(pFactory->CreateSwapChainForComposition((IUnknown*)pCommandQueue, &swapChainDesc, null, &pTempSwapChain));
// Set the composition surface // Set the composition surface
if (desc.target.compositionSurface != null) if (desc.Target.CompositionSurface != null)
{ {
using var swapChainPanelNative = ISwapChainPanelNative.FromSwapChainPanel(desc.target.compositionSurface); using var swapChainPanelNative = ISwapChainPanelNative.FromSwapChainPanel(desc.Target.CompositionSurface);
swapChainPanelNative.SetSwapChain((IntPtr)pTempSwapChain); swapChainPanelNative.SetSwapChain((IntPtr)pTempSwapChain);
} }
break; break;
@@ -112,7 +115,7 @@ internal unsafe class D3D12SwapChain : ISwapChain
pFactory->CreateSwapChainForHwnd( pFactory->CreateSwapChainForHwnd(
(IUnknown*)pCommandQueue, (IUnknown*)pCommandQueue,
new HWND(desc.target.windowHandle.ToPointer()), new HWND(desc.Target.WindowHandle.ToPointer()),
&swapChainDesc, &swapChainDesc,
&swapChainFullscreenDesc, &swapChainFullscreenDesc,
null, null,
@@ -141,7 +144,12 @@ internal unsafe class D3D12SwapChain : ISwapChain
var rtv = _descriptorAllocator.AllocateRTV(); var rtv = _descriptorAllocator.AllocateRTV();
_renderDevice.NativeDevice.Get()->CreateRenderTargetView(pBackBuffer, null, _descriptorAllocator.GetCpuHandle(rtv)); _renderDevice.NativeDevice.Get()->CreateRenderTargetView(pBackBuffer, null, _descriptorAllocator.GetCpuHandle(rtv));
var handle = _resourceDatabase.ImportExternalResource(pBackBuffer, ResourceState.Present, new ResourceViewGroup() { rtv = rtv }); var view = ResourceViewGroup.Invalid with
{
rtv = rtv
};
var handle = _resourceDatabase.ImportExternalResource(pBackBuffer, ResourceState.Present, view);
_backBuffers[i] = handle.AsTexture(); _backBuffers[i] = handle.AsTexture();
} }
} }

View File

@@ -164,12 +164,17 @@ internal unsafe struct D3D12DescriptorHeap : IDisposable
public void ReleaseDescriptors(int baseIndex, int count = 1) public void ReleaseDescriptors(int baseIndex, int count = 1)
{ {
if (baseIndex == _INVALID_DESCRIPTOR_INDEX)
{
return;
}
if (count == 0) if (count == 0)
{ {
return; return;
} }
if (baseIndex < _dynamicHeapStart) if (baseIndex >= _dynamicHeapStart)
{ {
// Dynamic allocations are not released individually. // Dynamic allocations are not released individually.
return; return;

View File

@@ -10,6 +10,19 @@ internal unsafe static class D3D12Utility
{ {
public static void SetName<T>(ref this T obj, ReadOnlySpan<char> name) public static void SetName<T>(ref this T obj, ReadOnlySpan<char> name)
where T : unmanaged, ID3D12Object.Interface where T : unmanaged, ID3D12Object.Interface
{
if (name.Length == 0)
{
return;
}
fixed (char* pName = name)
{
obj.SetName(pName);
}
}
public static void SetName(ref this D3D12MA_Allocation obj, ReadOnlySpan<char> name)
{ {
fixed (char* pName = name) fixed (char* pName = name)
{ {

View File

@@ -10,10 +10,12 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<IsAotCompatible>True</IsAotCompatible> <IsAotCompatible>True</IsAotCompatible>
<IsTrimmable>True</IsTrimmable>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<IsAotCompatible>True</IsAotCompatible> <IsAotCompatible>True</IsAotCompatible>
<IsTrimmable>True</IsTrimmable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -52,4 +54,10 @@
<Folder Include="RenderGraphModule\" /> <Folder Include="RenderGraphModule\" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Reference Include="Misaki.HighPerformance.LowLevel">
<HintPath>..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.LowLevel\bin\Release\net10.0\Misaki.HighPerformance.LowLevel.dll</HintPath>
</Reference>
</ItemGroup>
</Project> </Project>

View File

@@ -154,7 +154,6 @@ public ref struct GraphicsPSODescriptor
get; set; get; set;
} }
public ReadOnlySpan<TextureFormat> RtvFormats public ReadOnlySpan<TextureFormat> RtvFormats
{ {
get; set; get; set;
@@ -267,7 +266,6 @@ public struct RectDesc
{ {
get; set; get; set;
} }
} }
public struct SubResourceData public struct SubResourceData
@@ -311,6 +309,24 @@ public struct PassDepthStencilDesc
} }
public struct BarrierDesc
{
public Handle<GraphicsBuffer> Resource
{
get; set;
}
public ResourceState StateBefore
{
get; set;
}
public ResourceState StateAfter
{
get; set;
}
}
public struct ResourceDesc public struct ResourceDesc
{ {
[StructLayout(LayoutKind.Explicit)] [StructLayout(LayoutKind.Explicit)]
@@ -381,13 +397,13 @@ public struct ResourceDesc
} }
/// <summary> /// <summary>
/// Render target description /// Render Target description
/// Supports either color OR depth rendering, not both /// Supports either color OR depth rendering, not both
/// </summary> /// </summary>
public struct RenderTargetDesc public struct RenderTargetDesc
{ {
/// <summary> /// <summary>
/// Width of the render target /// Width of the render Target
/// </summary> /// </summary>
public uint Width public uint Width
{ {
@@ -395,7 +411,7 @@ public struct RenderTargetDesc
} }
/// <summary> /// <summary>
/// Height of the render target /// Height of the render Target
/// </summary> /// </summary>
public uint Height public uint Height
{ {
@@ -403,7 +419,7 @@ public struct RenderTargetDesc
} }
/// <summary> /// <summary>
/// Slice of the render target /// Slice of the render Target
/// </summary> /// </summary>
public uint Slice public uint Slice
{ {
@@ -411,7 +427,7 @@ public struct RenderTargetDesc
} }
/// <summary> /// <summary>
/// Type of render target /// Type of render Target
/// </summary> /// </summary>
public RenderTargetType Type public RenderTargetType Type
{ {
@@ -419,7 +435,7 @@ public struct RenderTargetDesc
} }
/// <summary> /// <summary>
/// Target texture format /// Target texture Format
/// </summary> /// </summary>
public TextureFormat Format public TextureFormat Format
{ {
@@ -435,7 +451,7 @@ public struct RenderTargetDesc
} }
/// <summary> /// <summary>
/// Creation flags for the render target /// Creation flags for the render Target
/// </summary> /// </summary>
public RenderTargetCreationFlags CreationFlags public RenderTargetCreationFlags CreationFlags
{ {
@@ -459,7 +475,7 @@ public struct RenderTargetDesc
} }
/// <summary> /// <summary>
/// Creates a color render target /// Creates a color render Target
/// </summary> /// </summary>
public static RenderTargetDesc Color(uint width, uint height, uint slice = 1, public static RenderTargetDesc Color(uint width, uint height, uint slice = 1,
TextureFormat format = TextureFormat.R8G8B8A8_UNorm, TextureDimension dimension = TextureDimension.Texture2D, TextureFormat format = TextureFormat.R8G8B8A8_UNorm, TextureDimension dimension = TextureDimension.Texture2D,
@@ -481,7 +497,7 @@ public struct RenderTargetDesc
} }
/// <summary> /// <summary>
/// Creates a depth render target /// Creates a depth render Target
/// </summary> /// </summary>
public static RenderTargetDesc Depth(uint width, uint height, uint slice = 1, public static RenderTargetDesc Depth(uint width, uint height, uint slice = 1,
TextureFormat format = TextureFormat.D24_UNorm_S8_UInt, TextureDimension dimension = TextureDimension.Texture2D, TextureFormat format = TextureFormat.D24_UNorm_S8_UInt, TextureDimension dimension = TextureDimension.Texture2D,
@@ -554,7 +570,7 @@ public struct TextureDesc
} }
/// <summary> /// <summary>
/// Texture format /// Texture Format
/// </summary> /// </summary>
public TextureFormat Format public TextureFormat Format
{ {
@@ -630,59 +646,82 @@ public struct SwapChainDesc
/// <summary> /// <summary>
/// Width of the swap chain /// Width of the swap chain
/// </summary> /// </summary>
public uint width; public uint Width
{
get; set;
}
/// <summary> /// <summary>
/// Height of the swap chain /// Height of the swap chain
/// </summary> /// </summary>
public uint height; public uint Height
/// <summary>
/// Back buffer format
/// </summary>
public TextureFormat format;
/// <summary>
/// Target for presentation (window handle or composition target)
/// </summary>
public SwapChainTarget target;
public SwapChainDesc(uint width, uint height, SwapChainTarget target, TextureFormat format = TextureFormat.B8G8R8A8_UNorm, uint bufferCount = 2)
{ {
this.width = width; get; set;
this.height = height; }
this.format = format;
this.target = target;
/// <summary>
/// Back buffer Format
/// </summary>
public TextureFormat Format
{
get; set;
}
/// <summary>
/// Target for presentation (window handle or composition Target)
/// </summary>
public SwapChainTarget Target
{
get; set;
}
public uint BufferCount
{
get; set;
} }
} }
/// <summary> /// <summary>
/// Swap chain target (window handle or composition surface) /// Swap chain Target (window handle or composition surface)
/// </summary> /// </summary>
public struct SwapChainTarget public struct SwapChainTarget
{ {
/// <summary> /// <summary>
/// Target type /// Target type
/// </summary> /// </summary>
public SwapChainTargetType type; public SwapChainTargetType Type
{
get; set;
}
/// <summary> /// <summary>
/// Window handle for HWND targets /// Window handle for HWND targets
/// </summary> /// </summary>
public nint windowHandle; public nint WindowHandle
{
get; set;
}
/// <summary> /// <summary>
/// Composition surface for UWP/WinUI targets /// Composition surface for UWP/WinUI targets
/// </summary> /// </summary>
public object? compositionSurface; public object? CompositionSurface
{
get; set;
}
public static SwapChainTarget FromWindowHandle(nint hwnd) public static SwapChainTarget FromWindowHandle(nint hwnd)
{ {
return new SwapChainTarget return new SwapChainTarget
{ {
type = SwapChainTargetType.WindowHandle, Type = SwapChainTargetType.WindowHandle,
windowHandle = hwnd, WindowHandle = hwnd,
compositionSurface = null CompositionSurface = null
}; };
} }
@@ -690,9 +729,9 @@ public struct SwapChainTarget
{ {
return new SwapChainTarget return new SwapChainTarget
{ {
type = SwapChainTargetType.Composition, Type = SwapChainTargetType.Composition,
windowHandle = nint.Zero, WindowHandle = 0,
compositionSurface = surface CompositionSurface = surface
}; };
} }
} }

View File

@@ -53,21 +53,21 @@ public interface ICommandBuffer : IDisposable
void SetScissorRect(RectDesc rect); void SetScissorRect(RectDesc rect);
/// <summary> /// <summary>
/// Sets the optional render targets and optional depth target for subsequent rendering operations. /// Sets the optional render targets and optional depth Target for subsequent rendering operations.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// To specify no render targets, provide an empty span for <paramref name="renderTargets"/>. /// To specify no render targets, provide an empty span for <paramref name="renderTargets"/>.
/// Use <see cref="Handle{Texture}.Invalid"/> for <paramref name="depthTarget"/> if no depth target is required. /// Use <see cref="Handle{Texture}.Invalid"/> for <paramref name="depthTarget"/> if no depth Target is required.
/// </remarks> /// </remarks>
/// <param name="renderTargets">A read-only span of handles to textures that will be used as render targets. /// <param name="renderTargets">A read-only span of handles to textures that will be used as render targets.
/// The order of handles determines the order in which render targets are bound.</param> /// The order of handles determines the order in which render targets are bound.</param>
/// <param name="depthTarget">A handle to the texture to be used as the depth target. Specify a invalid handle if no depth target is required.</param> /// <param name="depthTarget">A handle to the texture to be used as the depth Target. Specify a invalid handle if no depth Target is required.</param>
void SetRenderTargets(ReadOnlySpan<Handle<Texture>> renderTargets, Handle<Texture> depthTarget); void SetRenderTargets(ReadOnlySpan<Handle<Texture>> renderTargets, Handle<Texture> depthTarget);
/// <summary> /// <summary>
/// Begins a render pass with the specified render target /// Begins a render pass with the specified render Target
/// </summary> /// </summary>
/// <param name="rtDescs">Render target descriptions</param> /// <param name="rtDescs">Render Target descriptions</param>
/// <param name="depthDesc">Depth stencil description</param> /// <param name="depthDesc">Depth stencil description</param>
/// <param name="allowUAVWrites">Whether UAV writes are allowed during the render pass</param> /// <param name="allowUAVWrites">Whether UAV writes are allowed during the render pass</param>
void BeginRenderPass(ReadOnlySpan<PassRenderTargetDesc> rtDescs, PassDepthStencilDesc depthDesc, bool allowUAVWrites = false); void BeginRenderPass(ReadOnlySpan<PassRenderTargetDesc> rtDescs, PassDepthStencilDesc depthDesc, bool allowUAVWrites = false);
@@ -78,12 +78,18 @@ public interface ICommandBuffer : IDisposable
void EndRenderPass(); void EndRenderPass();
/// <summary> /// <summary>
/// Inserts a resource barrier for state transitions /// Inserts multiple resource barriers for state transitions.
/// </summary> /// </summary>
/// <param name="resource">Resource to transition</param> /// <param name="barrierDescs">Resource barrier descriptions</param>
/// <param name="before">Current resource state</param> void ResourceBarrier(ReadOnlySpan<BarrierDesc> barrierDescs);
/// <param name="after">Target resource state</param>
void ResourceBarrier(Handle<GPUResource> resource, ResourceState before, ResourceState after); /// <summary>
/// Inserts a resource barrier for state transitions.
/// </summary>
/// <param name="resource">A handle to the GPU resource to transition.</param>
/// <param name="stateBefore">The current state of the resource before the transition.</param>
/// <param name="stateAfter">The desired state of the resource after the transition.</param>
void ResourceBarrier(Handle<GPUResource> resource, ResourceState stateBefore, ResourceState stateAfter);
/// <summary> /// <summary>
/// Sets the pipeline state object /// Sets the pipeline state object
@@ -176,10 +182,10 @@ public interface ICommandBuffer : IDisposable
/// </summary> /// </summary>
/// <param name="texture">The texture resource to which the subresource data will be uploaded. Must be a valid, initialized texture handle.</param> /// <param name="texture">The texture resource to which the subresource data will be uploaded. Must be a valid, initialized texture handle.</param>
/// <param name="firstSubresource">The index of the first subresource in the texture to receive data. Must be less than the total number of subresources in the texture.</param> /// <param name="firstSubresource">The index of the first subresource in the texture to receive data. Must be less than the total number of subresources in the texture.</param>
/// <param name="subresources">A reference to the structure containing the subresource data to upload. The data must match the format and layout expected by the texture.</param> /// <param name="subresources">A reference to the structure containing the subresource data to upload. The data must match the Format and layout expected by the texture.</param>
/// <param name="numSubresources">The number of subresources to upload, starting from <paramref name="firstSubresource"/>. /// <param name="numSubresources">The number of subresources to upload, starting from <paramref name="firstSubresource"/>.
/// Must be greater than zero and not exceed the remaining subresources in the texture.</param> /// Must be greater than zero and not exceed the remaining subresources in the texture.</param>
void UploadTexture(Handle<Texture> texture, params ReadOnlySpan<SubResourceData> subresources); void UploadTexture(Handle<Texture> texture, ReadOnlySpan<SubResourceData> subresources);
/// <summary> /// <summary>
/// Copies a specified number of bytes from the source graphics buffer to the destination graphics buffer. /// Copies a specified number of bytes from the source graphics buffer to the destination graphics buffer.

View File

@@ -23,5 +23,4 @@ public interface IPipelineLibrary : IDisposable
void InitializeLibrary(string? filePath); void InitializeLibrary(string? filePath);
void SaveLibraryToDisk(string filePath); void SaveLibraryToDisk(string filePath);
Result<GraphicsPipelineKey> CompilePSO(ref readonly GraphicsPSODescriptor descriptor, ref readonly GraphicsCompiledResult compiled); Result<GraphicsPipelineKey> CompilePSO(ref readonly GraphicsPSODescriptor descriptor, ref readonly GraphicsCompiledResult compiled);
Result<CBufferInfo, ResultStatus> GetCBufferInfo(ShaderPassKey passId);
} }

View File

@@ -15,9 +15,9 @@ public interface IRenderer : IDisposable
} }
/// <summary> /// <summary>
/// Sets the render target for this renderer /// Sets the render Target for this renderer
/// </summary> /// </summary>
/// <param name="renderTarget">Render target to render into</param> /// <param name="renderTarget">Render Target to render into</param>
public void SetRenderTarget(Handle<Texture> renderTarget); public void SetRenderTarget(Handle<Texture> renderTarget);
/// <summary> /// <summary>

View File

@@ -15,9 +15,9 @@ public interface IResourceAllocator : IDisposable
public Handle<Texture> CreateTexture(ref readonly TextureDesc desc, bool tempResource = false); public Handle<Texture> CreateTexture(ref readonly TextureDesc desc, bool tempResource = false);
/// <summary> /// <summary>
/// Creates a render target for off-screen rendering /// Creates a render Target for off-screen rendering
/// </summary> /// </summary>
/// <param name="desc">Render target description</param> /// <param name="desc">Render Target description</param>
/// <returns>A new texture handle point to the resource</returns> /// <returns>A new texture handle point to the resource</returns>
public Handle<Texture> CreateRenderTarget(ref readonly RenderTargetDesc desc, bool tempResource = false); public Handle<Texture> CreateRenderTarget(ref readonly RenderTargetDesc desc, bool tempResource = false);

View File

@@ -11,6 +11,9 @@ public interface IResourceReleasable
void ReleaseResource(IResourceDatabase database); void ReleaseResource(IResourceDatabase database);
} }
// TODO: Consider adding methods for resource enumeration, statistics, and bulk operations.
// TODO: Consider adding async resource loading and streaming support.
// TODO: Mesh, Material, Shader management could be separated into their own interfaces for better modularity because they are not bound to specific graphics API.
public interface IResourceDatabase : IDisposable public interface IResourceDatabase : IDisposable
{ {
/* /*
@@ -155,18 +158,4 @@ public interface IResourceDatabase : IDisposable
/// </summary> /// </summary>
/// <param name="id">The identifier of the shader to release. Must refer to a valid, previously created shader.</param> /// <param name="id">The identifier of the shader to release. Must refer to a valid, previously created shader.</param>
void ReleaseShader(Identifier<Shader> id); void ReleaseShader(Identifier<Shader> id);
/// <summary>
/// Adds a shader pass to the collection using the specified identifier.
/// </summary>
/// <param name="passKey">The unique identifier for the shader pass.</param>
/// <param name="pass">The shader pass to add. Cannot be null.</param>
void AddShaderPass(ShaderPassKey passKey, ShaderPass pass);
/// <summary>
/// Retrieves the shader pass associated with the specified pass identifier.
/// </summary>
/// <param name="passKey">The unique identifier of the shader pass to retrieve.</param>
/// <returns>The <see cref="ShaderPass"/> corresponding to the specified identifier, or null if no matching shader pass is found.</returns>
ShaderPass GetShaderPass(ShaderPassKey passKey);
} }

View File

@@ -22,8 +22,8 @@ internal static class RHIUtility
var packed = false; var packed = false;
var planar = false; var planar = false;
var bpe = 0; var bpe = 0;
//switch (format) //switch (Format)
//{ //{
// case Format.BC1Typeless: // case Format.BC1Typeless:
// case Format.BC1Unorm: // case Format.BC1Unorm:

View File

@@ -5,23 +5,33 @@ using Ghost.Graphics.Core;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Ghost.Graphics.Utilities; using Ghost.Graphics.Utilities;
using Ghost.SDL.Compiler; using Ghost.SDL.Compiler;
using Misaki.HighPerformance.Image;
using Misaki.HighPerformance.Mathematics; using Misaki.HighPerformance.Mathematics;
using Misaki.HighPerformance.Utilities; using System.Runtime.InteropServices;
using TerraFX.Interop.Windows;
namespace Ghost.Graphics.RenderPasses; namespace Ghost.Graphics.RenderPasses;
/// <summary> /// <summary>
/// Simplified bindless mesh render pass using high-level bindless APIs with fully bindless vertex/index buffer access /// Simplified bindless mesh render pass using high-level bindless APIs with fully bindless vertex/index buffer access
/// </summary> /// </summary>
internal unsafe class MeshRenderPass : IRenderPass internal class MeshRenderPass : IRenderPass
{ {
[StructLayout(LayoutKind.Sequential)]
private struct ShaderProperties_MyShader_Standard
{
public float4 color;
public uint texture1;
public uint texture2;
public uint texture3;
public uint texture4;
}
private Handle<Mesh> _mesh; private Handle<Mesh> _mesh;
private Identifier<Shader> _shader; private Identifier<Shader> _shader;
private Handle<Material> _material; private Handle<Material> _material;
private Handle<Texture>[]? _textures; private Handle<Texture>[]? _textures;
private GraphicsCompiledResult[]? _compileResults;
// Texture file paths for this demo // Texture file paths for this demo
private readonly string[] _textureFiles = [ private readonly string[] _textureFiles = [
"C:/Users/Misaki/Downloads/Im/Icon.png", "C:/Users/Misaki/Downloads/Im/Icon.png",
@@ -34,9 +44,11 @@ internal unsafe class MeshRenderPass : IRenderPass
{ {
var shaderDescriptor = SDLCompiler.CompileShader("F:/csharp/GhostEngine/Ghost.Graphics/test.gshader", "C:/Users/Misaki/Downloads/Archive").GetValueOrThrow(); var shaderDescriptor = SDLCompiler.CompileShader("F:/csharp/GhostEngine/Ghost.Graphics/test.gshader", "C:/Users/Misaki/Downloads/Archive").GetValueOrThrow();
foreach (var pass in shaderDescriptor.passes) _compileResults = new GraphicsCompiledResult[shaderDescriptor.passes.Count];
for (var i = 0; i < shaderDescriptor.passes.Count; i++)
{ {
var compileResult = ctx.ShaderCompiler.CompilePass(pass); var pass = shaderDescriptor.passes[i];
var compileResult = ctx.ShaderCompiler.CompilePass(pass, shaderDescriptor.generatedCodePath);
if (compileResult.IsFailure || pass is not FullPassDescriptor fullPass) if (compileResult.IsFailure || pass is not FullPassDescriptor fullPass)
{ {
continue; continue;
@@ -55,18 +67,30 @@ internal unsafe class MeshRenderPass : IRenderPass
DsvFormat = TextureFormat.Unknown, DsvFormat = TextureFormat.Unknown,
}; };
ctx.PipelineLibrary.CompilePSO(in psoDes, in compileResult.GetValueRef()).GetValueOrThrow(); _compileResults[i] = compileResult.Value;
ctx.PipelineLibrary.CompilePSO(in psoDes, in _compileResults[i]).GetValueOrThrow();
} }
MeshBuilder.CreateCube(0.75f, default, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent, out var vertices, out var indices); MeshBuilder.CreateCube(0.75f, default, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent, out var vertices, out var indices);
_mesh = ctx.CreateMesh(vertices, indices); _mesh = ctx.CreateMesh(vertices, indices, true);
ctx.UpdateObjectData(_mesh, float4x4.identity); ctx.UpdateObjectData(_mesh, float4x4.identity);
ctx.UploadMesh(_mesh, true);
_shader = ctx.ResourceAllocator.CreateGraphicsShader(shaderDescriptor); _shader = ctx.ResourceAllocator.CreateGraphicsShader(shaderDescriptor);
_material = ctx.ResourceAllocator.CreateMaterial(_shader); _material = ctx.ResourceAllocator.CreateMaterial(_shader);
ref var matRef = ref ctx.ResourceDatabase.GetMaterialReference(_material);
var matProps = new ShaderProperties_MyShader_Standard
{
color = new float4(1.0f, 1.0f, 1.0f, 1.0f),
texture1 = 0,
texture2 = 1,
texture3 = 2,
texture4 = 3,
};
matRef.SetPropertyCache(in matProps);
//_textures = new Handle<Texture>[_textureFiles.Length]; //_textures = new Handle<Texture>[_textureFiles.Length];
//for (var i = 0; i < _textureFiles.Length; i++) //for (var i = 0; i < _textureFiles.Length; i++)
//{ //{
@@ -107,5 +131,13 @@ internal unsafe class MeshRenderPass : IRenderPass
resourceDatabase.ReleaseResource(texture.AsResource()); resourceDatabase.ReleaseResource(texture.AsResource());
} }
} }
if (_compileResults != null)
{
for (var i = 0; i < _compileResults.Length; i++)
{
_compileResults[i].Dispose();
}
}
} }
} }

View File

@@ -1,15 +1,7 @@
#include GENERATED_CODE_PATH #include GENERATED_CODE_PATH
#include "F:/csharp/GhostEngine/Ghost.Shader/BuiltIn/Properties.hlsl" #include "F:/csharp/GhostEngine/Ghost.Shader/BuiltIn/Properties.hlsl"
#include "F:/csharp/GhostEngine/Ghost.Shader/BuiltIn/Common.hlsl"
struct Vertex
{
float4 position;
float4 normal;
float4 tangent;
float4 color;
float4 uv;
};
struct PixelInput struct PixelInput
{ {
@@ -26,24 +18,8 @@ void MSMain(
out vertices PixelInput outVerts[3], out vertices PixelInput outVerts[3],
out indices uint3 outTris[1]) out indices uint3 outTris[1])
{ {
#if 0
// Fetch bindless buffers
ByteAddressBuffer vertexBuffer = ResourceDescriptorHeap[g_PerObjectData.vertexBuffer];
ByteAddressBuffer indexBuffer = ResourceDescriptorHeap[g_PerObjectData.indexBuffer];
// Compute the triangles vertex indices
uint vertexId = groupThreadID.x; uint vertexId = groupThreadID.x;
uint indexOffset = (groupID.x * 3 + vertexId) * 4; // uint32 index Vertex v = LoadVertexData(vertexId, groupID.x, g_PerObjectData.vertexBuffer, g_PerObjectData.indexBuffer);
uint vertexIndex = indexBuffer.Load(indexOffset);
// Load vertex attributes
uint vertexOffset = vertexIndex * 80; // 80 bytes per vertex
Vertex v;
v.position = asfloat(vertexBuffer.Load4(vertexOffset + 0));
v.normal = asfloat(vertexBuffer.Load4(vertexOffset + 16));
v.tangent = asfloat(vertexBuffer.Load4(vertexOffset + 32));
v.color = asfloat(vertexBuffer.Load4(vertexOffset + 48));
v.uv = asfloat(vertexBuffer.Load4(vertexOffset + 64));
SetMeshOutputCounts(3, 1); SetMeshOutputCounts(3, 1);
//v.position = mul(g_PerViewData.cameraMatrix, mul(g_PerObjectData.localToWorld, v.position)); //v.position = mul(g_PerViewData.cameraMatrix, mul(g_PerObjectData.localToWorld, v.position));
@@ -58,50 +34,16 @@ void MSMain(
{ {
outTris[0] = uint3(0, 1, 2); outTris[0] = uint3(0, 1, 2);
} }
#else
// 1. Tell the hardware how much data to expect
SetMeshOutputCounts(3, 1);
// 2. Hardcoded Clip Space Positions (X, Y, Z, W)
// Visible range: X[-1, 1], Y[-1, 1], Z[0, 1]
// W must be 1.0
float4 positions[3] =
{
float4(0.0f, 0.5f, 0.5f, 1.0f), // Top
float4(0.5f, -0.5f, 0.5f, 1.0f), // Bottom Right
float4(-0.5f, -0.5f, 0.5f, 1.0f) // Bottom Left
};
float4 colors[3] =
{
float4(g_PerObjectData.vertexBuffer, 0.0f, 0.0f, 1.0f), // Red
float4(0.0f, g_PerObjectData.indexBuffer, 0.0f, 1.0f), // Green
float4(0.0f, 0.0f, 0.0f, 1.0f) // Blue
};
uint gtid = groupThreadID.x;
// 3. Write Vertex Data (Parallel)
outVerts[gtid].position = positions[gtid];
outVerts[gtid].color = colors[gtid];
// 4. Write Index Data (Only 1st thread needs to do this)
if (gtid == 0)
{
// Clockwise winding (Standard for DX12)
outTris[0] = uint3(0, 1, 2);
}
#endif
} }
float4 PSMain(PixelInput input) : SV_TARGET float4 PSMain(PixelInput input) : SV_TARGET
{ {
//float4 color1 = SAMPLE_TEXTURE2D_BINDLESS(g_PerMaterialData.texture1, 0, input.uv.xy); //float4 color1 = SAMPLE_TEXTURE2D(g_PerMaterialData.texture1, 0, input.uv.xy);
//float4 color2 = SAMPLE_TEXTURE2D_BINDLESS(g_PerMaterialData.texture2, 0, input.uv.xy); //float4 color2 = SAMPLE_TEXTURE2D(g_PerMaterialData.texture2, 0, input.uv.xy);
//float4 color3 = SAMPLE_TEXTURE2D_BINDLESS(g_PerMaterialData.texture3, 0, input.uv.xy); //float4 color3 = SAMPLE_TEXTURE2D(g_PerMaterialData.texture3, 0, input.uv.xy);
//float4 color4 = SAMPLE_TEXTURE2D_BINDLESS(g_PerMaterialData.texture4, 0, input.uv.xy); //float4 color4 = SAMPLE_TEXTURE2D(g_PerMaterialData.texture4, 0, input.uv.xy);
//float4 blendedColor = (color1 + color2 + color3 + color4) * 0.25f; //float4 blendedColor = (color1 + color2 + color3 + color4) * 0.25f;
return g_PerMaterialData.color + input.color;; return g_PerMaterialData.color + input.color;
//return input.color; //return input.color;
} }

View File

@@ -48,11 +48,6 @@ public interface IFenceSynchronizer
public interface IRenderSystem : IFenceSynchronizer, IDisposable public interface IRenderSystem : IFenceSynchronizer, IDisposable
{ {
RenderingConfig Config
{
get;
}
IGraphicsEngine GraphicsEngine IGraphicsEngine GraphicsEngine
{ {
get; get;
@@ -80,8 +75,8 @@ internal class RenderSystem : IRenderSystem
public FrameResource() public FrameResource()
{ {
cpuReadyEvent = new(false); cpuReadyEvent = new AutoResetEvent(false);
gpuReadyEvent = new(true); gpuReadyEvent = new AutoResetEvent(true);
} }
public void Dispose() public void Dispose()
@@ -105,7 +100,6 @@ internal class RenderSystem : IRenderSystem
private bool _isRunning; private bool _isRunning;
private bool _disposed; private bool _disposed;
public RenderingConfig Config => _config;
public IGraphicsEngine GraphicsEngine => _graphicsEngine; public IGraphicsEngine GraphicsEngine => _graphicsEngine;
public bool IsRunning => _isRunning; public bool IsRunning => _isRunning;
@@ -123,16 +117,16 @@ internal class RenderSystem : IRenderSystem
_ => throw new NotSupportedException($"Graphics API {config.GraphicsAPI} is not supported.") _ => throw new NotSupportedException($"Graphics API {config.GraphicsAPI} is not supported.")
}; };
_shutdownEvent = new(false); _shutdownEvent = new AutoResetEvent(false);
// Create frame resources for synchronization // Create frame resources for synchronization
_frameResources = new FrameResource[config.FrameBufferCount]; _frameResources = new FrameResource[config.FrameBufferCount];
for (var i = 0; i < config.FrameBufferCount; i++) for (var i = 0; i < config.FrameBufferCount; i++)
{ {
_frameResources[i] = new(); _frameResources[i] = new FrameResource();
} }
_renderThread = new(RenderLoop) _renderThread = new Thread(RenderLoop)
{ {
IsBackground = true, IsBackground = true,
Name = "Graphics Render Thread", Name = "Graphics Render Thread",

View File

@@ -138,7 +138,7 @@ internal sealed unsafe partial class DxcShaderCompiler : IShaderCompiler
_compiler.Attach(pCompiler); _compiler.Attach(pCompiler);
_utils.Attach(pUtils); _utils.Attach(pUtils);
_compiledResults = new(); _compiledResults = new Dictionary<ShaderPassKey, GraphicsCompiledResult>();
} }
~DxcShaderCompiler() ~DxcShaderCompiler()
@@ -153,10 +153,10 @@ internal sealed unsafe partial class DxcShaderCompiler : IShaderCompiler
try try
{ {
// Create DXC _utils.Get() to parse pReflection data // Create DXC _utils.Get() to parse reflection data
var dxcuID = CLSID.CLSID_DxcUtils; var dxcuID = CLSID.CLSID_DxcUtils;
// Create pReflection interface from blob // Create reflection interface from blob
var reflectionBuffer = new DxcBuffer var reflectionBuffer = new DxcBuffer
{ {
Ptr = pDxcReflectionBlob->GetBufferPointer(), Ptr = pDxcReflectionBlob->GetBufferPointer(),
@@ -242,137 +242,109 @@ internal sealed unsafe partial class DxcShaderCompiler : IShaderCompiler
} }
} }
public Result<CompileResult> Compile(ref readonly CompilerConfig config, Allocator allocator) public Result<ShaderCompileResult> Compile(ref readonly CompilerConfig config, Allocator allocator)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
IDxcIncludeHandler* pIncludeHandler = default; using ComPtr<IDxcIncludeHandler> includeHandler = default;
IDxcBlobEncoding* pSourceBlob = default; using ComPtr<IDxcBlobEncoding> sourceBlob = default;
// Create DXC _compiler.Get() and _utils.Get()
var dxccID = CLSID.CLSID_DxcCompiler;
var dxcuID = CLSID.CLSID_DxcUtils;
ThrowIfFailed(_utils.Get()->CreateDefaultIncludeHandler(includeHandler.GetAddressOf()));
// Create source blob
fixed (char* pPath = config.shaderPath)
{
if (_utils.Get()->LoadFile(pPath, null, sourceBlob.GetAddressOf()).FAILED)
{
return Result.Failure($"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]);
}
using ComPtr<IDxcResult> result = default;
try try
{ {
// Create DXC _compiler.Get() and _utils.Get() // Compile shader
var dxccID = CLSID.CLSID_DxcCompiler; var buffer = new DxcBuffer
var dxcuID = CLSID.CLSID_DxcUtils;
ThrowIfFailed(_utils.Get()->CreateDefaultIncludeHandler(&pIncludeHandler));
// Create source blob
fixed (char* pPath = config.shaderPath)
{ {
if (_utils.Get()->LoadFile(pPath, null, &pSourceBlob).FAILED) Ptr = sourceBlob.Get()->GetBufferPointer(),
Size = sourceBlob.Get()->GetBufferSize(),
Encoding = DXC_CP_UTF8
};
var (iid, ppv) = Win32Utility.IID_PPV_ARGS(&result);
ThrowIfFailed(_compiler.Get()->Compile(&buffer, argPtrs, (uint)argsArray.Count, includeHandler, iid, ppv));
// Check compilation result
HRESULT hrStatus;
result.Get()->GetStatus(&hrStatus);
if (hrStatus.FAILED)
{
// Get error messages
IDxcBlobEncoding* pErrorBlob = default;
result.Get()->GetErrorBuffer(&pErrorBlob);
if (pErrorBlob != null)
{ {
return Result.Failure($"Failed to load shader file: {config.shaderPath}"); var errorMessage = Marshal.PtrToStringUTF8((IntPtr)pErrorBlob->GetBufferPointer());
pErrorBlob->Release();
return Result.Failure($"DXC shader compilation failed:\n{errorMessage}");
}
else
{
return Result.Failure("DXC shader compilation failed with unknown error.");
} }
} }
var argsArray = GetCompilerArguments(in config); // Get compiled bytecode
var argPtrs = stackalloc char*[argsArray.Count]; using ComPtr<IDxcBlob> bytecodeBlob = default;
for (var i = 0; i < argsArray.Count; i++) ThrowIfFailed(result.Get()->GetResult(bytecodeBlob.GetAddressOf()));
ShaderReflectionData reflectionData = default;
if (config.options.HasFlag(CompilerOption.KeepReflections))
{ {
argPtrs[i] = (char*)Marshal.StringToHGlobalUni(argsArray[i]); using ComPtr<IDxcBlob> reflection = default;
(iid, ppv) = Win32Utility.IID_PPV_ARGS(&reflection);
if (result.Get()->GetOutput(DXC_OUT_KIND.DXC_OUT_REFLECTION, iid, ppv, null).SUCCEEDED)
{
reflectionData = PerformDXCReflection(reflection).GetValueOrDefault();
}
} }
IDxcResult* pResult = default; var bytecodeSize = bytecodeBlob.Get()->GetBufferSize();
var bytecode = new UnsafeArray<byte>((int)bytecodeSize, allocator);
try NativeMemory.Copy(bytecodeBlob.Get()->GetBufferPointer(), bytecode.GetUnsafePtr(), bytecodeSize);
return new ShaderCompileResult
{ {
// Compile shader bytecode = bytecode,
var buffer = new DxcBuffer reflectionData = reflectionData,
{ };
Ptr = pSourceBlob->GetBufferPointer(),
Size = pSourceBlob->GetBufferSize(),
Encoding = DXC_CP_UTF8
};
ThrowIfFailed(_compiler.Get()->Compile(&buffer, argPtrs, (uint)argsArray.Count, pIncludeHandler, __uuidof(pResult), (void**)&pResult));
// Check compilation pResult
HRESULT hrStatus;
pResult->GetStatus(&hrStatus);
if (hrStatus.FAILED)
{
// Get error messages
IDxcBlobEncoding* pErrorBlob = default;
pResult->GetErrorBuffer(&pErrorBlob);
if (pErrorBlob != null)
{
var errorMessage = Marshal.PtrToStringUTF8((IntPtr)pErrorBlob->GetBufferPointer());
pErrorBlob->Release();
return Result.Failure($"DXC shader compilation failed:\n{errorMessage}");
}
else
{
return Result.Failure("DXC shader compilation failed with unknown error.");
}
}
// Get compiled bytecode
IDxcBlob* pBytecodeBlob = default;
try
{
ThrowIfFailed(pResult->GetResult(&pBytecodeBlob));
ShaderReflectionData reflection = default;
if (config.options.HasFlag(CompilerOption.KeepReflections))
{
IDxcBlob* pReflection = default;
if ((pResult->GetOutput(DXC_OUT_KIND.DXC_OUT_REFLECTION, __uuidof<IDxcBlob>(), (void**)&pReflection, null).SUCCEEDED))
{
reflection = PerformDXCReflection(pReflection).GetValueOrDefault();
}
if (pReflection != null)
{
pReflection->Release();
}
}
var bytecodeSize = pBytecodeBlob->GetBufferSize();
var bytecode = new UnsafeArray<byte>((int)bytecodeSize, allocator);
NativeMemory.Copy(pBytecodeBlob->GetBufferPointer(), bytecode.GetUnsafePtr(), bytecodeSize);
return new CompileResult
{
bytecode = bytecode,
reflectionData = reflection,
};
}
finally
{
if (pBytecodeBlob != null)
{
pBytecodeBlob->Release();
}
}
}
finally
{
for (var i = 0; i < argsArray.Count; i++)
{
Marshal.FreeHGlobal((nint)argPtrs[i]);
}
if (pResult != null)
{
pResult->Release();
}
}
} }
finally finally
{ {
if (pIncludeHandler != null) for (var i = 0; i < argsArray.Count; i++)
{ {
pIncludeHandler->Release(); Marshal.FreeHGlobal((nint)argPtrs[i]);
} }
} }
} }
public Result<GraphicsCompiledResult> CompilePass(IPassDescriptor descriptor) public Result<GraphicsCompiledResult> CompilePass(IPassDescriptor descriptor, string? generatedCodePath)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
@@ -381,14 +353,14 @@ internal sealed unsafe partial class DxcShaderCompiler : IShaderCompiler
return Result.Failure("FullPassDescriptor expected."); return Result.Failure("FullPassDescriptor expected.");
} }
CompileResult tsResult = default; ShaderCompileResult tsResult = default;
var tsEntry = fullDescriptor.taskShader; var tsEntry = fullDescriptor.taskShader;
if (tsEntry.IsCreated) if (tsEntry.IsCreated)
{ {
var config = new CompilerConfig var config = new CompilerConfig
{ {
defines = fullDescriptor.defines.AsSpan(), defines = fullDescriptor.defines.AsSpan(),
include = fullDescriptor.generatedCodePath, include = generatedCodePath,
shaderPath = tsEntry.shader, shaderPath = tsEntry.shader,
entryPoint = tsEntry.entry, entryPoint = tsEntry.entry,
stage = ShaderStage.TaskShader, stage = ShaderStage.TaskShader,
@@ -406,14 +378,14 @@ internal sealed unsafe partial class DxcShaderCompiler : IShaderCompiler
tsResult = result.Value; tsResult = result.Value;
} }
CompileResult msResult; ShaderCompileResult msResult;
var msEntry = fullDescriptor.meshShader; var msEntry = fullDescriptor.meshShader;
if (msEntry.IsCreated) if (msEntry.IsCreated)
{ {
var config = new CompilerConfig var config = new CompilerConfig
{ {
defines = fullDescriptor.defines.AsSpan(), defines = fullDescriptor.defines.AsSpan(),
include = fullDescriptor.generatedCodePath, include = generatedCodePath,
shaderPath = msEntry.shader, shaderPath = msEntry.shader,
entryPoint = msEntry.entry, entryPoint = msEntry.entry,
stage = ShaderStage.MeshShader, stage = ShaderStage.MeshShader,
@@ -435,14 +407,14 @@ internal sealed unsafe partial class DxcShaderCompiler : IShaderCompiler
return Result.Failure("Mesh shader expected."); return Result.Failure("Mesh shader expected.");
} }
CompileResult psResult; ShaderCompileResult psResult;
var psEntry = fullDescriptor.pixelShader; var psEntry = fullDescriptor.pixelShader;
if (psEntry.IsCreated) if (psEntry.IsCreated)
{ {
var config = new CompilerConfig var config = new CompilerConfig
{ {
defines = fullDescriptor.defines.AsSpan(), defines = fullDescriptor.defines.AsSpan(),
include = fullDescriptor.generatedCodePath, include = generatedCodePath,
shaderPath = psEntry.shader, shaderPath = psEntry.shader,
entryPoint = psEntry.entry, entryPoint = psEntry.entry,
stage = ShaderStage.PixelShader, stage = ShaderStage.PixelShader,

View File

@@ -231,7 +231,7 @@ public unsafe static class MeshBuilder
public static void ComputeTangents(UnsafeList<Vertex> vertices, UnsafeList<uint> indices) public static void ComputeTangents(UnsafeList<Vertex> vertices, UnsafeList<uint> indices)
{ {
using var scope = AllocationManager.CreateStackScope(); using var scope = AllocationManager.CreateStackScope();
var bitangents = new UnsafeArray<float4>(vertices.Count, Allocator.Stack); using var bitangents = new UnsafeArray<float4>(vertices.Count, Allocator.Stack);
for (var i = 0; i < indices.Count; i += 3) for (var i = 0; i < indices.Count; i += 3)
{ {

View File

@@ -3,10 +3,10 @@ shader "MyShader/Standard"
properties properties
{ {
float4 color = float4(1, 1, 1, 1); float4 color = float4(1, 1, 1, 1);
tex2d_b texture1 = tex2d_b(black); tex2d texture1 = tex2d(black);
tex2d_b texture2 = tex2d_b(white); tex2d texture2 = tex2d(white);
tex2d_b texture3 = tex2d_b(grey); tex2d texture3 = tex2d(grey);
tex2d_b texture4 = tex2d_b(normal); tex2d texture4 = tex2d(normal);
} }
pass "Forward" pass "Forward"
@@ -23,4 +23,4 @@ shader "MyShader/Standard"
ms("F:/csharp/GhostEngine/Ghost.Graphics/RenderPasses/ShaderCode.hlsl", "MSMain"); ms("F:/csharp/GhostEngine/Ghost.Graphics/RenderPasses/ShaderCode.hlsl", "MSMain");
ps("F:/csharp/GhostEngine/Ghost.Graphics/RenderPasses/ShaderCode.hlsl", "PSMain"); ps("F:/csharp/GhostEngine/Ghost.Graphics/RenderPasses/ShaderCode.hlsl", "PSMain");
} }
} }

View File

@@ -30,11 +30,7 @@ if (model == null)
} }
var descriptor = SDLCompiler.ResolveShader(model); var descriptor = SDLCompiler.ResolveShader(model);
SDLCompiler.GenerateShaderCode(descriptor, "C:/Users/Misaki/Downloads/Archive");
foreach (var pass in descriptor.passes)
{
SDLCompiler.GeneratePass(pass, "C:/Users/Misaki/Downloads/Archive");
}
Console.WriteLine("Shader compiled successfully:"); Console.WriteLine("Shader compiled successfully:");

View File

@@ -1,97 +1,95 @@
#ifndef COMMON_HLSL #ifndef BUILTIN_COMMON_HLSL
#define COMMON_HLSL #define BUILTIN_COMMON_HLSL
#undef USE_TRADITIONAL_BINDLESS // Just for testing, this should be handled by engine feature level.
struct Vertex
{
float4 position;
float4 normal;
float4 tangent;
float4 uv;
float4 color;
};
// Resource descriptor heap definitions // Resource descriptor heap definitions
#if defined(USE_TRADITIONAL_BINDLESS)
#define GLOBAL_TEXTURE2D_HEAP_SIZE 32768
#define GLOBAL_TEXTURE3D_HEAP_SIZE 32
#define GLOBAL_TEXTURECUBE_HEAP_SIZE 32
#define GLOBAL_TEXTURE2D_ARRAY_HEAP_SIZE 256
#define GLOBAL_TEXTURECUBE_ARRAY_HEAP_SIZE 32
#define GLOBAL_SAMPLER_HEAP_SIZE 32
#define GLOBAL_TEXTURE2D_HEAP GlobalTexture2DHeap
#define GLOBAL_TEXTURE3D_HEAP GlobalTexture3DHeap
#define GLOBAL_TEXTURECUBE_HEAP GlobalTextureCubeHeap
#define GLOBAL_TEXTURE2D_ARRAY_HEAP GlobalTexture2DArrayHeap
#define GLOBAL_TEXTURECUBE_ARRAY_HEAP GlobalTextureCubeArrayHeap
#define GLOBAL_SAMPLER_HEAP GlobalSamplerHeap
#else
#define GLOBAL_TEXTURE2D_HEAP ResourceDescriptorHeap #define GLOBAL_TEXTURE2D_HEAP ResourceDescriptorHeap
#define GLOBAL_TEXTURE3D_HEAP ResourceDescriptorHeap #define GLOBAL_TEXTURE3D_HEAP ResourceDescriptorHeap
#define GLOBAL_TEXTURECUBE_HEAP ResourceDescriptorHeap #define GLOBAL_TEXTURECUBE_HEAP ResourceDescriptorHeap
#define GLOBAL_TEXTURE2D_ARRAY_HEAP ResourceDescriptorHeap #define GLOBAL_TEXTURE2D_ARRAY_HEAP ResourceDescriptorHeap
#define GLOBAL_TEXTURECUBE_ARRAY_HEAP ResourceDescriptorHeap #define GLOBAL_TEXTURECUBE_ARRAY_HEAP ResourceDescriptorHeap
#define GLOBAL_BUFFER_HEAP ResourceDescriptorHeap
#define GLOBAL_SAMPLER_HEAP SamplerDescriptorHeap #define GLOBAL_SAMPLER_HEAP SamplerDescriptorHeap
#endif
// Bindless resource type definitions // Bindless resource type definitions
#define TEXTURE2D_BINDLESS uint #define TEXTURE2D uint
#define TEXTURE3D_BINDLESS uint #define TEXTURE3D uint
#define TEXTURECUBE_BINDLESS uint #define TEXTURECUBE uint
#define TEXTURE2D_ARRAY_BINDLESS uint #define TEXTURE2D_ARRAY uint
#define TEXTURECUBE_ARRAY_BINDLESS uint #define TEXTURECUBE_ARRAY uint
#define SAMPLER_BINDLESS uint #define SAMPLER uint
#define STRUCT_BUFFER_BINDLESS uint #define STRUCT_BUFFER uint
#define BYTE_ADDRESS_BUFFER_BINDLESS uint #define BYTE_ADDRESS_BUFFER uint
#define TEXTURE2D Texture2D
#define TEXTURE3D Texture3D
#define TEXTURECUBE TextureCube
#define TEXTURE2D_ARRAY Texture2DArray
#define TEXTURECUBE_ARRAY TextureCubeArray
#define SAMPLER SamplerState
#define STRUCT_BUFFER(type) StructuredBuffer<type>
#define BYTE_ADDRESS_BUFFER ByteAddressBuffer
// Texture and sampler access macros // Texture and sampler access macros
#define GET_TEXTURE2D_BINDLESS(id) GLOBAL_TEXTURE2D_HEAP[id] #define GET_TEXTURE2D(id) GLOBAL_TEXTURE2D_HEAP[id]
#define GET_TEXTURE2D_ARRAY_BINDLESS(id) GLOBAL_TEXTURE2D_ARRAY_HEAP[id] #define GET_TEXTURE2D_ARRAY(id) GLOBAL_TEXTURE2D_ARRAY_HEAP[id]
#define GET_TEXTURE3D_BINDLESS(id) GLOBAL_TEXTURE3D_HEAP[id] #define GET_TEXTURE3D(id) GLOBAL_TEXTURE3D_HEAP[id]
#define GET_TEXTURECUBE_BINDLESS(id) GLOBAL_TEXTURECUBE_HEAP[id] #define GET_TEXTURECUBE(id) GLOBAL_TEXTURECUBE_HEAP[id]
#define GET_TEXTURECUBE_ARRAY_BINDLESS(id) GLOBAL_TEXTURECUBE_ARRAY_HEAP[id] #define GET_TEXTURECUBE_ARRAY(id) GLOBAL_TEXTURECUBE_ARRAY_HEAP[id]
#define GET_SAMPLER_BINDLESS(id) GLOBAL_SAMPLER_HEAP[id] #define GET_BUFFER(id) GLOBAL_BUFFER_HEAP[id]
#define GET_SAMPLER(id) GLOBAL_SAMPLER_HEAP[id]
// Texture sampling macros
#define SAMPLE_TEXTURE2D(tex, samp, uv) tex.Sample(samp, uv)
#define SAMPLE_TEXTURE2D_LEVEL(tex, samp, uv, level) tex.SampleLevel(samp, uv, level)
#define SAMPLE_TEXTURE2D_BINDLESS(texId, sampId, uv) SampleTexture2DBindless(texId, sampId, uv)
#define SAMPLE_TEXTURE2D_LEVEL_BINDLESS(texId, sampId, uv, level) SampleTexture2DLevelBindless(texId, sampId, uv, level)
#define SAMPLE_TEXTURE2D_ARRAY(tex, samp, uv, index) tex.Sample(samp, uv, index)
#define SAMPLE_TEXTURE2D_ARRAY_BINDLESS(texId, sampId, uv, index) GET_TEXTURE2D_ARRAY_BINDLESS(texId).Sample(GET_SAMPLER_BINDLESS(sampId), uv, index)
static inline float4 SampleTexture2DBindless(uint texId, uint sampId, float2 uv)
{
Texture2D tex = GET_TEXTURE2D_BINDLESS(texId);
SamplerState samp = GET_SAMPLER_BINDLESS(sampId);
return tex.Sample(samp, uv);
}
static inline float4 SampleTexture2DLevelBindless(uint texId, uint sampId, float2 uv, float level)
{
Texture2D tex = GET_TEXTURE2D_BINDLESS(texId);
SamplerState samp = GET_SAMPLER_BINDLESS(sampId);
return tex.SampleLevel(samp, uv, level);
}
#define MESH_SHADER_THREADS(x) [NumThreads(x, 1, 1)] #define MESH_SHADER_THREADS(x) [NumThreads(x, 1, 1)]
#endif // COMMON_HLSL
inline float4 SampleTexture2D(uint texId, uint sampId, float2 uv)
{
Texture2D tex = GET_TEXTURE2D(texId);
SamplerState samp = GET_SAMPLER(sampId);
return tex.Sample(samp, uv);
}
inline float4 SampleTexture2DLevel(uint texId, uint sampId, float2 uv, float level)
{
Texture2D tex = GET_TEXTURE2D(texId);
SamplerState samp = GET_SAMPLER(sampId);
return tex.SampleLevel(samp, uv, level);
}
inline float4 SampleTextureArray(uint texId, uint sampId, float3 uv)
{
Texture2DArray tex = GET_TEXTURE2D_ARRAY(texId);
SamplerState samp = GET_SAMPLER(sampId);
return tex.Sample(samp, uvw.xyz);
}
inline Vertex LoadVertexData(uint vertexID, uint groupID, BYTE_ADDRESS_BUFFER vertexBuffer, BYTE_ADDRESS_BUFFER indexBuffer)
{
// Fetch bindless buffers
ByteAddressBuffer vertexBuffer = GET_BUFFER(vertexBuffer);
ByteAddressBuffer indexBuffer = GET_BUFFER(indexBuffer);
// Compute the triangles vertex indices
uint indexOffset = (groupID * 3 + vertexID) * 4; // uint32 index
uint vertexIndex = indexBuffer.Load(indexOffset);
// Load vertex attributes
uint vertexOffset = vertexIndex * 80; // 80 bytes per vertex
Vertex v;
v.position = asfloat(vertexBuffer.Load4(vertexOffset + 0));
v.normal = asfloat(vertexBuffer.Load4(vertexOffset + 16));
v.tangent = asfloat(vertexBuffer.Load4(vertexOffset + 32));
v.uv = asfloat(vertexBuffer.Load4(vertexOffset + 48));
v.color = asfloat(vertexBuffer.Load4(vertexOffset + 64));
return v;
}
#endif // BUILTIN_COMMON_HLSL

View File

@@ -1,5 +1,5 @@
#ifndef PROPERTIES_HLSL #ifndef BUILTIN_PROPERTIES_HLSL
#define PROPERTIES_HLSL #define BUILTIN_PROPERTIES_HLSL
#include "F:/csharp/GhostEngine/Ghost.Shader/BuiltIn/Common.hlsl" #include "F:/csharp/GhostEngine/Ghost.Shader/BuiltIn/Common.hlsl"
@@ -48,4 +48,4 @@ TextureCubeArray GlobalTextureCubeArrayHeap[GLOBAL_TEXTURECUBE_ARRAY_HEAP_SIZE]
SamplerState GlobalSamplerHeap[GLOBAL_SAMPLER_HEAP_SIZE] : register(s0); SamplerState GlobalSamplerHeap[GLOBAL_SAMPLER_HEAP_SIZE] : register(s0);
#endif #endif
#endif #endif // BUILTIN_PROPERTIES_HLSL

View File

@@ -1,59 +0,0 @@
namespace Ghost.SDL.Compiler.Parser;
internal class IncludesBlock : IBlockParser<List<Token>, List<string>>
{
public static bool ShouldEnter(Token token)
{
return token.Match(TokenType.Keyword, TokenLexicon.KnownKeywords.INCLUDES);
}
public static List<Token> Parse(TokenStreamSlice stream)
{
stream.Expect(TokenType.Keyword);
stream.Expect(TokenType.LBrace);
var includes = new List<Token>();
var bodyStream = stream.Slice(stream.Remaining - 1);
while (bodyStream.HasMore)
{
var includeToken = bodyStream.Expect(TokenType.StringLiteral);
includes.Add(includeToken);
bodyStream.Expect(TokenType.Semicolon);
}
stream.Expect(TokenType.RBrace);
return includes;
}
public static List<string>? SemanticAnalysis(List<Token>? syntax, List<SDLError> errors)
{
if (syntax == null || syntax.Count == 0)
{
return null;
}
var includes = new List<string>(syntax.Count);
foreach (var includeToken in syntax)
{
var path = includeToken.lexeme;
if (File.Exists(path))
{
includes.Add(path);
}
else
{
errors.Add(new SDLError
{
message = $"Included file '{path}' not found.",
line = includeToken.line,
column = includeToken.column
});
continue;
}
}
return includes;
}
}

View File

@@ -27,10 +27,6 @@ internal class PassBlock : IBlockParser<PassSyntax, PassSemantic>
{ {
pass.defines = DefinesBlock.Parse(bodyStream.SliceNextBlock()); pass.defines = DefinesBlock.Parse(bodyStream.SliceNextBlock());
} }
else if (IncludesBlock.ShouldEnter(nextToken))
{
pass.includes = IncludesBlock.Parse(bodyStream.SliceNextBlock());
}
else if (KeywordsBlock.ShouldEnter(nextToken)) else if (KeywordsBlock.ShouldEnter(nextToken))
{ {
pass.keywords = KeywordsBlock.Parse(bodyStream.SliceNextBlock()); pass.keywords = KeywordsBlock.Parse(bodyStream.SliceNextBlock());
@@ -39,10 +35,6 @@ internal class PassBlock : IBlockParser<PassSyntax, PassSemantic>
{ {
pass.localPipeline = PipelineBlock.Parse(bodyStream.SliceNextBlock()); pass.localPipeline = PipelineBlock.Parse(bodyStream.SliceNextBlock());
} }
else if (PropertiesBlock.ShouldEnter(nextToken))
{
pass.localProperties = PropertiesBlock.Parse(bodyStream.SliceNextBlock());
}
else if (nextToken.Match(TokenType.Identifier)) else if (nextToken.Match(TokenType.Identifier))
{ {
var func = ParseUtility.ParseFunction(ref bodyStream, TokenType.StringLiteral); var func = ParseUtility.ParseFunction(ref bodyStream, TokenType.StringLiteral);
@@ -72,23 +64,10 @@ internal class PassBlock : IBlockParser<PassSyntax, PassSemantic>
{ {
name = syntax.name.lexeme, name = syntax.name.lexeme,
defines = DefinesBlock.SemanticAnalysis(syntax.defines, errors), defines = DefinesBlock.SemanticAnalysis(syntax.defines, errors),
includes = IncludesBlock.SemanticAnalysis(syntax.includes, errors),
keywords = KeywordsBlock.SemanticAnalysis(syntax.keywords, errors), keywords = KeywordsBlock.SemanticAnalysis(syntax.keywords, errors),
localProperties = PropertiesBlock.SemanticAnalysis(syntax.localProperties, errors),
localPipeline = PipelineBlock.SemanticAnalysis(syntax.localPipeline, errors), localPipeline = PipelineBlock.SemanticAnalysis(syntax.localPipeline, errors),
}; };
if (semantic.localProperties != null
&& semantic.localProperties.Any(p => p.scope == PropertyScope.Global))
{
errors.Add(new SDLError
{
message = "Global properties cannot be declared inside a pass. Move them to the shader properties block.",
line = syntax.name.line,
column = syntax.name.column
});
}
if (syntax.functionCalls != null) if (syntax.functionCalls != null)
{ {
foreach (var func in syntax.functionCalls) foreach (var func in syntax.functionCalls)

View File

@@ -73,9 +73,9 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
ParseBoolValue(syntax[3], errors))), ParseBoolValue(syntax[3], errors))),
// Textures (single identifier argument) // Textures (single identifier argument)
[ShaderPropertyType.Texture2DBindless] = new(1, TokenType.Identifier, (syntax, errors) => ParseTextureDefault(syntax[0], errors)), [ShaderPropertyType.Texture2D] = new(1, TokenType.Identifier, (syntax, errors) => ParseTextureDefault(syntax[0], errors)),
[ShaderPropertyType.Texture3DBindless] = new(1, TokenType.Identifier, (syntax, errors) => ParseTextureDefault(syntax[0], errors)), [ShaderPropertyType.Texture3D] = new(1, TokenType.Identifier, (syntax, errors) => ParseTextureDefault(syntax[0], errors)),
[ShaderPropertyType.TextureCubeBindless] = new(1, TokenType.Identifier, (syntax, errors) => ParseTextureDefault(syntax[0], errors)), [ShaderPropertyType.TextureCube] = new(1, TokenType.Identifier, (syntax, errors) => ParseTextureDefault(syntax[0], errors)),
}; };
private static float ParseFloatValue(Token token, List<SDLError> errors) private static float ParseFloatValue(Token token, List<SDLError> errors)
@@ -166,6 +166,7 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
TokenLexicon.KnownTypes.FLOAT2 => ShaderPropertyType.Float2, TokenLexicon.KnownTypes.FLOAT2 => ShaderPropertyType.Float2,
TokenLexicon.KnownTypes.FLOAT3 => ShaderPropertyType.Float3, TokenLexicon.KnownTypes.FLOAT3 => ShaderPropertyType.Float3,
TokenLexicon.KnownTypes.FLOAT4 => ShaderPropertyType.Float4, TokenLexicon.KnownTypes.FLOAT4 => ShaderPropertyType.Float4,
TokenLexicon.KnownTypes.FLOAT4X4 => ShaderPropertyType.Float4x4,
TokenLexicon.KnownTypes.INT => ShaderPropertyType.Int, TokenLexicon.KnownTypes.INT => ShaderPropertyType.Int,
TokenLexicon.KnownTypes.INT2 => ShaderPropertyType.Int2, TokenLexicon.KnownTypes.INT2 => ShaderPropertyType.Int2,
TokenLexicon.KnownTypes.INT3 => ShaderPropertyType.Int3, TokenLexicon.KnownTypes.INT3 => ShaderPropertyType.Int3,
@@ -178,9 +179,9 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
TokenLexicon.KnownTypes.BOOL2 => ShaderPropertyType.Bool2, TokenLexicon.KnownTypes.BOOL2 => ShaderPropertyType.Bool2,
TokenLexicon.KnownTypes.BOOL3 => ShaderPropertyType.Bool3, TokenLexicon.KnownTypes.BOOL3 => ShaderPropertyType.Bool3,
TokenLexicon.KnownTypes.BOOL4 => ShaderPropertyType.Bool4, TokenLexicon.KnownTypes.BOOL4 => ShaderPropertyType.Bool4,
TokenLexicon.KnownTypes.TEXTURE2D_BINDLESS => ShaderPropertyType.Texture2DBindless, TokenLexicon.KnownTypes.TEXTURE2D => ShaderPropertyType.Texture2D,
TokenLexicon.KnownTypes.TEXTURE3D_BINDLESS => ShaderPropertyType.Texture3DBindless, TokenLexicon.KnownTypes.TEXTURE3D => ShaderPropertyType.Texture3D,
TokenLexicon.KnownTypes.TEXTURECUBE_BINDLESS => ShaderPropertyType.TextureCubeBindless, TokenLexicon.KnownTypes.TEXTURECUBE => ShaderPropertyType.TextureCube,
_ => ShaderPropertyType.None, _ => ShaderPropertyType.None,
}; };
} }

View File

@@ -54,7 +54,7 @@ internal static class SDLCompiler
public static SDLSemantics? SemanticAnalysis(SDLSyntax syntax, out List<SDLError> errors) public static SDLSemantics? SemanticAnalysis(SDLSyntax syntax, out List<SDLError> errors)
{ {
errors = new(); errors = new List<SDLError>();
if (string.IsNullOrWhiteSpace(syntax.name.lexeme)) if (string.IsNullOrWhiteSpace(syntax.name.lexeme))
{ {
@@ -145,31 +145,23 @@ internal static class SDLCompiler
}; };
} }
private static List<PropertyDescriptor> MergeProperties(List<PropertySemantic>? semantics, List<PropertyDescriptor>? parent) private static uint CalculateCBufferSize(List<PropertyDescriptor> properties)
{ {
var result = new List<PropertyDescriptor>(); var currentOffset = 0u;
if (parent != null)
{
result.AddRange(parent);
}
if (semantics != null) foreach (var prop in properties)
{ {
foreach (var prop in semantics) var size = prop.type.GetSize();
if ((currentOffset % 16) + size > 16)
{ {
if (prop.scope == PropertyScope.Local) currentOffset = (currentOffset + 15u) & ~15u;
{
result.Add(new PropertyDescriptor
{
name = prop.name,
type = prop.type,
defaultValue = prop.defaultValue
});
}
} }
currentOffset += size;
} }
return result.DistinctBy(p => p.name).ToList(); return (currentOffset + 15u) & ~15u;
} }
// TODO: Implement shader inheritance resolution, including property and pass merging. // TODO: Implement shader inheritance resolution, including property and pass merging.
@@ -181,19 +173,23 @@ internal static class SDLCompiler
name = semantics.name name = semantics.name
}; };
var shaderGlobalProperties = semantics.properties?.Where(p => p.scope == PropertyScope.Global).Select(p => new PropertyDescriptor var shaderGlobalProperties = semantics.properties?
{ .Where(p => p.scope == PropertyScope.Global)
name = p.name, .Select(p => new PropertyDescriptor
type = p.type, {
defaultValue = p.defaultValue name = p.name,
}).ToList(); type = p.type,
defaultValue = p.defaultValue
}).ToList();
var shaderLocalProperties = semantics.properties?.Where(p => p.scope == PropertyScope.Local).Select(p => new PropertyDescriptor var shaderLocalProperties = semantics.properties?
{ .Where(p => p.scope == PropertyScope.Local)
name = p.name, .Select(p => new PropertyDescriptor
type = p.type, {
defaultValue = p.defaultValue name = p.name,
}).ToList(); type = p.type,
defaultValue = p.defaultValue
}).ToList();
if (shaderGlobalProperties != null) if (shaderGlobalProperties != null)
{ {
@@ -201,13 +197,18 @@ internal static class SDLCompiler
descriptor.globalProperties.AddRange(shaderGlobalProperties); descriptor.globalProperties.AddRange(shaderGlobalProperties);
} }
if (shaderLocalProperties != null)
{
descriptor.properties ??= new List<PropertyDescriptor>();
descriptor.properties.AddRange(shaderLocalProperties);
descriptor.cbufferSize = CalculateCBufferSize(descriptor.properties);
}
if (semantics.passes != null) if (semantics.passes != null)
{ {
foreach (var pass in semantics.passes) foreach (var pass in semantics.passes)
{ {
var localPipeline = MeragePipeline(pass.localPipeline, PipelineDescriptor.Default); var localPipeline = MeragePipeline(pass.localPipeline, PipelineDescriptor.Default);
var localProperties = MergeProperties(pass.localProperties, shaderLocalProperties); // TODO: Merge with base shader properties if inheritance is implemented.
var fullPass = new FullPassDescriptor var fullPass = new FullPassDescriptor
{ {
uniqueIdentifier = GetPassUniqueId(semantics, pass), uniqueIdentifier = GetPassUniqueId(semantics, pass),
@@ -217,9 +218,7 @@ internal static class SDLCompiler
pixelShader = pass.pixelShader, pixelShader = pass.pixelShader,
localPipeline = localPipeline, localPipeline = localPipeline,
defines = pass.defines, defines = pass.defines,
includes = pass.includes,
keywords = pass.keywords, keywords = pass.keywords,
properties = localProperties
}; };
descriptor.passes.Add(fullPass); descriptor.passes.Add(fullPass);
@@ -258,24 +257,14 @@ internal static class SDLCompiler
return Result.Failure("Failed to generate global properties: " + globalPropResult.Message); return Result.Failure("Failed to generate global properties: " + globalPropResult.Message);
} }
foreach (var pass in desc.passes) var generatedResult = GenerateShaderCode(desc, generatedOutputDirectory);
if (generatedResult.IsFailure)
{ {
if (pass is not FullPassDescriptor fullPass) return Result.Failure("Failed to generate pass files: " + generatedResult.Message);
{
continue;
}
fullPass.includes ??= new List<string>();
fullPass.includes.Add(globalPropResult.Value);
var generatedResult = GeneratePass(fullPass, generatedOutputDirectory);
if (generatedResult.IsFailure)
{
return Result.Failure("Failed to generate pass files: " + generatedResult.Message);
}
fullPass.generatedCodePath = generatedResult.Value;
} }
desc.generatedCodePath = generatedResult.Value;
return desc; return desc;
} }
catch (Exception ex) catch (Exception ex)
@@ -305,28 +294,23 @@ internal static class SDLCompiler
ShaderPropertyType.Bool3 => "bool3", ShaderPropertyType.Bool3 => "bool3",
ShaderPropertyType.Bool4 => "bool4", ShaderPropertyType.Bool4 => "bool4",
// NOTE: Textures here are bindless, represented as uint (descriptor index). // NOTE: Textures here are bindless, represented as uint (descriptor index).
ShaderPropertyType.Texture2DBindless => "TEXTURE2D_BINDLESS", ShaderPropertyType.Texture2D => "TEXTURE2D_BINDLESS",
ShaderPropertyType.Texture3DBindless => "TEXTURE3D_BINDLESS", ShaderPropertyType.Texture3D => "TEXTURE3D_BINDLESS",
ShaderPropertyType.TextureCubeBindless => "TEXTURECUBE_BINDLESS", ShaderPropertyType.TextureCube => "TEXTURECUBE_BINDLESS",
ShaderPropertyType.Texture2DArrayBindless => "TEXTURE2D_ARRAY_BINDLESS", ShaderPropertyType.Texture2DArray => "TEXTURE2D_ARRAY_BINDLESS",
ShaderPropertyType.TextureCubeArrayBindless => "TEXTURECUBE_ARRAY_BINDLESS", ShaderPropertyType.TextureCubeArray => "TEXTURECUBE_ARRAY_BINDLESS",
_ => throw new ArgumentOutOfRangeException(nameof(type), $"Unsupported shader property type: {type}") _ => throw new ArgumentOutOfRangeException(nameof(type), $"Unsupported shader property type: {type}")
}; };
} }
public static Result<string> GeneratePass(IPassDescriptor descriptor, string targetDirectory) public static Result<string> GenerateShaderCode(ShaderDescriptor descriptor, string targetDirectory)
{ {
if (descriptor is not FullPassDescriptor fullPass)
{
return Result.Failure("Only full pass descriptors are supported for compilation.");
}
if (!Directory.Exists(targetDirectory)) if (!Directory.Exists(targetDirectory))
{ {
return Result.Failure("Target directory does not exist."); return Result.Failure("Target directory does not exist.");
} }
var outputFileName = fullPass.uniqueIdentifier.Replace(' ', '_'); var outputFileName = descriptor.name.Replace('/', '_');
var outputFilePath = Path.Combine(targetDirectory, outputFileName + ".g.hlsl"); var outputFilePath = Path.Combine(targetDirectory, outputFileName + ".g.hlsl");
var outputDirectory = Path.GetDirectoryName(outputFilePath); var outputDirectory = Path.GetDirectoryName(outputFilePath);
@@ -346,30 +330,17 @@ internal static class SDLCompiler
#define {fileDefine} #define {fileDefine}
#include ""F:/csharp/GhostEngine/Ghost.Shader/BuiltIn/Common.hlsl"""); #include ""F:/csharp/GhostEngine/Ghost.Shader/BuiltIn/Common.hlsl""");
if (fullPass.includes != null)
{
foreach (var include in fullPass.includes)
{
sb.Append($@"
#include ""{include}""");
}
sb.AppendLine(); sb.Append(@"
}
if (fullPass.properties != null)
{
sb.Append(@"
struct PerMaterialData struct PerMaterialData
{"); {");
foreach (var prop in fullPass.properties) foreach (var prop in descriptor.properties)
{ {
sb.Append($@" sb.Append($@"
{ShaderPropertyTypeToHLSLType(prop.type)} {prop.name};"); {ShaderPropertyTypeToHLSLType(prop.type)} {prop.name};");
}
sb.Append(@"
};");
} }
sb.Append(@"
};");
sb.AppendLine(); sb.AppendLine();
sb.AppendLine(@$" sb.AppendLine(@$"
@@ -414,4 +385,4 @@ struct GlobalData
return globalFilePath; return globalFilePath;
} }
} }

View File

@@ -32,9 +32,7 @@ internal class PassSemantic
public ShaderEntryPoint meshShader; public ShaderEntryPoint meshShader;
public ShaderEntryPoint pixelShader; public ShaderEntryPoint pixelShader;
public List<string>? defines; public List<string>? defines;
public List<string>? includes;
public List<KeywordsGroup>? keywords; public List<KeywordsGroup>? keywords;
public List<PropertySemantic>? localProperties;
public PipelineSemantic? localPipeline; public PipelineSemantic? localPipeline;
} }

View File

@@ -36,9 +36,7 @@ internal class PassSyntax
{ {
public Token name; public Token name;
public PipelineSyntax? localPipeline; public PipelineSyntax? localPipeline;
public PropertiesSyntax? localProperties;
public List<Token>? defines; public List<Token>? defines;
public List<Token>? includes;
public List<FunctionCallDeclaration>? keywords; public List<FunctionCallDeclaration>? keywords;
public List<FunctionCallDeclaration>? functionCalls; public List<FunctionCallDeclaration>? functionCalls;
} }

View File

@@ -153,6 +153,7 @@ internal static class TokenLexicon
public const string FLOAT2 = "float2"; public const string FLOAT2 = "float2";
public const string FLOAT3 = "float3"; public const string FLOAT3 = "float3";
public const string FLOAT4 = "float4"; public const string FLOAT4 = "float4";
public const string FLOAT4X4 = "float4x4";
public const string INT = "int"; public const string INT = "int";
public const string INT2 = "int2"; public const string INT2 = "int2";
@@ -170,11 +171,11 @@ internal static class TokenLexicon
public const string BOOL4 = "bool4"; public const string BOOL4 = "bool4";
// Texture types // Texture types
public const string TEXTURE2D_BINDLESS = "tex2d_b"; public const string TEXTURE2D = "tex2d";
public const string TEXTURE2D_ARRAY_BINDLESS = "tex2d_arr_b"; public const string TEXTURE2D_ARRAY = "tex2d_arr";
public const string TEXTURE3D_BINDLESS = "tex3d_b"; public const string TEXTURE3D = "tex3d";
public const string TEXTURECUBE_BINDLESS = "texcube_b"; public const string TEXTURECUBE = "texcube";
public const string TEXTURECUBE_ARRAY_BINDLESS = "texcube_arr_b"; public const string TEXTURECUBE_ARRAY = "texcube_arr";
} }
public static class KnownTextureValue public static class KnownTextureValue
@@ -211,12 +212,12 @@ internal static class TokenLexicon
private static readonly HashSet<string> s_types = new() private static readonly HashSet<string> s_types = new()
{ {
KnownTypes.FLOAT, KnownTypes.FLOAT2, KnownTypes.FLOAT3, KnownTypes.FLOAT4, KnownTypes.FLOAT, KnownTypes.FLOAT2, KnownTypes.FLOAT3, KnownTypes.FLOAT4, KnownTypes.FLOAT4X4,
KnownTypes.INT, KnownTypes.INT2, KnownTypes.INT3, KnownTypes.INT4, KnownTypes.INT, KnownTypes.INT2, KnownTypes.INT3, KnownTypes.INT4,
KnownTypes.UINT, KnownTypes.UINT2, KnownTypes.UINT3, KnownTypes.UINT4, KnownTypes.UINT, KnownTypes.UINT2, KnownTypes.UINT3, KnownTypes.UINT4,
KnownTypes.BOOL, KnownTypes.BOOL2, KnownTypes.BOOL3, KnownTypes.BOOL4, KnownTypes.BOOL, KnownTypes.BOOL2, KnownTypes.BOOL3, KnownTypes.BOOL4,
KnownTypes.TEXTURE2D_BINDLESS, KnownTypes.TEXTURE2D_ARRAY_BINDLESS, KnownTypes.TEXTURE3D_BINDLESS, KnownTypes.TEXTURE2D, KnownTypes.TEXTURE2D_ARRAY, KnownTypes.TEXTURE3D,
KnownTypes.TEXTURECUBE_BINDLESS, KnownTypes.TEXTURECUBE_ARRAY_BINDLESS, KnownTypes.TEXTURECUBE, KnownTypes.TEXTURECUBE_ARRAY,
}; };
private static readonly HashSet<string> s_textureDefaultValues = new() private static readonly HashSet<string> s_textureDefaultValues = new()