Refactor D3D12 Resource Management

Refactored and renamed components related to D3D12 graphics programming, replacing "descriptor" with "viewGroup" to improve resource grouping and management. Updated `D3D12CommandBuffer`, `D3D12DescriptorAllocator`, and `D3D12PipelineLibrary` to reflect these changes. Simplified material and shader creation in `D3D12ResourceAllocator`. Enhanced `D3D12ResourceDatabase` with resource naming for debugging and improved management. Refactored `Shader` and `ShaderPass` to use modern C# features and `IResourceReleasable` interface. Introduced `D3D12Utility` for centralized utility methods. Updated `Material` class for efficient buffer creation. Renamed `ShaderCompiler` to `SDLCompiler` with improved error handling. Updated `MeshRenderPass` to use new shader compilation process. Various improvements in error handling, code readability, and utility methods.
This commit is contained in:
2025-10-23 14:42:53 +09:00
parent d2d9f5feb7
commit 28c386b0bb
28 changed files with 393 additions and 306 deletions

View File

@@ -126,14 +126,14 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
throw new ArgumentException($"Render target at index {i} is not a valid texture handle"); throw new ArgumentException($"Render target at index {i} is not a valid texture handle");
} }
var descriptor = _resourceDatabase.GetResourceInfo(handle.AsResource()).descriptor; var descriptor = _resourceDatabase.GetResourceInfo(handle.AsResource()).viewGroup;
rtvHandles[i] = _descriptorAllocator.GetCpuHandle(descriptor.rtv); rtvHandles[i] = _descriptorAllocator.GetCpuHandle(descriptor.rtv);
} }
var dsvHandle = stackalloc D3D12_CPU_DESCRIPTOR_HANDLE[depthTarget.IsValid ? 1 : 0]; var dsvHandle = stackalloc D3D12_CPU_DESCRIPTOR_HANDLE[depthTarget.IsValid ? 1 : 0];
if (dsvHandle != null) if (dsvHandle != null)
{ {
*dsvHandle = _descriptorAllocator.GetCpuHandle(_resourceDatabase.GetResourceInfo(depthTarget.AsResource()).descriptor.dsv); *dsvHandle = _descriptorAllocator.GetCpuHandle(_resourceDatabase.GetResourceInfo(depthTarget.AsResource()).viewGroup.dsv);
} }
_commandList.Get()->OMSetRenderTargets((uint)renderTargets.Length, rtvHandles, FALSE, dsvHandle); _commandList.Get()->OMSetRenderTargets((uint)renderTargets.Length, rtvHandles, FALSE, dsvHandle);
@@ -270,7 +270,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
_commandList.Get()->SetPipelineState(d3d12Pipeline.pipelineState.Get()); _commandList.Get()->SetPipelineState(d3d12Pipeline.pipelineState.Get());
_commandList.Get()->SetGraphicsRootSignature(_pipelineLibrary.DefaultRootSignature); _commandList.Get()->SetGraphicsRootSignature(_pipelineLibrary.DefaultRootSignature);
// Set descriptor heaps - CRUCIAL: Use the specialized bindless heap for SM 6.6 // Set viewGroup heaps - CRUCIAL: Use the specialized bindless heap for SM 6.6
var heaps = stackalloc ID3D12DescriptorHeap*[2]; var heaps = stackalloc ID3D12DescriptorHeap*[2];
heaps[0] = _descriptorAllocator.GetCbvSrvUavHeap(); // Bindless resource heap heaps[0] = _descriptorAllocator.GetCbvSrvUavHeap(); // Bindless resource heap
heaps[1] = _descriptorAllocator.GetSamplerHeap(); // Bindless sampler heap heaps[1] = _descriptorAllocator.GetSamplerHeap(); // Bindless sampler heap

View File

