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");
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -8,7 +8,7 @@ using static TerraFX.Aliases.D3D12_Alias;
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
/// <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>
|
||||
internal unsafe class D3D12DescriptorAllocator : IDisposable
|
||||
{
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -555,32 +555,16 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator, IDisposable
|
||||
|
||||
public Handle<Material> CreateMaterial(Identifier<Shader> 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<Shader> CreateShader(ShaderDescriptor descriptor)
|
||||
{
|
||||
var shaderData = new Shader();
|
||||
return _resourceDatabase.AddShader(ref shaderData);
|
||||
var shader = new Shader(descriptor);
|
||||
return _resourceDatabase.AddShader(shader);
|
||||
}
|
||||
|
||||
#region Conversion Methods
|
||||
|
||||
@@ -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<ResourceRecord> _resources;
|
||||
#if DEBUG || GHOST_EDITOR
|
||||
private readonly Dictionary<ResourceRecord, string> _resourceName;
|
||||
private readonly Dictionary<Handle<GPUResource>, string> _resourceName;
|
||||
#endif
|
||||
|
||||
private readonly UnsafeSlotMap<Mesh> _meshes;
|
||||
private readonly UnsafeSlotMap<Material> _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<Slot<Shader>> _shaders;
|
||||
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.
|
||||
private readonly Dictionary<ShaderPassKey, ShaderPass> _shaderPasses;
|
||||
|
||||
private bool _disposed;
|
||||
@@ -135,28 +134,41 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
|
||||
where T : IResourceReleasable
|
||||
{
|
||||
resource.ReleaseResource(this);
|
||||
resource = default!;
|
||||
}
|
||||
|
||||
public Handle<GPUResource> ImportExternalResource<T>(T resource, ResourceState initialState)
|
||||
where T : unmanaged
|
||||
public Handle<GPUResource> ImportExternalResource(ComPtr<ID3D12Resource> resource, ResourceState initialState, string? name = null)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (resource is not ComPtr<ID3D12Resource> d3d12Resource)
|
||||
{
|
||||
throw new InvalidOperationException($"Expect ComPtr<ID3D12Resource> in D3D12ResourceDatabase, but got {typeof(T)}.");
|
||||
}
|
||||
var id = _resources.Add(new ResourceRecord(resource, initialState), out var generation);
|
||||
var handle = new Handle<GPUResource>(id, generation);
|
||||
|
||||
var id = _resources.Add(new ResourceRecord(d3d12Resource, initialState), out var generation);
|
||||
return new Handle<GPUResource>(id, generation);
|
||||
#if DEBUG || GHOST_EDITOR
|
||||
if (name != null)
|
||||
{
|
||||
_resourceName[handle] = name;
|
||||
}
|
||||
#endif
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
public Handle<GPUResource> AddResource(ComPtr<D3D12MA_Allocation> allocation, uint cpuFenceValue, ResourceState initialState, ResourceViewGroup resourceDescriptor, ResourceDesc desc)
|
||||
public Handle<GPUResource> AddResource(ComPtr<D3D12MA_Allocation> allocation, uint cpuFenceValue, ResourceState initialState, ResourceViewGroup resourceDescriptor, ResourceDesc desc, string? name = null)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
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)
|
||||
@@ -218,13 +230,28 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
|
||||
|
||||
public int GetBindlessIndex(Handle<GPUResource> 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<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)
|
||||
@@ -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<Mesh> 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<Mesh> 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<Material> 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<Material> 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<Shader> AddShader(ref readonly Shader shader)
|
||||
public Identifier<Shader> AddShader(Shader shader)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var id = _shaders.Count;
|
||||
_shaders.Add(new Slot<Shader> { value = shader, occupied = true });
|
||||
_shaders.Add(shader);
|
||||
return new Identifier<Shader>(id);
|
||||
}
|
||||
|
||||
public bool HasShader(Identifier<Shader> 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<Shader> id)
|
||||
public Shader GetShaderReference(Identifier<Shader> 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<Shader> 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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<char> 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<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 uint AlignedSize => _alignedSize;
|
||||
|
||||
public unsafe CBufferCache(IResourceAllocator allocator, uint bufferSize)
|
||||
public unsafe CBufferCache(Handle<GraphicsBuffer> 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 => _shader;
|
||||
|
||||
public Material(Identifier<Shader> 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<T>(string propertyName, in T value)
|
||||
|
||||
@@ -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<string, int> _propertyLookup;
|
||||
private readonly UnsafeList<PropertyInfo> _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();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A representation of a GPU shader, including all the passes it contains.
|
||||
/// </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, 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)
|
||||
{
|
||||
_passIDs = new ShaderPassKey[descriptor.passes.Count];
|
||||
_passIDs = new UnsafeArray<ShaderPassKey>(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<int> GetPropertyPassIndices(string propertyName)
|
||||
public IReadOnlyCollection<int> 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();
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ghost.Core\Ghost.Core.csproj" />
|
||||
<ProjectReference Include="..\Ghost.Shader\Ghost.Shader.csproj" />
|
||||
</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.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
|
||||
/// <summary>
|
||||
/// Swap chain description
|
||||
/// </summary>
|
||||
public struct SwapChainDesc
|
||||
{
|
||||
public static TextureDimension ToTextureDimension(this D3D12_RESOURCE_DIMENSION dimension)
|
||||
/// <summary>
|
||||
/// Width of the swap chain
|
||||
/// </summary>
|
||||
public uint width;
|
||||
|
||||
/// <summary>
|
||||
/// Height of the swap chain
|
||||
/// </summary>
|
||||
public uint height;
|
||||
|
||||
/// <summary>
|
||||
/// Back buffer format
|
||||
/// </summary>
|
||||
public TextureFormat format;
|
||||
|
||||
/// <summary>
|
||||
/// Target for presentation (window handle or composition target)
|
||||
/// </summary>
|
||||
public SwapChainTarget target;
|
||||
|
||||
public SwapChainDesc(uint width, uint height, SwapChainTarget target, TextureFormat format = TextureFormat.B8G8R8A8_UNorm, uint bufferCount = 2)
|
||||
{
|
||||
return dimension switch
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.format = format;
|
||||
this.target = target;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Swap chain target (window handle or composition surface)
|
||||
/// </summary>
|
||||
public struct SwapChainTarget
|
||||
{
|
||||
/// <summary>
|
||||
/// Target type
|
||||
/// </summary>
|
||||
public SwapChainTargetType type;
|
||||
|
||||
/// <summary>
|
||||
/// Window handle for HWND targets
|
||||
/// </summary>
|
||||
public nint windowHandle;
|
||||
|
||||
/// <summary>
|
||||
/// Composition surface for UWP/WinUI targets
|
||||
/// </summary>
|
||||
public object? compositionSurface;
|
||||
|
||||
public static SwapChainTarget FromWindowHandle(nint hwnd)
|
||||
{
|
||||
return new SwapChainTarget
|
||||
{
|
||||
D3D12_RESOURCE_DIMENSION.D3D12_RESOURCE_DIMENSION_TEXTURE1D => TextureDimension.Texture2D,
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Swap chain target types
|
||||
/// </summary>
|
||||
public enum SwapChainTargetType
|
||||
{
|
||||
WindowHandle,
|
||||
Composition
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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.
|
||||
/// </summary>
|
||||
/// <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>
|
||||
/// Release a resource given its handle
|
||||
|
||||
@@ -13,6 +13,7 @@ public interface IResourceReleasable
|
||||
|
||||
public interface IResourceDatabase
|
||||
{
|
||||
/*
|
||||
/// <summary>
|
||||
/// Imports an external unmanaged resource and returns a handle for use within the resource management system.
|
||||
/// </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="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>
|
||||
unsafe Handle<GPUResource> ImportExternalResource<T>(T resourcePtr, ResourceState initialState)
|
||||
unsafe Handle<GPUResource> ImportExternalResource<T>(T resourcePtr, ResourceState initialState, string? name = null)
|
||||
where T : unmanaged;
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// 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>
|
||||
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>
|
||||
/// Removes a resource from the database using its handle.
|
||||
/// </summary>
|
||||
@@ -116,7 +128,7 @@ public interface IResourceDatabase
|
||||
/// </summary>
|
||||
/// <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>
|
||||
Identifier<Shader> AddShader(ref readonly Shader shader);
|
||||
Identifier<Shader> AddShader(Shader shader);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a shader with the specified identifier exists in the collection.
|
||||
@@ -130,7 +142,7 @@ public interface IResourceDatabase
|
||||
/// </summary>
|
||||
/// <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>
|
||||
ref Shader GetShaderReference(Identifier<Shader> id);
|
||||
Shader GetShaderReference(Identifier<Shader> id);
|
||||
|
||||
/// <summary>
|
||||
/// Releases the shader associated with the specified identifier, freeing any resources allocated to it.
|
||||
|
||||
@@ -50,88 +50,4 @@ public interface ISwapChain : IDisposable
|
||||
/// <param name="width">New width</param>
|
||||
/// <param name="height">New height</param>
|
||||
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;
|
||||
|
||||
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
|
||||
@@ -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<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)
|
||||
|
||||
@@ -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:");
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -4,5 +4,5 @@ internal interface IBlockParser<T, U>
|
||||
{
|
||||
public static abstract bool ShouldEnter(Token token);
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -44,7 +44,7 @@ internal class IncludesBlock : IBlockParser<List<Token>, List<string>>
|
||||
}
|
||||
else
|
||||
{
|
||||
errors.Add(new ShaderError
|
||||
errors.Add(new SDLError
|
||||
{
|
||||
message = $"Included file '{path}' not found.",
|
||||
line = includeToken.line,
|
||||
|
||||
@@ -30,7 +30,7 @@ internal class KeywordsBlock : IBlockParser<List<FunctionCallDeclaration>, List<
|
||||
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)
|
||||
{
|
||||
@@ -42,7 +42,7 @@ internal class KeywordsBlock : IBlockParser<List<FunctionCallDeclaration>, 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<FunctionCallDeclaration>, 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,
|
||||
|
||||
@@ -61,7 +61,7 @@ internal class PassBlock : IBlockParser<PassSyntax, PassSemantic>
|
||||
return pass;
|
||||
}
|
||||
|
||||
public static PassSemantic? SemanticAnalysis(PassSyntax? syntax, List<ShaderError> errors)
|
||||
public static PassSemantic? SemanticAnalysis(PassSyntax? syntax, List<SDLError> errors)
|
||||
{
|
||||
if (syntax == null)
|
||||
{
|
||||
@@ -81,7 +81,7 @@ internal class PassBlock : IBlockParser<PassSyntax, PassSemantic>
|
||||
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<PassSyntax, PassSemantic>
|
||||
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<PassSyntax, PassSemantic>
|
||||
{
|
||||
// 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<PassSyntax, PassSemantic>
|
||||
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)
|
||||
{
|
||||
errors.Add(new ShaderError
|
||||
errors.Add(new SDLError
|
||||
{
|
||||
message = "Shader declaration requires exactly two arguments: (shaderPath, entryPoint).",
|
||||
line = func.name.line,
|
||||
|
||||
@@ -38,7 +38,7 @@ internal class PipelineBlock : IBlockParser<PipelineSyntax, PipelineSemantic>
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
public static PipelineSemantic? SemanticAnalysis(PipelineSyntax? syntax, List<ShaderError> errors)
|
||||
public static PipelineSemantic? SemanticAnalysis(PipelineSyntax? syntax, List<SDLError> errors)
|
||||
{
|
||||
if (syntax == null)
|
||||
{
|
||||
@@ -80,7 +80,7 @@ internal class PipelineBlock : IBlockParser<PipelineSyntax, PipelineSemantic>
|
||||
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<PipelineSyntax, PipelineSemantic>
|
||||
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<PipelineSyntax, PipelineSemantic>
|
||||
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<PipelineSyntax, PipelineSemantic>
|
||||
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<PipelineSyntax, PipelineSemantic>
|
||||
}
|
||||
else
|
||||
{
|
||||
errors.Add(new ShaderError
|
||||
errors.Add(new SDLError
|
||||
{
|
||||
message = $"Invalid Color Mask value: {valueDecl.value.lexeme}",
|
||||
line = valueDecl.value.line,
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Ghost.Shader.Compiler.Parser;
|
||||
|
||||
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);
|
||||
|
||||
@@ -78,11 +78,11 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
|
||||
[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))
|
||||
{
|
||||
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<PropertiesSyntax, List<PropertySem
|
||||
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))
|
||||
{
|
||||
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<PropertiesSyntax, List<PropertySem
|
||||
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))
|
||||
{
|
||||
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<PropertiesSyntax, List<PropertySem
|
||||
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))
|
||||
{
|
||||
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<PropertiesSyntax, List<PropertySem
|
||||
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))
|
||||
{
|
||||
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<PropertiesSyntax, List<PropertySem
|
||||
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)
|
||||
{
|
||||
@@ -295,11 +295,11 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
|
||||
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))
|
||||
{
|
||||
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<PropertiesSyntax, List<PropertySem
|
||||
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))
|
||||
{
|
||||
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<PropertiesSyntax, List<PropertySem
|
||||
}
|
||||
else if (usedPropertyNames.Contains(property.name.lexeme))
|
||||
{
|
||||
errors.Add(new ShaderError
|
||||
errors.Add(new SDLError
|
||||
{
|
||||
message = $"Shader property name '{property.name.lexeme}' is duplicated.",
|
||||
line = property.name.line,
|
||||
@@ -342,12 +342,12 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
|
||||
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;
|
||||
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<PropertiesSyntax, List<PropertySem
|
||||
var constructorValue = constructor.Value;
|
||||
if (string.IsNullOrWhiteSpace(constructorValue.name.lexeme))
|
||||
{
|
||||
errors.Add(new ShaderError
|
||||
errors.Add(new SDLError
|
||||
{
|
||||
message = "Shader property constructor has an empty name.",
|
||||
line = constructorValue.name.line,
|
||||
@@ -372,7 +372,7 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
|
||||
|
||||
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}'.",
|
||||
line = constructorValue.name.line,
|
||||
@@ -384,7 +384,7 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
|
||||
|
||||
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}'.",
|
||||
line = constructorValue.name.line,
|
||||
@@ -397,7 +397,7 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
|
||||
// Count check
|
||||
if (constructorValue.arguments == null)
|
||||
{
|
||||
errors.Add(new ShaderError
|
||||
errors.Add(new SDLError
|
||||
{
|
||||
message = "Shader property constructor arguments are null.",
|
||||
line = constructorValue.name.line,
|
||||
@@ -409,7 +409,7 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
|
||||
|
||||
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}.",
|
||||
line = constructorValue.name.line,
|
||||
@@ -426,7 +426,7 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
|
||||
var arg = constructorValue.arguments[i];
|
||||
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}'.",
|
||||
line = arg.line,
|
||||
@@ -451,7 +451,7 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errors.Add(new ShaderError
|
||||
errors.Add(new SDLError
|
||||
{
|
||||
message = $"Failed to construct default value for property '{property.name.lexeme}': {ex.Message}",
|
||||
line = constructorValue.name.line,
|
||||
|
||||
@@ -49,7 +49,7 @@ internal class ShaderBlock : IBlockParser<ShaderSyntax, ShaderSemantics>
|
||||
return shader;
|
||||
}
|
||||
|
||||
public static ShaderSemantics? SemanticAnalysis(ShaderSyntax? syntax, List<ShaderError> errors)
|
||||
public static ShaderSemantics? SemanticAnalysis(ShaderSyntax? syntax, List<SDLError> errors)
|
||||
{
|
||||
if (syntax == null)
|
||||
{
|
||||
@@ -85,7 +85,7 @@ internal class ShaderBlock : IBlockParser<ShaderSyntax, ShaderSemantics>
|
||||
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<ShaderSyntax, ShaderSemantics>
|
||||
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,
|
||||
|
||||
@@ -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<ShaderError> errors)
|
||||
public static ShaderSemantics? SemanticAnalysis(ShaderSyntax syntax, out List<SDLError> 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<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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user