diff --git a/Ghost.Graphics/D3D12/D3D12CommandBuffer.cs b/Ghost.Graphics/D3D12/D3D12CommandBuffer.cs
index dc10619..cf42bb8 100644
--- a/Ghost.Graphics/D3D12/D3D12CommandBuffer.cs
+++ b/Ghost.Graphics/D3D12/D3D12CommandBuffer.cs
@@ -126,14 +126,14 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
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);
}
var dsvHandle = stackalloc D3D12_CPU_DESCRIPTOR_HANDLE[depthTarget.IsValid ? 1 : 0];
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);
@@ -270,7 +270,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
_commandList.Get()->SetPipelineState(d3d12Pipeline.pipelineState.Get());
_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];
heaps[0] = _descriptorAllocator.GetCbvSrvUavHeap(); // Bindless resource heap
heaps[1] = _descriptorAllocator.GetSamplerHeap(); // Bindless sampler heap
diff --git a/Ghost.Graphics/D3D12/D3D12DescriptorAllocator.cs b/Ghost.Graphics/D3D12/D3D12DescriptorAllocator.cs
index 4b34b9d..35fa603 100644
--- a/Ghost.Graphics/D3D12/D3D12DescriptorAllocator.cs
+++ b/Ghost.Graphics/D3D12/D3D12DescriptorAllocator.cs
@@ -8,7 +8,7 @@ using static TerraFX.Aliases.D3D12_Alias;
namespace Ghost.Graphics.D3D12;
///
-/// D3D12 implementation of descriptor allocator that manages different types of descriptor heaps.
+/// D3D12 implementation of viewGroup allocator that manages different types of viewGroup heaps.
///
internal unsafe class D3D12DescriptorAllocator : IDisposable
{
diff --git a/Ghost.Graphics/D3D12/D3D12PipelineLibrary.cs b/Ghost.Graphics/D3D12/D3D12PipelineLibrary.cs
index 99ad60d..ad2e014 100644
--- a/Ghost.Graphics/D3D12/D3D12PipelineLibrary.cs
+++ b/Ghost.Graphics/D3D12/D3D12PipelineLibrary.cs
@@ -91,7 +91,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
{
_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];
rootParameters[0] = new D3D12_ROOT_PARAMETER1
{
@@ -325,7 +325,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
SampleMask = UINT32_MAX,
SampleDesc = new DXGI_SAMPLE_DESC(1, 0),
NumRenderTargets = rtvCount,
- DSVFormat = dsv.ToD3D12Format(),
+ DSVFormat = dsv.ToDXGIFormat(),
DepthStencilState = BuildDepthStencil(in pipelineDescriptor),
NodeMask = 0,
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++)
{
- desc.RTVFormats[i] = rtvs[i].ToD3D12Format();
+ desc.RTVFormats[i] = rtvs[i].ToDXGIFormat();
desc.BlendState.RenderTarget[i].RenderTargetWriteMask = (byte)(pipelineDescriptor.colorMask & 0x0F);
hash.rtvFormats[i] = rtvs[i];
}
diff --git a/Ghost.Graphics/D3D12/D3D12ResourceAllocator.cs b/Ghost.Graphics/D3D12/D3D12ResourceAllocator.cs
index f2df292..cce2840 100644
--- a/Ghost.Graphics/D3D12/D3D12ResourceAllocator.cs
+++ b/Ghost.Graphics/D3D12/D3D12ResourceAllocator.cs
@@ -555,32 +555,16 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator, IDisposable
public Handle CreateMaterial(Identifier shader)
{
- var materialData = new Material
- {
- Shader = shader,
- };
+ var material = new Material();
+ material.SetShader(shader, this, _resourceDatabase);
- ref var shaderRef = ref _resourceDatabase.GetShaderReference(shader);
-
- // 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);
+ return _resourceDatabase.AddMaterial(ref material);
}
public Identifier CreateShader(ShaderDescriptor descriptor)
{
- var shaderData = new Shader();
- return _resourceDatabase.AddShader(ref shaderData);
+ var shader = new Shader(descriptor);
+ return _resourceDatabase.AddShader(shader);
}
#region Conversion Methods
diff --git a/Ghost.Graphics/D3D12/D3D12ResourceDatabase.cs b/Ghost.Graphics/D3D12/D3D12ResourceDatabase.cs
index 1a0a475..7d75e83 100644
--- a/Ghost.Graphics/D3D12/D3D12ResourceDatabase.cs
+++ b/Ghost.Graphics/D3D12/D3D12ResourceDatabase.cs
@@ -4,6 +4,7 @@ using Ghost.Graphics.RHI;
using Misaki.HighPerformance.Collections;
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
+using System.Diagnostics;
using System.Runtime.InteropServices;
using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows;
@@ -34,7 +35,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
}
public ResourceDesc desc;
- public ResourceViewGroup descriptor;
+ public ResourceViewGroup viewGroup;
public ResourceUnion resourceUnion;
public ResourceState state;
public uint cpuFenceValue;
@@ -48,7 +49,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
this.resourceUnion = new ResourceUnion(allocation);
this.isExternal = false;
- this.descriptor = resourceDescriptor;
+ this.viewGroup = resourceDescriptor;
this.cpuFenceValue = cpuFenceValue;
this.state = state;
this.desc = desc;
@@ -59,7 +60,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
this.resourceUnion = new ResourceUnion(resource);
this.isExternal = true;
- this.descriptor = default;
+ this.viewGroup = default;
this.cpuFenceValue = ~0u;
this.state = state;
this.desc = ResourceDesc.FromD3D12(resource.Get()->GetDesc());
@@ -80,10 +81,10 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
}
resourceUnion = default;
- descriptor = default;
+ viewGroup = default;
}
- descriptorAllocator.Release(descriptor);
+ descriptorAllocator.Release(viewGroup);
return refCount;
}
@@ -99,14 +100,12 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
private UnsafeSlotMap _resources;
#if DEBUG || GHOST_EDITOR
- private readonly Dictionary _resourceName;
+ private readonly Dictionary, string> _resourceName;
#endif
private readonly UnsafeSlotMap _meshes;
private readonly UnsafeSlotMap _materials;
-
- // 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> _shaders;
+ private readonly DynamicArray _shaders; // NOTE: We use a simple list since shader is not frequently added/removed. This can save 4 bytes for each ecs component.
private readonly Dictionary _shaderPasses;
private bool _disposed;
@@ -135,28 +134,41 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
where T : IResourceReleasable
{
resource.ReleaseResource(this);
+ resource = default!;
}
- public Handle ImportExternalResource(T resource, ResourceState initialState)
- where T : unmanaged
+ public Handle ImportExternalResource(ComPtr resource, ResourceState initialState, string? name = null)
{
ObjectDisposedException.ThrowIf(_disposed, this);
- if (resource is not ComPtr d3d12Resource)
- {
- throw new InvalidOperationException($"Expect ComPtr in D3D12ResourceDatabase, but got {typeof(T)}.");
- }
+ var id = _resources.Add(new ResourceRecord(resource, initialState), out var generation);
+ var handle = new Handle(id, generation);
- var id = _resources.Add(new ResourceRecord(d3d12Resource, initialState), out var generation);
- return new Handle(id, generation);
+#if DEBUG || GHOST_EDITOR
+ if (name != null)
+ {
+ _resourceName[handle] = name;
+ }
+#endif
+
+ return handle;
}
- public Handle AddResource(ComPtr allocation, uint cpuFenceValue, ResourceState initialState, ResourceViewGroup resourceDescriptor, ResourceDesc desc)
+ public Handle AddResource(ComPtr allocation, uint cpuFenceValue, ResourceState initialState, ResourceViewGroup resourceDescriptor, ResourceDesc desc, string? name = null)
{
ObjectDisposedException.ThrowIf(_disposed, this);
var id = _resources.Add(new ResourceRecord(allocation, cpuFenceValue, initialState, resourceDescriptor, desc), out var generation);
- return new Handle(id, generation);
+ var handle = new Handle(id, generation);
+
+#if DEBUG || GHOST_EDITOR
+ if (name != null)
+ {
+ _resourceName[handle] = name;
+ }
+#endif
+
+ return handle;
}
public ref ResourceRecord GetResourceInfo(Handle handle)
@@ -218,13 +230,28 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
public int GetBindlessIndex(Handle handle)
{
+ ObjectDisposedException.ThrowIf(_disposed, this);
+
ref var info = ref GetResourceInfo(handle, out var exist);
if (!exist || !info.Allocated)
{
return -1;
}
- return info.descriptor.srv.value;
+ return info.viewGroup.srv.value;
+ }
+
+ public string? GetResourceName(Handle 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 handle)
@@ -244,9 +271,10 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
var refCount = info.Release(_descriptorAllocator);
#if DEBUG || GHOST_EDITOR
+ _resourceName.Remove(handle, out var name);
if (refCount > 0)
{
- throw new GPUResourceLeakException(refCount, info.ResourcePtr, _resourceName[info]);
+ throw new GPUResourceLeakException(refCount, info.ResourcePtr, name ?? "Unknown Resource");
}
#endif
@@ -269,6 +297,8 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
public ref Mesh GetMeshReference(Handle handle)
{
+ ObjectDisposedException.ThrowIf(_disposed, this);
+
ref var mesh = ref _meshes.GetElementReferenceAt(handle.id, handle.generation, out var exist);
if (!exist)
{
@@ -280,6 +310,8 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
public void ReleaseMesh(Handle handle)
{
+ ObjectDisposedException.ThrowIf(_disposed, this);
+
ref var mesh = ref _meshes.GetElementReferenceAt(handle.id, handle.generation, out var exist);
if (!exist)
{
@@ -306,6 +338,8 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
public ref Material GetMaterialReference(Handle handle)
{
+ ObjectDisposedException.ThrowIf(_disposed, this);
+
ref var material = ref _materials.GetElementReferenceAt(handle.id, handle.generation, out var exist);
if (!exist)
{
@@ -317,6 +351,8 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
public void ReleaseMaterial(Handle handle)
{
+ ObjectDisposedException.ThrowIf(_disposed, this);
+
ref var material = ref _materials.GetElementReferenceAt(handle.id, handle.generation, out var exist);
if (!exist)
{
@@ -327,43 +363,45 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
_materials.Remove(handle.id, handle.generation);
}
- public Identifier AddShader(ref readonly Shader shader)
+ public Identifier AddShader(Shader shader)
{
ObjectDisposedException.ThrowIf(_disposed, this);
var id = _shaders.Count;
- _shaders.Add(new Slot { value = shader, occupied = true });
+ _shaders.Add(shader);
return new Identifier(id);
}
public bool HasShader(Identifier id)
{
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 id)
+ public Shader GetShaderReference(Identifier id)
{
+ ObjectDisposedException.ThrowIf(_disposed, this);
+
if (!HasShader(id))
{
throw new ArgumentOutOfRangeException(nameof(id), $"Shader id {id} is invalid.");
}
- ref var shader = ref _shaders[id.value].value;
- return ref shader;
+ var shader = _shaders[id.value]!;
+ return shader;
}
public void ReleaseShader(Identifier id)
{
+ ObjectDisposedException.ThrowIf(_disposed, this);
+
if (!HasShader(id))
{
return;
}
- ref var shaderSlot = ref _shaders[id.value];
-
- ReleaseResource(ref shaderSlot.value);
- shaderSlot.occupied = false;
+ ref var shader = ref _shaders[id.value]!;
+ ReleaseResource(ref shader);
}
public void AddShaderPass(ShaderPassKey passKey, ShaderPass pass)
@@ -384,36 +422,63 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
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()
{
+ [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)
{
return;
}
-#if DEBUG || GHOST_EDITOR
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)
{
- 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)
{
- 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();
_meshes.Dispose();
_materials.Dispose();
diff --git a/Ghost.Graphics/D3D12/D3D12ShaderCompiler.cs b/Ghost.Graphics/D3D12/D3D12ShaderCompiler.cs
index 3d9e422..fa017ec 100644
--- a/Ghost.Graphics/D3D12/D3D12ShaderCompiler.cs
+++ b/Ghost.Graphics/D3D12/D3D12ShaderCompiler.cs
@@ -123,7 +123,6 @@ internal readonly struct ShaderReflectionData
internal static unsafe class D3D12ShaderCompiler
{
-
private static string GetProfileString(ShaderStage stage, CompilerTier version)
{
return (stage, version) switch
diff --git a/Ghost.Graphics/D3D12/D3D12SwapChain.cs b/Ghost.Graphics/D3D12/D3D12SwapChain.cs
index 8df2428..e16cc68 100644
--- a/Ghost.Graphics/D3D12/D3D12SwapChain.cs
+++ b/Ghost.Graphics/D3D12/D3D12SwapChain.cs
@@ -60,7 +60,7 @@ internal unsafe class D3D12SwapChain : ISwapChain
{
Width = desc.width,
Height = desc.height,
- Format = desc.format.ToD3D12Format(),
+ 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,
diff --git a/Ghost.Graphics/D3D12/Utilities/D3D12Utility.cs b/Ghost.Graphics/D3D12/Utilities/D3D12Utility.cs
index e5027f5..616860f 100644
--- a/Ghost.Graphics/D3D12/Utilities/D3D12Utility.cs
+++ b/Ghost.Graphics/D3D12/Utilities/D3D12Utility.cs
@@ -1,21 +1,11 @@
-using Misaki.HighPerformance.LowLevel.Utilities;
+using Ghost.Graphics.RHI;
+using Misaki.HighPerformance.LowLevel.Utilities;
using TerraFX.Interop.DirectX;
using static TerraFX.Aliases.D3D12_Alias;
namespace Ghost.Graphics.D3D12.Utilities;
-internal unsafe static class ID3D12Resource_Extensions
-{
- extension(ID3D12Resource resource)
- {
- public void SetName(ReadOnlySpan name)
- {
- resource.SetName(name.GetUnsafePtr());
- }
- }
-}
-
internal static class D3D12_RASTERIZER_DESC_Extensions
{
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 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,
+ };
+ }
}
\ No newline at end of file
diff --git a/Ghost.Graphics/Data/Material.cs b/Ghost.Graphics/Data/Material.cs
index 1525bad..6fa9407 100644
--- a/Ghost.Graphics/Data/Material.cs
+++ b/Ghost.Graphics/Data/Material.cs
@@ -18,20 +18,12 @@ internal struct CBufferCache : IResourceReleasable
public readonly Handle GpuResource => _gpuResource;
public readonly uint AlignedSize => _alignedSize;
- public unsafe CBufferCache(IResourceAllocator allocator, uint bufferSize)
+ public unsafe CBufferCache(Handle buffer, uint bufferSize)
{
_alignedSize = (bufferSize + 255u) & ~255u;
_cpuData = new((int)AlignedSize, Allocator.Persistent);
-
- var desc = new BufferDesc
- {
- Size = bufferSize,
- Usage = BufferUsage.Constant,
- MemoryType = ResourceMemoryType.Default,
- };
-
- _gpuResource = allocator.CreateBuffer(ref desc);
+ _gpuResource = buffer;
}
public void ReleaseResource(IResourceDatabase database)
@@ -52,11 +44,6 @@ public struct Material : IResourceReleasable, IHandleType
public readonly Identifier Shader => _shader;
- public Material(Identifier shader, IResourceAllocator allocator, IResourceDatabase database)
- {
- SetShader(shader, allocator, database);
- }
-
internal ref CBufferCache GetPassCache(int passIndex)
{
return ref _materialPropertiesCache[passIndex];
@@ -77,7 +64,16 @@ public struct Material : IResourceReleasable, IHandleType
{
var pass = database.GetShaderPass(shader.GetPassKey(i));
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
{
private ref Material _materialData;
- private readonly ref Shader _shader;
+ private Shader _shader;
private readonly IResourceDatabase _resourceDatabase;
@@ -104,7 +100,7 @@ public ref struct MaterialAccessor
_resourceDatabase = resourceDatabase;
_materialData = ref resourceDatabase.GetMaterialReference(material);
- _shader = ref resourceDatabase.GetShaderReference(_materialData.Shader);
+ _shader = resourceDatabase.GetShaderReference(_materialData.Shader);
}
private readonly unsafe void WriteToCache(string propertyName, in T value)
diff --git a/Ghost.Graphics/Data/Shader.cs b/Ghost.Graphics/Data/Shader.cs
index 438e649..a0ae305 100644
--- a/Ghost.Graphics/Data/Shader.cs
+++ b/Ghost.Graphics/Data/Shader.cs
@@ -1,7 +1,7 @@
using Ghost.Core;
-using Ghost.Core.Contracts;
using Ghost.Core.Graphics;
using Ghost.Graphics.RHI;
+using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
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.
private readonly Dictionary _propertyLookup;
private readonly UnsafeList _properties;
- internal readonly CBufferInfo PassPropertyInfo
+ internal CBufferInfo PassPropertyInfo
{
get;
}
@@ -69,36 +69,41 @@ public readonly unsafe struct ShaderPass
_propertyLookup = propertyNameToIdMap;
}
- public readonly int GetPropertyId(string propertyName)
+ public int GetPropertyId(string propertyName)
{
return _propertyLookup.TryGetValue(propertyName, out var id) ? id : -1;
}
- public readonly PropertyInfo GetPropertyInfo(int id)
+ public PropertyInfo GetPropertyInfo(int id)
{
return _properties[id];
}
- public readonly PropertyInfo GetPropertyInfo(string propertyName)
+ public PropertyInfo GetPropertyInfo(string propertyName)
{
return _properties[GetPropertyId(propertyName)];
}
+
+ void IResourceReleasable.ReleaseResource(IResourceDatabase database)
+ {
+ _properties.Dispose();
+ }
}
///
/// A representation of a GPU shader, including all the passes it contains.
///
-public readonly struct Shader : IResourceReleasable, IIdentifierType
+public class Shader : IResourceReleasable, IIdentifierType
{
- private readonly ShaderPassKey[] _passIDs;
+ private UnsafeArray _passIDs;
private readonly Dictionary _passLookup; // pass name to index
private readonly Dictionary> _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)
{
- _passIDs = new ShaderPassKey[descriptor.passes.Count];
+ _passIDs = new UnsafeArray(descriptor.passes.Count, Allocator.Persistent);
_passLookup = 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];
}
- public readonly bool TryGetPassKey(string passName, out ShaderPassKey? passID)
+ public bool TryGetPassKey(string passName, out ShaderPassKey? passID)
{
var index = _passLookup.GetValueOrDefault(passName, -1);
if (index == -1)
@@ -150,7 +155,7 @@ public readonly struct Shader : IResourceReleasable, IIdentifierType
return true;
}
- public readonly IReadOnlyCollection GetPropertyPassIndices(string propertyName)
+ public IReadOnlyCollection GetPropertyPassIndices(string propertyName)
{
if (_propertyLookup.TryGetValue(propertyName, out var passIndices))
{
@@ -162,6 +167,6 @@ public readonly struct Shader : IResourceReleasable, IIdentifierType
void IResourceReleasable.ReleaseResource(IResourceDatabase database)
{
- // Should we do something here?
+ _passIDs.Dispose();
}
}
\ No newline at end of file
diff --git a/Ghost.Graphics/Ghost.Graphics.csproj b/Ghost.Graphics/Ghost.Graphics.csproj
index d7603b4..80ad45a 100644
--- a/Ghost.Graphics/Ghost.Graphics.csproj
+++ b/Ghost.Graphics/Ghost.Graphics.csproj
@@ -23,6 +23,7 @@
+
diff --git a/Ghost.Graphics/RHI/Common.cs b/Ghost.Graphics/RHI/Common.cs
index d457020..1e18e8f 100644
--- a/Ghost.Graphics/RHI/Common.cs
+++ b/Ghost.Graphics/RHI/Common.cs
@@ -1,4 +1,5 @@
-using Misaki.HighPerformance.Utilities;
+using Ghost.Graphics.D3D12.Utilities;
+using Misaki.HighPerformance.Utilities;
using System.IO.Hashing;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -74,7 +75,7 @@ internal struct GraphicsPipelineHash
data[1] = rtvCount;
data[2] = (ulong)dsvFormat;
- for (int i = 0; i < 8; i++)
+ for (var i = 0; i < 8; i++)
{
data[3 + i] = (ulong)rtvFormats[i];
}
@@ -425,18 +426,88 @@ public struct BufferDesc
}
}
-public static class TextureDimensionExtension
+///
+/// Swap chain description
+///
+public struct SwapChainDesc
{
- public static TextureDimension ToTextureDimension(this D3D12_RESOURCE_DIMENSION dimension)
+ ///
+ /// Width of the swap chain
+ ///
+ public uint width;
+
+ ///
+ /// Height of the swap chain
+ ///
+ public uint height;
+
+ ///
+ /// Back buffer format
+ ///
+ public TextureFormat format;
+
+ ///
+ /// Target for presentation (window handle or composition target)
+ ///
+ 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;
+ }
+}
+
+///
+/// Swap chain target (window handle or composition surface)
+///
+public struct SwapChainTarget
+{
+ ///
+ /// Target type
+ ///
+ public SwapChainTargetType type;
+
+ ///
+ /// Window handle for HWND targets
+ ///
+ public nint windowHandle;
+
+ ///
+ /// Composition surface for UWP/WinUI targets
+ ///
+ public object? compositionSurface;
+
+ public static SwapChainTarget FromWindowHandle(nint hwnd)
+ {
+ return new SwapChainTarget
{
- 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,
+ type = SwapChainTargetType.WindowHandle,
+ windowHandle = hwnd,
+ compositionSurface = null
};
}
+
+ public static SwapChainTarget FromCompositionSurface(object surface)
+ {
+ return new SwapChainTarget
+ {
+ type = SwapChainTargetType.Composition,
+ windowHandle = nint.Zero,
+ compositionSurface = surface
+ };
+ }
+}
+
+///
+/// Swap chain target types
+///
+public enum SwapChainTargetType
+{
+ WindowHandle,
+ Composition
}
diff --git a/Ghost.Graphics/RHI/IResourceAllocator.cs b/Ghost.Graphics/RHI/IResourceAllocator.cs
index 2dcca9b..e0b1060 100644
--- a/Ghost.Graphics/RHI/IResourceAllocator.cs
+++ b/Ghost.Graphics/RHI/IResourceAllocator.cs
@@ -1,4 +1,5 @@
using Ghost.Core;
+using Ghost.Core.Graphics;
using Ghost.Graphics.Data;
using Misaki.HighPerformance.LowLevel.Collections;
@@ -46,7 +47,8 @@ public interface IResourceAllocator
/// Creates a new shader and returns its unique identifier.
///
/// An representing the newly created shader.
- public Identifier CreateShader();
+ /// The viewGroup containing the shader's properties and passes.
+ public Identifier CreateShader(ShaderDescriptor descriptor);
///
/// Release a resource given its handle
diff --git a/Ghost.Graphics/RHI/IResourceDatabase.cs b/Ghost.Graphics/RHI/IResourceDatabase.cs
index 26d00a6..f80f5b9 100644
--- a/Ghost.Graphics/RHI/IResourceDatabase.cs
+++ b/Ghost.Graphics/RHI/IResourceDatabase.cs
@@ -13,6 +13,7 @@ public interface IResourceReleasable
public interface IResourceDatabase
{
+ /*
///
/// Imports an external unmanaged resource and returns a handle for use within the resource management system.
///
@@ -20,8 +21,9 @@ public interface IResourceDatabase
/// A pointer to the external unmanaged resource to be imported. Must remain valid for the duration of the resource's usage.
/// The initial state to assign to the imported resource.
/// A handle representing the imported resource, which can be used for subsequent operations.
- unsafe Handle ImportExternalResource(T resourcePtr, ResourceState initialState)
+ unsafe Handle ImportExternalResource(T resourcePtr, ResourceState initialState, string? name = null)
where T : unmanaged;
+ */
///
/// Retrieves the current state of the specified resource.
@@ -51,6 +53,16 @@ public interface IResourceDatabase
/// The bindless index corresponding to the specified GPU resource handle. -1 if the resource does not support bindless access or is not found.
int GetBindlessIndex(Handle handle);
+ ///
+ /// Retrieves the name of the GPU resource associated with the specified handle.
+ ///
+ ///
+ /// You should only use this method in debug builds or inside engine editor.
+ ///
+ /// A handle to the GPU resource for which to obtain the name. Must reference a valid resource.
+ /// The name of the GPU resource associated with the specified handle, or null if the resource does not have a name.
+ string? GetResourceName(Handle handle);
+
///
/// Removes a resource from the database using its handle.
///
@@ -116,7 +128,7 @@ public interface IResourceDatabase
///
/// The shader to add. The shader is passed by read-only reference and will not be modified.
/// The representing the newly added shader.
- Identifier AddShader(ref readonly Shader shader);
+ Identifier AddShader(Shader shader);
///
/// Determines whether a shader with the specified identifier exists in the collection.
@@ -130,7 +142,7 @@ public interface IResourceDatabase
///
/// The identifier of the shader to retrieve. Must refer to a valid shader.
/// A reference to the shader corresponding to the specified identifier.
- ref Shader GetShaderReference(Identifier id);
+ Shader GetShaderReference(Identifier id);
///
/// Releases the shader associated with the specified identifier, freeing any resources allocated to it.
diff --git a/Ghost.Graphics/RHI/ISwapChain.cs b/Ghost.Graphics/RHI/ISwapChain.cs
index bccffd7..7868d72 100644
--- a/Ghost.Graphics/RHI/ISwapChain.cs
+++ b/Ghost.Graphics/RHI/ISwapChain.cs
@@ -50,88 +50,4 @@ public interface ISwapChain : IDisposable
/// New width
/// New height
public void Resize(uint width, uint height);
-}
-
-///
-/// Swap chain description
-///
-public struct SwapChainDesc
-{
- ///
- /// Width of the swap chain
- ///
- public uint width;
-
- ///
- /// Height of the swap chain
- ///
- public uint height;
-
- ///
- /// Back buffer format
- ///
- public TextureFormat format;
-
- ///
- /// Target for presentation (window handle or composition target)
- ///
- 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;
- }
-}
-
-///
-/// Swap chain target (window handle or composition surface)
-///
-public struct SwapChainTarget
-{
- ///
- /// Target type
- ///
- public SwapChainTargetType type;
-
- ///
- /// Window handle for HWND targets
- ///
- public nint windowHandle;
-
- ///
- /// Composition surface for UWP/WinUI targets
- ///
- 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
- };
- }
-}
-
-///
-/// Swap chain target types
-///
-public enum SwapChainTargetType
-{
- WindowHandle,
- Composition
}
\ No newline at end of file
diff --git a/Ghost.Graphics/RHI/IResource.cs b/Ghost.Graphics/RHI/RHIUtility.cs
similarity index 70%
rename from Ghost.Graphics/RHI/IResource.cs
rename to Ghost.Graphics/RHI/RHIUtility.cs
index 9171020..997f9e0 100644
--- a/Ghost.Graphics/RHI/IResource.cs
+++ b/Ghost.Graphics/RHI/RHIUtility.cs
@@ -1,37 +1,9 @@
-using TerraFX.Interop.DirectX;
+using TerraFX.Interop.DirectX;
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)
{
return format switch
diff --git a/Ghost.Graphics/RenderPasses/MeshRenderPass.cs b/Ghost.Graphics/RenderPasses/MeshRenderPass.cs
index 895cfb9..c4cad32 100644
--- a/Ghost.Graphics/RenderPasses/MeshRenderPass.cs
+++ b/Ghost.Graphics/RenderPasses/MeshRenderPass.cs
@@ -3,6 +3,7 @@ using Ghost.Graphics.Contracts;
using Ghost.Graphics.Data;
using Ghost.Graphics.RHI;
using Ghost.Graphics.Utilities;
+using Ghost.Shader.Compiler;
using Misaki.HighPerformance.Image;
namespace Ghost.Graphics.RenderPasses;
@@ -27,13 +28,17 @@ internal unsafe class MeshRenderPass : IRenderPass
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);
_mesh = ctx.CreateMesh(vertices, indices);
ctx.UploadMesh(_mesh, true);
- _shader = resourceAllocator.CreateShader();
-
+ _shader = resourceAllocator.CreateShader(shaderDescriptor);
_material = resourceAllocator.CreateMaterial(_shader);
var imageResults = new ImageResult[_textureFiles.Length];
@@ -59,9 +64,6 @@ internal unsafe class MeshRenderPass : IRenderPass
_textures[i] = ctx.CreateTexture(ref desc);
ctx.UploadTexture(_textures[i], new Span(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)
diff --git a/Ghost.Shader.Test/Program.cs b/Ghost.Shader.Test/Program.cs
index 69f199e..1b4c7bd 100644
--- a/Ghost.Shader.Test/Program.cs
+++ b/Ghost.Shader.Test/Program.cs
@@ -10,8 +10,8 @@ var source = File.ReadAllText("F:/csharp/GhostEngine/Ghost.Graphics/test.gshader
var lexer = new Lexer(source);
var stream = new TokenStream(lexer.Tokenize());
-var shaderInfo = ShaderCompiler.ParseShaders(stream);
-var model = ShaderCompiler.SemanticAnalysis(shaderInfo[0], out var errors);
+var shaderInfo = SDLCompiler.ParseShaders(stream);
+var model = SDLCompiler.SemanticAnalysis(shaderInfo[0], out var errors);
foreach (var error in errors)
{
@@ -29,8 +29,8 @@ if (model == null)
return;
}
-var descriptor = ShaderCompiler.ResolveShader(model);
-ShaderCompiler.CompileShader(descriptor, "C:/Users/Misaki/Downloads/Archive");
+var descriptor = SDLCompiler.ResolveShader(model);
+SDLCompiler.GenerateShader(descriptor, "C:/Users/Misaki/Downloads/Archive");
Console.WriteLine("Shader compiled successfully:");
diff --git a/Ghost.Shader/AssemblyInfo.cs b/Ghost.Shader/AssemblyInfo.cs
index c320e1e..fe76bd6 100644
--- a/Ghost.Shader/AssemblyInfo.cs
+++ b/Ghost.Shader/AssemblyInfo.cs
@@ -1,3 +1,4 @@
using System.Runtime.CompilerServices;
-[assembly: InternalsVisibleTo("Ghost.Shader.Test")]
\ No newline at end of file
+[assembly: InternalsVisibleTo("Ghost.Shader.Test")]
+[assembly: InternalsVisibleTo("Ghost.Graphics")]
\ No newline at end of file
diff --git a/Ghost.Shader/Compiler/Parser/DefinesBlock.cs b/Ghost.Shader/Compiler/Parser/DefinesBlock.cs
index fafecd5..a46dba8 100644
--- a/Ghost.Shader/Compiler/Parser/DefinesBlock.cs
+++ b/Ghost.Shader/Compiler/Parser/DefinesBlock.cs
@@ -27,7 +27,7 @@ internal class DefinesBlock : IBlockParser, List>
return defines;
}
- public static List? SemanticAnalysis(List? syntax, List errors)
+ public static List? SemanticAnalysis(List? syntax, List errors)
{
if (syntax == null)
{
diff --git a/Ghost.Shader/Compiler/Parser/IBlockParser.cs b/Ghost.Shader/Compiler/Parser/IBlockParser.cs
index 76eb468..ed3c2a0 100644
--- a/Ghost.Shader/Compiler/Parser/IBlockParser.cs
+++ b/Ghost.Shader/Compiler/Parser/IBlockParser.cs
@@ -4,5 +4,5 @@ internal interface IBlockParser
{
public static abstract bool ShouldEnter(Token token);
public static abstract T? Parse(TokenStreamSlice ts);
- public static abstract U? SemanticAnalysis(T? syntax, List errors);
+ public static abstract U? SemanticAnalysis(T? syntax, List errors);
}
diff --git a/Ghost.Shader/Compiler/Parser/IncludesBlock.cs b/Ghost.Shader/Compiler/Parser/IncludesBlock.cs
index 19f31ce..7fc86b5 100644
--- a/Ghost.Shader/Compiler/Parser/IncludesBlock.cs
+++ b/Ghost.Shader/Compiler/Parser/IncludesBlock.cs
@@ -27,7 +27,7 @@ internal class IncludesBlock : IBlockParser, List>
return includes;
}
- public static List? SemanticAnalysis(List? syntax, List errors)
+ public static List? SemanticAnalysis(List? syntax, List errors)
{
if (syntax == null || syntax.Count == 0)
{
@@ -44,7 +44,7 @@ internal class IncludesBlock : IBlockParser, List>
}
else
{
- errors.Add(new ShaderError
+ errors.Add(new SDLError
{
message = $"Included file '{path}' not found.",
line = includeToken.line,
diff --git a/Ghost.Shader/Compiler/Parser/KeywordsBlock.cs b/Ghost.Shader/Compiler/Parser/KeywordsBlock.cs
index 0b39499..761d268 100644
--- a/Ghost.Shader/Compiler/Parser/KeywordsBlock.cs
+++ b/Ghost.Shader/Compiler/Parser/KeywordsBlock.cs
@@ -30,7 +30,7 @@ internal class KeywordsBlock : IBlockParser, List<
return keywords;
}
- public static List? SemanticAnalysis(List? syntax, List errors)
+ public static List? SemanticAnalysis(List? syntax, List errors)
{
if (syntax == null)
{
@@ -42,7 +42,7 @@ internal class KeywordsBlock : IBlockParser, List<
{
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.",
line = keyword.name.line,
@@ -61,7 +61,7 @@ internal class KeywordsBlock : IBlockParser, List<
group.type = KeywordType.Static;
break;
default:
- errors.Add(new ShaderError
+ errors.Add(new SDLError
{
message = $"Unknown function name '{keyword.name.lexeme}'.",
line = keyword.name.line,
diff --git a/Ghost.Shader/Compiler/Parser/PassBlock.cs b/Ghost.Shader/Compiler/Parser/PassBlock.cs
index a2962eb..bae5904 100644
--- a/Ghost.Shader/Compiler/Parser/PassBlock.cs
+++ b/Ghost.Shader/Compiler/Parser/PassBlock.cs
@@ -61,7 +61,7 @@ internal class PassBlock : IBlockParser
return pass;
}
- public static PassSemantic? SemanticAnalysis(PassSyntax? syntax, List errors)
+ public static PassSemantic? SemanticAnalysis(PassSyntax? syntax, List errors)
{
if (syntax == null)
{
@@ -81,7 +81,7 @@ internal class PassBlock : IBlockParser
if (semantic.localProperties != null
&& 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.",
line = syntax.name.line,
@@ -108,7 +108,7 @@ internal class PassBlock : IBlockParser
break;
default:
- errors.Add(new ShaderError
+ errors.Add(new SDLError
{
message = $"Unknown function '{func.name.lexeme}' in pass {syntax.name.lexeme}.",
line = func.name.line,
@@ -123,7 +123,7 @@ internal class PassBlock : IBlockParser
{
// TODO: Inheritance from base pass.
// 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.",
line = syntax.name.line,
@@ -134,11 +134,11 @@ internal class PassBlock : IBlockParser
return semantic;
}
- private static void AnalysisShaderEntry(List errors, FunctionCallDeclaration func, ref ShaderEntryPoint shaderEntryPoint)
+ private static void AnalysisShaderEntry(List errors, FunctionCallDeclaration func, ref ShaderEntryPoint shaderEntryPoint)
{
if (func.arguments?.Count != 2)
{
- errors.Add(new ShaderError
+ errors.Add(new SDLError
{
message = "Shader declaration requires exactly two arguments: (shaderPath, entryPoint).",
line = func.name.line,
diff --git a/Ghost.Shader/Compiler/Parser/PipelineBlock.cs b/Ghost.Shader/Compiler/Parser/PipelineBlock.cs
index f7aa62e..d0e08e6 100644
--- a/Ghost.Shader/Compiler/Parser/PipelineBlock.cs
+++ b/Ghost.Shader/Compiler/Parser/PipelineBlock.cs
@@ -38,7 +38,7 @@ internal class PipelineBlock : IBlockParser
return pipeline;
}
- public static PipelineSemantic? SemanticAnalysis(PipelineSyntax? syntax, List errors)
+ public static PipelineSemantic? SemanticAnalysis(PipelineSyntax? syntax, List errors)
{
if (syntax == null)
{
@@ -80,7 +80,7 @@ internal class PipelineBlock : IBlockParser
semantic.zTest = ZTestOptions.Always;
break;
default:
- errors.Add(new ShaderError
+ errors.Add(new SDLError
{
message = $"Invalid ZTest option: {valueDecl.value.lexeme}",
line = valueDecl.value.line,
@@ -100,7 +100,7 @@ internal class PipelineBlock : IBlockParser
semantic.zWrite = ZWriteOptions.Off;
break;
default:
- errors.Add(new ShaderError
+ errors.Add(new SDLError
{
message = $"Invalid ZWrite option: {valueDecl.value.lexeme}",
line = valueDecl.value.line,
@@ -123,7 +123,7 @@ internal class PipelineBlock : IBlockParser
semantic.cull = CullOptions.Back;
break;
default:
- errors.Add(new ShaderError
+ errors.Add(new SDLError
{
message = $"Invalid Cull option: {valueDecl.value.lexeme}",
line = valueDecl.value.line,
@@ -152,7 +152,7 @@ internal class PipelineBlock : IBlockParser
semantic.blend = BlendOptions.PremultipliedAlpha;
break;
default:
- errors.Add(new ShaderError
+ errors.Add(new SDLError
{
message = $"Invalid Blend option: {valueDecl.value.lexeme}",
line = valueDecl.value.line,
@@ -169,7 +169,7 @@ internal class PipelineBlock : IBlockParser
}
else
{
- errors.Add(new ShaderError
+ errors.Add(new SDLError
{
message = $"Invalid Color Mask value: {valueDecl.value.lexeme}",
line = valueDecl.value.line,
diff --git a/Ghost.Shader/Compiler/Parser/PropertiesBlock.cs b/Ghost.Shader/Compiler/Parser/PropertiesBlock.cs
index b4975f7..b83b37c 100644
--- a/Ghost.Shader/Compiler/Parser/PropertiesBlock.cs
+++ b/Ghost.Shader/Compiler/Parser/PropertiesBlock.cs
@@ -6,7 +6,7 @@ namespace Ghost.Shader.Compiler.Parser;
internal class PropertiesBlock : IBlockParser>
{
- private delegate object? PropertyValueBuilder(List tokens, List errors);
+ private delegate object? PropertyValueBuilder(List tokens, List errors);
private sealed record PropTypeInfo(int ArgCount, TokenType ArgTokenType, PropertyValueBuilder? Builder);
@@ -78,11 +78,11 @@ internal class PropertiesBlock : IBlockParser ParseTextureDefault(syntax[0], errors)),
};
- private static float ParseFloatValue(Token token, List errors)
+ private static float ParseFloatValue(Token token, List errors)
{
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}'.",
line = token.line,
@@ -93,11 +93,11 @@ internal class PropertiesBlock : IBlockParser errors)
+ private static int ParseIntValue(Token token, List errors)
{
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}'.",
line = token.line,
@@ -108,11 +108,11 @@ internal class PropertiesBlock : IBlockParser errors)
+ private static uint ParseUIntValue(Token token, List errors)
{
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}'.",
line = token.line,
@@ -123,11 +123,11 @@ internal class PropertiesBlock : IBlockParser errors)
+ private static bool ParseBoolValue(Token token, List errors)
{
if (!bool.TryParse(token.lexeme, out var result))
{
- errors.Add(new ShaderError
+ errors.Add(new SDLError
{
message = $"Failed to parse bool value '{token.lexeme}'.",
line = token.line,
@@ -138,11 +138,11 @@ internal class PropertiesBlock : IBlockParser errors)
+ private static string ParseTextureDefault(Token token, List errors)
{
if (!TokenLexicon.IsTextureDefaultValue(token.lexeme))
{
- errors.Add(new ShaderError
+ errors.Add(new SDLError
{
message = $"Texture default value '{token.lexeme}' is not valid.",
line = token.line,
@@ -242,7 +242,7 @@ internal class PropertiesBlock : IBlockParser? SemanticAnalysis(PropertiesSyntax? syntax, List errors)
+ public static List? SemanticAnalysis(PropertiesSyntax? syntax, List errors)
{
if (syntax == null)
{
@@ -295,11 +295,11 @@ internal class PropertiesBlock : IBlockParser errors, PropertyDeclaration property, PropertySemantic model)
+ private static bool ValidatePropertyType(List errors, PropertyDeclaration property, PropertySemantic model)
{
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.",
line = property.type.line,
@@ -313,11 +313,11 @@ internal class PropertiesBlock : IBlockParser errors, HashSet usedPropertyNames, PropertyDeclaration property, PropertySemantic model)
+ private static bool ValidatePropertyName(List errors, HashSet usedPropertyNames, PropertyDeclaration property, PropertySemantic model)
{
if (string.IsNullOrWhiteSpace(property.name.lexeme))
{
- errors.Add(new ShaderError
+ errors.Add(new SDLError
{
message = "Shader property has an empty name.",
line = property.name.line,
@@ -328,7 +328,7 @@ internal class PropertiesBlock : IBlockParser errors, PropertyDeclaration property, PropertySemantic model)
+ private static bool ValidatePropertyConstructor(List errors, PropertyDeclaration property, PropertySemantic model)
{
var constructor = property.propertyConstructor;
if (!constructor.HasValue)
{
- errors.Add(new ShaderError
+ errors.Add(new SDLError
{
message = "Shader property constructor is null.",
line = property.name.line,
@@ -360,7 +360,7 @@ internal class PropertiesBlock : IBlockParser
return shader;
}
- public static ShaderSemantics? SemanticAnalysis(ShaderSyntax? syntax, List errors)
+ public static ShaderSemantics? SemanticAnalysis(ShaderSyntax? syntax, List errors)
{
if (syntax == null)
{
@@ -85,7 +85,7 @@ internal class ShaderBlock : IBlockParser
case TokenLexicon.KnownFunctions.FALLBACK:
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).",
line = func.name.line,
@@ -98,7 +98,7 @@ internal class ShaderBlock : IBlockParser
shaderModel.fallback = func.arguments[0].lexeme;
break;
default:
- errors.Add(new ShaderError
+ errors.Add(new SDLError
{
message = $"Unknown function '{func.name.lexeme}' in shader.",
line = func.name.line,
diff --git a/Ghost.Shader/Compiler/ShaderCompiler.cs b/Ghost.Shader/Compiler/SDLCompiler.cs
similarity index 90%
rename from Ghost.Shader/Compiler/ShaderCompiler.cs
rename to Ghost.Shader/Compiler/SDLCompiler.cs
index 53a76f3..7c7fa26 100644
--- a/Ghost.Shader/Compiler/ShaderCompiler.cs
+++ b/Ghost.Shader/Compiler/SDLCompiler.cs
@@ -1,10 +1,11 @@
-using Ghost.Core.Graphics;
+using Ghost.Core;
+using Ghost.Core.Graphics;
using Ghost.Shader.Compiler.Parser;
using System.Text;
namespace Ghost.Shader.Compiler;
-public struct ShaderError
+public struct SDLError
{
public string message;
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 _GENERATED_FILE_HEADER = "// Auto-generated shader file. Please do not edit this file directly.";
@@ -51,13 +52,13 @@ internal static class ShaderCompiler
return shaders;
}
- public static ShaderSemantics? SemanticAnalysis(ShaderSyntax syntax, out List errors)
+ public static ShaderSemantics? SemanticAnalysis(ShaderSyntax syntax, out List errors)
{
errors = new();
if (string.IsNullOrWhiteSpace(syntax.name.lexeme))
{
- errors.Add(new ShaderError
+ errors.Add(new SDLError
{
message = "Shader name cannot be empty.",
line = syntax.name.line,
@@ -244,6 +245,29 @@ internal static class ShaderCompiler
return descriptor;
}
+ public static Result 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.Fail("Failed to compile shader due to errors:\n" + errorMessages.ToString());
+ }
+
+ return ResolveShader(model);
+ }
+
private static string ShaderPropertyTypeToHLSLType(ShaderPropertyType type)
{
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)
{
@@ -330,7 +354,7 @@ struct PerMaterialData
return outputFilePath;
}
- public static void CompileShader(ShaderDescriptor descriptor, string targetDirectory)
+ public static void GenerateShader(ShaderDescriptor descriptor, string targetDirectory)
{
if (!Directory.Exists(targetDirectory))
{
@@ -369,7 +393,7 @@ struct GlobalData
// Compile each pass.
foreach (var pass in descriptor.passes)
{
- CompilePass(pass, targetDirectory);
+ GeneratePass(pass, targetDirectory);
}
}
}
\ No newline at end of file