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:
@@ -10,17 +10,18 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<IsAotCompatible>True</IsAotCompatible>
|
||||
<DefineConstants>$(DefineConstants);PLATEFORME_WIN64</DefineConstants>
|
||||
<IsTrimmable>True</IsTrimmable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<IsAotCompatible>True</IsAotCompatible>
|
||||
<DefineConstants>$(DefineConstants);PLATEFORME_WIN64</DefineConstants>
|
||||
<IsTrimmable>True</IsTrimmable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<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.1" />
|
||||
<PackageReference Include="Misaki.HighPerformance.LowLevel" Version="1.2.5" />
|
||||
<PackageReference Include="Misaki.HighPerformance.Mathematics" Version="1.2.6" />
|
||||
<PackageReference Include="System.IO.Hashing" Version="10.0.0" />
|
||||
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.26100.5" />
|
||||
|
||||
@@ -10,11 +10,12 @@ public enum ShaderPropertyType
|
||||
{
|
||||
None,
|
||||
Float, Float2, Float3, Float4,
|
||||
Float4x4,
|
||||
Int, Int2, Int3, Int4,
|
||||
UInt, UInt2, UInt3, UInt4,
|
||||
Bool, Bool2, Bool3, Bool4,
|
||||
Texture2DBindless, Texture3DBindless, TextureCubeBindless,
|
||||
Texture2DArrayBindless, TextureCubeArrayBindless,
|
||||
Texture2D, Texture3D, TextureCube,
|
||||
Texture2DArray, TextureCubeArray,
|
||||
}
|
||||
|
||||
public struct ShaderEntryPoint
|
||||
@@ -77,11 +78,8 @@ public class FullPassDescriptor : IPassDescriptor
|
||||
public ShaderEntryPoint taskShader;
|
||||
public ShaderEntryPoint meshShader;
|
||||
public ShaderEntryPoint pixelShader;
|
||||
public string? generatedCodePath;
|
||||
public List<string>? defines;
|
||||
public List<string>? includes;
|
||||
public List<KeywordsGroup>? keywords;
|
||||
public List<PropertyDescriptor>? properties;
|
||||
public PipelineDescriptor localPipeline;
|
||||
|
||||
public string Identifier => uniqueIdentifier;
|
||||
@@ -100,6 +98,42 @@ public class FallbackPassDescriptor : IPassDescriptor
|
||||
public class ShaderDescriptor
|
||||
{
|
||||
public string name = string.Empty;
|
||||
public string? generatedCodePath;
|
||||
public uint cbufferSize;
|
||||
public List<PropertyDescriptor> globalProperties = new();
|
||||
public List<PropertyDescriptor> properties = 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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Ghost.Core;
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Ghost.Core;
|
||||
|
||||
@@ -44,6 +43,11 @@ public readonly struct Result
|
||||
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 static implicit operator bool(Result result) => result.IsSuccess;
|
||||
@@ -68,16 +72,6 @@ public readonly struct Result<T>
|
||||
_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)
|
||||
{
|
||||
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>(Result result) => result.IsSuccess ? Success(default!) : Failure(result.Message);
|
||||
|
||||
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
|
||||
@@ -126,11 +118,6 @@ public readonly struct Result<T, S>
|
||||
_status = status;
|
||||
}
|
||||
|
||||
public ref readonly T GetValueRef()
|
||||
{
|
||||
return ref Unsafe.AsRef(in _value);
|
||||
}
|
||||
|
||||
public static Result<T, S> Create(T value, S 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 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 void ThrowIfFailed(this Result result)
|
||||
|
||||
@@ -32,14 +32,14 @@ internal static unsafe partial class Win32Utility
|
||||
public static Guid* IID_NULL
|
||||
{
|
||||
[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)]
|
||||
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
|
||||
{
|
||||
return new IID_PPV(Windows.__uuidof<T>(), comPtr.PPV());
|
||||
return new IID_PPV(Windows.__uuidof<T>(), (void**)comPtr);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -80,27 +80,6 @@ internal static unsafe partial class Win32Utility
|
||||
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)]
|
||||
public static void** ReleaseAndGetVoidAddressOf<T>(ref this ComPtr<T> comPtr)
|
||||
where T : unmanaged, IUnknown.Interface
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
|
||||
<ResourceDictionary Source="ms-appx:///Microsoft.UI.Xaml/DensityStyles/Compact.xaml" />
|
||||
<core:ControlsDictionary />
|
||||
<ResourceDictionary Source="/Themes/Override.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
@@ -55,7 +55,6 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ghost.Editor.Core\Ghost.Editor.Core.csproj" />
|
||||
<ProjectReference Include="..\Ghost.Engine\Ghost.Engine.csproj" />
|
||||
<ProjectReference Include="..\Ghost.Entities\Ghost.Entities.csproj" />
|
||||
<ProjectReference Include="..\Ghost.Test.Core\Ghost.Test.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -84,9 +83,14 @@
|
||||
<PropertyGroup>
|
||||
<PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun>
|
||||
<PublishReadyToRun Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRun>
|
||||
<PublishTrimmed>False</PublishTrimmed>
|
||||
<SupportedOSPlatformVersion>10.0.20348.0</SupportedOSPlatformVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
</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>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"profiles": {
|
||||
"Ghost.Graphics.Test (Package)": {
|
||||
"commandName": "MsixPackage",
|
||||
"nativeDebugging": true
|
||||
"nativeDebugging": false
|
||||
},
|
||||
"Ghost.Graphics.Test (Unpackaged)": {
|
||||
"commandName": "Project"
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
using Ghost.Graphics;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using TerraFX.Interop.WinRT;
|
||||
using WinRT;
|
||||
|
||||
namespace Ghost.Graphics.Test.Windows;
|
||||
|
||||
public sealed partial class GraphicsTestWindow : Window
|
||||
{
|
||||
private bool _isFirstActivationHandled = false;
|
||||
|
||||
private IRenderSystem? _renderSystem;
|
||||
private IRenderer? _renderer;
|
||||
private ISwapChain? _swapChain;
|
||||
@@ -18,33 +17,46 @@ public sealed partial class GraphicsTestWindow : Window
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
Panel.Loaded += SwapChainPanel_Loaded;
|
||||
Panel.Unloaded += SwapChainPanel_Unloaded;
|
||||
Activated += GraphicsTestWindow_Activated;
|
||||
Closed += GraphicsTestWindow_Closed;
|
||||
|
||||
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
|
||||
AllocationManager.EnableDebugLayer();
|
||||
#endif
|
||||
|
||||
_renderSystem = new RenderSystem(new()
|
||||
_renderSystem = new RenderSystem(new RenderingConfig()
|
||||
{
|
||||
FrameBufferCount = 2,
|
||||
GraphicsAPI = GraphicsAPI.Direct3D12
|
||||
});
|
||||
_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);
|
||||
|
||||
_renderSystem.Start();
|
||||
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;
|
||||
_renderSystem?.Stop();
|
||||
@@ -52,6 +64,10 @@ public sealed partial class GraphicsTestWindow : Window
|
||||
_renderer?.Dispose();
|
||||
_swapChain?.Dispose();
|
||||
_renderSystem?.Dispose();
|
||||
|
||||
#if DEBUG
|
||||
AllocationManager.Dispose();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void SwapChainPanel_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
@@ -69,7 +85,7 @@ public sealed partial class GraphicsTestWindow : Window
|
||||
return;
|
||||
}
|
||||
|
||||
if (_renderSystem.CPUFenceValue < _renderSystem.GPUFenceValue + _renderSystem.Config.FrameBufferCount)
|
||||
if (_renderSystem.CPUFenceValue < _renderSystem.GPUFenceValue + _renderSystem.MaxFrameLatency)
|
||||
{
|
||||
DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.High, () =>
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@ using Misaki.HighPerformance.LowLevel.Collections;
|
||||
|
||||
namespace Ghost.Graphics.Contracts;
|
||||
|
||||
public struct CompileResult : IDisposable
|
||||
public struct ShaderCompileResult : IDisposable
|
||||
{
|
||||
public UnsafeArray<byte> bytecode;
|
||||
public ShaderReflectionData reflectionData;
|
||||
@@ -21,9 +21,9 @@ public struct CompileResult : IDisposable
|
||||
|
||||
public struct GraphicsCompiledResult : IDisposable
|
||||
{
|
||||
public CompileResult tsResult;
|
||||
public CompileResult msResult;
|
||||
public CompileResult psResult;
|
||||
public ShaderCompileResult tsResult;
|
||||
public ShaderCompileResult msResult;
|
||||
public ShaderCompileResult psResult;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
@@ -143,7 +143,7 @@ public readonly struct ShaderReflectionData
|
||||
|
||||
public interface IShaderCompiler : IDisposable
|
||||
{
|
||||
Result<CompileResult> Compile(ref readonly CompilerConfig config, Allocator allocator);
|
||||
Result<GraphicsCompiledResult> CompilePass(IPassDescriptor descriptor);
|
||||
Result<ShaderCompileResult> Compile(ref readonly CompilerConfig config, Allocator allocator);
|
||||
Result<GraphicsCompiledResult> CompilePass(IPassDescriptor descriptor, string? generatedCodePath);
|
||||
Result<GraphicsCompiledResult> LoadCompiledCache(ShaderPassKey key);
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@ using Ghost.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ghost.Graphics.Core;
|
||||
@@ -12,29 +10,38 @@ internal struct CBufferCache : IResourceReleasable
|
||||
{
|
||||
private UnsafeArray<byte> _cpuData;
|
||||
private Handle<GraphicsBuffer> _gpuResource;
|
||||
private uint _size;
|
||||
private uint _alignedSize;
|
||||
|
||||
public readonly UnsafeArray<byte> CpuData => _cpuData;
|
||||
public readonly Handle<GraphicsBuffer> GpuResource => _gpuResource;
|
||||
public readonly uint Size => _size;
|
||||
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)
|
||||
{
|
||||
_size = bufferSize;
|
||||
_alignedSize = (bufferSize + 255u) & ~255u;
|
||||
|
||||
_cpuData = new((int)AlignedSize, Allocator.Persistent);
|
||||
_cpuData = new UnsafeArray<byte>((int)AlignedSize, Allocator.Persistent);
|
||||
_gpuResource = buffer;
|
||||
}
|
||||
|
||||
public void ReleaseResource(IResourceDatabase database)
|
||||
{
|
||||
if (!IsCreated)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_cpuData.Dispose();
|
||||
|
||||
database.ReleaseResource(GpuResource.AsResource());
|
||||
_gpuResource = Handle<GraphicsBuffer>.Invalid;
|
||||
|
||||
_size = 0;
|
||||
_alignedSize = 0;
|
||||
}
|
||||
}
|
||||
@@ -42,183 +49,96 @@ internal struct CBufferCache : IResourceReleasable
|
||||
public struct Material : IResourceReleasable, IHandleType
|
||||
{
|
||||
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;
|
||||
|
||||
internal ref CBufferCache GetPassCache(int passIndex)
|
||||
{
|
||||
return ref _materialPropertiesCache[passIndex];
|
||||
}
|
||||
|
||||
public void SetShader(Identifier<Shader> shaderId, IResourceAllocator allocator, IResourceDatabase database)
|
||||
public Result SetShader(Identifier<Shader> shaderId, IResourceAllocator allocator, IResourceDatabase database)
|
||||
{
|
||||
if (!shaderId.IsValid)
|
||||
{
|
||||
throw new ArgumentException("Shader ID is invalid.");
|
||||
return Result.Failure("Shader ID is invalid.");
|
||||
}
|
||||
|
||||
_cBufferCache.ReleaseResource(database);
|
||||
_shader = shaderId;
|
||||
|
||||
var shader = database.GetShaderReference(shaderId);
|
||||
_materialPropertiesCache = new UnsafeArray<CBufferCache>(shader.PassCount, Allocator.Persistent);
|
||||
for (var i = 0; i < shader.PassCount; i++)
|
||||
if (shader.CBufferSize != 0)
|
||||
{
|
||||
var pass = database.GetShaderPass(shader.GetPassKey(i));
|
||||
var cbufferInfo = pass.CBuffer;
|
||||
|
||||
if (cbufferInfo.SizeInBytes == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var desc = new BufferDesc
|
||||
{
|
||||
Size = cbufferInfo.SizeInBytes,
|
||||
Size = shader.CBufferSize,
|
||||
Usage = BufferUsage.Constant,
|
||||
MemoryType = ResourceMemoryType.Default,
|
||||
};
|
||||
|
||||
var buffer = allocator.CreateBuffer(ref desc);
|
||||
_materialPropertiesCache[i] = new CBufferCache(buffer, cbufferInfo.SizeInBytes);
|
||||
}
|
||||
}
|
||||
|
||||
void IResourceReleasable.ReleaseResource(IResourceDatabase database)
|
||||
{
|
||||
foreach (var cache in _materialPropertiesCache)
|
||||
{
|
||||
cache.ReleaseResource(database);
|
||||
_cBufferCache = new CBufferCache(buffer, shader.CBufferSize);
|
||||
}
|
||||
|
||||
_materialPropertiesCache.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
foreach (var index in _shader.GetPropertyPassIndices(propertyName))
|
||||
if (sizeof(T) != _cBufferCache.Size)
|
||||
{
|
||||
var passKey = _shader.GetPassKey(index);
|
||||
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.");
|
||||
return Result.Create(default(T), ResultStatus.InvalidArgument);
|
||||
}
|
||||
|
||||
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)]
|
||||
public readonly void SetBufferBindless(string propertyName, Handle<GraphicsBuffer> buffer)
|
||||
public readonly Span<byte> GetRawPropertyCache()
|
||||
{
|
||||
var bindlessIndex = _resourceDatabase.GetBindlessIndex(buffer.AsResource());
|
||||
if (bindlessIndex == -1)
|
||||
if (_cBufferCache.Size == 0)
|
||||
{
|
||||
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)]
|
||||
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);
|
||||
cmb.UploadBuffer<byte>(cache.GpuResource, cache.CpuData.AsSpan());
|
||||
return new Result<ResultStatus>(false, ResultStatus.InvalidArgument);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@ namespace Ghost.Graphics.Core;
|
||||
|
||||
public struct Mesh : IResourceReleasable, IHandleType
|
||||
{
|
||||
private UnsafeList<Vertex> _vertices;
|
||||
private UnsafeList<uint> _indices;
|
||||
|
||||
internal bool IsMeshDataDirty
|
||||
{
|
||||
get; private set;
|
||||
@@ -21,10 +24,10 @@ public struct Mesh : IResourceReleasable, IHandleType
|
||||
/// </summary>
|
||||
public UnsafeList<Vertex> Vertices
|
||||
{
|
||||
readonly get => field;
|
||||
readonly get => _vertices;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
_vertices = value;
|
||||
VertexCount = value.Count;
|
||||
IsMeshDataDirty = true;
|
||||
}
|
||||
@@ -35,10 +38,10 @@ public struct Mesh : IResourceReleasable, IHandleType
|
||||
/// </summary>
|
||||
public UnsafeList<uint> Indices
|
||||
{
|
||||
readonly get => field;
|
||||
readonly get => _indices;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
_indices = value;
|
||||
IndexCount = value.Count;
|
||||
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)
|
||||
{
|
||||
Vertices = new(vertices.Length, Allocator.Persistent);
|
||||
Indices = new(indices.Length, Allocator.Persistent);
|
||||
Vertices = new UnsafeList<Vertex>(vertices.Length, Allocator.Persistent);
|
||||
Indices = new UnsafeList<uint>(indices.Length, Allocator.Persistent);
|
||||
Vertices.CopyFrom(vertices);
|
||||
Indices.CopyFrom(indices);
|
||||
VertexBuffer = vertexBuffer;
|
||||
@@ -112,8 +115,8 @@ public struct Mesh : IResourceReleasable, IHandleType
|
||||
|
||||
public readonly void ReleaseCpuResources()
|
||||
{
|
||||
Vertices.Dispose();
|
||||
Indices.Dispose();
|
||||
_vertices.Dispose();
|
||||
_indices.Dispose();
|
||||
}
|
||||
|
||||
void IResourceReleasable.ReleaseResource(IResourceDatabase database)
|
||||
|
||||
@@ -56,13 +56,32 @@ public unsafe readonly ref struct RenderingContext
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
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 indexList = new UnsafeList<uint>(indices.Length, Allocator.Persistent);
|
||||
@@ -70,12 +89,7 @@ public unsafe readonly ref struct RenderingContext
|
||||
vertexList.CopyFrom(vertices);
|
||||
indexList.CopyFrom(indices);
|
||||
|
||||
return CreateMesh(vertexList, indexList);
|
||||
}
|
||||
|
||||
public MaterialAccessor GetMaterialAccessor(Handle<Material> material)
|
||||
{
|
||||
return new MaterialAccessor(material, ResourceDatabase);
|
||||
return CreateMesh(vertexList, indexList, staticMesh);
|
||||
}
|
||||
|
||||
// TODO: Make one memory pool for upload.
|
||||
@@ -108,12 +122,12 @@ public unsafe readonly ref struct RenderingContext
|
||||
|
||||
if (needVertexTransition)
|
||||
{
|
||||
_directCmd.ResourceBarrier(meshData.VertexBuffer.AsResource(), ResourceState.CopyDest, ResourceState.VertexAndConstantBuffer);
|
||||
_directCmd.ResourceBarrier(meshData.VertexBuffer.AsResource(), ResourceState.CopyDest, vertexState);
|
||||
}
|
||||
|
||||
if (needIndexTransition)
|
||||
{
|
||||
_directCmd.ResourceBarrier(meshData.IndexBuffer.AsResource(), ResourceState.CopyDest, ResourceState.IndexBuffer);
|
||||
_directCmd.ResourceBarrier(meshData.IndexBuffer.AsResource(), ResourceState.CopyDest, indexState);
|
||||
}
|
||||
|
||||
if (markMeshStatic)
|
||||
@@ -177,7 +191,7 @@ public unsafe readonly ref struct RenderingContext
|
||||
slicePitch = slicePitch
|
||||
};
|
||||
|
||||
_directCmd.UploadTexture(texture, subresourceData);
|
||||
_directCmd.UploadTexture(texture, [subresourceData]);
|
||||
}
|
||||
|
||||
if (needTransition)
|
||||
@@ -194,10 +208,15 @@ public unsafe readonly ref struct RenderingContext
|
||||
ref var materialRef = ref ResourceDatabase.GetMaterialReference(material);
|
||||
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
|
||||
{
|
||||
Id = passKey,
|
||||
Id = keyResult.Value.Identifier,
|
||||
RtvCount = 1,
|
||||
DsvFormat = TextureFormat.Unknown,
|
||||
};
|
||||
@@ -209,20 +228,13 @@ public unsafe readonly ref struct RenderingContext
|
||||
_directCmd.SetConstantBufferView(RootSignatureLayout.PER_OBJECT_BUFFER_SLOT, meshRef.ObjectDataBuffer);
|
||||
|
||||
// NOTE: We use fixed root signature layout for bindless rendering.
|
||||
ref var cache = ref materialRef.GetPassCache(passIndex);
|
||||
var cache = materialRef.CBufferCache;
|
||||
if (cache.IsCreated)
|
||||
{
|
||||
_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.
|
||||
// TODO: Maybe handle the traditional bindless model?
|
||||
#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);
|
||||
var threadGroupCountX = ((uint)meshRef.IndexCount + numThreadsX - 1) / numThreadsX;
|
||||
_directCmd.DispatchMesh(threadGroupCountX, 1, 1);
|
||||
}
|
||||
}
|
||||
@@ -3,45 +3,44 @@ using Ghost.Core.Graphics;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ghost.Graphics.Core;
|
||||
|
||||
public class ShaderPass : IResourceReleasable
|
||||
public struct ShaderPass : IResourceReleasable
|
||||
{
|
||||
private CBufferInfo _cbufferInfo;
|
||||
// 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)
|
||||
public ShaderPassKey Identifier
|
||||
{
|
||||
_cbufferInfo = info;
|
||||
|
||||
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;
|
||||
}
|
||||
get; init;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
}
|
||||
@@ -52,46 +51,43 @@ public class ShaderPass : IResourceReleasable
|
||||
/// </summary>
|
||||
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
|
||||
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)
|
||||
{
|
||||
_passIDs = new UnsafeArray<ShaderPassKey>(descriptor.passes.Count, Allocator.Persistent);
|
||||
_passLookup = new(descriptor.passes.Count);
|
||||
_propertyLookup = new(descriptor.passes.Count);
|
||||
_cbufferSize = descriptor.cbufferSize;
|
||||
_passes = new UnsafeArray<ShaderPass>(descriptor.passes.Count, Allocator.Persistent);
|
||||
_passLookup = new Dictionary<string, int>(descriptor.passes.Count);
|
||||
|
||||
for (var i = 0; i < descriptor.passes.Count; i++)
|
||||
{
|
||||
var pass = descriptor.passes[i];
|
||||
|
||||
// TODO: Handle inherited passes
|
||||
if (pass is not FullPassDescriptor fullPass)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var passKey = new ShaderPassKey(pass.Identifier);
|
||||
|
||||
_passIDs[i] = passKey;
|
||||
_passLookup[pass.Name] = i;
|
||||
|
||||
if (pass is FullPassDescriptor fullPass)
|
||||
_passes[i] = new ShaderPass
|
||||
{
|
||||
if (fullPass.properties == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Identifier = passKey,
|
||||
ZTest = fullPass.localPipeline.zTest,
|
||||
ZWrite = fullPass.localPipeline.zWrite,
|
||||
Cull = fullPass.localPipeline.cull,
|
||||
Blend = fullPass.localPipeline.blend,
|
||||
ColorMask = fullPass.localPipeline.colorMask
|
||||
};
|
||||
|
||||
foreach (var property in fullPass.properties)
|
||||
{
|
||||
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
|
||||
_passLookup[pass.Name] = i;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,38 +96,26 @@ public class Shader : IResourceReleasable, IIdentifierType
|
||||
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);
|
||||
if (index == -1)
|
||||
{
|
||||
passIndex = -1;
|
||||
passID = new(0);
|
||||
return false;
|
||||
return Result.CreateRef(ref Unsafe.NullRef<ShaderPass>(), ResultStatus.NotFound);
|
||||
}
|
||||
|
||||
passIndex = index;
|
||||
passID = _passIDs[index];
|
||||
return true;
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<int> GetPropertyPassIndices(string propertyName)
|
||||
{
|
||||
if (_propertyLookup.TryGetValue(propertyName, out var passIndices))
|
||||
{
|
||||
return passIndices;
|
||||
}
|
||||
|
||||
return Array.Empty<int>();
|
||||
return Result.CreateRef(ref _passes[index], ResultStatus.Success);
|
||||
}
|
||||
|
||||
void IResourceReleasable.ReleaseResource(IResourceDatabase database)
|
||||
{
|
||||
_passIDs.Dispose();
|
||||
_passes.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Ghost.Graphics.Core;
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
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 int COUNT = 5;
|
||||
|
||||
@@ -173,17 +173,65 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
_commandList.Get()->RSSetScissorRects(1, &d3d12Rect);
|
||||
}
|
||||
|
||||
public void ResourceBarrier(Handle<GPUResource> resource, ResourceState before, ResourceState after)
|
||||
public void ResourceBarrier(ReadOnlySpan<BarrierDesc> barrierDescs)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
IncrementCommandCount();
|
||||
|
||||
var d3d12Resource = _resourceDatabase.GetResource(resource);
|
||||
var barrier = D3D12_RESOURCE_BARRIER.InitTransition(d3d12Resource,
|
||||
before.ToD3D12States(), after.ToD3D12States());
|
||||
var count = 0u;
|
||||
var pBarriers = stackalloc D3D12_RESOURCE_BARRIER[barrierDescs.Length];
|
||||
|
||||
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);
|
||||
resourceRecord.state = stateAfter;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public void UploadTexture(Handle<Texture> texture, params ReadOnlySpan<SubResourceData> subresources)
|
||||
public void UploadTexture(Handle<Texture> texture, ReadOnlySpan<SubResourceData> subresources)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
|
||||
@@ -19,7 +19,7 @@ internal unsafe class D3D12DescriptorAllocator : IDisposable
|
||||
|
||||
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);
|
||||
_dsvHeap = new D3D12DescriptorHeap("dsv", device, D3D12_DESCRIPTOR_HEAP_TYPE_DSV, initialDsvCount, initialDsvCount / 2);
|
||||
|
||||
@@ -6,7 +6,7 @@ using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
|
||||
internal class D3D12GraphicsEngine : IGraphicsEngine
|
||||
{
|
||||
#if DEBUG
|
||||
private readonly D3D12DebugLayer _debugLayer;
|
||||
@@ -34,17 +34,17 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
|
||||
public D3D12GraphicsEngine(IRenderSystem renderSystem)
|
||||
{
|
||||
#if DEBUG
|
||||
_debugLayer = new();
|
||||
_debugLayer = new D3D12DebugLayer();
|
||||
#endif
|
||||
_device = new();
|
||||
_shaderCompiler = new();
|
||||
_descriptorAllocator = new(_device);
|
||||
_device = new D3D12RenderDevice();
|
||||
_shaderCompiler = new DxcShaderCompiler();
|
||||
_descriptorAllocator = new D3D12DescriptorAllocator(_device);
|
||||
|
||||
_resourceDatabase = new(_descriptorAllocator);
|
||||
_pipelineLibrary = new(_device, _resourceDatabase);
|
||||
_resourceAllocator = new(renderSystem, _device, _descriptorAllocator, _resourceDatabase, _pipelineLibrary);
|
||||
_resourceDatabase = new D3D12ResourceDatabase(_descriptorAllocator);
|
||||
_pipelineLibrary = new D3D12PipelineLibrary(_device, _resourceDatabase);
|
||||
_resourceAllocator = new D3D12ResourceAllocator(renderSystem, _device, _descriptorAllocator, _resourceDatabase, _pipelineLibrary);
|
||||
|
||||
_copyCommandBuffer = new(
|
||||
_copyCommandBuffer = new D3D12CommandBuffer(
|
||||
_device,
|
||||
_pipelineLibrary,
|
||||
_resourceDatabase,
|
||||
@@ -136,6 +136,9 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
|
||||
|
||||
_copyCommandBuffer.End();
|
||||
_resourceAllocator.ReleaseTempResources();
|
||||
_descriptorAllocator.ResetCbvSrvUavDynamicHeap();
|
||||
_descriptorAllocator.ResetDSVDynamicHeap();
|
||||
_descriptorAllocator.ResetRTVDynamicHeap();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
@@ -8,8 +8,6 @@ using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||
using Misaki.HighPerformance.Utilities;
|
||||
using System.Runtime.InteropServices;
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
@@ -40,7 +38,6 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
|
||||
private UniquePtr<ID3D12RootSignature> _defaultRootSignature;
|
||||
|
||||
private readonly Dictionary<GraphicsPipelineKey, D3D12PipelineState> _pipelineCache;
|
||||
private readonly Dictionary<ShaderPassKey, CBufferInfo> _cbufferInfoCache;
|
||||
|
||||
public ID3D12RootSignature* DefaultRootSignature => _defaultRootSignature.Get();
|
||||
|
||||
@@ -50,9 +47,8 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
|
||||
_resourceDatabase = resourceDatabase;
|
||||
|
||||
_pipelineCache = new Dictionary<GraphicsPipelineKey, D3D12PipelineState>();
|
||||
_cbufferInfoCache = new Dictionary<ShaderPassKey, CBufferInfo>();
|
||||
|
||||
CreateDefaultRootSignature();
|
||||
CreateDefaultRootSignature().ThrowIfFailed();
|
||||
}
|
||||
|
||||
private Result CreateDefaultRootSignature()
|
||||
@@ -140,36 +136,21 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
|
||||
Desc_1_1 = rootSignatureDesc
|
||||
};
|
||||
|
||||
ID3DBlob* pSignature = default;
|
||||
ID3DBlob* pError = default;
|
||||
using ComPtr<ID3DBlob> pSignature = default;
|
||||
using ComPtr<ID3DBlob> pError = default;
|
||||
|
||||
try
|
||||
var serializeResult = D3D12SerializeVersionedRootSignature(&versionedDesc, pSignature.GetAddressOf(), pError.GetAddressOf());
|
||||
if (serializeResult.FAILED)
|
||||
{
|
||||
var serializeResult = D3D12SerializeVersionedRootSignature(&versionedDesc, &pSignature, &pError);
|
||||
if (serializeResult.FAILED)
|
||||
{
|
||||
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);
|
||||
var errorMsg = pError.Get() != null ? Marshal.PtrToStringUTF8((nint)pError.Get()->GetBufferPointer()) : "Unknown error";
|
||||
return Result.Failure($"Failed to serialize default root signature: {errorMsg}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (pSignature != null)
|
||||
{
|
||||
pSignature->Release();
|
||||
}
|
||||
|
||||
if (pError != null)
|
||||
{
|
||||
pError->Release();
|
||||
}
|
||||
}
|
||||
ID3D12RootSignature* pRootSignature = default;
|
||||
ThrowIfFailed(_device.NativeDevice.Get()->CreateRootSignature(0, pSignature.Get()->GetBufferPointer(), pSignature.Get()->GetBufferSize(),
|
||||
__uuidof(pRootSignature), (void**)&pRootSignature));
|
||||
|
||||
_defaultRootSignature.Attach(pRootSignature);
|
||||
|
||||
return Result.Success();
|
||||
}
|
||||
@@ -337,8 +318,6 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
|
||||
return Result.Failure(result.Message);
|
||||
}
|
||||
|
||||
_cbufferInfoCache[descriptor.PassId] = result.Value;
|
||||
|
||||
var desc = new D3DX12_MESH_SHADER_PIPELINE_STATE_DESC
|
||||
{
|
||||
pRootSignature = _defaultRootSignature.Get(),
|
||||
@@ -422,16 +401,6 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
|
||||
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)
|
||||
{
|
||||
if (_pipelineCache.TryGetValue(key, out var cacheEntry))
|
||||
|
||||
@@ -31,7 +31,6 @@ internal class D3D12Renderer : IRenderer
|
||||
}
|
||||
|
||||
private readonly D3D12GraphicsEngine _graphicsEngine;
|
||||
private readonly D3D12CommandQueue _commandQueue;
|
||||
private readonly FrameResource[] _frameResources;
|
||||
private uint _frameIndex;
|
||||
|
||||
@@ -60,7 +59,6 @@ internal class D3D12Renderer : IRenderer
|
||||
public D3D12Renderer(D3D12GraphicsEngine graphicsEngine, D3D12ResourceAllocator resourceAllocator, D3D12ResourceDatabase resourceDatabase)
|
||||
{
|
||||
_graphicsEngine = graphicsEngine;
|
||||
_commandQueue = (D3D12CommandQueue)graphicsEngine.Device.GraphicsQueue;
|
||||
_resourceAllocator = resourceAllocator;
|
||||
_resourceDatabase = resourceDatabase;
|
||||
|
||||
@@ -153,7 +151,7 @@ internal class D3D12Renderer : IRenderer
|
||||
_swapChain?.Resize(newSize.x, newSize.y);
|
||||
_currentSize = newSize;
|
||||
|
||||
// Update off-screen render target size
|
||||
// Update off-screen render Target size
|
||||
if (_swapChain != null)
|
||||
{
|
||||
_resourceDatabase.ReleaseResource(_renderTarget.AsResource());
|
||||
@@ -170,7 +168,7 @@ internal class D3D12Renderer : IRenderer
|
||||
|
||||
if (frame.fenceValue > 0)
|
||||
{
|
||||
_commandQueue.WaitForValue(frame.fenceValue);
|
||||
_graphicsEngine.Device.GraphicsQueue.WaitForValue(frame.fenceValue);
|
||||
}
|
||||
|
||||
if (_renderTarget.IsValid)
|
||||
@@ -202,12 +200,12 @@ internal class D3D12Renderer : IRenderer
|
||||
|
||||
frame.commandBuffer.End();
|
||||
|
||||
_commandQueue.Submit(frame.commandBuffer);
|
||||
_graphicsEngine.Device.GraphicsQueue.Submit(frame.commandBuffer);
|
||||
_swapChain?.Present();
|
||||
|
||||
}
|
||||
|
||||
frame.fenceValue = _commandQueue.Signal(_frameIndex);
|
||||
frame.fenceValue = _graphicsEngine.Device.GraphicsQueue.Signal(_frameIndex);
|
||||
_frameIndex++;
|
||||
}
|
||||
|
||||
@@ -257,7 +255,7 @@ internal class D3D12Renderer : IRenderer
|
||||
// Handle swap chain back buffer transitions if needed
|
||||
if (_swapChain != null)
|
||||
{
|
||||
// Transition back buffer to render target
|
||||
// Transition back buffer to render Target
|
||||
cmd.ResourceBarrier(destination.AsResource(), ResourceState.Present, ResourceState.RenderTarget);
|
||||
}
|
||||
|
||||
@@ -266,7 +264,7 @@ internal class D3D12Renderer : IRenderer
|
||||
|
||||
// FIX: Implement proper blit operation with shader
|
||||
// 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
|
||||
|
||||
// Handle swap chain back buffer transitions if needed
|
||||
@@ -284,7 +282,7 @@ internal class D3D12Renderer : IRenderer
|
||||
{
|
||||
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.
|
||||
_pass.Cleanup(_resourceDatabase);
|
||||
|
||||
// If using a swap chain, release the off-screen render target.
|
||||
// Otherwise, the render target is managed externally.
|
||||
// If using a swap chain, release the off-screen render Target.
|
||||
// Otherwise, the render Target is managed externally.
|
||||
if (_swapChain != null)
|
||||
{
|
||||
_resourceDatabase.ReleaseResource(_renderTarget.AsResource());
|
||||
|
||||
@@ -5,7 +5,7 @@ using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
@@ -522,10 +522,11 @@ internal sealed unsafe partial class D3D12ResourceAllocator
|
||||
return D3D12_RESOURCE_STATE_COPY_DEST;
|
||||
}
|
||||
|
||||
// Default to Common, but check for specific roles
|
||||
var state = D3D12_RESOURCE_STATE_COMMON;
|
||||
#if true
|
||||
return state;
|
||||
|
||||
#else
|
||||
// D3D12 does not support state other than COMMON for buffers at creation.
|
||||
if (usage.HasFlag(BufferUsage.Vertex) || usage.HasFlag(BufferUsage.Constant))
|
||||
{
|
||||
// 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
|
||||
return D3D12_RESOURCE_STATE_COMMON;
|
||||
#endif
|
||||
}
|
||||
|
||||
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 D3D12PipelineLibrary _pipelineLibrary;
|
||||
|
||||
private UnsafeQueue<Handle<GPUResource>> _temResources;
|
||||
private UnsafeQueue<Handle<GPUResource>> _tempResources;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
@@ -633,7 +635,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
_resourceDatabase = resourceDatabase;
|
||||
_pipelineLibrary = pipelineLibrary;
|
||||
|
||||
_temResources = new(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
|
||||
_tempResources = new UnsafeQueue<Handle<GPUResource>>(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
|
||||
}
|
||||
|
||||
~D3D12ResourceAllocator()
|
||||
@@ -648,7 +650,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
|
||||
if (isTemp)
|
||||
{
|
||||
_temResources.Enqueue(handle);
|
||||
_tempResources.Enqueue(handle);
|
||||
}
|
||||
|
||||
return handle;
|
||||
@@ -714,13 +716,15 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
var initialState = DetermineInitialTextureState(desc.Usage);
|
||||
|
||||
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;
|
||||
if (desc.Usage.HasFlag(TextureUsage.ShaderResource))
|
||||
{
|
||||
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 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))
|
||||
{
|
||||
resourceDescriptor.uav = _descriptorAllocator.AllocateCbvSrvUav(isTemp);
|
||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.uav);
|
||||
var cpuHandle = _descriptorAllocator.GetCpuHandleShaderVisible(resourceDescriptor.uav);
|
||||
var uavDesc = CreateTextureUavDesc(pAllocation->GetResource());
|
||||
|
||||
_device.NativeDevice.Get()->CreateUnorderedAccessView(pAllocation->GetResource(), null, &uavDesc, cpuHandle);
|
||||
@@ -906,24 +910,6 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -931,14 +917,14 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
while (_temResources.Count > 0)
|
||||
while (_tempResources.Count > 0)
|
||||
{
|
||||
var handle = _temResources.Peek();
|
||||
ref var info = ref _resourceDatabase.GetResourceInfo(handle, out var exist);
|
||||
var handle = _tempResources.Peek();
|
||||
ref var info = ref _resourceDatabase.GetResourceRecord(handle, out var exist);
|
||||
if (!exist || !info.Allocated)
|
||||
{
|
||||
// Resource already released or invalid, just dequeue
|
||||
_temResources.Dequeue();
|
||||
_tempResources.Dequeue();
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -950,7 +936,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
}
|
||||
|
||||
_resourceDatabase.ReleaseResource(handle);
|
||||
_temResources.Dequeue();
|
||||
_tempResources.Dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -961,20 +947,15 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
return;
|
||||
}
|
||||
|
||||
#if DEBUG || GHOST_EDITOR
|
||||
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
|
||||
Debug.Assert(_tempResources.Count == 0, "Temporary resources should be released before disposing the allocator.");
|
||||
|
||||
foreach (var handle in _temResources)
|
||||
foreach (var handle in _tempResources)
|
||||
{
|
||||
_resourceDatabase.ReleaseResource(handle);
|
||||
}
|
||||
|
||||
_d3d12MA.Dispose();
|
||||
_temResources.Dispose();
|
||||
_tempResources.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.Collections;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using TerraFX.Interop.DirectX;
|
||||
|
||||
@@ -42,7 +42,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
||||
public readonly bool isExternal;
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -131,7 +131,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
||||
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);
|
||||
|
||||
@@ -139,16 +139,14 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
||||
var handle = new Handle<GPUResource>(id, generation);
|
||||
|
||||
#if DEBUG || GHOST_EDITOR
|
||||
if (name != null)
|
||||
{
|
||||
_resourceName[handle] = name;
|
||||
}
|
||||
pResource->SetName(name);
|
||||
_resourceName[handle] = name;
|
||||
#endif
|
||||
|
||||
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);
|
||||
|
||||
@@ -156,10 +154,8 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
||||
var handle = new Handle<GPUResource>(id, generation);
|
||||
|
||||
#if DEBUG || GHOST_EDITOR
|
||||
if (name != null)
|
||||
{
|
||||
_resourceName[handle] = name;
|
||||
}
|
||||
allocation->SetName(name);
|
||||
_resourceName[handle] = name;
|
||||
#endif
|
||||
|
||||
return handle;
|
||||
@@ -184,7 +180,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
||||
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);
|
||||
return ref _resources.GetElementReferenceAt(handle.id, handle.generation, out exist);
|
||||
@@ -232,7 +228,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
||||
{
|
||||
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)
|
||||
{
|
||||
return -1;
|
||||
@@ -254,7 +250,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
||||
return null;
|
||||
}
|
||||
|
||||
public unsafe void ReleaseResource(Handle<GPUResource> handle)
|
||||
public void ReleaseResource(Handle<GPUResource> handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
@@ -269,14 +265,10 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
||||
return;
|
||||
}
|
||||
|
||||
var refCount = info.Release(_descriptorAllocator);
|
||||
info.Release(_descriptorAllocator);
|
||||
//Debug.Assert(info.Release(_descriptorAllocator) == 0);
|
||||
#if DEBUG || GHOST_EDITOR
|
||||
_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
|
||||
|
||||
_resources.Remove(handle.id, handle.generation);
|
||||
@@ -405,34 +397,6 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
||||
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()
|
||||
{
|
||||
static void ThrowMemoryLeakException(string resourceType, int count)
|
||||
|
||||
@@ -7,6 +7,7 @@ using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
@@ -27,7 +28,7 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
||||
private UniquePtr<IDXGISwapChain4> _swapChain;
|
||||
private UnsafeArray<Handle<Texture>> _backBuffers;
|
||||
|
||||
private object? _compositionSurface;
|
||||
private readonly object? _compositionSurface;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
@@ -48,20 +49,22 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
||||
|
||||
public D3D12SwapChain(D3D12ResourceDatabase resourceDatabase, D3D12DescriptorAllocator descriptorAllocator, D3D12RenderDevice device, SwapChainDesc desc)
|
||||
{
|
||||
Debug.Assert(desc.BufferCount >= 2);
|
||||
|
||||
_resourceDatabase = resourceDatabase;
|
||||
_descriptorAllocator = descriptorAllocator;
|
||||
_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;
|
||||
Height = desc.height;
|
||||
BufferCount = D3D12PipelineResource.BACK_BUFFER_COUNT;
|
||||
Width = desc.Width;
|
||||
Height = desc.Height;
|
||||
BufferCount = desc.BufferCount;
|
||||
|
||||
CreateSwapChain(desc);
|
||||
CreateBackBuffers();
|
||||
|
||||
_compositionSurface = desc.target.compositionSurface;
|
||||
_compositionSurface = desc.Target.CompositionSurface;
|
||||
}
|
||||
|
||||
~D3D12SwapChain()
|
||||
@@ -73,9 +76,9 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
||||
{
|
||||
var swapChainDesc = new DXGI_SWAP_CHAIN_DESC1
|
||||
{
|
||||
Width = desc.width,
|
||||
Height = desc.height,
|
||||
Format = desc.format.ToDXGIFormat(),
|
||||
Width = desc.Width,
|
||||
Height = desc.Height,
|
||||
Format = desc.Format.ToDXGIFormat(),
|
||||
SampleDesc = new DXGI_SAMPLE_DESC(1, 0),
|
||||
BufferUsage = DXGI_USAGE_BACK_BUFFER | DXGI_USAGE_RENDER_TARGET_OUTPUT,
|
||||
BufferCount = D3D12PipelineResource.BACK_BUFFER_COUNT,
|
||||
@@ -91,15 +94,15 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
||||
var pFactory = _renderDevice.DXGIFactory.Get();
|
||||
var pCommandQueue = _renderDevice.NativeGraphicsQueue.Get();
|
||||
|
||||
switch (desc.target.type)
|
||||
switch (desc.Target.Type)
|
||||
{
|
||||
case SwapChainTargetType.Composition:
|
||||
ThrowIfFailed(pFactory->CreateSwapChainForComposition((IUnknown*)pCommandQueue, &swapChainDesc, null, &pTempSwapChain));
|
||||
|
||||
// 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);
|
||||
}
|
||||
break;
|
||||
@@ -112,7 +115,7 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
||||
|
||||
pFactory->CreateSwapChainForHwnd(
|
||||
(IUnknown*)pCommandQueue,
|
||||
new HWND(desc.target.windowHandle.ToPointer()),
|
||||
new HWND(desc.Target.WindowHandle.ToPointer()),
|
||||
&swapChainDesc,
|
||||
&swapChainFullscreenDesc,
|
||||
null,
|
||||
@@ -141,7 +144,12 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
||||
var rtv = _descriptorAllocator.AllocateRTV();
|
||||
_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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,12 +164,17 @@ internal unsafe struct D3D12DescriptorHeap : IDisposable
|
||||
|
||||
public void ReleaseDescriptors(int baseIndex, int count = 1)
|
||||
{
|
||||
if (baseIndex == _INVALID_DESCRIPTOR_INDEX)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (baseIndex < _dynamicHeapStart)
|
||||
if (baseIndex >= _dynamicHeapStart)
|
||||
{
|
||||
// Dynamic allocations are not released individually.
|
||||
return;
|
||||
|
||||
@@ -10,6 +10,19 @@ internal unsafe static class D3D12Utility
|
||||
{
|
||||
public static void SetName<T>(ref this T obj, ReadOnlySpan<char> name)
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -10,10 +10,12 @@
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<IsAotCompatible>True</IsAotCompatible>
|
||||
<IsTrimmable>True</IsTrimmable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<IsAotCompatible>True</IsAotCompatible>
|
||||
<IsTrimmable>True</IsTrimmable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -52,4 +54,10 @@
|
||||
<Folder Include="RenderGraphModule\" />
|
||||
</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>
|
||||
|
||||
@@ -154,7 +154,6 @@ public ref struct GraphicsPSODescriptor
|
||||
get; set;
|
||||
}
|
||||
|
||||
|
||||
public ReadOnlySpan<TextureFormat> RtvFormats
|
||||
{
|
||||
get; set;
|
||||
@@ -267,7 +266,6 @@ public struct RectDesc
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
@@ -381,13 +397,13 @@ public struct ResourceDesc
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Render target description
|
||||
/// Render Target description
|
||||
/// Supports either color OR depth rendering, not both
|
||||
/// </summary>
|
||||
public struct RenderTargetDesc
|
||||
{
|
||||
/// <summary>
|
||||
/// Width of the render target
|
||||
/// Width of the render Target
|
||||
/// </summary>
|
||||
public uint Width
|
||||
{
|
||||
@@ -395,7 +411,7 @@ public struct RenderTargetDesc
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Height of the render target
|
||||
/// Height of the render Target
|
||||
/// </summary>
|
||||
public uint Height
|
||||
{
|
||||
@@ -403,7 +419,7 @@ public struct RenderTargetDesc
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Slice of the render target
|
||||
/// Slice of the render Target
|
||||
/// </summary>
|
||||
public uint Slice
|
||||
{
|
||||
@@ -411,7 +427,7 @@ public struct RenderTargetDesc
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Type of render target
|
||||
/// Type of render Target
|
||||
/// </summary>
|
||||
public RenderTargetType Type
|
||||
{
|
||||
@@ -419,7 +435,7 @@ public struct RenderTargetDesc
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Target texture format
|
||||
/// Target texture Format
|
||||
/// </summary>
|
||||
public TextureFormat Format
|
||||
{
|
||||
@@ -435,7 +451,7 @@ public struct RenderTargetDesc
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creation flags for the render target
|
||||
/// Creation flags for the render Target
|
||||
/// </summary>
|
||||
public RenderTargetCreationFlags CreationFlags
|
||||
{
|
||||
@@ -459,7 +475,7 @@ public struct RenderTargetDesc
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a color render target
|
||||
/// Creates a color render Target
|
||||
/// </summary>
|
||||
public static RenderTargetDesc Color(uint width, uint height, uint slice = 1,
|
||||
TextureFormat format = TextureFormat.R8G8B8A8_UNorm, TextureDimension dimension = TextureDimension.Texture2D,
|
||||
@@ -481,7 +497,7 @@ public struct RenderTargetDesc
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a depth render target
|
||||
/// Creates a depth render Target
|
||||
/// </summary>
|
||||
public static RenderTargetDesc Depth(uint width, uint height, uint slice = 1,
|
||||
TextureFormat format = TextureFormat.D24_UNorm_S8_UInt, TextureDimension dimension = TextureDimension.Texture2D,
|
||||
@@ -554,7 +570,7 @@ public struct TextureDesc
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Texture format
|
||||
/// Texture Format
|
||||
/// </summary>
|
||||
public TextureFormat Format
|
||||
{
|
||||
@@ -630,59 +646,82 @@ public struct SwapChainDesc
|
||||
/// <summary>
|
||||
/// Width of the swap chain
|
||||
/// </summary>
|
||||
public uint width;
|
||||
public uint Width
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Height of the swap chain
|
||||
/// </summary>
|
||||
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)
|
||||
public uint Height
|
||||
{
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.format = format;
|
||||
this.target = target;
|
||||
get; set;
|
||||
}
|
||||
|
||||
|
||||
/// <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>
|
||||
/// Swap chain target (window handle or composition surface)
|
||||
/// Swap chain Target (window handle or composition surface)
|
||||
/// </summary>
|
||||
public struct SwapChainTarget
|
||||
{
|
||||
/// <summary>
|
||||
/// Target type
|
||||
/// </summary>
|
||||
public SwapChainTargetType type;
|
||||
public SwapChainTargetType Type
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Window handle for HWND targets
|
||||
/// </summary>
|
||||
public nint windowHandle;
|
||||
public nint WindowHandle
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Composition surface for UWP/WinUI targets
|
||||
/// </summary>
|
||||
public object? compositionSurface;
|
||||
public object? CompositionSurface
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
|
||||
public static SwapChainTarget FromWindowHandle(nint hwnd)
|
||||
{
|
||||
return new SwapChainTarget
|
||||
{
|
||||
type = SwapChainTargetType.WindowHandle,
|
||||
windowHandle = hwnd,
|
||||
compositionSurface = null
|
||||
Type = SwapChainTargetType.WindowHandle,
|
||||
WindowHandle = hwnd,
|
||||
CompositionSurface = null
|
||||
};
|
||||
}
|
||||
|
||||
@@ -690,9 +729,9 @@ public struct SwapChainTarget
|
||||
{
|
||||
return new SwapChainTarget
|
||||
{
|
||||
type = SwapChainTargetType.Composition,
|
||||
windowHandle = nint.Zero,
|
||||
compositionSurface = surface
|
||||
Type = SwapChainTargetType.Composition,
|
||||
WindowHandle = 0,
|
||||
CompositionSurface = surface
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,21 +53,21 @@ public interface ICommandBuffer : IDisposable
|
||||
void SetScissorRect(RectDesc rect);
|
||||
|
||||
/// <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>
|
||||
/// <remarks>
|
||||
/// 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>
|
||||
/// <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>
|
||||
/// <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);
|
||||
|
||||
/// <summary>
|
||||
/// Begins a render pass with the specified render target
|
||||
/// Begins a render pass with the specified render Target
|
||||
/// </summary>
|
||||
/// <param name="rtDescs">Render target descriptions</param>
|
||||
/// <param name="rtDescs">Render Target descriptions</param>
|
||||
/// <param name="depthDesc">Depth stencil description</param>
|
||||
/// <param name="allowUAVWrites">Whether UAV writes are allowed during the render pass</param>
|
||||
void BeginRenderPass(ReadOnlySpan<PassRenderTargetDesc> rtDescs, PassDepthStencilDesc depthDesc, bool allowUAVWrites = false);
|
||||
@@ -78,12 +78,18 @@ public interface ICommandBuffer : IDisposable
|
||||
void EndRenderPass();
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a resource barrier for state transitions
|
||||
/// Inserts multiple resource barriers for state transitions.
|
||||
/// </summary>
|
||||
/// <param name="resource">Resource to transition</param>
|
||||
/// <param name="before">Current resource state</param>
|
||||
/// <param name="after">Target resource state</param>
|
||||
void ResourceBarrier(Handle<GPUResource> resource, ResourceState before, ResourceState after);
|
||||
/// <param name="barrierDescs">Resource barrier descriptions</param>
|
||||
void ResourceBarrier(ReadOnlySpan<BarrierDesc> barrierDescs);
|
||||
|
||||
/// <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>
|
||||
/// Sets the pipeline state object
|
||||
@@ -176,10 +182,10 @@ public interface ICommandBuffer : IDisposable
|
||||
/// </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="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"/>.
|
||||
/// 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>
|
||||
/// Copies a specified number of bytes from the source graphics buffer to the destination graphics buffer.
|
||||
|
||||
@@ -23,5 +23,4 @@ public interface IPipelineLibrary : IDisposable
|
||||
void InitializeLibrary(string? filePath);
|
||||
void SaveLibraryToDisk(string filePath);
|
||||
Result<GraphicsPipelineKey> CompilePSO(ref readonly GraphicsPSODescriptor descriptor, ref readonly GraphicsCompiledResult compiled);
|
||||
Result<CBufferInfo, ResultStatus> GetCBufferInfo(ShaderPassKey passId);
|
||||
}
|
||||
|
||||
@@ -15,9 +15,9 @@ public interface IRenderer : IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the render target for this renderer
|
||||
/// Sets the render Target for this renderer
|
||||
/// </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);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -15,9 +15,9 @@ public interface IResourceAllocator : IDisposable
|
||||
public Handle<Texture> CreateTexture(ref readonly TextureDesc desc, bool tempResource = false);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a render target for off-screen rendering
|
||||
/// Creates a render Target for off-screen rendering
|
||||
/// </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>
|
||||
public Handle<Texture> CreateRenderTarget(ref readonly RenderTargetDesc desc, bool tempResource = false);
|
||||
|
||||
|
||||
@@ -11,6 +11,9 @@ public interface IResourceReleasable
|
||||
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
|
||||
{
|
||||
/*
|
||||
@@ -155,18 +158,4 @@ public interface IResourceDatabase : IDisposable
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the shader to release. Must refer to a valid, previously created shader.</param>
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ internal static class RHIUtility
|
||||
var planar = false;
|
||||
var bpe = 0;
|
||||
|
||||
//switch (format)
|
||||
//switch (Format)
|
||||
//{
|
||||
// case Format.BC1Typeless:
|
||||
// case Format.BC1Unorm:
|
||||
|
||||
@@ -5,23 +5,33 @@ using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Ghost.Graphics.Utilities;
|
||||
using Ghost.SDL.Compiler;
|
||||
using Misaki.HighPerformance.Image;
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
using Misaki.HighPerformance.Utilities;
|
||||
using TerraFX.Interop.Windows;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ghost.Graphics.RenderPasses;
|
||||
|
||||
/// <summary>
|
||||
/// Simplified bindless mesh render pass using high-level bindless APIs with fully bindless vertex/index buffer access
|
||||
/// </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 Identifier<Shader> _shader;
|
||||
private Handle<Material> _material;
|
||||
private Handle<Texture>[]? _textures;
|
||||
|
||||
private GraphicsCompiledResult[]? _compileResults;
|
||||
|
||||
// Texture file paths for this demo
|
||||
private readonly string[] _textureFiles = [
|
||||
"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();
|
||||
|
||||
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)
|
||||
{
|
||||
continue;
|
||||
@@ -55,18 +67,30 @@ internal unsafe class MeshRenderPass : IRenderPass
|
||||
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);
|
||||
|
||||
_mesh = ctx.CreateMesh(vertices, indices);
|
||||
_mesh = ctx.CreateMesh(vertices, indices, true);
|
||||
ctx.UpdateObjectData(_mesh, float4x4.identity);
|
||||
ctx.UploadMesh(_mesh, true);
|
||||
|
||||
_shader = ctx.ResourceAllocator.CreateGraphicsShader(shaderDescriptor);
|
||||
_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];
|
||||
//for (var i = 0; i < _textureFiles.Length; i++)
|
||||
//{
|
||||
@@ -107,5 +131,13 @@ internal unsafe class MeshRenderPass : IRenderPass
|
||||
resourceDatabase.ReleaseResource(texture.AsResource());
|
||||
}
|
||||
}
|
||||
|
||||
if (_compileResults != null)
|
||||
{
|
||||
for (var i = 0; i < _compileResults.Length; i++)
|
||||
{
|
||||
_compileResults[i].Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
|
||||
#include GENERATED_CODE_PATH
|
||||
#include "F:/csharp/GhostEngine/Ghost.Shader/BuiltIn/Properties.hlsl"
|
||||
|
||||
struct Vertex
|
||||
{
|
||||
float4 position;
|
||||
float4 normal;
|
||||
float4 tangent;
|
||||
float4 color;
|
||||
float4 uv;
|
||||
};
|
||||
#include "F:/csharp/GhostEngine/Ghost.Shader/BuiltIn/Common.hlsl"
|
||||
|
||||
struct PixelInput
|
||||
{
|
||||
@@ -26,24 +18,8 @@ void MSMain(
|
||||
out vertices PixelInput outVerts[3],
|
||||
out indices uint3 outTris[1])
|
||||
{
|
||||
#if 0
|
||||
// Fetch bindless buffers
|
||||
ByteAddressBuffer vertexBuffer = ResourceDescriptorHeap[g_PerObjectData.vertexBuffer];
|
||||
ByteAddressBuffer indexBuffer = ResourceDescriptorHeap[g_PerObjectData.indexBuffer];
|
||||
|
||||
// Compute the triangle’s vertex indices
|
||||
uint vertexId = groupThreadID.x;
|
||||
uint indexOffset = (groupID.x * 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.color = asfloat(vertexBuffer.Load4(vertexOffset + 48));
|
||||
v.uv = asfloat(vertexBuffer.Load4(vertexOffset + 64));
|
||||
Vertex v = LoadVertexData(vertexId, groupID.x, g_PerObjectData.vertexBuffer, g_PerObjectData.indexBuffer);
|
||||
|
||||
SetMeshOutputCounts(3, 1);
|
||||
//v.position = mul(g_PerViewData.cameraMatrix, mul(g_PerObjectData.localToWorld, v.position));
|
||||
@@ -58,50 +34,16 @@ void MSMain(
|
||||
{
|
||||
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 color1 = SAMPLE_TEXTURE2D_BINDLESS(g_PerMaterialData.texture1, 0, input.uv.xy);
|
||||
//float4 color2 = SAMPLE_TEXTURE2D_BINDLESS(g_PerMaterialData.texture2, 0, input.uv.xy);
|
||||
//float4 color3 = SAMPLE_TEXTURE2D_BINDLESS(g_PerMaterialData.texture3, 0, input.uv.xy);
|
||||
//float4 color4 = SAMPLE_TEXTURE2D_BINDLESS(g_PerMaterialData.texture4, 0, input.uv.xy);
|
||||
//float4 color1 = SAMPLE_TEXTURE2D(g_PerMaterialData.texture1, 0, input.uv.xy);
|
||||
//float4 color2 = SAMPLE_TEXTURE2D(g_PerMaterialData.texture2, 0, input.uv.xy);
|
||||
//float4 color3 = SAMPLE_TEXTURE2D(g_PerMaterialData.texture3, 0, input.uv.xy);
|
||||
//float4 color4 = SAMPLE_TEXTURE2D(g_PerMaterialData.texture4, 0, input.uv.xy);
|
||||
|
||||
//float4 blendedColor = (color1 + color2 + color3 + color4) * 0.25f;
|
||||
return g_PerMaterialData.color + input.color;;
|
||||
return g_PerMaterialData.color + input.color;
|
||||
//return input.color;
|
||||
}
|
||||
|
||||
@@ -48,11 +48,6 @@ public interface IFenceSynchronizer
|
||||
|
||||
public interface IRenderSystem : IFenceSynchronizer, IDisposable
|
||||
{
|
||||
RenderingConfig Config
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
IGraphicsEngine GraphicsEngine
|
||||
{
|
||||
get;
|
||||
@@ -80,8 +75,8 @@ internal class RenderSystem : IRenderSystem
|
||||
|
||||
public FrameResource()
|
||||
{
|
||||
cpuReadyEvent = new(false);
|
||||
gpuReadyEvent = new(true);
|
||||
cpuReadyEvent = new AutoResetEvent(false);
|
||||
gpuReadyEvent = new AutoResetEvent(true);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@@ -105,7 +100,6 @@ internal class RenderSystem : IRenderSystem
|
||||
private bool _isRunning;
|
||||
private bool _disposed;
|
||||
|
||||
public RenderingConfig Config => _config;
|
||||
public IGraphicsEngine GraphicsEngine => _graphicsEngine;
|
||||
public bool IsRunning => _isRunning;
|
||||
|
||||
@@ -123,16 +117,16 @@ internal class RenderSystem : IRenderSystem
|
||||
_ => throw new NotSupportedException($"Graphics API {config.GraphicsAPI} is not supported.")
|
||||
};
|
||||
|
||||
_shutdownEvent = new(false);
|
||||
_shutdownEvent = new AutoResetEvent(false);
|
||||
|
||||
// Create frame resources for synchronization
|
||||
_frameResources = new FrameResource[config.FrameBufferCount];
|
||||
for (var i = 0; i < config.FrameBufferCount; i++)
|
||||
{
|
||||
_frameResources[i] = new();
|
||||
_frameResources[i] = new FrameResource();
|
||||
}
|
||||
|
||||
_renderThread = new(RenderLoop)
|
||||
_renderThread = new Thread(RenderLoop)
|
||||
{
|
||||
IsBackground = true,
|
||||
Name = "Graphics Render Thread",
|
||||
|
||||
@@ -138,7 +138,7 @@ internal sealed unsafe partial class DxcShaderCompiler : IShaderCompiler
|
||||
_compiler.Attach(pCompiler);
|
||||
_utils.Attach(pUtils);
|
||||
|
||||
_compiledResults = new();
|
||||
_compiledResults = new Dictionary<ShaderPassKey, GraphicsCompiledResult>();
|
||||
}
|
||||
|
||||
~DxcShaderCompiler()
|
||||
@@ -153,10 +153,10 @@ internal sealed unsafe partial class DxcShaderCompiler : IShaderCompiler
|
||||
|
||||
try
|
||||
{
|
||||
// Create DXC _utils.Get() to parse pReflection data
|
||||
// Create DXC _utils.Get() to parse reflection data
|
||||
var dxcuID = CLSID.CLSID_DxcUtils;
|
||||
|
||||
// Create pReflection interface from blob
|
||||
// Create reflection interface from blob
|
||||
var reflectionBuffer = new DxcBuffer
|
||||
{
|
||||
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);
|
||||
|
||||
IDxcIncludeHandler* pIncludeHandler = default;
|
||||
IDxcBlobEncoding* pSourceBlob = default;
|
||||
using ComPtr<IDxcIncludeHandler> includeHandler = 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
|
||||
{
|
||||
// Create DXC _compiler.Get() and _utils.Get()
|
||||
var dxccID = CLSID.CLSID_DxcCompiler;
|
||||
var dxcuID = CLSID.CLSID_DxcUtils;
|
||||
|
||||
|
||||
ThrowIfFailed(_utils.Get()->CreateDefaultIncludeHandler(&pIncludeHandler));
|
||||
|
||||
// Create source blob
|
||||
fixed (char* pPath = config.shaderPath)
|
||||
// Compile shader
|
||||
var buffer = new DxcBuffer
|
||||
{
|
||||
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);
|
||||
var argPtrs = stackalloc char*[argsArray.Count];
|
||||
for (var i = 0; i < argsArray.Count; i++)
|
||||
// Get compiled bytecode
|
||||
using ComPtr<IDxcBlob> bytecodeBlob = default;
|
||||
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
|
||||
var buffer = new DxcBuffer
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
bytecode = bytecode,
|
||||
reflectionData = reflectionData,
|
||||
};
|
||||
}
|
||||
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);
|
||||
|
||||
@@ -381,14 +353,14 @@ internal sealed unsafe partial class DxcShaderCompiler : IShaderCompiler
|
||||
return Result.Failure("FullPassDescriptor expected.");
|
||||
}
|
||||
|
||||
CompileResult tsResult = default;
|
||||
ShaderCompileResult tsResult = default;
|
||||
var tsEntry = fullDescriptor.taskShader;
|
||||
if (tsEntry.IsCreated)
|
||||
{
|
||||
var config = new CompilerConfig
|
||||
{
|
||||
defines = fullDescriptor.defines.AsSpan(),
|
||||
include = fullDescriptor.generatedCodePath,
|
||||
include = generatedCodePath,
|
||||
shaderPath = tsEntry.shader,
|
||||
entryPoint = tsEntry.entry,
|
||||
stage = ShaderStage.TaskShader,
|
||||
@@ -406,14 +378,14 @@ internal sealed unsafe partial class DxcShaderCompiler : IShaderCompiler
|
||||
tsResult = result.Value;
|
||||
}
|
||||
|
||||
CompileResult msResult;
|
||||
ShaderCompileResult msResult;
|
||||
var msEntry = fullDescriptor.meshShader;
|
||||
if (msEntry.IsCreated)
|
||||
{
|
||||
var config = new CompilerConfig
|
||||
{
|
||||
defines = fullDescriptor.defines.AsSpan(),
|
||||
include = fullDescriptor.generatedCodePath,
|
||||
include = generatedCodePath,
|
||||
shaderPath = msEntry.shader,
|
||||
entryPoint = msEntry.entry,
|
||||
stage = ShaderStage.MeshShader,
|
||||
@@ -435,14 +407,14 @@ internal sealed unsafe partial class DxcShaderCompiler : IShaderCompiler
|
||||
return Result.Failure("Mesh shader expected.");
|
||||
}
|
||||
|
||||
CompileResult psResult;
|
||||
ShaderCompileResult psResult;
|
||||
var psEntry = fullDescriptor.pixelShader;
|
||||
if (psEntry.IsCreated)
|
||||
{
|
||||
var config = new CompilerConfig
|
||||
{
|
||||
defines = fullDescriptor.defines.AsSpan(),
|
||||
include = fullDescriptor.generatedCodePath,
|
||||
include = generatedCodePath,
|
||||
shaderPath = psEntry.shader,
|
||||
entryPoint = psEntry.entry,
|
||||
stage = ShaderStage.PixelShader,
|
||||
|
||||
@@ -231,7 +231,7 @@ public unsafe static class MeshBuilder
|
||||
public static void ComputeTangents(UnsafeList<Vertex> vertices, UnsafeList<uint> indices)
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -3,10 +3,10 @@ shader "MyShader/Standard"
|
||||
properties
|
||||
{
|
||||
float4 color = float4(1, 1, 1, 1);
|
||||
tex2d_b texture1 = tex2d_b(black);
|
||||
tex2d_b texture2 = tex2d_b(white);
|
||||
tex2d_b texture3 = tex2d_b(grey);
|
||||
tex2d_b texture4 = tex2d_b(normal);
|
||||
tex2d texture1 = tex2d(black);
|
||||
tex2d texture2 = tex2d(white);
|
||||
tex2d texture3 = tex2d(grey);
|
||||
tex2d texture4 = tex2d(normal);
|
||||
}
|
||||
|
||||
pass "Forward"
|
||||
|
||||
@@ -30,11 +30,7 @@ if (model == null)
|
||||
}
|
||||
|
||||
var descriptor = SDLCompiler.ResolveShader(model);
|
||||
|
||||
foreach (var pass in descriptor.passes)
|
||||
{
|
||||
SDLCompiler.GeneratePass(pass, "C:/Users/Misaki/Downloads/Archive");
|
||||
}
|
||||
SDLCompiler.GenerateShaderCode(descriptor, "C:/Users/Misaki/Downloads/Archive");
|
||||
|
||||
Console.WriteLine("Shader compiled successfully:");
|
||||
|
||||
|
||||
@@ -1,97 +1,95 @@
|
||||
#ifndef COMMON_HLSL
|
||||
#define COMMON_HLSL
|
||||
|
||||
#undef USE_TRADITIONAL_BINDLESS // Just for testing, this should be handled by engine feature level.
|
||||
#ifndef BUILTIN_COMMON_HLSL
|
||||
#define BUILTIN_COMMON_HLSL
|
||||
|
||||
struct Vertex
|
||||
{
|
||||
float4 position;
|
||||
float4 normal;
|
||||
float4 tangent;
|
||||
float4 uv;
|
||||
float4 color;
|
||||
};
|
||||
|
||||
// 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_TEXTURE3D_HEAP ResourceDescriptorHeap
|
||||
#define GLOBAL_TEXTURECUBE_HEAP ResourceDescriptorHeap
|
||||
#define GLOBAL_TEXTURE2D_ARRAY_HEAP ResourceDescriptorHeap
|
||||
#define GLOBAL_TEXTURECUBE_ARRAY_HEAP ResourceDescriptorHeap
|
||||
#define GLOBAL_BUFFER_HEAP ResourceDescriptorHeap
|
||||
#define GLOBAL_SAMPLER_HEAP SamplerDescriptorHeap
|
||||
#endif
|
||||
|
||||
|
||||
// Bindless resource type definitions
|
||||
|
||||
#define TEXTURE2D_BINDLESS uint
|
||||
#define TEXTURE3D_BINDLESS uint
|
||||
#define TEXTURECUBE_BINDLESS uint
|
||||
#define TEXTURE2D_ARRAY_BINDLESS uint
|
||||
#define TEXTURECUBE_ARRAY_BINDLESS uint
|
||||
#define TEXTURE2D uint
|
||||
#define TEXTURE3D uint
|
||||
#define TEXTURECUBE uint
|
||||
#define TEXTURE2D_ARRAY uint
|
||||
#define TEXTURECUBE_ARRAY uint
|
||||
|
||||
#define SAMPLER_BINDLESS uint
|
||||
#define SAMPLER uint
|
||||
|
||||
#define STRUCT_BUFFER_BINDLESS uint
|
||||
#define BYTE_ADDRESS_BUFFER_BINDLESS 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
|
||||
#define STRUCT_BUFFER uint
|
||||
#define BYTE_ADDRESS_BUFFER uint
|
||||
|
||||
|
||||
// Texture and sampler access macros
|
||||
|
||||
#define GET_TEXTURE2D_BINDLESS(id) GLOBAL_TEXTURE2D_HEAP[id]
|
||||
#define GET_TEXTURE2D_ARRAY_BINDLESS(id) GLOBAL_TEXTURE2D_ARRAY_HEAP[id]
|
||||
#define GET_TEXTURE3D_BINDLESS(id) GLOBAL_TEXTURE3D_HEAP[id]
|
||||
#define GET_TEXTURECUBE_BINDLESS(id) GLOBAL_TEXTURECUBE_HEAP[id]
|
||||
#define GET_TEXTURECUBE_ARRAY_BINDLESS(id) GLOBAL_TEXTURECUBE_ARRAY_HEAP[id]
|
||||
#define GET_SAMPLER_BINDLESS(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 GET_TEXTURE2D(id) GLOBAL_TEXTURE2D_HEAP[id]
|
||||
#define GET_TEXTURE2D_ARRAY(id) GLOBAL_TEXTURE2D_ARRAY_HEAP[id]
|
||||
#define GET_TEXTURE3D(id) GLOBAL_TEXTURE3D_HEAP[id]
|
||||
#define GET_TEXTURECUBE(id) GLOBAL_TEXTURECUBE_HEAP[id]
|
||||
#define GET_TEXTURECUBE_ARRAY(id) GLOBAL_TEXTURECUBE_ARRAY_HEAP[id]
|
||||
#define GET_BUFFER(id) GLOBAL_BUFFER_HEAP[id]
|
||||
#define GET_SAMPLER(id) GLOBAL_SAMPLER_HEAP[id]
|
||||
|
||||
|
||||
#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 triangle’s 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef PROPERTIES_HLSL
|
||||
#define PROPERTIES_HLSL
|
||||
#ifndef BUILTIN_PROPERTIES_HLSL
|
||||
#define BUILTIN_PROPERTIES_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);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif // BUILTIN_PROPERTIES_HLSL
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -27,10 +27,6 @@ internal class PassBlock : IBlockParser<PassSyntax, PassSemantic>
|
||||
{
|
||||
pass.defines = DefinesBlock.Parse(bodyStream.SliceNextBlock());
|
||||
}
|
||||
else if (IncludesBlock.ShouldEnter(nextToken))
|
||||
{
|
||||
pass.includes = IncludesBlock.Parse(bodyStream.SliceNextBlock());
|
||||
}
|
||||
else if (KeywordsBlock.ShouldEnter(nextToken))
|
||||
{
|
||||
pass.keywords = KeywordsBlock.Parse(bodyStream.SliceNextBlock());
|
||||
@@ -39,10 +35,6 @@ internal class PassBlock : IBlockParser<PassSyntax, PassSemantic>
|
||||
{
|
||||
pass.localPipeline = PipelineBlock.Parse(bodyStream.SliceNextBlock());
|
||||
}
|
||||
else if (PropertiesBlock.ShouldEnter(nextToken))
|
||||
{
|
||||
pass.localProperties = PropertiesBlock.Parse(bodyStream.SliceNextBlock());
|
||||
}
|
||||
else if (nextToken.Match(TokenType.Identifier))
|
||||
{
|
||||
var func = ParseUtility.ParseFunction(ref bodyStream, TokenType.StringLiteral);
|
||||
@@ -72,23 +64,10 @@ internal class PassBlock : IBlockParser<PassSyntax, PassSemantic>
|
||||
{
|
||||
name = syntax.name.lexeme,
|
||||
defines = DefinesBlock.SemanticAnalysis(syntax.defines, errors),
|
||||
includes = IncludesBlock.SemanticAnalysis(syntax.includes, errors),
|
||||
keywords = KeywordsBlock.SemanticAnalysis(syntax.keywords, errors),
|
||||
localProperties = PropertiesBlock.SemanticAnalysis(syntax.localProperties, 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)
|
||||
{
|
||||
foreach (var func in syntax.functionCalls)
|
||||
|
||||
@@ -73,9 +73,9 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
|
||||
ParseBoolValue(syntax[3], errors))),
|
||||
|
||||
// Textures (single identifier argument)
|
||||
[ShaderPropertyType.Texture2DBindless] = new(1, TokenType.Identifier, (syntax, errors) => ParseTextureDefault(syntax[0], errors)),
|
||||
[ShaderPropertyType.Texture3DBindless] = new(1, TokenType.Identifier, (syntax, errors) => ParseTextureDefault(syntax[0], errors)),
|
||||
[ShaderPropertyType.TextureCubeBindless] = new(1, TokenType.Identifier, (syntax, errors) => ParseTextureDefault(syntax[0], errors)),
|
||||
[ShaderPropertyType.Texture2D] = new(1, TokenType.Identifier, (syntax, errors) => ParseTextureDefault(syntax[0], errors)),
|
||||
[ShaderPropertyType.Texture3D] = 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)
|
||||
@@ -166,6 +166,7 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
|
||||
TokenLexicon.KnownTypes.FLOAT2 => ShaderPropertyType.Float2,
|
||||
TokenLexicon.KnownTypes.FLOAT3 => ShaderPropertyType.Float3,
|
||||
TokenLexicon.KnownTypes.FLOAT4 => ShaderPropertyType.Float4,
|
||||
TokenLexicon.KnownTypes.FLOAT4X4 => ShaderPropertyType.Float4x4,
|
||||
TokenLexicon.KnownTypes.INT => ShaderPropertyType.Int,
|
||||
TokenLexicon.KnownTypes.INT2 => ShaderPropertyType.Int2,
|
||||
TokenLexicon.KnownTypes.INT3 => ShaderPropertyType.Int3,
|
||||
@@ -178,9 +179,9 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
|
||||
TokenLexicon.KnownTypes.BOOL2 => ShaderPropertyType.Bool2,
|
||||
TokenLexicon.KnownTypes.BOOL3 => ShaderPropertyType.Bool3,
|
||||
TokenLexicon.KnownTypes.BOOL4 => ShaderPropertyType.Bool4,
|
||||
TokenLexicon.KnownTypes.TEXTURE2D_BINDLESS => ShaderPropertyType.Texture2DBindless,
|
||||
TokenLexicon.KnownTypes.TEXTURE3D_BINDLESS => ShaderPropertyType.Texture3DBindless,
|
||||
TokenLexicon.KnownTypes.TEXTURECUBE_BINDLESS => ShaderPropertyType.TextureCubeBindless,
|
||||
TokenLexicon.KnownTypes.TEXTURE2D => ShaderPropertyType.Texture2D,
|
||||
TokenLexicon.KnownTypes.TEXTURE3D => ShaderPropertyType.Texture3D,
|
||||
TokenLexicon.KnownTypes.TEXTURECUBE => ShaderPropertyType.TextureCube,
|
||||
_ => ShaderPropertyType.None,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ internal static class SDLCompiler
|
||||
|
||||
public static SDLSemantics? SemanticAnalysis(SDLSyntax syntax, out List<SDLError> errors)
|
||||
{
|
||||
errors = new();
|
||||
errors = new List<SDLError>();
|
||||
|
||||
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>();
|
||||
if (parent != null)
|
||||
{
|
||||
result.AddRange(parent);
|
||||
}
|
||||
var currentOffset = 0u;
|
||||
|
||||
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)
|
||||
{
|
||||
result.Add(new PropertyDescriptor
|
||||
{
|
||||
name = prop.name,
|
||||
type = prop.type,
|
||||
defaultValue = prop.defaultValue
|
||||
});
|
||||
}
|
||||
currentOffset = (currentOffset + 15u) & ~15u;
|
||||
}
|
||||
|
||||
currentOffset += size;
|
||||
}
|
||||
|
||||
return result.DistinctBy(p => p.name).ToList();
|
||||
return (currentOffset + 15u) & ~15u;
|
||||
}
|
||||
|
||||
// TODO: Implement shader inheritance resolution, including property and pass merging.
|
||||
@@ -181,19 +173,23 @@ internal static class SDLCompiler
|
||||
name = semantics.name
|
||||
};
|
||||
|
||||
var shaderGlobalProperties = semantics.properties?.Where(p => p.scope == PropertyScope.Global).Select(p => new PropertyDescriptor
|
||||
{
|
||||
name = p.name,
|
||||
type = p.type,
|
||||
defaultValue = p.defaultValue
|
||||
}).ToList();
|
||||
var shaderGlobalProperties = semantics.properties?
|
||||
.Where(p => p.scope == PropertyScope.Global)
|
||||
.Select(p => new PropertyDescriptor
|
||||
{
|
||||
name = p.name,
|
||||
type = p.type,
|
||||
defaultValue = p.defaultValue
|
||||
}).ToList();
|
||||
|
||||
var shaderLocalProperties = semantics.properties?.Where(p => p.scope == PropertyScope.Local).Select(p => new PropertyDescriptor
|
||||
{
|
||||
name = p.name,
|
||||
type = p.type,
|
||||
defaultValue = p.defaultValue
|
||||
}).ToList();
|
||||
var shaderLocalProperties = semantics.properties?
|
||||
.Where(p => p.scope == PropertyScope.Local)
|
||||
.Select(p => new PropertyDescriptor
|
||||
{
|
||||
name = p.name,
|
||||
type = p.type,
|
||||
defaultValue = p.defaultValue
|
||||
}).ToList();
|
||||
|
||||
if (shaderGlobalProperties != null)
|
||||
{
|
||||
@@ -201,13 +197,18 @@ internal static class SDLCompiler
|
||||
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)
|
||||
{
|
||||
foreach (var pass in semantics.passes)
|
||||
{
|
||||
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
|
||||
{
|
||||
uniqueIdentifier = GetPassUniqueId(semantics, pass),
|
||||
@@ -217,9 +218,7 @@ internal static class SDLCompiler
|
||||
pixelShader = pass.pixelShader,
|
||||
localPipeline = localPipeline,
|
||||
defines = pass.defines,
|
||||
includes = pass.includes,
|
||||
keywords = pass.keywords,
|
||||
properties = localProperties
|
||||
};
|
||||
|
||||
descriptor.passes.Add(fullPass);
|
||||
@@ -258,24 +257,14 @@ internal static class SDLCompiler
|
||||
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)
|
||||
{
|
||||
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;
|
||||
return Result.Failure("Failed to generate pass files: " + generatedResult.Message);
|
||||
}
|
||||
|
||||
desc.generatedCodePath = generatedResult.Value;
|
||||
|
||||
return desc;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -305,28 +294,23 @@ internal static class SDLCompiler
|
||||
ShaderPropertyType.Bool3 => "bool3",
|
||||
ShaderPropertyType.Bool4 => "bool4",
|
||||
// NOTE: Textures here are bindless, represented as uint (descriptor index).
|
||||
ShaderPropertyType.Texture2DBindless => "TEXTURE2D_BINDLESS",
|
||||
ShaderPropertyType.Texture3DBindless => "TEXTURE3D_BINDLESS",
|
||||
ShaderPropertyType.TextureCubeBindless => "TEXTURECUBE_BINDLESS",
|
||||
ShaderPropertyType.Texture2DArrayBindless => "TEXTURE2D_ARRAY_BINDLESS",
|
||||
ShaderPropertyType.TextureCubeArrayBindless => "TEXTURECUBE_ARRAY_BINDLESS",
|
||||
ShaderPropertyType.Texture2D => "TEXTURE2D_BINDLESS",
|
||||
ShaderPropertyType.Texture3D => "TEXTURE3D_BINDLESS",
|
||||
ShaderPropertyType.TextureCube => "TEXTURECUBE_BINDLESS",
|
||||
ShaderPropertyType.Texture2DArray => "TEXTURE2D_ARRAY_BINDLESS",
|
||||
ShaderPropertyType.TextureCubeArray => "TEXTURECUBE_ARRAY_BINDLESS",
|
||||
_ => 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))
|
||||
{
|
||||
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 outputDirectory = Path.GetDirectoryName(outputFilePath);
|
||||
|
||||
@@ -346,30 +330,17 @@ internal static class SDLCompiler
|
||||
#define {fileDefine}
|
||||
|
||||
#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();
|
||||
}
|
||||
|
||||
if (fullPass.properties != null)
|
||||
{
|
||||
sb.Append(@"
|
||||
sb.Append(@"
|
||||
struct PerMaterialData
|
||||
{");
|
||||
foreach (var prop in fullPass.properties)
|
||||
{
|
||||
sb.Append($@"
|
||||
foreach (var prop in descriptor.properties)
|
||||
{
|
||||
sb.Append($@"
|
||||
{ShaderPropertyTypeToHLSLType(prop.type)} {prop.name};");
|
||||
}
|
||||
sb.Append(@"
|
||||
};");
|
||||
}
|
||||
sb.Append(@"
|
||||
};");
|
||||
|
||||
sb.AppendLine();
|
||||
sb.AppendLine(@$"
|
||||
|
||||
@@ -32,9 +32,7 @@ internal class PassSemantic
|
||||
public ShaderEntryPoint meshShader;
|
||||
public ShaderEntryPoint pixelShader;
|
||||
public List<string>? defines;
|
||||
public List<string>? includes;
|
||||
public List<KeywordsGroup>? keywords;
|
||||
public List<PropertySemantic>? localProperties;
|
||||
public PipelineSemantic? localPipeline;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,9 +36,7 @@ internal class PassSyntax
|
||||
{
|
||||
public Token name;
|
||||
public PipelineSyntax? localPipeline;
|
||||
public PropertiesSyntax? localProperties;
|
||||
public List<Token>? defines;
|
||||
public List<Token>? includes;
|
||||
public List<FunctionCallDeclaration>? keywords;
|
||||
public List<FunctionCallDeclaration>? functionCalls;
|
||||
}
|
||||
|
||||
@@ -153,6 +153,7 @@ internal static class TokenLexicon
|
||||
public const string FLOAT2 = "float2";
|
||||
public const string FLOAT3 = "float3";
|
||||
public const string FLOAT4 = "float4";
|
||||
public const string FLOAT4X4 = "float4x4";
|
||||
|
||||
public const string INT = "int";
|
||||
public const string INT2 = "int2";
|
||||
@@ -170,11 +171,11 @@ internal static class TokenLexicon
|
||||
public const string BOOL4 = "bool4";
|
||||
|
||||
// Texture types
|
||||
public const string TEXTURE2D_BINDLESS = "tex2d_b";
|
||||
public const string TEXTURE2D_ARRAY_BINDLESS = "tex2d_arr_b";
|
||||
public const string TEXTURE3D_BINDLESS = "tex3d_b";
|
||||
public const string TEXTURECUBE_BINDLESS = "texcube_b";
|
||||
public const string TEXTURECUBE_ARRAY_BINDLESS = "texcube_arr_b";
|
||||
public const string TEXTURE2D = "tex2d";
|
||||
public const string TEXTURE2D_ARRAY = "tex2d_arr";
|
||||
public const string TEXTURE3D = "tex3d";
|
||||
public const string TEXTURECUBE = "texcube";
|
||||
public const string TEXTURECUBE_ARRAY = "texcube_arr";
|
||||
}
|
||||
|
||||
public static class KnownTextureValue
|
||||
@@ -211,12 +212,12 @@ internal static class TokenLexicon
|
||||
|
||||
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.UINT, KnownTypes.UINT2, KnownTypes.UINT3, KnownTypes.UINT4,
|
||||
KnownTypes.BOOL, KnownTypes.BOOL2, KnownTypes.BOOL3, KnownTypes.BOOL4,
|
||||
KnownTypes.TEXTURE2D_BINDLESS, KnownTypes.TEXTURE2D_ARRAY_BINDLESS, KnownTypes.TEXTURE3D_BINDLESS,
|
||||
KnownTypes.TEXTURECUBE_BINDLESS, KnownTypes.TEXTURECUBE_ARRAY_BINDLESS,
|
||||
KnownTypes.TEXTURE2D, KnownTypes.TEXTURE2D_ARRAY, KnownTypes.TEXTURE3D,
|
||||
KnownTypes.TEXTURECUBE, KnownTypes.TEXTURECUBE_ARRAY,
|
||||
};
|
||||
|
||||
private static readonly HashSet<string> s_textureDefaultValues = new()
|
||||
|
||||
Reference in New Issue
Block a user