Refactor and optimize rendering pipeline

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

View File

@@ -10,17 +10,18 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<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" />

View File

@@ -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,
};
}
}

View File

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

View File

@@ -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)

View File

@@ -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

View File

@@ -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>

View File

@@ -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>

View File

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

View File

@@ -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, () =>
{

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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)

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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;

View File

@@ -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();

View File

@@ -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);

View File

@@ -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()

View File

@@ -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))

View File

@@ -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());

View File

@@ -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);

View File

@@ -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)

View File

@@ -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();
}
}

View File

@@ -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;

View File

@@ -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)
{

View File

@@ -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>

View File

@@ -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
};
}
}

View File

@@ -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.

View File

@@ -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);
}

View File

@@ -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>

View File

@@ -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);

View File

@@ -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);
}

View File

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

View File

@@ -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();
}
}
}
}

View File

@@ -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 triangles 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;
}

View File

@@ -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",

View File

@@ -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,

View File

@@ -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)
{

View File

@@ -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"

View File

@@ -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:");

View File

@@ -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 triangles vertex indices
uint indexOffset = (groupID * 3 + vertexID) * 4; // uint32 index
uint vertexIndex = indexBuffer.Load(indexOffset);
// Load vertex attributes
uint vertexOffset = vertexIndex * 80; // 80 bytes per vertex
Vertex v;
v.position = asfloat(vertexBuffer.Load4(vertexOffset + 0));
v.normal = asfloat(vertexBuffer.Load4(vertexOffset + 16));
v.tangent = asfloat(vertexBuffer.Load4(vertexOffset + 32));
v.uv = asfloat(vertexBuffer.Load4(vertexOffset + 48));
v.color = asfloat(vertexBuffer.Load4(vertexOffset + 64));
return v;
}
#endif // BUILTIN_COMMON_HLSL

View File

@@ -1,5 +1,5 @@
#ifndef PROPERTIES_HLSL
#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

View File

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

View File

@@ -27,10 +27,6 @@ internal class PassBlock : IBlockParser<PassSyntax, PassSemantic>
{
pass.defines = DefinesBlock.Parse(bodyStream.SliceNextBlock());
}
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)

View File

@@ -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,
};
}

View File

@@ -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(@$"

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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()