@@ -8,7 +8,7 @@ using static TerraFX.Aliases.D3D12_Alias;
namespace Ghost.Graphics.D3D12; namespace Ghost.Graphics.D3D12;
/// <summary> /// <summary>
/// D3D12 implementation of descriptor allocator that manages different types of descriptor heaps. /// D3D12 implementation of viewGroup allocator that manages different types of viewGroup heaps.
/// </summary> /// </summary>
internal unsafe class D3D12DescriptorAllocator : IDisposable internal unsafe class D3D12DescriptorAllocator : IDisposable
{ {

View File

@@ -91,7 +91,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
{ {
_defaultRootSignature = default; _defaultRootSignature = default;
// NOTE: Since we are targeting SM 6.6, we can use ResourceDescriptorHeap and SamplerDescriptorHeap directly without needing to set up descriptor tables. // NOTE: Since we are targeting SM 6.6, we can use ResourceDescriptorHeap and SamplerDescriptorHeap directly without needing to set up viewGroup tables.
var rootParameters = stackalloc D3D12_ROOT_PARAMETER1[_ROOT_PARAM_COUNT]; var rootParameters = stackalloc D3D12_ROOT_PARAMETER1[_ROOT_PARAM_COUNT];
rootParameters[0] = new D3D12_ROOT_PARAMETER1 rootParameters[0] = new D3D12_ROOT_PARAMETER1
{ {
@@ -325,7 +325,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
SampleMask = UINT32_MAX, SampleMask = UINT32_MAX,
SampleDesc = new DXGI_SAMPLE_DESC(1, 0), SampleDesc = new DXGI_SAMPLE_DESC(1, 0),
NumRenderTargets = rtvCount, NumRenderTargets = rtvCount,
DSVFormat = dsv.ToD3D12Format(), DSVFormat = dsv.ToDXGIFormat(),
DepthStencilState = BuildDepthStencil(in pipelineDescriptor), DepthStencilState = BuildDepthStencil(in pipelineDescriptor),
NodeMask = 0, NodeMask = 0,
Flags = D3D12_PIPELINE_STATE_FLAG_NONE, Flags = D3D12_PIPELINE_STATE_FLAG_NONE,
@@ -362,7 +362,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
for (var i = 0; i < rtvCount && i < 6; i++) for (var i = 0; i < rtvCount && i < 6; i++)
{ {
desc.RTVFormats[i] = rtvs[i].ToD3D12Format(); desc.RTVFormats[i] = rtvs[i].ToDXGIFormat();
desc.BlendState.RenderTarget[i].RenderTargetWriteMask = (byte)(pipelineDescriptor.colorMask & 0x0F); desc.BlendState.RenderTarget[i].RenderTargetWriteMask = (byte)(pipelineDescriptor.colorMask & 0x0F);
hash.rtvFormats[i] = rtvs[i]; hash.rtvFormats[i] = rtvs[i];
} }

View File

@@ -555,32 +555,16 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator, IDisposable
public Handle<Material> CreateMaterial(Identifier<Shader> shader) public Handle<Material> CreateMaterial(Identifier<Shader> shader)
{ {
var materialData = new Material var material = new Material();
{ material.SetShader(shader, this, _resourceDatabase);
Shader = shader,
};
ref var shaderRef = ref _resourceDatabase.GetShaderReference(shader); return _resourceDatabase.AddMaterial(ref material);
// TODO: Get per-material constant buffer size from database
var desc = new BufferDesc
{
Size = shaderRef.PerMaterialBufferInfo.Size,
Usage = BufferUsage.Constant,
MemoryType = ResourceMemoryType.Default,
};
var buffer = CreateBuffer(ref desc);
materialData._materialPropertiesCache = new CBufferCache(buffer, shaderRef.PerMaterialBufferInfo.Size);
return _resourceDatabase.AddMaterial(ref materialData);
} }
public Identifier<Shader> CreateShader(ShaderDescriptor descriptor) public Identifier<Shader> CreateShader(ShaderDescriptor descriptor)
{ {
var shaderData = new Shader(); var shader = new Shader(descriptor);
return _resourceDatabase.AddShader(ref shaderData); return _resourceDatabase.AddShader(shader);
} }
#region Conversion Methods #region Conversion Methods

View File

@@ -4,6 +4,7 @@ using Ghost.Graphics.RHI;
using Misaki.HighPerformance.Collections; using Misaki.HighPerformance.Collections;
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Collections;
using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using TerraFX.Interop.DirectX; using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows; using TerraFX.Interop.Windows;
@@ -34,7 +35,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
} }
public ResourceDesc desc; public ResourceDesc desc;
public ResourceViewGroup descriptor; public ResourceViewGroup viewGroup;
public ResourceUnion resourceUnion; public ResourceUnion resourceUnion;
public ResourceState state; public ResourceState state;
public uint cpuFenceValue; public uint cpuFenceValue;
@@ -48,7 +49,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
this.resourceUnion = new ResourceUnion(allocation); this.resourceUnion = new ResourceUnion(allocation);
this.isExternal = false; this.isExternal = false;
this.descriptor = resourceDescriptor; this.viewGroup = resourceDescriptor;
this.cpuFenceValue = cpuFenceValue; this.cpuFenceValue = cpuFenceValue;
this.state = state; this.state = state;
this.desc = desc; this.desc = desc;
@@ -59,7 +60,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
this.resourceUnion = new ResourceUnion(resource); this.resourceUnion = new ResourceUnion(resource);
this.isExternal = true; this.isExternal = true;
this.descriptor = default; this.viewGroup = default;
this.cpuFenceValue = ~0u; this.cpuFenceValue = ~0u;
this.state = state; this.state = state;
this.desc = ResourceDesc.FromD3D12(resource.Get()->GetDesc()); this.desc = ResourceDesc.FromD3D12(resource.Get()->GetDesc());
@@ -80,10 +81,10 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
} }
resourceUnion = default; resourceUnion = default;
descriptor = default; viewGroup = default;
} }
descriptorAllocator.Release(descriptor); descriptorAllocator.Release(viewGroup);
return refCount; return refCount;
} }
@@ -99,14 +100,12 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
private UnsafeSlotMap<ResourceRecord> _resources; private UnsafeSlotMap<ResourceRecord> _resources;
#if DEBUG || GHOST_EDITOR #if DEBUG || GHOST_EDITOR
private readonly Dictionary<ResourceRecord, string> _resourceName; private readonly Dictionary<Handle<GPUResource>, string> _resourceName;
#endif #endif
private readonly UnsafeSlotMap<Mesh> _meshes; private readonly UnsafeSlotMap<Mesh> _meshes;
private readonly UnsafeSlotMap<Material> _materials; private readonly UnsafeSlotMap<Material> _materials;
private readonly DynamicArray<Shader?> _shaders; // NOTE: We use a simple list since shader is not frequently added/removed. This can save 4 bytes for each ecs component.
// NOTE: We use a simple list since shaderSlot is not frequently added/removed. This can save 4 bytes for each ecs component.
private readonly DynamicArray<Slot<Shader>> _shaders;
private readonly Dictionary<ShaderPassKey, ShaderPass> _shaderPasses; private readonly Dictionary<ShaderPassKey, ShaderPass> _shaderPasses;
private bool _disposed; private bool _disposed;
@@ -135,28 +134,41 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
where T : IResourceReleasable where T : IResourceReleasable
{ {
resource.ReleaseResource(this); resource.ReleaseResource(this);
resource = default!;
} }
public Handle<GPUResource> ImportExternalResource<T>(T resource, ResourceState initialState) public Handle<GPUResource> ImportExternalResource(ComPtr<ID3D12Resource> resource, ResourceState initialState, string? name = null)
where T : unmanaged
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
if (resource is not ComPtr<ID3D12Resource> d3d12Resource) var id = _resources.Add(new ResourceRecord(resource, initialState), out var generation);
{ var handle = new Handle<GPUResource>(id, generation);
throw new InvalidOperationException($"Expect ComPtr<ID3D12Resource> in D3D12ResourceDatabase, but got {typeof(T)}.");
}
var id = _resources.Add(new ResourceRecord(d3d12Resource, initialState), out var generation); #if DEBUG || GHOST_EDITOR
return new Handle<GPUResource>(id, generation); if (name != null)
{
_resourceName[handle] = name;
}
#endif
return handle;
} }
public Handle<GPUResource> AddResource(ComPtr<D3D12MA_Allocation> allocation, uint cpuFenceValue, ResourceState initialState, ResourceViewGroup resourceDescriptor, ResourceDesc desc) public Handle<GPUResource> AddResource(ComPtr<D3D12MA_Allocation> allocation, uint cpuFenceValue, ResourceState initialState, ResourceViewGroup resourceDescriptor, ResourceDesc desc, string? name = null)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
var id = _resources.Add(new ResourceRecord(allocation, cpuFenceValue, initialState, resourceDescriptor, desc), out var generation); var id = _resources.Add(new ResourceRecord(allocation, cpuFenceValue, initialState, resourceDescriptor, desc), out var generation);
return new Handle<GPUResource>(id, generation); var handle = new Handle<GPUResource>(id, generation);
#if DEBUG || GHOST_EDITOR
if (name != null)
{
_resourceName[handle] = name;
}
#endif
return handle;
} }
public ref ResourceRecord GetResourceInfo(Handle<GPUResource> handle) public ref ResourceRecord GetResourceInfo(Handle<GPUResource> handle)
@@ -218,13 +230,28 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
public int GetBindlessIndex(Handle<GPUResource> handle) public int GetBindlessIndex(Handle<GPUResource> handle)
{ {
ObjectDisposedException.ThrowIf(_disposed, this);
ref var info = ref GetResourceInfo(handle, out var exist); ref var info = ref GetResourceInfo(handle, out var exist);
if (!exist || !info.Allocated) if (!exist || !info.Allocated)
{ {
return -1; return -1;
} }
return info.descriptor.srv.value; return info.viewGroup.srv.value;
}
public string? GetResourceName(Handle<GPUResource> handle)
{
ObjectDisposedException.ThrowIf(_disposed, this);
#if DEBUG || GHOST_EDITOR
if (_resourceName.TryGetValue(handle, out var name))
{
return name;
}
#endif
return null;
} }
public unsafe void ReleaseResource(Handle<GPUResource> handle) public unsafe void ReleaseResource(Handle<GPUResource> handle)
@@ -244,9 +271,10 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
var refCount = info.Release(_descriptorAllocator); var refCount = info.Release(_descriptorAllocator);
#if DEBUG || GHOST_EDITOR #if DEBUG || GHOST_EDITOR
_resourceName.Remove(handle, out var name);
if (refCount > 0) if (refCount > 0)
{ {
throw new GPUResourceLeakException(refCount, info.ResourcePtr, _resourceName[info]); throw new GPUResourceLeakException(refCount, info.ResourcePtr, name ?? "Unknown Resource");
} }
#endif #endif
@@ -269,6 +297,8 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
public ref Mesh GetMeshReference(Handle<Mesh> handle) public ref Mesh GetMeshReference(Handle<Mesh> handle)
{ {
ObjectDisposedException.ThrowIf(_disposed, this);
ref var mesh = ref _meshes.GetElementReferenceAt(handle.id, handle.generation, out var exist); ref var mesh = ref _meshes.GetElementReferenceAt(handle.id, handle.generation, out var exist);
if (!exist) if (!exist)
{ {
@@ -280,6 +310,8 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
public void ReleaseMesh(Handle<Mesh> handle) public void ReleaseMesh(Handle<Mesh> handle)
{ {
ObjectDisposedException.ThrowIf(_disposed, this);
ref var mesh = ref _meshes.GetElementReferenceAt(handle.id, handle.generation, out var exist); ref var mesh = ref _meshes.GetElementReferenceAt(handle.id, handle.generation, out var exist);
if (!exist) if (!exist)
{ {
@@ -306,6 +338,8 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
public ref Material GetMaterialReference(Handle<Material> handle) public ref Material GetMaterialReference(Handle<Material> handle)
{ {
ObjectDisposedException.ThrowIf(_disposed, this);
ref var material = ref _materials.GetElementReferenceAt(handle.id, handle.generation, out var exist); ref var material = ref _materials.GetElementReferenceAt(handle.id, handle.generation, out var exist);
if (!exist) if (!exist)
{ {
@@ -317,6 +351,8 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
public void ReleaseMaterial(Handle<Material> handle) public void ReleaseMaterial(Handle<Material> handle)
{ {
ObjectDisposedException.ThrowIf(_disposed, this);
ref var material = ref _materials.GetElementReferenceAt(handle.id, handle.generation, out var exist); ref var material = ref _materials.GetElementReferenceAt(handle.id, handle.generation, out var exist);
if (!exist) if (!exist)
{ {
@@ -327,43 +363,45 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
_materials.Remove(handle.id, handle.generation); _materials.Remove(handle.id, handle.generation);
} }
public Identifier<Shader> AddShader(ref readonly Shader shader) public Identifier<Shader> AddShader(Shader shader)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
var id = _shaders.Count; var id = _shaders.Count;
_shaders.Add(new Slot<Shader> { value = shader, occupied = true }); _shaders.Add(shader);
return new Identifier<Shader>(id); return new Identifier<Shader>(id);
} }
public bool HasShader(Identifier<Shader> id) public bool HasShader(Identifier<Shader> id)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
return id.value >= 0 && id.value < _shaders.Count && _shaders[id.value].occupied; return id.value >= 0 && id.value < _shaders.Count && _shaders[id.value] != null;
} }
public ref Shader GetShaderReference(Identifier<Shader> id) public Shader GetShaderReference(Identifier<Shader> id)
{ {
ObjectDisposedException.ThrowIf(_disposed, this);
if (!HasShader(id)) if (!HasShader(id))
{ {
throw new ArgumentOutOfRangeException(nameof(id), $"Shader id {id} is invalid."); throw new ArgumentOutOfRangeException(nameof(id), $"Shader id {id} is invalid.");
} }
ref var shader = ref _shaders[id.value].value; var shader = _shaders[id.value]!;
return ref shader; return shader;
} }
public void ReleaseShader(Identifier<Shader> id) public void ReleaseShader(Identifier<Shader> id)
{ {
ObjectDisposedException.ThrowIf(_disposed, this);
if (!HasShader(id)) if (!HasShader(id))
{ {
return; return;
} }
ref var shaderSlot = ref _shaders[id.value]; ref var shader = ref _shaders[id.value]!;
ReleaseResource(ref shader);
ReleaseResource(ref shaderSlot.value);
shaderSlot.occupied = false;
} }
public void AddShaderPass(ShaderPassKey passKey, ShaderPass pass) public void AddShaderPass(ShaderPassKey passKey, ShaderPass pass)
@@ -384,36 +422,63 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
return pass; return pass;
} }
// Should we need to release the shaderSlot pass? public void RemoveShaderPass(ShaderPassKey passKey)
{
ObjectDisposedException.ThrowIf(_disposed, this);
if (_shaderPasses.Remove(passKey, out var pass))
{
ReleaseResource(ref pass);
}
}
public void Dispose() public void Dispose()
{ {
[Conditional("DEBUG"), Conditional("GHOST_EDITOR")]
static void ThrowMemoryLeakException(string resourceType, int count)
{
throw new InvalidOperationException($"ResourceAllocator is being disposed with {count} {resourceType} still registered. Ensure all resources are released before disposing.");
}
if (_disposed) if (_disposed)
{ {
return; return;
} }
#if DEBUG || GHOST_EDITOR
if (_resources.Count > 0) if (_resources.Count > 0)
{ {
throw new InvalidOperationException($"ResourceAllocator is being disposed with {_resources.Count} allocations still registered. Ensure all resources are released before disposing."); ThrowMemoryLeakException("GPU resources", _resources.Count);
} }
if (_meshes.Count > 0) if (_meshes.Count > 0)
{ {
throw new InvalidOperationException($"ResourceAllocator is being disposed with {_meshes.Count} meshes still registered. Ensure all meshes are released before disposing."); ThrowMemoryLeakException("meshes", _meshes.Count);
} }
if (_materials.Count > 0) if (_materials.Count > 0)
{ {
throw new InvalidOperationException($"ResourceAllocator is being disposed with {_materials.Count} materials still registered. Ensure all materials are released before disposing."); ThrowMemoryLeakException("materials", _materials.Count);
} }
if (_shaders.Count > 0) // Shader are reference type, it will be managed by GC, so we don't throw exception here.
for (var i = 0; i < _shaders.Count; i++)
{ {
throw new InvalidOperationException($"ResourceAllocator is being disposed with {_shaders.Count} shaders still registered. Ensure all shaders are released before disposing."); ref var shader = ref _shaders[i];
if (shader == null)
{
continue;
}
ReleaseResource(ref shader);
} }
#endif
// Same for shader pass.
foreach (var kv in _shaderPasses)
{
var pass = kv.Value;
ReleaseResource(ref pass);
}
_resources.Dispose(); _resources.Dispose();
_meshes.Dispose(); _meshes.Dispose();
_materials.Dispose(); _materials.Dispose();

View File

@@ -123,7 +123,6 @@ internal readonly struct ShaderReflectionData
internal static unsafe class D3D12ShaderCompiler internal static unsafe class D3D12ShaderCompiler
{ {
private static string GetProfileString(ShaderStage stage, CompilerTier version) private static string GetProfileString(ShaderStage stage, CompilerTier version)
{ {
return (stage, version) switch return (stage, version) switch

View File

@@ -60,7 +60,7 @@ internal unsafe class D3D12SwapChain : ISwapChain
{ {
Width = desc.width, Width = desc.width,
Height = desc.height, Height = desc.height,
Format = desc.format.ToD3D12Format(), Format = desc.format.ToDXGIFormat(),
SampleDesc = new DXGI_SAMPLE_DESC(1, 0), SampleDesc = new DXGI_SAMPLE_DESC(1, 0),
BufferUsage = DXGI_USAGE_BACK_BUFFER | DXGI_USAGE_RENDER_TARGET_OUTPUT, BufferUsage = DXGI_USAGE_BACK_BUFFER | DXGI_USAGE_RENDER_TARGET_OUTPUT,
BufferCount = D3D12PipelineResource.BACK_BUFFER_COUNT, BufferCount = D3D12PipelineResource.BACK_BUFFER_COUNT,

View File

@@ -1,21 +1,11 @@
using Misaki.HighPerformance.LowLevel.Utilities; using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel.Utilities;
using TerraFX.Interop.DirectX; using TerraFX.Interop.DirectX;
using static TerraFX.Aliases.D3D12_Alias; using static TerraFX.Aliases.D3D12_Alias;
namespace Ghost.Graphics.D3D12.Utilities; namespace Ghost.Graphics.D3D12.Utilities;
internal unsafe static class ID3D12Resource_Extensions
{
extension(ID3D12Resource resource)
{
public void SetName(ReadOnlySpan<char> name)
{
resource.SetName(name.GetUnsafePtr());
}
}
}
internal static class D3D12_RASTERIZER_DESC_Extensions internal static class D3D12_RASTERIZER_DESC_Extensions
{ {
extension(D3D12_RASTERIZER_DESC) extension(D3D12_RASTERIZER_DESC)
@@ -150,4 +140,51 @@ internal static class D3D12_DEPTH_STENCILOP_DESC_Extensions
}; };
} }
} }
}
internal unsafe static class D3D12Utility
{
public static void SetName(ref this ID3D12Resource resource, ReadOnlySpan<char> name)
{
resource.SetName(name.GetUnsafePtr());
}
public static TextureDimension ToTextureDimension(this D3D12_RESOURCE_DIMENSION dimension)
{
return dimension switch
{
D3D12_RESOURCE_DIMENSION.D3D12_RESOURCE_DIMENSION_TEXTURE1D => TextureDimension.Texture2D,
D3D12_RESOURCE_DIMENSION.D3D12_RESOURCE_DIMENSION_TEXTURE2D => TextureDimension.Texture2D,
D3D12_RESOURCE_DIMENSION.D3D12_RESOURCE_DIMENSION_TEXTURE3D => TextureDimension.Texture3D,
_ => TextureDimension.Unknown,
};
}
public static DXGI_FORMAT ToDXGIFormat(this TextureFormat format)
{
return format switch
{
TextureFormat.R8G8B8A8_UNorm => DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM,
TextureFormat.B8G8R8A8_UNorm => DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM,
TextureFormat.R16G16B16A16_Float => DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_FLOAT,
TextureFormat.R32G32B32A32_Float => DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT,
TextureFormat.D24_UNorm_S8_UInt => DXGI_FORMAT.DXGI_FORMAT_D24_UNORM_S8_UINT,
TextureFormat.D32_Float => DXGI_FORMAT.DXGI_FORMAT_D32_FLOAT,
_ => throw new NotSupportedException($"Texture format {format} is not supported."),
};
}
public static TextureFormat ToTextureFormat(this DXGI_FORMAT format)
{
return format switch
{
DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM => TextureFormat.R8G8B8A8_UNorm,
DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM => TextureFormat.B8G8R8A8_UNorm,
DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_FLOAT => TextureFormat.R16G16B16A16_Float,
DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT => TextureFormat.R32G32B32A32_Float,
DXGI_FORMAT.DXGI_FORMAT_D24_UNORM_S8_UINT => TextureFormat.D24_UNorm_S8_UInt,
DXGI_FORMAT.DXGI_FORMAT_D32_FLOAT => TextureFormat.D32_Float,
_ => TextureFormat.Unknown,
};
}
} }

View File

@@ -18,20 +18,12 @@ internal struct CBufferCache : IResourceReleasable
public readonly Handle<GraphicsBuffer> GpuResource => _gpuResource; public readonly Handle<GraphicsBuffer> GpuResource => _gpuResource;
public readonly uint AlignedSize => _alignedSize; public readonly uint AlignedSize => _alignedSize;
public unsafe CBufferCache(IResourceAllocator allocator, uint bufferSize) public unsafe CBufferCache(Handle<GraphicsBuffer> buffer, uint bufferSize)
{ {
_alignedSize = (bufferSize + 255u) & ~255u; _alignedSize = (bufferSize + 255u) & ~255u;
_cpuData = new((int)AlignedSize, Allocator.Persistent); _cpuData = new((int)AlignedSize, Allocator.Persistent);
_gpuResource = buffer;
var desc = new BufferDesc
{
Size = bufferSize,
Usage = BufferUsage.Constant,
MemoryType = ResourceMemoryType.Default,
};
_gpuResource = allocator.CreateBuffer(ref desc);
} }
public void ReleaseResource(IResourceDatabase database) public void ReleaseResource(IResourceDatabase database)
@@ -52,11 +44,6 @@ public struct Material : IResourceReleasable, IHandleType
public readonly Identifier<Shader> Shader => _shader; public readonly Identifier<Shader> Shader => _shader;
public Material(Identifier<Shader> shader, IResourceAllocator allocator, IResourceDatabase database)
{
SetShader(shader, allocator, database);
}
internal ref CBufferCache GetPassCache(int passIndex) internal ref CBufferCache GetPassCache(int passIndex)
{ {
return ref _materialPropertiesCache[passIndex]; return ref _materialPropertiesCache[passIndex];
@@ -77,7 +64,16 @@ public struct Material : IResourceReleasable, IHandleType
{ {
var pass = database.GetShaderPass(shader.GetPassKey(i)); var pass = database.GetShaderPass(shader.GetPassKey(i));
var cbufferInfo = pass.PassPropertyInfo; var cbufferInfo = pass.PassPropertyInfo;
_materialPropertiesCache[i] = new CBufferCache(allocator, cbufferInfo.Size);
var desc = new BufferDesc
{
Size = cbufferInfo.Size,
Usage = BufferUsage.Constant,
MemoryType = ResourceMemoryType.Default,
};
var buffer = allocator.CreateBuffer(ref desc);
_materialPropertiesCache[i] = new CBufferCache(buffer, cbufferInfo.Size);
} }
} }
@@ -95,7 +91,7 @@ public struct Material : IResourceReleasable, IHandleType
public ref struct MaterialAccessor public ref struct MaterialAccessor
{ {
private ref Material _materialData; private ref Material _materialData;
private readonly ref Shader _shader; private Shader _shader;
private readonly IResourceDatabase _resourceDatabase; private readonly IResourceDatabase _resourceDatabase;
@@ -104,7 +100,7 @@ public ref struct MaterialAccessor
_resourceDatabase = resourceDatabase; _resourceDatabase = resourceDatabase;
_materialData = ref resourceDatabase.GetMaterialReference(material); _materialData = ref resourceDatabase.GetMaterialReference(material);
_shader = ref resourceDatabase.GetShaderReference(_materialData.Shader); _shader = resourceDatabase.GetShaderReference(_materialData.Shader);
} }
private readonly unsafe void WriteToCache<T>(string propertyName, in T value) private readonly unsafe void WriteToCache<T>(string propertyName, in T value)

View File

@@ -1,7 +1,7 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Core.Contracts;
using Ghost.Core.Graphics; using Ghost.Core.Graphics;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Collections;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -51,13 +51,13 @@ public readonly struct CBufferInfo
} }
} }
public readonly unsafe struct ShaderPass public unsafe class ShaderPass : IResourceReleasable
{ {
// NOTE: This is for per pass cbuffer only. Global, per view, and per mesh cbuffers are fixed. // NOTE: This is for per pass cbuffer only. Global, per view, and per mesh cbuffers are fixed.
private readonly Dictionary<string, int> _propertyLookup; private readonly Dictionary<string, int> _propertyLookup;
private readonly UnsafeList<PropertyInfo> _properties; private readonly UnsafeList<PropertyInfo> _properties;
internal readonly CBufferInfo PassPropertyInfo internal CBufferInfo PassPropertyInfo
{ {
get; get;
} }
@@ -69,36 +69,41 @@ public readonly unsafe struct ShaderPass
_propertyLookup = propertyNameToIdMap; _propertyLookup = propertyNameToIdMap;
} }
public readonly int GetPropertyId(string propertyName) public int GetPropertyId(string propertyName)
{ {
return _propertyLookup.TryGetValue(propertyName, out var id) ? id : -1; return _propertyLookup.TryGetValue(propertyName, out var id) ? id : -1;
} }
public readonly PropertyInfo GetPropertyInfo(int id) public PropertyInfo GetPropertyInfo(int id)
{ {
return _properties[id]; return _properties[id];
} }
public readonly PropertyInfo GetPropertyInfo(string propertyName) public PropertyInfo GetPropertyInfo(string propertyName)
{ {
return _properties[GetPropertyId(propertyName)]; return _properties[GetPropertyId(propertyName)];
} }
void IResourceReleasable.ReleaseResource(IResourceDatabase database)
{
_properties.Dispose();
}
} }
/// <summary> /// <summary>
/// A representation of a GPU shader, including all the passes it contains. /// A representation of a GPU shader, including all the passes it contains.
/// </summary> /// </summary>
public readonly struct Shader : IResourceReleasable, IIdentifierType public class Shader : IResourceReleasable, IIdentifierType
{ {
private readonly ShaderPassKey[] _passIDs; private UnsafeArray<ShaderPassKey> _passIDs;
private readonly Dictionary<string, int> _passLookup; // pass name to index private readonly Dictionary<string, int> _passLookup; // pass name to index
private readonly Dictionary<string, List<int>> _propertyLookup; // property name to pass index (property name to list of pass indices that contain the property) 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.Length; public int PassCount => _passIDs.Count;
internal Shader(ShaderDescriptor descriptor) internal Shader(ShaderDescriptor descriptor)
{ {
_passIDs = new ShaderPassKey[descriptor.passes.Count]; _passIDs = new UnsafeArray<ShaderPassKey>(descriptor.passes.Count, Allocator.Persistent);
_passLookup = new(descriptor.passes.Count); _passLookup = new(descriptor.passes.Count);
_propertyLookup = new(descriptor.passes.Count); _propertyLookup = new(descriptor.passes.Count);
@@ -132,12 +137,12 @@ public readonly struct Shader : IResourceReleasable, IIdentifierType
} }
} }
public readonly ShaderPassKey GetPassKey(int index) public ShaderPassKey GetPassKey(int index)
{ {
return _passIDs[index]; return _passIDs[index];
} }
public readonly bool TryGetPassKey(string passName, out ShaderPassKey? passID) public bool TryGetPassKey(string passName, out ShaderPassKey? passID)
{ {
var index = _passLookup.GetValueOrDefault(passName, -1); var index = _passLookup.GetValueOrDefault(passName, -1);
if (index == -1) if (index == -1)
@@ -150,7 +155,7 @@ public readonly struct Shader : IResourceReleasable, IIdentifierType
return true; return true;
} }
public readonly IReadOnlyCollection<int> GetPropertyPassIndices(string propertyName) public IReadOnlyCollection<int> GetPropertyPassIndices(string propertyName)
{ {
if (_propertyLookup.TryGetValue(propertyName, out var passIndices)) if (_propertyLookup.TryGetValue(propertyName, out var passIndices))
{ {
@@ -162,6 +167,6 @@ public readonly struct Shader : IResourceReleasable, IIdentifierType
void IResourceReleasable.ReleaseResource(IResourceDatabase database) void IResourceReleasable.ReleaseResource(IResourceDatabase database)
{ {
// Should we do something here? _passIDs.Dispose();
} }
} }

View File

@@ -23,6 +23,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Ghost.Core\Ghost.Core.csproj" /> <ProjectReference Include="..\Ghost.Core\Ghost.Core.csproj" />
<ProjectReference Include="..\Ghost.Shader\Ghost.Shader.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -1,4 +1,5 @@
using Misaki.HighPerformance.Utilities; using Ghost.Graphics.D3D12.Utilities;
using Misaki.HighPerformance.Utilities;
using System.IO.Hashing; using System.IO.Hashing;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -74,7 +75,7 @@ internal struct GraphicsPipelineHash
data[1] = rtvCount; data[1] = rtvCount;
data[2] = (ulong)dsvFormat; data[2] = (ulong)dsvFormat;
for (int i = 0; i < 8; i++) for (var i = 0; i < 8; i++)
{ {
data[3 + i] = (ulong)rtvFormats[i]; data[3 + i] = (ulong)rtvFormats[i];
} }
@@ -425,18 +426,88 @@ public struct BufferDesc
} }
} }
public static class TextureDimensionExtension /// <summary>
/// Swap chain description
/// </summary>
public struct SwapChainDesc
{ {
public static TextureDimension ToTextureDimension(this D3D12_RESOURCE_DIMENSION dimension) /// <summary>
/// Width of the swap chain
/// </summary>
public uint width;
/// <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)
{ {
return dimension switch this.width = width;
this.height = height;
this.format = format;
this.target = target;
}
}
/// <summary>
/// Swap chain target (window handle or composition surface)
/// </summary>
public struct SwapChainTarget
{
/// <summary>
/// Target type
/// </summary>
public SwapChainTargetType type;
/// <summary>
/// Window handle for HWND targets
/// </summary>
public nint windowHandle;
/// <summary>
/// Composition surface for UWP/WinUI targets
/// </summary>
public object? compositionSurface;
public static SwapChainTarget FromWindowHandle(nint hwnd)
{
return new SwapChainTarget
{ {
D3D12_RESOURCE_DIMENSION.D3D12_RESOURCE_DIMENSION_TEXTURE1D => TextureDimension.Texture2D, type = SwapChainTargetType.WindowHandle,
D3D12_RESOURCE_DIMENSION.D3D12_RESOURCE_DIMENSION_TEXTURE2D => TextureDimension.Texture2D, windowHandle = hwnd,
D3D12_RESOURCE_DIMENSION.D3D12_RESOURCE_DIMENSION_TEXTURE3D => TextureDimension.Texture3D, compositionSurface = null
_ => TextureDimension.Unknown,
}; };
} }
public static SwapChainTarget FromCompositionSurface(object surface)
{
return new SwapChainTarget
{
type = SwapChainTargetType.Composition,
windowHandle = nint.Zero,
compositionSurface = surface
};
}
}
/// <summary>
/// Swap chain target types
/// </summary>
public enum SwapChainTargetType
{
WindowHandle,
Composition
} }

View File

@@ -1,4 +1,5 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Core.Graphics;
using Ghost.Graphics.Data; using Ghost.Graphics.Data;
using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Collections;
@@ -46,7 +47,8 @@ public interface IResourceAllocator
/// Creates a new shader and returns its unique identifier. /// Creates a new shader and returns its unique identifier.
/// </summary> /// </summary>
/// <returns>An <see cref="Identifier{Shader}"/> representing the newly created shader.</returns> /// <returns>An <see cref="Identifier{Shader}"/> representing the newly created shader.</returns>
public Identifier<Shader> CreateShader(); /// <param name="descriptor">The viewGroup containing the shader's properties and passes.</param>
public Identifier<Shader> CreateShader(ShaderDescriptor descriptor);
/// <summary> /// <summary>
/// Release a resource given its handle /// Release a resource given its handle

View File

@@ -13,6 +13,7 @@ public interface IResourceReleasable
public interface IResourceDatabase public interface IResourceDatabase
{ {
/*
/// <summary> /// <summary>
/// Imports an external unmanaged resource and returns a handle for use within the resource management system. /// Imports an external unmanaged resource and returns a handle for use within the resource management system.
/// </summary> /// </summary>
@@ -20,8 +21,9 @@ public interface IResourceDatabase
/// <param name="resourcePtr">A pointer to the external unmanaged resource to be imported. Must remain valid for the duration of the resource's usage.</param> /// <param name="resourcePtr">A pointer to the external unmanaged resource to be imported. Must remain valid for the duration of the resource's usage.</param>
/// <param name="initialState">The initial state to assign to the imported resource.</param> /// <param name="initialState">The initial state to assign to the imported resource.</param>
/// <returns>A handle representing the imported resource, which can be used for subsequent operations.</returns> /// <returns>A handle representing the imported resource, which can be used for subsequent operations.</returns>
unsafe Handle<GPUResource> ImportExternalResource<T>(T resourcePtr, ResourceState initialState) unsafe Handle<GPUResource> ImportExternalResource<T>(T resourcePtr, ResourceState initialState, string? name = null)
where T : unmanaged; where T : unmanaged;
*/
/// <summary> /// <summary>
/// Retrieves the current state of the specified resource. /// Retrieves the current state of the specified resource.
@@ -51,6 +53,16 @@ public interface IResourceDatabase
/// <returns>The bindless index corresponding to the specified GPU resource handle. -1 if the resource does not support bindless access or is not found.</returns> /// <returns>The bindless index corresponding to the specified GPU resource handle. -1 if the resource does not support bindless access or is not found.</returns>
int GetBindlessIndex(Handle<GPUResource> handle); int GetBindlessIndex(Handle<GPUResource> handle);
/// <summary>
/// Retrieves the name of the GPU resource associated with the specified handle.
/// </summary>
/// <remarks>
/// You should only use this method in debug builds or inside engine editor.
/// </remarks>
/// <param name="handle">A handle to the GPU resource for which to obtain the name. Must reference a valid resource.</param>
/// <returns>The name of the GPU resource associated with the specified handle, or null if the resource does not have a name.</returns>
string? GetResourceName(Handle<GPUResource> handle);
/// <summary> /// <summary>
/// Removes a resource from the database using its handle. /// Removes a resource from the database using its handle.
/// </summary> /// </summary>
@@ -116,7 +128,7 @@ public interface IResourceDatabase
/// </summary> /// </summary>
/// <param name="shader">The shader to add. The shader is passed by read-only reference and will not be modified.</param> /// <param name="shader">The shader to add. The shader is passed by read-only reference and will not be modified.</param>
/// <returns>The <see cref="Identifier{Shader}"/> representing the newly added shader.</returns> /// <returns>The <see cref="Identifier{Shader}"/> representing the newly added shader.</returns>
Identifier<Shader> AddShader(ref readonly Shader shader); Identifier<Shader> AddShader(Shader shader);
/// <summary> /// <summary>
/// Determines whether a shader with the specified identifier exists in the collection. /// Determines whether a shader with the specified identifier exists in the collection.
@@ -130,7 +142,7 @@ public interface IResourceDatabase
/// </summary> /// </summary>
/// <param name="id">The identifier of the shader to retrieve. Must refer to a valid shader.</param> /// <param name="id">The identifier of the shader to retrieve. Must refer to a valid shader.</param>
/// <returns>A reference to the shader corresponding to the specified identifier.</returns> /// <returns>A reference to the shader corresponding to the specified identifier.</returns>
ref Shader GetShaderReference(Identifier<Shader> id); Shader GetShaderReference(Identifier<Shader> id);
/// <summary> /// <summary>
/// Releases the shader associated with the specified identifier, freeing any resources allocated to it. /// Releases the shader associated with the specified identifier, freeing any resources allocated to it.

View File

@@ -50,88 +50,4 @@ public interface ISwapChain : IDisposable
/// <param name="width">New width</param> /// <param name="width">New width</param>
/// <param name="height">New height</param> /// <param name="height">New height</param>
public void Resize(uint width, uint height); public void Resize(uint width, uint height);
}
/// <summary>
/// Swap chain description
/// </summary>
public struct SwapChainDesc
{
/// <summary>
/// Width of the swap chain
/// </summary>
public uint width;
/// <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)
{
this.width = width;
this.height = height;
this.format = format;
this.target = target;
}
}
/// <summary>
/// Swap chain target (window handle or composition surface)
/// </summary>
public struct SwapChainTarget
{
/// <summary>
/// Target type
/// </summary>
public SwapChainTargetType type;
/// <summary>
/// Window handle for HWND targets
/// </summary>
public nint windowHandle;
/// <summary>
/// Composition surface for UWP/WinUI targets
/// </summary>
public object? compositionSurface;
public static SwapChainTarget FromWindowHandle(nint hwnd)
{
return new SwapChainTarget
{
type = SwapChainTargetType.WindowHandle,
windowHandle = hwnd,
compositionSurface = null
};
}
public static SwapChainTarget FromCompositionSurface(object surface)
{
return new SwapChainTarget
{
type = SwapChainTargetType.Composition,
windowHandle = nint.Zero,
compositionSurface = surface
};
}
}
/// <summary>
/// Swap chain target types
/// </summary>
public enum SwapChainTargetType
{
WindowHandle,
Composition
} }

View File

@@ -1,37 +1,9 @@
using TerraFX.Interop.DirectX; using TerraFX.Interop.DirectX;
namespace Ghost.Graphics.RHI; namespace Ghost.Graphics.RHI;
internal static class TextureFormatExtensions internal static class RHIUtility
{ {
public static DXGI_FORMAT ToD3D12Format(this TextureFormat format)
{
return format switch
{
TextureFormat.R8G8B8A8_UNorm => DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM,
TextureFormat.B8G8R8A8_UNorm => DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM,
TextureFormat.R16G16B16A16_Float => DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_FLOAT,
TextureFormat.R32G32B32A32_Float => DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT,
TextureFormat.D24_UNorm_S8_UInt => DXGI_FORMAT.DXGI_FORMAT_D24_UNORM_S8_UINT,
TextureFormat.D32_Float => DXGI_FORMAT.DXGI_FORMAT_D32_FLOAT,
_ => throw new NotSupportedException($"Texture format {format} is not supported."),
};
}
public static TextureFormat ToTextureFormat(this DXGI_FORMAT format)
{
return format switch
{
DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM => TextureFormat.R8G8B8A8_UNorm,
DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM => TextureFormat.B8G8R8A8_UNorm,
DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_FLOAT => TextureFormat.R16G16B16A16_Float,
DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT => TextureFormat.R32G32B32A32_Float,
DXGI_FORMAT.DXGI_FORMAT_D24_UNORM_S8_UINT => TextureFormat.D24_UNorm_S8_UInt,
DXGI_FORMAT.DXGI_FORMAT_D32_FLOAT => TextureFormat.D32_Float,
_ => TextureFormat.Unknown,
};
}
public static int GetBytesPerPixel(this TextureFormat format) public static int GetBytesPerPixel(this TextureFormat format)
{ {
return format switch return format switch

View File

@@ -3,6 +3,7 @@ using Ghost.Graphics.Contracts;
using Ghost.Graphics.Data; using Ghost.Graphics.Data;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Ghost.Graphics.Utilities; using Ghost.Graphics.Utilities;
using Ghost.Shader.Compiler;
using Misaki.HighPerformance.Image; using Misaki.HighPerformance.Image;
namespace Ghost.Graphics.RenderPasses; namespace Ghost.Graphics.RenderPasses;
@@ -27,13 +28,17 @@ internal unsafe class MeshRenderPass : IRenderPass
public void Initialize(ref readonly RenderingContext ctx, IResourceAllocator resourceAllocator, IPipelineLibrary stateController) public void Initialize(ref readonly RenderingContext ctx, IResourceAllocator resourceAllocator, IPipelineLibrary stateController)
{ {
var shaderDescriptor = SDLCompiler.CompileShader("F:\\csharp\\GhostEngine\\Ghost.Graphics\\RenderPasses\\ShaderCode.hlsl").GetValueOrThrow();
stateController.CompileShader(shaderDescriptor);
stateController.PreCookPipelineState();
MeshBuilder.CreateCube(0.75f, default, out var vertices, out var indices); MeshBuilder.CreateCube(0.75f, default, out var vertices, out var indices);
_mesh = ctx.CreateMesh(vertices, indices); _mesh = ctx.CreateMesh(vertices, indices);
ctx.UploadMesh(_mesh, true); ctx.UploadMesh(_mesh, true);
_shader = resourceAllocator.CreateShader(); _shader = resourceAllocator.CreateShader(shaderDescriptor);
_material = resourceAllocator.CreateMaterial(_shader); _material = resourceAllocator.CreateMaterial(_shader);
var imageResults = new ImageResult[_textureFiles.Length]; var imageResults = new ImageResult[_textureFiles.Length];
@@ -59,9 +64,6 @@ internal unsafe class MeshRenderPass : IRenderPass
_textures[i] = ctx.CreateTexture(ref desc); _textures[i] = ctx.CreateTexture(ref desc);
ctx.UploadTexture(_textures[i], new Span<byte>(imageData.Data, (int)imageData.Size)); ctx.UploadTexture(_textures[i], new Span<byte>(imageData.Data, (int)imageData.Size));
} }
stateController.CompileShader(_shader, "F:\\csharp\\GhostEngine\\Ghost.Graphics\\RenderPasses\\ShaderCode.hlsl");
stateController.PreCookPipelineState();
} }
public void Execute(ref readonly RenderingContext ctx) public void Execute(ref readonly RenderingContext ctx)

View File

@@ -10,8 +10,8 @@ var source = File.ReadAllText("F:/csharp/GhostEngine/Ghost.Graphics/test.gshader
var lexer = new Lexer(source); var lexer = new Lexer(source);
var stream = new TokenStream(lexer.Tokenize()); var stream = new TokenStream(lexer.Tokenize());
var shaderInfo = ShaderCompiler.ParseShaders(stream); var shaderInfo = SDLCompiler.ParseShaders(stream);
var model = ShaderCompiler.SemanticAnalysis(shaderInfo[0], out var errors); var model = SDLCompiler.SemanticAnalysis(shaderInfo[0], out var errors);
foreach (var error in errors) foreach (var error in errors)
{ {
@@ -29,8 +29,8 @@ if (model == null)
return; return;
} }
var descriptor = ShaderCompiler.ResolveShader(model); var descriptor = SDLCompiler.ResolveShader(model);
ShaderCompiler.CompileShader(descriptor, "C:/Users/Misaki/Downloads/Archive"); SDLCompiler.GenerateShader(descriptor, "C:/Users/Misaki/Downloads/Archive");
Console.WriteLine("Shader compiled successfully:"); Console.WriteLine("Shader compiled successfully:");

View File

@@ -1,3 +1,4 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Ghost.Shader.Test")] [assembly: InternalsVisibleTo("Ghost.Shader.Test")]
[assembly: InternalsVisibleTo("Ghost.Graphics")]

View File

@@ -27,7 +27,7 @@ internal class DefinesBlock : IBlockParser<List<Token>, List<string>>
return defines; return defines;
} }
public static List<string>? SemanticAnalysis(List<Token>? syntax, List<ShaderError> errors) public static List<string>? SemanticAnalysis(List<Token>? syntax, List<SDLError> errors)
{ {
if (syntax == null) if (syntax == null)
{ {

View File

@@ -4,5 +4,5 @@ internal interface IBlockParser<T, U>
{ {
public static abstract bool ShouldEnter(Token token); public static abstract bool ShouldEnter(Token token);
public static abstract T? Parse(TokenStreamSlice ts); public static abstract T? Parse(TokenStreamSlice ts);
public static abstract U? SemanticAnalysis(T? syntax, List<ShaderError> errors); public static abstract U? SemanticAnalysis(T? syntax, List<SDLError> errors);
} }

View File

@@ -27,7 +27,7 @@ internal class IncludesBlock : IBlockParser<List<Token>, List<string>>
return includes; return includes;
} }
public static List<string>? SemanticAnalysis(List<Token>? syntax, List<ShaderError> errors) public static List<string>? SemanticAnalysis(List<Token>? syntax, List<SDLError> errors)
{ {
if (syntax == null || syntax.Count == 0) if (syntax == null || syntax.Count == 0)
{ {
@@ -44,7 +44,7 @@ internal class IncludesBlock : IBlockParser<List<Token>, List<string>>
} }
else else
{ {
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = $"Included file '{path}' not found.", message = $"Included file '{path}' not found.",
line = includeToken.line, line = includeToken.line,

View File

@@ -30,7 +30,7 @@ internal class KeywordsBlock : IBlockParser<List<FunctionCallDeclaration>, List<
return keywords; return keywords;
} }
public static List<KeywordsGroup>? SemanticAnalysis(List<FunctionCallDeclaration>? syntax, List<ShaderError> errors) public static List<KeywordsGroup>? SemanticAnalysis(List<FunctionCallDeclaration>? syntax, List<SDLError> errors)
{ {
if (syntax == null) if (syntax == null)
{ {
@@ -42,7 +42,7 @@ internal class KeywordsBlock : IBlockParser<List<FunctionCallDeclaration>, List<
{ {
if (keyword.arguments == null || keyword.arguments.Count == 0) if (keyword.arguments == null || keyword.arguments.Count == 0)
{ {
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = $"Function '{keyword.name.lexeme}' must have at least one argument.", message = $"Function '{keyword.name.lexeme}' must have at least one argument.",
line = keyword.name.line, line = keyword.name.line,
@@ -61,7 +61,7 @@ internal class KeywordsBlock : IBlockParser<List<FunctionCallDeclaration>, List<
group.type = KeywordType.Static; group.type = KeywordType.Static;
break; break;
default: default:
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = $"Unknown function name '{keyword.name.lexeme}'.", message = $"Unknown function name '{keyword.name.lexeme}'.",
line = keyword.name.line, line = keyword.name.line,

View File

@@ -61,7 +61,7 @@ internal class PassBlock : IBlockParser<PassSyntax, PassSemantic>
return pass; return pass;
} }
public static PassSemantic? SemanticAnalysis(PassSyntax? syntax, List<ShaderError> errors) public static PassSemantic? SemanticAnalysis(PassSyntax? syntax, List<SDLError> errors)
{ {
if (syntax == null) if (syntax == null)
{ {
@@ -81,7 +81,7 @@ internal class PassBlock : IBlockParser<PassSyntax, PassSemantic>
if (semantic.localProperties != null if (semantic.localProperties != null
&& semantic.localProperties.Any(p => p.scope == PropertyScope.Global)) && semantic.localProperties.Any(p => p.scope == PropertyScope.Global))
{ {
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = "Global properties cannot be declared inside a pass. Move them to the shader properties block.", message = "Global properties cannot be declared inside a pass. Move them to the shader properties block.",
line = syntax.name.line, line = syntax.name.line,
@@ -108,7 +108,7 @@ internal class PassBlock : IBlockParser<PassSyntax, PassSemantic>
break; break;
default: default:
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = $"Unknown function '{func.name.lexeme}' in pass {syntax.name.lexeme}.", message = $"Unknown function '{func.name.lexeme}' in pass {syntax.name.lexeme}.",
line = func.name.line, line = func.name.line,
@@ -123,7 +123,7 @@ internal class PassBlock : IBlockParser<PassSyntax, PassSemantic>
{ {
// TODO: Inheritance from base pass. // TODO: Inheritance from base pass.
// TODO: Add mesh shader support. // TODO: Add mesh shader support.
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = $"Pass {syntax.name.lexeme} must contain a mesh shader (ms) and a pixel shader (ps) declaration.", message = $"Pass {syntax.name.lexeme} must contain a mesh shader (ms) and a pixel shader (ps) declaration.",
line = syntax.name.line, line = syntax.name.line,
@@ -134,11 +134,11 @@ internal class PassBlock : IBlockParser<PassSyntax, PassSemantic>
return semantic; return semantic;
} }
private static void AnalysisShaderEntry(List<ShaderError> errors, FunctionCallDeclaration func, ref ShaderEntryPoint shaderEntryPoint) private static void AnalysisShaderEntry(List<SDLError> errors, FunctionCallDeclaration func, ref ShaderEntryPoint shaderEntryPoint)
{ {
if (func.arguments?.Count != 2) if (func.arguments?.Count != 2)
{ {
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = "Shader declaration requires exactly two arguments: (shaderPath, entryPoint).", message = "Shader declaration requires exactly two arguments: (shaderPath, entryPoint).",
line = func.name.line, line = func.name.line,

View File

@@ -38,7 +38,7 @@ internal class PipelineBlock : IBlockParser<PipelineSyntax, PipelineSemantic>
return pipeline; return pipeline;
} }
public static PipelineSemantic? SemanticAnalysis(PipelineSyntax? syntax, List<ShaderError> errors) public static PipelineSemantic? SemanticAnalysis(PipelineSyntax? syntax, List<SDLError> errors)
{ {
if (syntax == null) if (syntax == null)
{ {
@@ -80,7 +80,7 @@ internal class PipelineBlock : IBlockParser<PipelineSyntax, PipelineSemantic>
semantic.zTest = ZTestOptions.Always; semantic.zTest = ZTestOptions.Always;
break; break;
default: default:
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = $"Invalid ZTest option: {valueDecl.value.lexeme}", message = $"Invalid ZTest option: {valueDecl.value.lexeme}",
line = valueDecl.value.line, line = valueDecl.value.line,
@@ -100,7 +100,7 @@ internal class PipelineBlock : IBlockParser<PipelineSyntax, PipelineSemantic>
semantic.zWrite = ZWriteOptions.Off; semantic.zWrite = ZWriteOptions.Off;
break; break;
default: default:
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = $"Invalid ZWrite option: {valueDecl.value.lexeme}", message = $"Invalid ZWrite option: {valueDecl.value.lexeme}",
line = valueDecl.value.line, line = valueDecl.value.line,
@@ -123,7 +123,7 @@ internal class PipelineBlock : IBlockParser<PipelineSyntax, PipelineSemantic>
semantic.cull = CullOptions.Back; semantic.cull = CullOptions.Back;
break; break;
default: default:
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = $"Invalid Cull option: {valueDecl.value.lexeme}", message = $"Invalid Cull option: {valueDecl.value.lexeme}",
line = valueDecl.value.line, line = valueDecl.value.line,
@@ -152,7 +152,7 @@ internal class PipelineBlock : IBlockParser<PipelineSyntax, PipelineSemantic>
semantic.blend = BlendOptions.PremultipliedAlpha; semantic.blend = BlendOptions.PremultipliedAlpha;
break; break;
default: default:
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = $"Invalid Blend option: {valueDecl.value.lexeme}", message = $"Invalid Blend option: {valueDecl.value.lexeme}",
line = valueDecl.value.line, line = valueDecl.value.line,
@@ -169,7 +169,7 @@ internal class PipelineBlock : IBlockParser<PipelineSyntax, PipelineSemantic>
} }
else else
{ {
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = $"Invalid Color Mask value: {valueDecl.value.lexeme}", message = $"Invalid Color Mask value: {valueDecl.value.lexeme}",
line = valueDecl.value.line, line = valueDecl.value.line,

View File

@@ -6,7 +6,7 @@ namespace Ghost.Shader.Compiler.Parser;
internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySemantic>> internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySemantic>>
{ {
private delegate object? PropertyValueBuilder(List<Token> tokens, List<ShaderError> errors); private delegate object? PropertyValueBuilder(List<Token> tokens, List<SDLError> errors);
private sealed record PropTypeInfo(int ArgCount, TokenType ArgTokenType, PropertyValueBuilder? Builder); private sealed record PropTypeInfo(int ArgCount, TokenType ArgTokenType, PropertyValueBuilder? Builder);
@@ -78,11 +78,11 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
[ShaderPropertyType.TextureCube] = 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<ShaderError> errors) private static float ParseFloatValue(Token token, List<SDLError> errors)
{ {
if (!float.TryParse(token.lexeme, CultureInfo.InvariantCulture, out var result)) if (!float.TryParse(token.lexeme, CultureInfo.InvariantCulture, out var result))
{ {
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = $"Failed to parse float value '{token.lexeme}'.", message = $"Failed to parse float value '{token.lexeme}'.",
line = token.line, line = token.line,
@@ -93,11 +93,11 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
return result; return result;
} }
private static int ParseIntValue(Token token, List<ShaderError> errors) private static int ParseIntValue(Token token, List<SDLError> errors)
{ {
if (!int.TryParse(token.lexeme, CultureInfo.InvariantCulture, out var result)) if (!int.TryParse(token.lexeme, CultureInfo.InvariantCulture, out var result))
{ {
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = $"Failed to parse int value '{token.lexeme}'.", message = $"Failed to parse int value '{token.lexeme}'.",
line = token.line, line = token.line,
@@ -108,11 +108,11 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
return result; return result;
} }
private static uint ParseUIntValue(Token token, List<ShaderError> errors) private static uint ParseUIntValue(Token token, List<SDLError> errors)
{ {
if (!uint.TryParse(token.lexeme, CultureInfo.InvariantCulture, out var result)) if (!uint.TryParse(token.lexeme, CultureInfo.InvariantCulture, out var result))
{ {
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = $"Failed to parse uint value '{token.lexeme}'.", message = $"Failed to parse uint value '{token.lexeme}'.",
line = token.line, line = token.line,
@@ -123,11 +123,11 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
return result; return result;
} }
private static bool ParseBoolValue(Token token, List<ShaderError> errors) private static bool ParseBoolValue(Token token, List<SDLError> errors)
{ {
if (!bool.TryParse(token.lexeme, out var result)) if (!bool.TryParse(token.lexeme, out var result))
{ {
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = $"Failed to parse bool value '{token.lexeme}'.", message = $"Failed to parse bool value '{token.lexeme}'.",
line = token.line, line = token.line,
@@ -138,11 +138,11 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
return result; return result;
} }
private static string ParseTextureDefault(Token token, List<ShaderError> errors) private static string ParseTextureDefault(Token token, List<SDLError> errors)
{ {
if (!TokenLexicon.IsTextureDefaultValue(token.lexeme)) if (!TokenLexicon.IsTextureDefaultValue(token.lexeme))
{ {
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = $"Texture default value '{token.lexeme}' is not valid.", message = $"Texture default value '{token.lexeme}' is not valid.",
line = token.line, line = token.line,
@@ -242,7 +242,7 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
return syntax; return syntax;
} }
public static List<PropertySemantic>? SemanticAnalysis(PropertiesSyntax? syntax, List<ShaderError> errors) public static List<PropertySemantic>? SemanticAnalysis(PropertiesSyntax? syntax, List<SDLError> errors)
{ {
if (syntax == null) if (syntax == null)
{ {
@@ -295,11 +295,11 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
return models; return models;
} }
private static bool ValidatePropertyType(List<ShaderError> errors, PropertyDeclaration property, PropertySemantic model) private static bool ValidatePropertyType(List<SDLError> errors, PropertyDeclaration property, PropertySemantic model)
{ {
if (!TokenLexicon.IsType(property.type.lexeme)) if (!TokenLexicon.IsType(property.type.lexeme))
{ {
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = $"Shader property type '{property.type.lexeme}' is not a valid type.", message = $"Shader property type '{property.type.lexeme}' is not a valid type.",
line = property.type.line, line = property.type.line,
@@ -313,11 +313,11 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
return true; return true;
} }
private static bool ValidatePropertyName(List<ShaderError> errors, HashSet<string> usedPropertyNames, PropertyDeclaration property, PropertySemantic model) private static bool ValidatePropertyName(List<SDLError> errors, HashSet<string> usedPropertyNames, PropertyDeclaration property, PropertySemantic model)
{ {
if (string.IsNullOrWhiteSpace(property.name.lexeme)) if (string.IsNullOrWhiteSpace(property.name.lexeme))
{ {
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = "Shader property has an empty name.", message = "Shader property has an empty name.",
line = property.name.line, line = property.name.line,
@@ -328,7 +328,7 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
} }
else if (usedPropertyNames.Contains(property.name.lexeme)) else if (usedPropertyNames.Contains(property.name.lexeme))
{ {
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = $"Shader property name '{property.name.lexeme}' is duplicated.", message = $"Shader property name '{property.name.lexeme}' is duplicated.",
line = property.name.line, line = property.name.line,
@@ -342,12 +342,12 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
return true; return true;
} }
private static bool ValidatePropertyConstructor(List<ShaderError> errors, PropertyDeclaration property, PropertySemantic model) private static bool ValidatePropertyConstructor(List<SDLError> errors, PropertyDeclaration property, PropertySemantic model)
{ {
var constructor = property.propertyConstructor; var constructor = property.propertyConstructor;
if (!constructor.HasValue) if (!constructor.HasValue)
{ {
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = "Shader property constructor is null.", message = "Shader property constructor is null.",
line = property.name.line, line = property.name.line,
@@ -360,7 +360,7 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
var constructorValue = constructor.Value; var constructorValue = constructor.Value;
if (string.IsNullOrWhiteSpace(constructorValue.name.lexeme)) if (string.IsNullOrWhiteSpace(constructorValue.name.lexeme))
{ {
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = "Shader property constructor has an empty name.", message = "Shader property constructor has an empty name.",
line = constructorValue.name.line, line = constructorValue.name.line,
@@ -372,7 +372,7 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
if (constructorValue.name.lexeme != property.type.lexeme) if (constructorValue.name.lexeme != property.type.lexeme)
{ {
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = $"Shader property constructor name '{constructorValue.name.lexeme}' does not match property type '{property.type.lexeme}'.", message = $"Shader property constructor name '{constructorValue.name.lexeme}' does not match property type '{property.type.lexeme}'.",
line = constructorValue.name.line, line = constructorValue.name.line,
@@ -384,7 +384,7 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
if (!s_propTypeInfo.TryGetValue(model.type, out var info)) if (!s_propTypeInfo.TryGetValue(model.type, out var info))
{ {
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = $"No constructor metadata registered for property type '{model.type}'.", message = $"No constructor metadata registered for property type '{model.type}'.",
line = constructorValue.name.line, line = constructorValue.name.line,
@@ -397,7 +397,7 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
// Count check // Count check
if (constructorValue.arguments == null) if (constructorValue.arguments == null)
{ {
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = "Shader property constructor arguments are null.", message = "Shader property constructor arguments are null.",
line = constructorValue.name.line, line = constructorValue.name.line,
@@ -409,7 +409,7 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
if (constructorValue.arguments.Count != info.ArgCount) if (constructorValue.arguments.Count != info.ArgCount)
{ {
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = $"Shader property constructor for type '{property.type.lexeme}' expects {info.ArgCount} argument(s), but got {constructorValue.arguments.Count}.", message = $"Shader property constructor for type '{property.type.lexeme}' expects {info.ArgCount} argument(s), but got {constructorValue.arguments.Count}.",
line = constructorValue.name.line, line = constructorValue.name.line,
@@ -426,7 +426,7 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
var arg = constructorValue.arguments[i]; var arg = constructorValue.arguments[i];
if (!arg.Match(info.ArgTokenType)) if (!arg.Match(info.ArgTokenType))
{ {
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = $"Shader property constructor argument {i} expects token kind '{info.ArgTokenType}', but got '{arg.type}'.", message = $"Shader property constructor argument {i} expects token kind '{info.ArgTokenType}', but got '{arg.type}'.",
line = arg.line, line = arg.line,
@@ -451,7 +451,7 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
} }
catch (Exception ex) catch (Exception ex)
{ {
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = $"Failed to construct default value for property '{property.name.lexeme}': {ex.Message}", message = $"Failed to construct default value for property '{property.name.lexeme}': {ex.Message}",
line = constructorValue.name.line, line = constructorValue.name.line,

View File

@@ -49,7 +49,7 @@ internal class ShaderBlock : IBlockParser<ShaderSyntax, ShaderSemantics>
return shader; return shader;
} }
public static ShaderSemantics? SemanticAnalysis(ShaderSyntax? syntax, List<ShaderError> errors) public static ShaderSemantics? SemanticAnalysis(ShaderSyntax? syntax, List<SDLError> errors)
{ {
if (syntax == null) if (syntax == null)
{ {
@@ -85,7 +85,7 @@ internal class ShaderBlock : IBlockParser<ShaderSyntax, ShaderSemantics>
case TokenLexicon.KnownFunctions.FALLBACK: case TokenLexicon.KnownFunctions.FALLBACK:
if (func.arguments == null || func.arguments.Count != 1) if (func.arguments == null || func.arguments.Count != 1)
{ {
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = "Fallback declaration requires exactly one arguments: (fallback shader name).", message = "Fallback declaration requires exactly one arguments: (fallback shader name).",
line = func.name.line, line = func.name.line,
@@ -98,7 +98,7 @@ internal class ShaderBlock : IBlockParser<ShaderSyntax, ShaderSemantics>
shaderModel.fallback = func.arguments[0].lexeme; shaderModel.fallback = func.arguments[0].lexeme;
break; break;
default: default:
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = $"Unknown function '{func.name.lexeme}' in shader.", message = $"Unknown function '{func.name.lexeme}' in shader.",
line = func.name.line, line = func.name.line,

View File

@@ -1,10 +1,11 @@
using Ghost.Core.Graphics; using Ghost.Core;
using Ghost.Core.Graphics;
using Ghost.Shader.Compiler.Parser; using Ghost.Shader.Compiler.Parser;
using System.Text; using System.Text;
namespace Ghost.Shader.Compiler; namespace Ghost.Shader.Compiler;
public struct ShaderError public struct SDLError
{ {
public string message; public string message;
public int line; public int line;
@@ -16,7 +17,7 @@ public struct ShaderError
} }
} }
internal static class ShaderCompiler internal static class SDLCompiler
{ {
private const string _GLOBAL_PROPERTY_FILE_NAME = "GlobalData.g.hlsl"; private const string _GLOBAL_PROPERTY_FILE_NAME = "GlobalData.g.hlsl";
private const string _GENERATED_FILE_HEADER = "// Auto-generated shader file. Please do not edit this file directly."; private const string _GENERATED_FILE_HEADER = "// Auto-generated shader file. Please do not edit this file directly.";
@@ -51,13 +52,13 @@ internal static class ShaderCompiler
return shaders; return shaders;
} }
public static ShaderSemantics? SemanticAnalysis(ShaderSyntax syntax, out List<ShaderError> errors) public static ShaderSemantics? SemanticAnalysis(ShaderSyntax syntax, out List<SDLError> errors)
{ {
errors = new(); errors = new();
if (string.IsNullOrWhiteSpace(syntax.name.lexeme)) if (string.IsNullOrWhiteSpace(syntax.name.lexeme))
{ {
errors.Add(new ShaderError errors.Add(new SDLError
{ {
message = "Shader name cannot be empty.", message = "Shader name cannot be empty.",
line = syntax.name.line, line = syntax.name.line,
@@ -244,6 +245,29 @@ internal static class ShaderCompiler
return descriptor; return descriptor;
} }
public static Result<ShaderDescriptor> CompileShader(string shaderPath)
{
var source = File.ReadAllText(shaderPath);
var lexer = new Lexer(source);
var stream = new TokenStream(lexer.Tokenize());
var shaderInfo = ParseShaders(stream);
var model = SemanticAnalysis(shaderInfo[0], out var errors);
if (errors.Count != 0 || model == null)
{
var errorMessages = new StringBuilder();
foreach (var error in errors)
{
errorMessages.AppendLine(error.ToString());
}
return Result<ShaderDescriptor>.Fail("Failed to compile shader due to errors:\n" + errorMessages.ToString());
}
return ResolveShader(model);
}
private static string ShaderPropertyTypeToHLSLType(ShaderPropertyType type) private static string ShaderPropertyTypeToHLSLType(ShaderPropertyType type)
{ {
return type switch return type switch
@@ -274,7 +298,7 @@ internal static class ShaderCompiler
}; };
} }
public static string CompilePass(IPassDescriptor descriptor, string targetDirectory) public static string GeneratePass(IPassDescriptor descriptor, string targetDirectory)
{ {
if (descriptor is not FullPassDescriptor fullPass) if (descriptor is not FullPassDescriptor fullPass)
{ {
@@ -330,7 +354,7 @@ struct PerMaterialData
return outputFilePath; return outputFilePath;
} }
public static void CompileShader(ShaderDescriptor descriptor, string targetDirectory) public static void GenerateShader(ShaderDescriptor descriptor, string targetDirectory)
{ {
if (!Directory.Exists(targetDirectory)) if (!Directory.Exists(targetDirectory))
{ {
@@ -369,7 +393,7 @@ struct GlobalData
// Compile each pass. // Compile each pass.
foreach (var pass in descriptor.passes) foreach (var pass in descriptor.passes)
{ {
CompilePass(pass, targetDirectory); GeneratePass(pass, targetDirectory);
} }
} }
} }