forked from Misaki/GhostEngine
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:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
#if DEBUG || GHOST_EDITOR
|
||||||
|
if (name != null)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"Expect ComPtr<ID3D12Resource> in D3D12ResourceDatabase, but got {typeof(T)}.");
|
_resourceName[handle] = name;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
var id = _resources.Add(new ResourceRecord(d3d12Resource, initialState), out var generation);
|
public Handle<GPUResource> AddResource(ComPtr<D3D12MA_Allocation> allocation, uint cpuFenceValue, ResourceState initialState, ResourceViewGroup resourceDescriptor, ResourceDesc desc, string? name = null)
|
||||||
return new Handle<GPUResource>(id, generation);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Handle<GPUResource> AddResource(ComPtr<D3D12MA_Allocation> allocation, uint cpuFenceValue, ResourceState initialState, ResourceViewGroup resourceDescriptor, ResourceDesc desc)
|
|
||||||
{
|
{
|
||||||
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;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
ReleaseResource(ref shader);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -151,3 +141,50 @@ 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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
D3D12_RESOURCE_DIMENSION.D3D12_RESOURCE_DIMENSION_TEXTURE1D => TextureDimension.Texture2D,
|
/// <summary>
|
||||||
D3D12_RESOURCE_DIMENSION.D3D12_RESOURCE_DIMENSION_TEXTURE2D => TextureDimension.Texture2D,
|
/// Target type
|
||||||
D3D12_RESOURCE_DIMENSION.D3D12_RESOURCE_DIMENSION_TEXTURE3D => TextureDimension.Texture3D,
|
/// </summary>
|
||||||
_ => TextureDimension.Unknown,
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -51,87 +51,3 @@ public interface ISwapChain : IDisposable
|
|||||||
/// <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
|
|
||||||
}
|
|
||||||
@@ -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
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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:");
|
||||||
|
|
||||||
|
|||||||
@@ -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")]
|
||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user