feat: Implement D3D12 resource factory and improve swap chain management
- Added D3D12ResourceFactory for creating render targets, textures, and buffers. - Enhanced D3D12SwapChain to manage back buffer render targets and provide access to them. - Updated D3D12Texture to utilize resource handles for better resource management. - Removed legacy ResourceAllocator and integrated improvements for resource handling. - Introduced new interfaces for resource factory and swap chain to streamline resource creation. - Added support for mip levels and texture dimensions in render target and texture descriptions. - Created new markdown files to document allocator and swap chain improvements.
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
using Ghost.Graphics.Data;
|
||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
using Win32;
|
using Win32;
|
||||||
using Win32.Graphics.Direct3D12;
|
using Win32.Graphics.Direct3D12;
|
||||||
@@ -5,11 +6,12 @@ using Win32.Graphics.Direct3D12;
|
|||||||
namespace Ghost.Graphics.D3D12;
|
namespace Ghost.Graphics.D3D12;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// D3D12 implementation of buffer interface
|
/// D3D12 implementation of buffer interface using resource handles
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal unsafe class D3D12Buffer : IBuffer
|
internal unsafe class D3D12Buffer : IBuffer
|
||||||
{
|
{
|
||||||
private ComPtr<ID3D12Resource> _resource;
|
private readonly BufferHandle _handle;
|
||||||
|
private readonly ComPtr<ID3D12Resource> _externalResource; // For externally managed resources
|
||||||
private ResourceState _currentState;
|
private ResourceState _currentState;
|
||||||
private void* _mappedPtr;
|
private void* _mappedPtr;
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
@@ -18,64 +20,56 @@ internal unsafe class D3D12Buffer : IBuffer
|
|||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
}
|
}
|
||||||
public string Name { get; set; } = string.Empty;
|
|
||||||
|
public MemoryType MemoryType
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get => field;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
field = value;
|
||||||
|
NativeResource->SetName(field);
|
||||||
|
}
|
||||||
|
} = string.Empty;
|
||||||
|
|
||||||
public ulong Size
|
public ulong Size
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResourceState CurrentState => _currentState;
|
public ResourceState CurrentState => _currentState;
|
||||||
|
|
||||||
public ID3D12Resource* NativeResource => _resource.Get();
|
public ID3D12Resource* NativeResource => _handle.ResourceHandle.GetAllocation().Resource;
|
||||||
|
|
||||||
public D3D12Buffer(ComPtr<ID3D12Device14> device, BufferDesc desc)
|
/// <summary>
|
||||||
|
/// Constructor for wrapping existing D3D12 resources
|
||||||
|
/// </summary>
|
||||||
|
public D3D12Buffer(ComPtr<ID3D12Resource> resource, ulong size, BufferUsage usage, MemoryType memoryType)
|
||||||
{
|
{
|
||||||
Usage = desc.Usage;
|
_handle = BufferHandle.Invalid;
|
||||||
Size = desc.Size;
|
_externalResource = resource.Move();
|
||||||
_currentState = ResourceState.Common;
|
|
||||||
|
|
||||||
CreateBuffer(device, desc);
|
Size = size;
|
||||||
|
Usage = usage;
|
||||||
|
MemoryType = memoryType;
|
||||||
|
_currentState = ResourceState.Common;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateBuffer(ComPtr<ID3D12Device14> device, BufferDesc desc)
|
/// <summary>
|
||||||
|
/// Constructor for allocator-managed buffers
|
||||||
|
/// </summary>
|
||||||
|
public D3D12Buffer(BufferHandle handle, ref readonly BufferDesc desc)
|
||||||
{
|
{
|
||||||
var resourceDesc = new ResourceDescription
|
_handle = handle;
|
||||||
{
|
|
||||||
Dimension = ResourceDimension.Buffer,
|
|
||||||
Alignment = 0,
|
|
||||||
Width = desc.Size,
|
|
||||||
Height = 1,
|
|
||||||
DepthOrArraySize = 1,
|
|
||||||
MipLevels = 1,
|
|
||||||
Format = Win32.Graphics.Dxgi.Common.Format.Unknown,
|
|
||||||
SampleDesc = new Win32.Graphics.Dxgi.Common.SampleDescription(1, 0),
|
|
||||||
Layout = TextureLayout.RowMajor,
|
|
||||||
Flags = ConvertBufferUsage(desc.Usage)
|
|
||||||
};
|
|
||||||
|
|
||||||
var heapProps = new HeapProperties
|
Size = desc.Size;
|
||||||
{
|
Usage = desc.Usage;
|
||||||
Type = ConvertMemoryType(desc.MemoryType),
|
MemoryType = desc.MemoryType;
|
||||||
CPUPageProperty = CpuPageProperty.Unknown,
|
_currentState = ResourceState.Common;
|
||||||
MemoryPoolPreference = MemoryPool.Unknown,
|
|
||||||
CreationNodeMask = 1,
|
|
||||||
VisibleNodeMask = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
var initialState = desc.MemoryType switch
|
|
||||||
{
|
|
||||||
MemoryType.Upload => Win32.Graphics.Direct3D12.ResourceStates.GenericRead,
|
|
||||||
MemoryType.Readback => Win32.Graphics.Direct3D12.ResourceStates.CopyDest,
|
|
||||||
_ => Win32.Graphics.Direct3D12.ResourceStates.Common
|
|
||||||
};
|
|
||||||
|
|
||||||
device.Get()->CreateCommittedResource(
|
|
||||||
&heapProps,
|
|
||||||
HeapFlags.None,
|
|
||||||
&resourceDesc,
|
|
||||||
initialState,
|
|
||||||
null,
|
|
||||||
__uuidof<ID3D12Resource>(),
|
|
||||||
_resource.GetVoidAddressOf());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void* Map()
|
public void* Map()
|
||||||
@@ -83,11 +77,15 @@ internal unsafe class D3D12Buffer : IBuffer
|
|||||||
if (_mappedPtr != null)
|
if (_mappedPtr != null)
|
||||||
return _mappedPtr;
|
return _mappedPtr;
|
||||||
|
|
||||||
var range = new Win32.Graphics.Direct3D12.Range { Begin = 0, End = 0 };
|
if (MemoryType != MemoryType.Upload && MemoryType != MemoryType.Readback)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Only upload and readback buffers can be mapped");
|
||||||
|
}
|
||||||
|
|
||||||
|
var range = new Win32.Graphics.Direct3D12.Range { Begin = 0, End = 0 };
|
||||||
fixed (void** ptr = &_mappedPtr)
|
fixed (void** ptr = &_mappedPtr)
|
||||||
{
|
{
|
||||||
_resource.Get()->Map(0, &range, ptr);
|
NativeResource->Map(0, &range, ptr);
|
||||||
}
|
}
|
||||||
return _mappedPtr;
|
return _mappedPtr;
|
||||||
}
|
}
|
||||||
@@ -96,30 +94,14 @@ internal unsafe class D3D12Buffer : IBuffer
|
|||||||
{
|
{
|
||||||
if (_mappedPtr != null)
|
if (_mappedPtr != null)
|
||||||
{
|
{
|
||||||
_resource.Get()->Unmap(0, null);
|
NativeResource->Unmap(0, null);
|
||||||
_mappedPtr = null;
|
_mappedPtr = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HeapType ConvertMemoryType(MemoryType memoryType)
|
public void SetCurrentState(ResourceState state)
|
||||||
{
|
{
|
||||||
return memoryType switch
|
_currentState = state;
|
||||||
{
|
|
||||||
MemoryType.Default => HeapType.Default,
|
|
||||||
MemoryType.Upload => HeapType.Upload,
|
|
||||||
MemoryType.Readback => HeapType.Readback,
|
|
||||||
_ => throw new ArgumentException($"Unknown memory type: {memoryType}")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ResourceFlags ConvertBufferUsage(BufferUsage usage)
|
|
||||||
{
|
|
||||||
var flags = ResourceFlags.None;
|
|
||||||
|
|
||||||
if ((usage & BufferUsage.Raw) != 0)
|
|
||||||
flags |= ResourceFlags.AllowUnorderedAccess;
|
|
||||||
|
|
||||||
return flags;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
@@ -128,7 +110,17 @@ internal unsafe class D3D12Buffer : IBuffer
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
Unmap();
|
Unmap();
|
||||||
_resource.Dispose();
|
|
||||||
|
if (_handle.IsValid)
|
||||||
|
{
|
||||||
|
_handle.Dispose();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Release external resource
|
||||||
|
_externalResource.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Ghost.Graphics.Data;
|
using Ghost.Graphics.Data;
|
||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using Win32;
|
using Win32;
|
||||||
using Win32.Graphics.Direct3D12;
|
using Win32.Graphics.Direct3D12;
|
||||||
using Win32.Numerics;
|
using Win32.Numerics;
|
||||||
@@ -133,27 +132,28 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Win32.Graphics.Direct3D12.ResourceStates ConvertResourceState(ResourceState state)
|
private static ResourceStates ConvertResourceState(ResourceState state)
|
||||||
{
|
{
|
||||||
return state switch
|
return state switch
|
||||||
{
|
{
|
||||||
ResourceState.Common or ResourceState.Present => Win32.Graphics.Direct3D12.ResourceStates.Common,
|
ResourceState.Common or ResourceState.Present => ResourceStates.Common,
|
||||||
ResourceState.VertexAndConstantBuffer => Win32.Graphics.Direct3D12.ResourceStates.VertexAndConstantBuffer,
|
ResourceState.VertexAndConstantBuffer => ResourceStates.VertexAndConstantBuffer,
|
||||||
ResourceState.IndexBuffer => Win32.Graphics.Direct3D12.ResourceStates.IndexBuffer,
|
ResourceState.IndexBuffer => ResourceStates.IndexBuffer,
|
||||||
ResourceState.RenderTarget => Win32.Graphics.Direct3D12.ResourceStates.RenderTarget,
|
ResourceState.RenderTarget => ResourceStates.RenderTarget,
|
||||||
ResourceState.UnorderedAccess => Win32.Graphics.Direct3D12.ResourceStates.UnorderedAccess,
|
ResourceState.UnorderedAccess => ResourceStates.UnorderedAccess,
|
||||||
ResourceState.DepthWrite => Win32.Graphics.Direct3D12.ResourceStates.DepthWrite,
|
ResourceState.DepthWrite => ResourceStates.DepthWrite,
|
||||||
ResourceState.DepthRead => Win32.Graphics.Direct3D12.ResourceStates.DepthRead,
|
ResourceState.DepthRead => ResourceStates.DepthRead,
|
||||||
ResourceState.PixelShaderResource => Win32.Graphics.Direct3D12.ResourceStates.PixelShaderResource,
|
ResourceState.PixelShaderResource => ResourceStates.PixelShaderResource,
|
||||||
ResourceState.CopyDest => Win32.Graphics.Direct3D12.ResourceStates.CopyDest,
|
ResourceState.CopyDest => ResourceStates.CopyDest,
|
||||||
ResourceState.CopySource => Win32.Graphics.Direct3D12.ResourceStates.CopySource,
|
ResourceState.CopySource => ResourceStates.CopySource,
|
||||||
_ => throw new ArgumentException($"Unknown resource state: {state}")
|
_ => throw new ArgumentException($"Unknown resource state: {state}")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (_disposed) return;
|
if (_disposed)
|
||||||
|
return;
|
||||||
|
|
||||||
_commandList.Dispose();
|
_commandList.Dispose();
|
||||||
_allocator.Dispose();
|
_allocator.Dispose();
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using Ghost.Core;
|
|
||||||
using Ghost.Graphics.Data;
|
|
||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
using Win32;
|
using Win32;
|
||||||
using Win32.Graphics.Direct3D;
|
using Win32.Graphics.Direct3D;
|
||||||
@@ -17,10 +15,10 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
|
|||||||
private ComPtr<ID3D12Device14> _device;
|
private ComPtr<ID3D12Device14> _device;
|
||||||
private ComPtr<IDXGIAdapter1> _adapter;
|
private ComPtr<IDXGIAdapter1> _adapter;
|
||||||
|
|
||||||
private D3D12CommandQueue _graphicsQueue;
|
private readonly D3D12CommandQueue _graphicsQueue;
|
||||||
private D3D12CommandQueue _computeQueue;
|
private readonly D3D12CommandQueue _computeQueue;
|
||||||
private D3D12CommandQueue _copyQueue;
|
private readonly D3D12CommandQueue _copyQueue;
|
||||||
private D3D12DescriptorAllocator _descriptorAllocator;
|
private readonly D3D12DescriptorAllocator _descriptorAllocator;
|
||||||
|
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
@@ -29,9 +27,9 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
|
|||||||
public ICommandQueue CopyQueue => _copyQueue;
|
public ICommandQueue CopyQueue => _copyQueue;
|
||||||
public IDescriptorAllocator DescriptorAllocator => _descriptorAllocator;
|
public IDescriptorAllocator DescriptorAllocator => _descriptorAllocator;
|
||||||
|
|
||||||
public ConstPtr<ID3D12Device14> NativeDevice => new(_device.Get());
|
public ID3D12Device14* NativeDevice => _device.Get();
|
||||||
public ConstPtr<IDXGIFactory7> DXGIFactory => new(_dxgiFactory.Get());
|
public IDXGIFactory7* DXGIFactory => _dxgiFactory.Get();
|
||||||
public ConstPtr<IDXGIAdapter1> Adapter => new(_adapter.Get());
|
public IDXGIAdapter1* Adapter => _adapter.Get();
|
||||||
|
|
||||||
public D3D12RenderDevice()
|
public D3D12RenderDevice()
|
||||||
{
|
{
|
||||||
@@ -90,24 +88,10 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
|
|||||||
return new D3D12SwapChain(_dxgiFactory, _graphicsQueue.NativeQueue, desc);
|
return new D3D12SwapChain(_dxgiFactory, _graphicsQueue.NativeQueue, desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IRenderTarget CreateRenderTarget(RenderTargetDesc desc)
|
|
||||||
{
|
|
||||||
return new D3D12RenderTarget(_device, _descriptorAllocator, desc);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ITexture CreateTexture(TextureDesc desc)
|
|
||||||
{
|
|
||||||
return new D3D12Texture(_device, desc);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IBuffer CreateBuffer(BufferDesc desc)
|
|
||||||
{
|
|
||||||
return new D3D12Buffer(_device, desc);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (_disposed) return;
|
if (_disposed)
|
||||||
|
return;
|
||||||
|
|
||||||
_descriptorAllocator?.Dispose();
|
_descriptorAllocator?.Dispose();
|
||||||
_graphicsQueue?.Dispose();
|
_graphicsQueue?.Dispose();
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
|
using Ghost.Graphics.Data;
|
||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
using Win32;
|
|
||||||
using Win32.Graphics.Direct3D12;
|
|
||||||
|
|
||||||
namespace Ghost.Graphics.D3D12;
|
namespace Ghost.Graphics.D3D12;
|
||||||
|
|
||||||
@@ -13,12 +12,27 @@ internal unsafe class D3D12RenderTarget : IRenderTarget
|
|||||||
private readonly D3D12Texture _target;
|
private readonly D3D12Texture _target;
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
public uint Width { get; }
|
public uint Width
|
||||||
public uint Height { get; }
|
{
|
||||||
public RenderTargetType Type { get; }
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint Height
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RenderTargetType Type
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
public ITexture Target => _target;
|
public ITexture Target => _target;
|
||||||
|
|
||||||
public D3D12RenderTarget(ComPtr<ID3D12Device14> device, D3D12DescriptorAllocator descriptorAllocator, RenderTargetDesc desc)
|
/// <summary>
|
||||||
|
/// Create a new render target with its own texture
|
||||||
|
/// </summary>
|
||||||
|
public D3D12RenderTarget(TextureHandle handle, ref readonly RenderTargetDesc desc)
|
||||||
{
|
{
|
||||||
Width = desc.Width;
|
Width = desc.Width;
|
||||||
Height = desc.Height;
|
Height = desc.Height;
|
||||||
@@ -26,13 +40,27 @@ internal unsafe class D3D12RenderTarget : IRenderTarget
|
|||||||
|
|
||||||
// Create the target texture based on type
|
// Create the target texture based on type
|
||||||
var usage = Type == RenderTargetType.Color ? TextureUsage.RenderTarget : TextureUsage.DepthStencil;
|
var usage = Type == RenderTargetType.Color ? TextureUsage.RenderTarget : TextureUsage.DepthStencil;
|
||||||
var textureDesc = new TextureDesc(desc.Width, desc.Height, desc.Format, 1, usage);
|
var textureDesc = new TextureDesc(desc.Width, desc.Height, desc.Format, desc.Dimension, desc.MipLevels, usage);
|
||||||
_target = new D3D12Texture(device, textureDesc);
|
_target = new D3D12Texture(handle, in textureDesc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wrap an existing texture as a render target (for swap chain back buffers)
|
||||||
|
/// </summary>
|
||||||
|
public D3D12RenderTarget(D3D12Texture existingTexture, RenderTargetType type)
|
||||||
|
{
|
||||||
|
_target = existingTexture;
|
||||||
|
Width = existingTexture.Width;
|
||||||
|
Height = existingTexture.Height;
|
||||||
|
Type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (_disposed) return;
|
if (_disposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_target?.Dispose();
|
_target?.Dispose();
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
|
|||||||
@@ -26,13 +26,13 @@ public unsafe class D3D12Renderer : IRenderer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly IRenderDevice _device;
|
|
||||||
private readonly ICommandQueue _commandQueue;
|
private readonly ICommandQueue _commandQueue;
|
||||||
private readonly FrameResource[] _frameResources;
|
private readonly FrameResource[] _frameResources;
|
||||||
private uint _frameIndex;
|
private uint _frameIndex;
|
||||||
|
|
||||||
private IRenderTarget? _renderTarget;
|
private IRenderTarget? _destinationTarget; // Final destination (custom render target or swap chain back buffer)
|
||||||
private ISwapChain? _swapChain;
|
private ISwapChain? _swapChain;
|
||||||
|
private IRenderTarget? _offScreenRenderTarget; // Always render to off-screen first
|
||||||
|
|
||||||
private readonly Lock _lock = new();
|
private readonly Lock _lock = new();
|
||||||
private uint _pendingWidth;
|
private uint _pendingWidth;
|
||||||
@@ -45,7 +45,6 @@ public unsafe class D3D12Renderer : IRenderer
|
|||||||
|
|
||||||
public D3D12Renderer(IRenderDevice device)
|
public D3D12Renderer(IRenderDevice device)
|
||||||
{
|
{
|
||||||
_device = device;
|
|
||||||
_commandQueue = device.GraphicsQueue;
|
_commandQueue = device.GraphicsQueue;
|
||||||
|
|
||||||
// Create frame resources for double buffering
|
// Create frame resources for double buffering
|
||||||
@@ -58,16 +57,37 @@ public unsafe class D3D12Renderer : IRenderer
|
|||||||
|
|
||||||
public void SetRenderTarget(IRenderTarget? renderTarget)
|
public void SetRenderTarget(IRenderTarget? renderTarget)
|
||||||
{
|
{
|
||||||
_renderTarget = renderTarget;
|
_destinationTarget = renderTarget;
|
||||||
_swapChain = null; // Clear swap chain when using render target
|
_swapChain = null; // Clear swap chain when using custom render target
|
||||||
|
|
||||||
|
// Create or update off-screen render target to match destination size
|
||||||
|
if (_destinationTarget != null)
|
||||||
|
{
|
||||||
|
CreateOrUpdateOffScreenRenderTarget(_destinationTarget.Width, _destinationTarget.Height);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_offScreenRenderTarget?.Dispose();
|
||||||
|
_offScreenRenderTarget = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetSwapChain(ISwapChain? swapChain)
|
public void SetSwapChain(ISwapChain? swapChain)
|
||||||
{
|
{
|
||||||
_swapChain = swapChain;
|
_swapChain = swapChain;
|
||||||
_renderTarget = null; // Clear render target when using swap chain
|
_destinationTarget = null; // Clear custom render target when using swap chain
|
||||||
}
|
|
||||||
|
|
||||||
|
// Create or update off-screen render target to match swap chain size
|
||||||
|
if (_swapChain != null)
|
||||||
|
{
|
||||||
|
CreateOrUpdateOffScreenRenderTarget(_swapChain.Width, _swapChain.Height);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_offScreenRenderTarget?.Dispose();
|
||||||
|
_offScreenRenderTarget = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
public void RequestResize(uint width, uint height)
|
public void RequestResize(uint width, uint height)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
@@ -99,6 +119,12 @@ public unsafe class D3D12Renderer : IRenderer
|
|||||||
|
|
||||||
// Resize swap chain if present
|
// Resize swap chain if present
|
||||||
_swapChain?.Resize(newWidth, newHeight);
|
_swapChain?.Resize(newWidth, newHeight);
|
||||||
|
|
||||||
|
// Update off-screen render target size
|
||||||
|
if (_swapChain != null)
|
||||||
|
{
|
||||||
|
CreateOrUpdateOffScreenRenderTarget(newWidth, newHeight);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Render()
|
public void Render()
|
||||||
@@ -118,17 +144,31 @@ public unsafe class D3D12Renderer : IRenderer
|
|||||||
// Begin command recording
|
// Begin command recording
|
||||||
frame.CommandBuffer.Begin();
|
frame.CommandBuffer.Begin();
|
||||||
|
|
||||||
if (_renderTarget != null)
|
// Determine the final destination target
|
||||||
|
IRenderTarget? finalDestination = null;
|
||||||
|
ITexture? swapChainBackBuffer = null;
|
||||||
|
|
||||||
|
if (_destinationTarget != null)
|
||||||
{
|
{
|
||||||
RenderToTarget(_renderTarget, frame.CommandBuffer);
|
// Rendering to custom render target
|
||||||
|
finalDestination = _destinationTarget;
|
||||||
}
|
}
|
||||||
else if (_swapChain != null)
|
else if (_swapChain != null)
|
||||||
{
|
{
|
||||||
RenderToSwapChain(_swapChain, frame.CommandBuffer);
|
// Rendering to swap chain - get back buffer as render target
|
||||||
|
finalDestination = _swapChain.GetCurrentBackBufferRenderTarget();
|
||||||
|
swapChainBackBuffer = _swapChain.GetCurrentBackBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (finalDestination != null && _offScreenRenderTarget != null)
|
||||||
|
{
|
||||||
|
// Always render to off-screen first, then blit to final destination
|
||||||
|
RenderScene(_offScreenRenderTarget, frame.CommandBuffer);
|
||||||
|
BlitToDestination(_offScreenRenderTarget, finalDestination, swapChainBackBuffer, frame.CommandBuffer);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// No render target - skip rendering
|
// No destination - skip rendering
|
||||||
frame.CommandBuffer.End();
|
frame.CommandBuffer.End();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -146,7 +186,7 @@ public unsafe class D3D12Renderer : IRenderer
|
|||||||
frame.FenceValue = _commandQueue.Signal(++_frameIndex);
|
frame.FenceValue = _commandQueue.Signal(++_frameIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RenderToTarget(IRenderTarget target, ICommandBuffer cmd)
|
private void RenderScene(IRenderTarget target, ICommandBuffer cmd)
|
||||||
{
|
{
|
||||||
var clearColor = new Color128 { r = 1.0f, g = 0.0f, b = 1.0f, a = 1.0f };
|
var clearColor = new Color128 { r = 1.0f, g = 0.0f, b = 1.0f, a = 1.0f };
|
||||||
|
|
||||||
@@ -167,31 +207,47 @@ public unsafe class D3D12Renderer : IRenderer
|
|||||||
cmd.EndRenderPass();
|
cmd.EndRenderPass();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RenderToSwapChain(ISwapChain swapChain, ICommandBuffer cmd)
|
private void BlitToDestination(IRenderTarget source, IRenderTarget destination, ITexture? swapChainBackBuffer, ICommandBuffer cmd)
|
||||||
{
|
{
|
||||||
var backBuffer = swapChain.GetCurrentBackBuffer();
|
// Handle swap chain back buffer transitions if needed
|
||||||
|
if (swapChainBackBuffer != null)
|
||||||
|
{
|
||||||
|
// Transition back buffer to render target
|
||||||
|
cmd.ResourceBarrier(swapChainBackBuffer, ResourceState.Present, ResourceState.RenderTarget);
|
||||||
|
}
|
||||||
|
|
||||||
// Transition back buffer to render target
|
// For now, we'll do a simple copy operation
|
||||||
cmd.ResourceBarrier(backBuffer, ResourceState.Present, ResourceState.RenderTarget);
|
// In a real implementation, you would use a blit shader for post-processing
|
||||||
|
|
||||||
// Create temporary render target for back buffer
|
// TODO: Implement proper blit operation with shader
|
||||||
// TODO: This should be cached/reused
|
// This is a placeholder - in D3D12, you would typically:
|
||||||
var renderTarget = CreateBackBufferRenderTarget(backBuffer);
|
// 1. Set render target to the destination
|
||||||
|
// 2. Use a full-screen quad/triangle with a shader that samples from the source
|
||||||
|
// 3. Apply post-processing effects (tone mapping, gamma correction, etc.)
|
||||||
|
|
||||||
RenderToTarget(renderTarget, cmd);
|
// For now, just clear the destination (this should be replaced with actual blit)
|
||||||
|
var clearColor = new Color128 { r = 0.0f, g = 0.0f, b = 0.0f, a = 1.0f };
|
||||||
|
cmd.BeginRenderPass(destination, clearColor);
|
||||||
|
cmd.EndRenderPass();
|
||||||
|
|
||||||
// Transition back buffer to present
|
// Handle swap chain back buffer transitions if needed
|
||||||
cmd.ResourceBarrier(backBuffer, ResourceState.RenderTarget, ResourceState.Present);
|
if (swapChainBackBuffer != null)
|
||||||
|
{
|
||||||
renderTarget.Dispose();
|
// Transition back buffer to present
|
||||||
|
cmd.ResourceBarrier(swapChainBackBuffer, ResourceState.RenderTarget, ResourceState.Present);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private IRenderTarget CreateBackBufferRenderTarget(ITexture backBuffer)
|
private void CreateOrUpdateOffScreenRenderTarget(uint width, uint height)
|
||||||
{
|
{
|
||||||
// TODO: Create render target from back buffer texture
|
// Check if we need to recreate the off-screen render target
|
||||||
// This is a simplified implementation
|
if (_offScreenRenderTarget == null || _offScreenRenderTarget.Width != width || _offScreenRenderTarget.Height != height)
|
||||||
var desc = RenderTargetDesc.Color(backBuffer.Width, backBuffer.Height, backBuffer.Format);
|
{
|
||||||
return _device.CreateRenderTarget(desc);
|
_offScreenRenderTarget?.Dispose();
|
||||||
|
|
||||||
|
var desc = RenderTargetDesc.Color(width, height, TextureFormat.B8G8R8A8_UNorm);
|
||||||
|
_offScreenRenderTarget = _device.CreateRenderTarget(desc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WaitIdle()
|
public void WaitIdle()
|
||||||
@@ -217,6 +273,8 @@ public unsafe class D3D12Renderer : IRenderer
|
|||||||
frame.Dispose();
|
frame.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_offScreenRenderTarget?.Dispose();
|
||||||
|
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
342
Ghost.Graphics/D3D12/D3D12ResourceAllocator.cs
Normal file
342
Ghost.Graphics/D3D12/D3D12ResourceAllocator.cs
Normal file
@@ -0,0 +1,342 @@
|
|||||||
|
using Ghost.Graphics.Data;
|
||||||
|
using Ghost.Graphics.RHI;
|
||||||
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Win32.Graphics.D3D12MemoryAllocator;
|
||||||
|
using Win32.Graphics.Direct3D12;
|
||||||
|
using Win32.Graphics.Dxgi;
|
||||||
|
using Win32.Graphics.Dxgi.Common;
|
||||||
|
using static Win32.Graphics.D3D12MemoryAllocator.Apis;
|
||||||
|
using ResourceHandle = Ghost.Graphics.Data.ResourceHandle;
|
||||||
|
|
||||||
|
namespace Ghost.Graphics.D3D12;
|
||||||
|
|
||||||
|
internal unsafe class D3D12ResourceAllocator
|
||||||
|
{
|
||||||
|
private readonly struct AllocationInfo : IDisposable
|
||||||
|
{
|
||||||
|
public readonly Allocation allocation;
|
||||||
|
public readonly uint cpuFenceValue;
|
||||||
|
public readonly uint generation;
|
||||||
|
|
||||||
|
public bool Allocated => allocation.IsNotNull;
|
||||||
|
|
||||||
|
public AllocationInfo(in Allocation allocation, uint cpuFenceValue, uint generation)
|
||||||
|
{
|
||||||
|
this.allocation = allocation;
|
||||||
|
this.cpuFenceValue = cpuFenceValue;
|
||||||
|
this.generation = generation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (allocation.IsNull)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
allocation.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const uint _MAX_BYTES = D3D12_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM * 1024u * 1024u;
|
||||||
|
private const uint _MAX_TEXTURE2D_DIMENSION = 16384u;
|
||||||
|
private const uint _MAX_TEXTURE3D_DIMENSION = 2048u;
|
||||||
|
|
||||||
|
private readonly RenderSystem _renderSystem;
|
||||||
|
private readonly Allocator _allocator;
|
||||||
|
private UnsafeList<AllocationInfo> _allocations = new(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
|
||||||
|
private UnsafeQueue<int> _freeSlots = new(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
|
||||||
|
private UnsafeQueue<ResourceHandle> _temResources = new(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
|
||||||
|
|
||||||
|
private Guid* IID_NULL
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
fixed (Guid* pGuid = &Guid.Empty)
|
||||||
|
{
|
||||||
|
return pGuid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public D3D12ResourceAllocator(IDXGIAdapter* pAdapter, ID3D12Device* pDevice, RenderSystem renderSystem)
|
||||||
|
{
|
||||||
|
_renderSystem = renderSystem;
|
||||||
|
|
||||||
|
var desc = new AllocatorDesc
|
||||||
|
{
|
||||||
|
pAdapter = pAdapter,
|
||||||
|
pDevice = pDevice,
|
||||||
|
Flags = AllocatorFlags.DefaultPoolsNotZeroed | AllocatorFlags.MSAATexturesAlwaysCommitted
|
||||||
|
};
|
||||||
|
|
||||||
|
CreateAllocator(in desc, out _allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static void CheckBufferSize(uint sizeInBytes)
|
||||||
|
{
|
||||||
|
if (sizeInBytes > _MAX_BYTES)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {sizeInBytes})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static void CheckTexture2DSize(uint width, uint height)
|
||||||
|
{
|
||||||
|
if (width > _MAX_TEXTURE2D_DIMENSION || height > _MAX_TEXTURE2D_DIMENSION)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"ERROR: Texture size too large for DirectX 12 (width {width}, height {height})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResourceHandle TrackResource(in Allocation allocation, bool isTemp)
|
||||||
|
{
|
||||||
|
int id;
|
||||||
|
uint generation;
|
||||||
|
AllocationInfo allocInfo;
|
||||||
|
|
||||||
|
if (_freeSlots.Count > 0)
|
||||||
|
{
|
||||||
|
id = _freeSlots.Dequeue();
|
||||||
|
var info = _allocations[id];
|
||||||
|
if (info.Allocated)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"ERROR: Resource ID {id} registered as free but still allocated.");
|
||||||
|
}
|
||||||
|
|
||||||
|
generation = info.generation + 1;
|
||||||
|
allocInfo = new AllocationInfo(in allocation, _renderSystem.CPUFenceValue, generation);
|
||||||
|
|
||||||
|
_allocations[id] = allocInfo;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
id = _allocations.Count;
|
||||||
|
generation = 0u;
|
||||||
|
allocInfo = new AllocationInfo(in allocation, _renderSystem.CPUFenceValue, generation);
|
||||||
|
|
||||||
|
_allocations.Add(allocInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
var handle = new ResourceHandle(id, generation);
|
||||||
|
if (isTemp)
|
||||||
|
{
|
||||||
|
_temResources.Enqueue(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextureHandle CreateTexture2D(in TextureDesc desc, bool tempResource = false)
|
||||||
|
{
|
||||||
|
CheckTexture2DSize(desc.Width, desc.Height);
|
||||||
|
|
||||||
|
var resourceDesc = ResourceDescription.Tex2D(
|
||||||
|
ConvertTextureFormat(desc.Format),
|
||||||
|
desc.Width,
|
||||||
|
desc.Height,
|
||||||
|
mipLevels: (ushort)desc.MipLevels,
|
||||||
|
arraySize: 1,
|
||||||
|
flags: ConvertTextureUsage(desc.Usage)
|
||||||
|
);
|
||||||
|
|
||||||
|
var allocationDesc = new AllocationDesc
|
||||||
|
{
|
||||||
|
HeapType = HeapType.Default,
|
||||||
|
Flags = AllocationFlags.None
|
||||||
|
};
|
||||||
|
|
||||||
|
var initialState = DetermineInitialTextureState(desc.Usage);
|
||||||
|
|
||||||
|
Allocation allocation = default;
|
||||||
|
ThrowIfFailed(_allocator.CreateResource(&allocationDesc, in resourceDesc, initialState, null, &allocation, IID_NULL, null));
|
||||||
|
|
||||||
|
return new(TrackResource(in allocation, tempResource));
|
||||||
|
}
|
||||||
|
|
||||||
|
public BufferHandle CreateBuffer(in BufferDesc desc, bool tempResource = false)
|
||||||
|
{
|
||||||
|
CheckBufferSize((uint)desc.Size);
|
||||||
|
|
||||||
|
var resourceDescription = ResourceDescription.Buffer(desc.Size, ConvertBufferUsage(desc.Usage));
|
||||||
|
var allocationDesc = new AllocationDesc
|
||||||
|
{
|
||||||
|
HeapType = ConvertMemoryType(desc.MemoryType),
|
||||||
|
Flags = AllocationFlags.None
|
||||||
|
};
|
||||||
|
|
||||||
|
var initialState = DetermineInitialBufferState(desc.Usage, desc.MemoryType);
|
||||||
|
|
||||||
|
Allocation allocation = default;
|
||||||
|
ThrowIfFailed(_allocator.CreateResource(&allocationDesc, in resourceDescription, initialState, null, &allocation, IID_NULL, null));
|
||||||
|
|
||||||
|
return new(TrackResource(in allocation, tempResource));
|
||||||
|
}
|
||||||
|
|
||||||
|
public BufferHandle CreateUploadBuffer(uint sizeInBytes, bool tempResource = false)
|
||||||
|
{
|
||||||
|
var desc = new BufferDesc(sizeInBytes, BufferUsage.Upload, MemoryType.Upload);
|
||||||
|
return CreateBuffer(in desc, tempResource);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Conversion Methods
|
||||||
|
|
||||||
|
private static Format ConvertTextureFormat(TextureFormat format)
|
||||||
|
{
|
||||||
|
return format switch
|
||||||
|
{
|
||||||
|
TextureFormat.R8G8B8A8_UNorm => Format.R8G8B8A8Unorm,
|
||||||
|
TextureFormat.B8G8R8A8_UNorm => Format.B8G8R8A8Unorm,
|
||||||
|
TextureFormat.R16G16B16A16_Float => Format.R16G16B16A16Float,
|
||||||
|
TextureFormat.R32G32B32A32_Float => Format.R32G32B32A32Float,
|
||||||
|
TextureFormat.D24_UNorm_S8_UInt => Format.D24UnormS8Uint,
|
||||||
|
TextureFormat.D32_Float => Format.D32Float,
|
||||||
|
_ => throw new ArgumentException($"Unsupported texture format: {format}")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ResourceFlags ConvertTextureUsage(TextureUsage usage)
|
||||||
|
{
|
||||||
|
var flags = ResourceFlags.None;
|
||||||
|
|
||||||
|
if (usage.HasFlag(TextureUsage.RenderTarget))
|
||||||
|
flags |= ResourceFlags.AllowRenderTarget;
|
||||||
|
|
||||||
|
if (usage.HasFlag(TextureUsage.DepthStencil))
|
||||||
|
flags |= ResourceFlags.AllowDepthStencil;
|
||||||
|
|
||||||
|
if (usage.HasFlag(TextureUsage.UnorderedAccess))
|
||||||
|
flags |= ResourceFlags.AllowUnorderedAccess;
|
||||||
|
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ResourceFlags ConvertBufferUsage(BufferUsage usage)
|
||||||
|
{
|
||||||
|
var flags = ResourceFlags.None;
|
||||||
|
|
||||||
|
if (usage.HasFlag(BufferUsage.Raw))
|
||||||
|
flags |= ResourceFlags.AllowUnorderedAccess;
|
||||||
|
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HeapType ConvertMemoryType(MemoryType memoryType)
|
||||||
|
{
|
||||||
|
return memoryType switch
|
||||||
|
{
|
||||||
|
MemoryType.Default => HeapType.Default,
|
||||||
|
MemoryType.Upload => HeapType.Upload,
|
||||||
|
MemoryType.Readback => HeapType.Readback,
|
||||||
|
_ => throw new ArgumentException($"Unsupported memory type: {memoryType}")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ResourceStates DetermineInitialTextureState(TextureUsage usage)
|
||||||
|
{
|
||||||
|
if (usage.HasFlag(TextureUsage.RenderTarget))
|
||||||
|
return ResourceStates.RenderTarget;
|
||||||
|
|
||||||
|
if (usage.HasFlag(TextureUsage.DepthStencil))
|
||||||
|
return ResourceStates.DepthWrite;
|
||||||
|
|
||||||
|
if (usage.HasFlag(TextureUsage.UnorderedAccess))
|
||||||
|
return ResourceStates.UnorderedAccess;
|
||||||
|
|
||||||
|
return ResourceStates.Common;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ResourceStates DetermineInitialBufferState(BufferUsage usage, MemoryType memoryType)
|
||||||
|
{
|
||||||
|
if (memoryType == MemoryType.Upload)
|
||||||
|
return ResourceStates.GenericRead;
|
||||||
|
|
||||||
|
if (memoryType == MemoryType.Readback)
|
||||||
|
return ResourceStates.CopyDest;
|
||||||
|
|
||||||
|
if (usage.HasFlag(BufferUsage.Vertex))
|
||||||
|
return ResourceStates.VertexAndConstantBuffer;
|
||||||
|
|
||||||
|
if (usage.HasFlag(BufferUsage.Index))
|
||||||
|
return ResourceStates.IndexBuffer;
|
||||||
|
|
||||||
|
if (usage.HasFlag(BufferUsage.Constant))
|
||||||
|
return ResourceStates.VertexAndConstantBuffer;
|
||||||
|
|
||||||
|
return ResourceStates.Common;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public void ReleaseTempResource()
|
||||||
|
{
|
||||||
|
while (_temResources.Count > 0)
|
||||||
|
{
|
||||||
|
ref var handle = ref _temResources.Peek();
|
||||||
|
ref var info = ref _allocations[handle.id];
|
||||||
|
|
||||||
|
if (info.Allocated && info.cpuFenceValue > _renderSystem.CPUFenceValue)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReleaseAllocation(in handle);
|
||||||
|
_temResources.Dequeue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Allocation GetAllocation(in ResourceHandle handle)
|
||||||
|
{
|
||||||
|
if (!handle.IsValid)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Invalid resource handle.");
|
||||||
|
}
|
||||||
|
|
||||||
|
ref var allocationInfo = ref _allocations[handle.id];
|
||||||
|
if (!allocationInfo.Allocated || allocationInfo.generation != handle.generation)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Resource with ID {handle.id} and generation {handle.generation} is not allocated or has been released.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return allocationInfo.allocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReleaseAllocation(in ResourceHandle handle)
|
||||||
|
{
|
||||||
|
if (!handle.IsValid)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref var allocationInfo = ref _allocations[handle.id];
|
||||||
|
|
||||||
|
if (!allocationInfo.Allocated || allocationInfo.generation != handle.generation)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
allocationInfo.Dispose();
|
||||||
|
_freeSlots.Enqueue(handle.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
if (_allocations.Count > 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"ResourceAllocator is being disposed with {_allocations.Count} allocations still registered. Ensure all resources are released before disposing.");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
for (var i = 0; i < _allocations.Count; i++)
|
||||||
|
{
|
||||||
|
_allocations[i].Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_allocations.Dispose();
|
||||||
|
_temResources.Dispose();
|
||||||
|
_allocator.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
53
Ghost.Graphics/D3D12/D3D12ResourceFactory.cs
Normal file
53
Ghost.Graphics/D3D12/D3D12ResourceFactory.cs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
using Ghost.Graphics.Data;
|
||||||
|
using Ghost.Graphics.RHI;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Win32.Graphics.Direct3D12;
|
||||||
|
using Win32.Graphics.Dxgi;
|
||||||
|
|
||||||
|
namespace Ghost.Graphics.D3D12;
|
||||||
|
internal class D3D12ResourceFactory : IResourceFactory
|
||||||
|
{
|
||||||
|
private readonly D3D12ResourceAllocator _allocator;
|
||||||
|
|
||||||
|
public unsafe D3D12ResourceFactory(D3D12RenderDevice device, RenderSystem renderSystem)
|
||||||
|
{
|
||||||
|
_allocator = new D3D12ResourceAllocator((IDXGIAdapter*)device.Adapter, (ID3D12Device*)device.NativeDevice, renderSystem);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public IRenderTarget CreateRenderTarget(ref readonly RenderTargetDesc desc)
|
||||||
|
{
|
||||||
|
var usage = desc.Type == RenderTargetType.Color ? TextureUsage.RenderTarget : TextureUsage.DepthStencil;
|
||||||
|
if (desc.CreationFlags.HasFlag(RenderTargetCreationFlags.AllowUAV))
|
||||||
|
{
|
||||||
|
usage |= TextureUsage.UnorderedAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
var textureDesc = new TextureDesc(desc.Width, desc.Height, desc.Format, desc.Dimension, 1, usage);
|
||||||
|
return new D3D12RenderTarget(CreateTextureHandle(in textureDesc), in desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public TextureHandle CreateTextureHandle(ref readonly TextureDesc desc)
|
||||||
|
{
|
||||||
|
return _allocator.CreateTexture2D(in desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public ITexture CreateTexture(ref readonly TextureDesc desc)
|
||||||
|
{
|
||||||
|
return new D3D12Texture(CreateTextureHandle(in desc), in desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public BufferHandle CreateBufferHandle(ref readonly BufferDesc desc)
|
||||||
|
{
|
||||||
|
return _allocator.CreateBuffer(in desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public IBuffer CreateBuffer(ref readonly BufferDesc desc)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
using Ghost.Graphics.Contracts;
|
using Ghost.Graphics.Contracts;
|
||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
using Ghost.Graphics.D3D12.Utilities;
|
|
||||||
using Win32;
|
using Win32;
|
||||||
using Win32.Graphics.Direct3D12;
|
using Win32.Graphics.Direct3D12;
|
||||||
using Win32.Graphics.Dxgi;
|
using Win32.Graphics.Dxgi;
|
||||||
@@ -15,16 +14,27 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
|||||||
{
|
{
|
||||||
private ComPtr<IDXGISwapChain4> _swapChain;
|
private ComPtr<IDXGISwapChain4> _swapChain;
|
||||||
private readonly D3D12Texture[] _backBuffers;
|
private readonly D3D12Texture[] _backBuffers;
|
||||||
|
private readonly D3D12RenderTarget[] _backBufferRenderTargets;
|
||||||
private uint _currentBackBufferIndex;
|
private uint _currentBackBufferIndex;
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
public uint Width { get; private set; }
|
public uint Width
|
||||||
public uint Height { get; private set; }
|
{
|
||||||
public uint BufferCount { get; }
|
get; private set;
|
||||||
|
}
|
||||||
|
public uint Height
|
||||||
|
{
|
||||||
|
get; private set;
|
||||||
|
}
|
||||||
|
public uint BufferCount
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
public D3D12SwapChain(ComPtr<IDXGIFactory7> factory, ID3D12CommandQueue* commandQueue, SwapChainDesc desc)
|
public D3D12SwapChain(ComPtr<IDXGIFactory7> factory, ID3D12CommandQueue* commandQueue, SwapChainDesc desc)
|
||||||
{
|
{
|
||||||
_backBuffers = new D3D12Texture[desc.BufferCount];
|
_backBuffers = new D3D12Texture[desc.BufferCount];
|
||||||
|
_backBufferRenderTargets = new D3D12RenderTarget[desc.BufferCount];
|
||||||
|
|
||||||
Width = desc.Width;
|
Width = desc.Width;
|
||||||
Height = desc.Height;
|
Height = desc.Height;
|
||||||
@@ -102,6 +112,9 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
|||||||
backBuffer.Get()->SetName($"SwapChain_BackBuffer_{i}");
|
backBuffer.Get()->SetName($"SwapChain_BackBuffer_{i}");
|
||||||
|
|
||||||
_backBuffers[i] = new D3D12Texture(backBuffer.Move(), Width, Height, TextureFormat.B8G8R8A8_UNorm);
|
_backBuffers[i] = new D3D12Texture(backBuffer.Move(), Width, Height, TextureFormat.B8G8R8A8_UNorm);
|
||||||
|
|
||||||
|
// Create render target wrapper for the back buffer
|
||||||
|
_backBufferRenderTargets[i] = new D3D12RenderTarget(_backBuffers[i], RenderTargetType.Color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,6 +124,12 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
|||||||
return _backBuffers[_currentBackBufferIndex];
|
return _backBuffers[_currentBackBufferIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IRenderTarget GetCurrentBackBufferRenderTarget()
|
||||||
|
{
|
||||||
|
_currentBackBufferIndex = _swapChain.Get()->GetCurrentBackBufferIndex();
|
||||||
|
return _backBufferRenderTargets[_currentBackBufferIndex];
|
||||||
|
}
|
||||||
|
|
||||||
public void Present(bool vsync = true)
|
public void Present(bool vsync = true)
|
||||||
{
|
{
|
||||||
var presentFlags = PresentFlags.None;
|
var presentFlags = PresentFlags.None;
|
||||||
@@ -127,9 +146,10 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
|||||||
if (Width == width && Height == height)
|
if (Width == width && Height == height)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Release old back buffers
|
// Release old back buffers and render targets
|
||||||
for (int i = 0; i < _backBuffers.Length; i++)
|
for (var i = 0; i < _backBuffers.Length; i++)
|
||||||
{
|
{
|
||||||
|
_backBufferRenderTargets[i]?.Dispose();
|
||||||
_backBuffers[i]?.Dispose();
|
_backBuffers[i]?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,10 +181,14 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
|||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (_disposed) return;
|
if (_disposed)
|
||||||
|
|
||||||
for (int i = 0; i < _backBuffers.Length; i++)
|
|
||||||
{
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < _backBuffers.Length; i++)
|
||||||
|
{
|
||||||
|
_backBufferRenderTargets[i]?.Dispose();
|
||||||
_backBuffers[i]?.Dispose();
|
_backBuffers[i]?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Ghost.Graphics.Data;
|
||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
using Win32;
|
using Win32;
|
||||||
using Win32.Graphics.Direct3D12;
|
using Win32.Graphics.Direct3D12;
|
||||||
@@ -5,110 +6,85 @@ using Win32.Graphics.Direct3D12;
|
|||||||
namespace Ghost.Graphics.D3D12;
|
namespace Ghost.Graphics.D3D12;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// D3D12 implementation of texture interface
|
/// D3D12 implementation of texture interface using resource handles
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal unsafe class D3D12Texture : ITexture
|
internal unsafe class D3D12Texture : ITexture
|
||||||
{
|
{
|
||||||
private ComPtr<ID3D12Resource> _resource;
|
private readonly TextureHandle _handle;
|
||||||
|
private readonly ComPtr<ID3D12Resource> _externalResource;
|
||||||
private ResourceState _currentState;
|
private ResourceState _currentState;
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
public uint Width { get; }
|
public uint Width
|
||||||
public uint Height { get; }
|
{
|
||||||
public TextureFormat Format { get; }
|
get;
|
||||||
public uint MipLevels { get; }
|
}
|
||||||
public string Name { get; set; } = string.Empty;
|
|
||||||
public ulong Size { get; }
|
public uint Height
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextureFormat Format
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint MipLevels
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
} = string.Empty;
|
||||||
|
|
||||||
|
public ulong Size
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
public ResourceState CurrentState => _currentState;
|
public ResourceState CurrentState => _currentState;
|
||||||
|
|
||||||
public ID3D12Resource* NativeResource => _resource.Get();
|
public ID3D12Resource* NativeResource => _handle.IsValid ? _handle.ResourceHandle.GetAllocation().Resource : _externalResource.Get();
|
||||||
|
|
||||||
public D3D12Texture(ComPtr<ID3D12Resource> resource, uint width, uint height, TextureFormat format, uint mipLevels = 1)
|
public D3D12Texture(ComPtr<ID3D12Resource> resource, uint width, uint height, TextureFormat format, uint mipLevels = 1)
|
||||||
{
|
{
|
||||||
_resource = resource.Move();
|
_handle = TextureHandle.Invalid;
|
||||||
|
_externalResource = resource.Move();
|
||||||
|
|
||||||
Width = width;
|
Width = width;
|
||||||
Height = height;
|
Height = height;
|
||||||
Format = format;
|
Format = format;
|
||||||
MipLevels = mipLevels;
|
MipLevels = mipLevels;
|
||||||
_currentState = ResourceState.Common;
|
_currentState = ResourceState.Common;
|
||||||
|
Size = Width * Height * GetBytesPerPixel(Format);
|
||||||
var desc = _resource.Get()->GetDesc();
|
|
||||||
Size = (ulong)(desc.Width * desc.Height * GetBytesPerPixel(format));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public D3D12Texture(ComPtr<ID3D12Device14> device, TextureDesc desc)
|
public D3D12Texture(TextureHandle handle, ref readonly TextureDesc desc)
|
||||||
{
|
{
|
||||||
|
_handle = handle;
|
||||||
|
_externalResource = default;
|
||||||
|
|
||||||
Width = desc.Width;
|
Width = desc.Width;
|
||||||
Height = desc.Height;
|
Height = desc.Height;
|
||||||
Format = desc.Format;
|
Format = desc.Format;
|
||||||
MipLevels = desc.MipLevels;
|
|
||||||
|
var mipLevels = desc.MipLevels;
|
||||||
|
if (mipLevels <= 0)
|
||||||
|
{
|
||||||
|
mipLevels = (uint)(Math.Floor(Math.Log2(Math.Max(Width, Height))) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
MipLevels = mipLevels;
|
||||||
_currentState = ResourceState.Common;
|
_currentState = ResourceState.Common;
|
||||||
|
Size = Width * Height * GetBytesPerPixel(Format);
|
||||||
CreateTexture(device, desc);
|
|
||||||
Size = (ulong)(Width * Height * GetBytesPerPixel(Format));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateTexture(ComPtr<ID3D12Device14> device, TextureDesc desc)
|
~D3D12Texture()
|
||||||
{
|
{
|
||||||
var resourceDesc = new ResourceDescription
|
Dispose();
|
||||||
{
|
|
||||||
Dimension = ResourceDimension.Texture2D,
|
|
||||||
Alignment = 0,
|
|
||||||
Width = desc.Width,
|
|
||||||
Height = desc.Height,
|
|
||||||
DepthOrArraySize = 1,
|
|
||||||
MipLevels = (ushort)desc.MipLevels,
|
|
||||||
Format = ConvertTextureFormat(desc.Format),
|
|
||||||
SampleDesc = new Win32.Graphics.Dxgi.Common.SampleDescription(1, 0),
|
|
||||||
Layout = TextureLayout.Unknown,
|
|
||||||
Flags = ConvertTextureUsage(desc.Usage)
|
|
||||||
};
|
|
||||||
|
|
||||||
var heapProps = new HeapProperties
|
|
||||||
{
|
|
||||||
Type = HeapType.Default,
|
|
||||||
CPUPageProperty = CpuPageProperty.Unknown,
|
|
||||||
MemoryPoolPreference = MemoryPool.Unknown,
|
|
||||||
CreationNodeMask = 1,
|
|
||||||
VisibleNodeMask = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
device.Get()->CreateCommittedResource(
|
|
||||||
&heapProps,
|
|
||||||
HeapFlags.None,
|
|
||||||
&resourceDesc,
|
|
||||||
Win32.Graphics.Direct3D12.ResourceStates.Common,
|
|
||||||
null,
|
|
||||||
__uuidof<ID3D12Resource>(),
|
|
||||||
_resource.GetVoidAddressOf());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Win32.Graphics.Dxgi.Common.Format ConvertTextureFormat(TextureFormat format)
|
|
||||||
{
|
|
||||||
return format switch
|
|
||||||
{
|
|
||||||
TextureFormat.R8G8B8A8_UNorm => Win32.Graphics.Dxgi.Common.Format.R8G8B8A8Unorm,
|
|
||||||
TextureFormat.B8G8R8A8_UNorm => Win32.Graphics.Dxgi.Common.Format.B8G8R8A8Unorm,
|
|
||||||
TextureFormat.R16G16B16A16_Float => Win32.Graphics.Dxgi.Common.Format.R16G16B16A16Float,
|
|
||||||
TextureFormat.R32G32B32A32_Float => Win32.Graphics.Dxgi.Common.Format.R32G32B32A32Float,
|
|
||||||
TextureFormat.D24_UNorm_S8_UInt => Win32.Graphics.Dxgi.Common.Format.D24UnormS8Uint,
|
|
||||||
TextureFormat.D32_Float => Win32.Graphics.Dxgi.Common.Format.D32Float,
|
|
||||||
_ => throw new ArgumentException($"Unsupported texture format: {format}")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ResourceFlags ConvertTextureUsage(TextureUsage usage)
|
|
||||||
{
|
|
||||||
var flags = ResourceFlags.None;
|
|
||||||
|
|
||||||
if ((usage & TextureUsage.RenderTarget) != 0)
|
|
||||||
flags |= ResourceFlags.AllowRenderTarget;
|
|
||||||
if ((usage & TextureUsage.DepthStencil) != 0)
|
|
||||||
flags |= ResourceFlags.AllowDepthStencil;
|
|
||||||
if ((usage & TextureUsage.UnorderedAccess) != 0)
|
|
||||||
flags |= ResourceFlags.AllowUnorderedAccess;
|
|
||||||
|
|
||||||
return flags;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static uint GetBytesPerPixel(TextureFormat format)
|
private static uint GetBytesPerPixel(TextureFormat format)
|
||||||
@@ -125,11 +101,29 @@ internal unsafe class D3D12Texture : ITexture
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetCurrentState(ResourceState state)
|
||||||
|
{
|
||||||
|
_currentState = state;
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (_disposed) return;
|
if (_disposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_handle.IsValid)
|
||||||
|
{
|
||||||
|
_handle.Dispose();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_externalResource.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
_resource.Dispose();
|
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
|
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,249 +0,0 @@
|
|||||||
using Ghost.Graphics.Data;
|
|
||||||
using Misaki.HighPerformance.LowLevel.Collections;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using Win32.Graphics.D3D12MemoryAllocator;
|
|
||||||
using Win32.Graphics.Direct3D12;
|
|
||||||
using Win32.Graphics.Dxgi;
|
|
||||||
using Win32.Graphics.Dxgi.Common;
|
|
||||||
using static Win32.Graphics.D3D12MemoryAllocator.Apis;
|
|
||||||
using ResourceHandle = Ghost.Graphics.Data.ResourceHandle;
|
|
||||||
|
|
||||||
namespace Ghost.Graphics.D3D12;
|
|
||||||
|
|
||||||
internal unsafe class ResourceAllocator
|
|
||||||
{
|
|
||||||
private readonly struct AllocationInfo : IDisposable
|
|
||||||
{
|
|
||||||
public readonly Allocation allocation;
|
|
||||||
public readonly uint cpuFenceValue;
|
|
||||||
public readonly uint generation;
|
|
||||||
|
|
||||||
public bool Allocated => allocation.IsNotNull;
|
|
||||||
|
|
||||||
public AllocationInfo(in Allocation allocation, uint cpuFenceValue, uint generation)
|
|
||||||
{
|
|
||||||
this.allocation = allocation;
|
|
||||||
this.cpuFenceValue = cpuFenceValue;
|
|
||||||
this.generation = generation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AllocationInfo(in Allocation allocation, uint generation)
|
|
||||||
: this(allocation, 0 /* TODO: Use proper fence value from render system */, generation)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (allocation.IsNull)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
allocation.Release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private const uint _MAX_BYTES = D3D12_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM * 1024u * 1024u;
|
|
||||||
private const uint _MAX_TEXTURE2D_DIMENSION = 16384u;
|
|
||||||
private const uint _MAX_TEXTURE3D_DIMENSION = 2048u;
|
|
||||||
|
|
||||||
private readonly Allocator _allocator;
|
|
||||||
private UnsafeList<AllocationInfo> _allocations = new(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
|
|
||||||
private UnsafeQueue<int> _freeSlots = new(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
|
|
||||||
private UnsafeQueue<ResourceHandle> _temResources = new(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
|
|
||||||
|
|
||||||
private readonly Lock _lock = new();
|
|
||||||
|
|
||||||
private Guid* IID_NULL
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
fixed (Guid* pGuid = &Guid.Empty)
|
|
||||||
{
|
|
||||||
return pGuid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ResourceAllocator(IDXGIAdapter* pAdapter, ID3D12Device* pDevice)
|
|
||||||
{
|
|
||||||
var desc = new AllocatorDesc
|
|
||||||
{
|
|
||||||
pAdapter = pAdapter,
|
|
||||||
pDevice = pDevice,
|
|
||||||
Flags = AllocatorFlags.DefaultPoolsNotZeroed | AllocatorFlags.MSAATexturesAlwaysCommitted
|
|
||||||
};
|
|
||||||
|
|
||||||
CreateAllocator(in desc, out _allocator);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static void CheckBufferSize(uint sizeInBytes)
|
|
||||||
{
|
|
||||||
if (sizeInBytes > _MAX_BYTES)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {sizeInBytes})");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static void CheckTexture2DSize(uint width, uint height)
|
|
||||||
{
|
|
||||||
if (width > _MAX_TEXTURE2D_DIMENSION || height > _MAX_TEXTURE2D_DIMENSION)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"ERROR: Texture size too large for DirectX 12 (width {width}, height {height})");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ResourceHandle TrackResource(in Allocation allocation, bool isTemp)
|
|
||||||
{
|
|
||||||
int id;
|
|
||||||
uint generation;
|
|
||||||
AllocationInfo allocInfo;
|
|
||||||
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
if (_freeSlots.Count > 0)
|
|
||||||
{
|
|
||||||
id = _freeSlots.Dequeue();
|
|
||||||
var info = _allocations[id];
|
|
||||||
if (info.Allocated)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"ERROR: Resource ID {id} registered as free but still allocated.");
|
|
||||||
}
|
|
||||||
|
|
||||||
generation = info.generation + 1;
|
|
||||||
allocInfo = new AllocationInfo(in allocation, generation);
|
|
||||||
|
|
||||||
_allocations[id] = allocInfo;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
id = _allocations.Count;
|
|
||||||
generation = 0u;
|
|
||||||
allocInfo = new AllocationInfo(in allocation, generation);
|
|
||||||
|
|
||||||
_allocations.Add(allocInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
var handle = new ResourceHandle(id, generation);
|
|
||||||
if (isTemp)
|
|
||||||
{
|
|
||||||
_temResources.Enqueue(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public TextureHandle CreateTexture2D(uint width, uint height, ushort mipLevels, Format format = Format.R8G8B8A8Unorm, ResourceFlags resFlags = ResourceFlags.None, AllocationFlags allocFlags = AllocationFlags.None, ResourceStates state = ResourceStates.Common, bool tempResource = false)
|
|
||||||
{
|
|
||||||
CheckTexture2DSize(width, height);
|
|
||||||
|
|
||||||
var resourceDesc = ResourceDescription.Tex2D(format, width, height, mipLevels: mipLevels, arraySize: 1, flags: resFlags);
|
|
||||||
var allocationDesc = new AllocationDesc
|
|
||||||
{
|
|
||||||
HeapType = HeapType.Default,
|
|
||||||
Flags = allocFlags
|
|
||||||
};
|
|
||||||
|
|
||||||
Allocation allocation = default;
|
|
||||||
ThrowIfFailed(_allocator.CreateResource(&allocationDesc, in resourceDesc, state, null, &allocation, IID_NULL, null));
|
|
||||||
|
|
||||||
return new(TrackResource(in allocation, tempResource));
|
|
||||||
}
|
|
||||||
|
|
||||||
public BufferHandle CreateBuffer(uint sizeInBytes, HeapType heapType = HeapType.Default, ResourceFlags resFlags = ResourceFlags.None, AllocationFlags allocFlags = AllocationFlags.None, ResourceStates initialState = ResourceStates.Common, bool tempResource = false)
|
|
||||||
{
|
|
||||||
CheckBufferSize(sizeInBytes);
|
|
||||||
|
|
||||||
var resourceDescription = ResourceDescription.Buffer(sizeInBytes, resFlags);
|
|
||||||
var allocationDesc = new AllocationDesc
|
|
||||||
{
|
|
||||||
HeapType = heapType,
|
|
||||||
Flags = allocFlags
|
|
||||||
};
|
|
||||||
|
|
||||||
Allocation allocation = default;
|
|
||||||
ThrowIfFailed(_allocator.CreateResource(&allocationDesc, in resourceDescription, initialState, null, &allocation, IID_NULL, null));
|
|
||||||
|
|
||||||
return new(TrackResource(in allocation, tempResource));
|
|
||||||
}
|
|
||||||
|
|
||||||
public BufferHandle CreateUploadBuffer(uint sizeInBytes, bool tempResource = false)
|
|
||||||
{
|
|
||||||
return CreateBuffer(sizeInBytes, HeapType.Upload, ResourceFlags.None, AllocationFlags.None, ResourceStates.GenericRead, tempResource);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ReleaseTempResource()
|
|
||||||
{
|
|
||||||
while (_temResources.Count > 0)
|
|
||||||
{
|
|
||||||
ref var handle = ref _temResources.Peek();
|
|
||||||
ref var info = ref _allocations[handle.id];
|
|
||||||
// TODO: Implement proper fence-based cleanup with RenderSystem
|
|
||||||
// For now, just release all temp resources
|
|
||||||
|
|
||||||
ReleaseAllocation(in handle);
|
|
||||||
_temResources.Dequeue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Allocation GetAllocation(in ResourceHandle handle)
|
|
||||||
{
|
|
||||||
if (!handle.IsValid)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Invalid resource handle.");
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
ref var allocationInfo = ref _allocations[handle.id];
|
|
||||||
if (!allocationInfo.Allocated || allocationInfo.generation != handle.generation)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"Resource with ID {handle.id} and generation {handle.generation} is not allocated or has been released.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return allocationInfo.allocation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ReleaseAllocation(in ResourceHandle handle)
|
|
||||||
{
|
|
||||||
if (!handle.IsValid)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
ref var allocationInfo = ref _allocations[handle.id];
|
|
||||||
|
|
||||||
if (!allocationInfo.Allocated || allocationInfo.generation != handle.generation)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
allocationInfo.Dispose();
|
|
||||||
_freeSlots.Enqueue(handle.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
#if DEBUG
|
|
||||||
if (_allocations.Count > 0)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"ResourceAllocator is being disposed with {_allocations.Count} allocations still registered. Ensure all resources are released before disposing.");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
for (var i = 0; i < _allocations.Count; i++)
|
|
||||||
{
|
|
||||||
_allocations[i].Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
_allocations.Dispose();
|
|
||||||
_temResources.Dispose();
|
|
||||||
_allocator.Release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0
Ghost.Graphics/D3D12_ALLOCATOR_IMPROVEMENTS.md
Normal file
0
Ghost.Graphics/D3D12_ALLOCATOR_IMPROVEMENTS.md
Normal file
@@ -81,6 +81,8 @@ public readonly struct TextureHandle : IEquatable<TextureHandle>, IDisposable
|
|||||||
|
|
||||||
internal ResourceHandle ResourceHandle => _resourceHandle;
|
internal ResourceHandle ResourceHandle => _resourceHandle;
|
||||||
|
|
||||||
|
public static TextureHandle Invalid => new(ResourceHandle.Invalid);
|
||||||
|
|
||||||
internal TextureHandle(ResourceHandle resourceHandle)
|
internal TextureHandle(ResourceHandle resourceHandle)
|
||||||
{
|
{
|
||||||
_resourceHandle = resourceHandle;
|
_resourceHandle = resourceHandle;
|
||||||
@@ -125,6 +127,8 @@ public readonly struct BufferHandle : IEquatable<BufferHandle>, IDisposable
|
|||||||
|
|
||||||
internal ResourceHandle ResourceHandle => _resourceHandle;
|
internal ResourceHandle ResourceHandle => _resourceHandle;
|
||||||
|
|
||||||
|
public static BufferHandle Invalid => new(ResourceHandle.Invalid);
|
||||||
|
|
||||||
internal BufferHandle(ResourceHandle resourceHandle)
|
internal BufferHandle(ResourceHandle resourceHandle)
|
||||||
{
|
{
|
||||||
_resourceHandle = resourceHandle;
|
_resourceHandle = resourceHandle;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public static class GraphicsPipeline
|
|||||||
|
|
||||||
private static GraphicsDevice? s_graphicsDevice;
|
private static GraphicsDevice? s_graphicsDevice;
|
||||||
private static DescriptorAllocator? s_descriptorAllocator;
|
private static DescriptorAllocator? s_descriptorAllocator;
|
||||||
private static ResourceAllocator? s_resourceAllocator;
|
private static D3D12ResourceAllocator? s_resourceAllocator;
|
||||||
private static ResourceUploadBatch? s_uploadBatch;
|
private static ResourceUploadBatch? s_uploadBatch;
|
||||||
|
|
||||||
// New RHI-based device for modern usage
|
// New RHI-based device for modern usage
|
||||||
@@ -31,7 +31,7 @@ public static class GraphicsPipeline
|
|||||||
private static bool s_initialized;
|
private static bool s_initialized;
|
||||||
|
|
||||||
internal static GraphicsDevice GraphicsDevice => s_graphicsDevice ?? throw new InvalidOperationException("Graphics device is not initialized.");
|
internal static GraphicsDevice GraphicsDevice => s_graphicsDevice ?? throw new InvalidOperationException("Graphics device is not initialized.");
|
||||||
internal static ResourceAllocator ResourceAllocator => s_resourceAllocator ?? throw new InvalidOperationException("Resource allocator is not initialized.");
|
internal static D3D12ResourceAllocator ResourceAllocator => s_resourceAllocator ?? throw new InvalidOperationException("Resource allocator is not initialized.");
|
||||||
internal static DescriptorAllocator DescriptorAllocator => s_descriptorAllocator ?? throw new InvalidOperationException("Descriptor allocator is not initialized.");
|
internal static DescriptorAllocator DescriptorAllocator => s_descriptorAllocator ?? throw new InvalidOperationException("Descriptor allocator is not initialized.");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -66,7 +66,7 @@ public static class GraphicsPipeline
|
|||||||
// Initialize legacy components for compatibility
|
// Initialize legacy components for compatibility
|
||||||
s_graphicsDevice = new GraphicsDevice();
|
s_graphicsDevice = new GraphicsDevice();
|
||||||
s_descriptorAllocator = new DescriptorAllocator();
|
s_descriptorAllocator = new DescriptorAllocator();
|
||||||
s_resourceAllocator = new ResourceAllocator((IDXGIAdapter*)s_graphicsDevice.Adapter.Ptr, (ID3D12Device*)s_graphicsDevice.NativeDevice.Ptr);
|
s_resourceAllocator = new D3D12ResourceAllocator((IDXGIAdapter*)s_graphicsDevice.Adapter.Ptr, (ID3D12Device*)s_graphicsDevice.NativeDevice.Ptr);
|
||||||
|
|
||||||
// Initialize modern RHI components
|
// Initialize modern RHI components
|
||||||
s_renderDevice = new D3D12.D3D12RenderDevice();
|
s_renderDevice = new D3D12.D3D12RenderDevice();
|
||||||
|
|||||||
@@ -1,160 +0,0 @@
|
|||||||
# Ghost Engine Graphics Refactoring Summary
|
|
||||||
|
|
||||||
## 🎯 Refactoring Goals Completed
|
|
||||||
|
|
||||||
✅ **1. Create a new RHI folder with interfaces for all abstractions**
|
|
||||||
- Created clean D3D12-native RHI interfaces in `Ghost.Graphics\RHI\`
|
|
||||||
- Interfaces include: `IRenderDevice`, `ICommandBuffer`, `ICommandQueue`, `ISwapChain`, `IRenderTarget`, `IDescriptorAllocator`
|
|
||||||
|
|
||||||
✅ **2. Move all D3D12-specific code into D3D12 implementations**
|
|
||||||
- All D3D12 implementations moved to `Ghost.Graphics\D3D12\`
|
|
||||||
- Clean separation between interface and implementation
|
|
||||||
|
|
||||||
✅ **3. Delete the current Renderer class and rewrite it to use interfaces**
|
|
||||||
- ❌ Removed legacy `Renderer.cs` (was tightly coupled to D3D12)
|
|
||||||
- ✅ Created new `D3D12Renderer` that implements `IRenderer` interface
|
|
||||||
|
|
||||||
✅ **4. Extract SwapChain logic from Renderer into its own class**
|
|
||||||
- ✅ Created `D3D12SwapChain` implementing `ISwapChain`
|
|
||||||
- SwapChain now manages presentation independently of rendering
|
|
||||||
|
|
||||||
✅ **5. Remove renderer management from GraphicsDevice - make it a pure device factory**
|
|
||||||
- ❌ Marked legacy `GraphicsDevice` as obsolete
|
|
||||||
- ✅ Created new `D3D12RenderDevice` as clean factory for D3D12 resources
|
|
||||||
|
|
||||||
✅ **6. Create separate CommandQueue classes for Graphics/Compute/Copy queues**
|
|
||||||
- ✅ Created `D3D12CommandQueue` supporting all three queue types
|
|
||||||
- `D3D12RenderDevice` exposes separate queues via properties
|
|
||||||
|
|
||||||
✅ **7. Abstract descriptor allocation behind an interface**
|
|
||||||
- ✅ Created `IDescriptorAllocator` interface
|
|
||||||
- ✅ Implemented `D3D12DescriptorAllocator`
|
|
||||||
|
|
||||||
✅ **8. Move frame synchronization to application level, not RHI level**
|
|
||||||
- ✅ Created `RenderSystem` class for application-level frame management
|
|
||||||
- ❌ Marked legacy `GraphicsPipeline` as obsolete
|
|
||||||
- Frame synchronization now handled by `RenderSystem`, not buried in graphics device
|
|
||||||
|
|
||||||
## 🏗️ New Architecture
|
|
||||||
|
|
||||||
### Core RHI Interfaces
|
|
||||||
```
|
|
||||||
Ghost.Graphics\RHI\
|
|
||||||
├── IRenderDevice.cs # Device factory for creating resources
|
|
||||||
├── ICommandBuffer.cs # Command recording interface
|
|
||||||
├── ICommandQueue.cs # Command submission interface
|
|
||||||
├── ISwapChain.cs # Presentation interface
|
|
||||||
├── IRenderTarget.cs # Render target interface (color OR depth only)
|
|
||||||
├── IRenderer.cs # High-level renderer interface
|
|
||||||
├── IDescriptorAllocator.cs # Descriptor management
|
|
||||||
└── IResource.cs # Base resource interfaces
|
|
||||||
```
|
|
||||||
|
|
||||||
### D3D12 Implementations
|
|
||||||
```
|
|
||||||
Ghost.Graphics\D3D12\
|
|
||||||
├── D3D12RenderDevice.cs # Main device factory
|
|
||||||
├── D3D12CommandBuffer.cs # Command list wrapper
|
|
||||||
├── D3D12CommandQueue.cs # Command queue wrapper
|
|
||||||
├── D3D12SwapChain.cs # Swap chain management
|
|
||||||
├── D3D12RenderTarget.cs # Render target (single type: color OR depth)
|
|
||||||
├── D3D12Renderer.cs # High-level renderer implementation
|
|
||||||
├── D3D12DescriptorAllocator.cs # Descriptor heap management
|
|
||||||
├── D3D12Texture.cs # Texture implementation
|
|
||||||
└── D3D12Buffer.cs # Buffer implementation
|
|
||||||
```
|
|
||||||
|
|
||||||
### Application Level
|
|
||||||
```
|
|
||||||
Ghost.Graphics\
|
|
||||||
├── RenderSystem.cs # Frame synchronization & renderer management
|
|
||||||
└── Examples\
|
|
||||||
└── ModernRenderingExample.cs # Usage examples
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🎯 Key Improvements
|
|
||||||
|
|
||||||
### **1. Clean Separation of Concerns**
|
|
||||||
- **SwapChain**: Only handles presentation
|
|
||||||
- **Renderer**: Only handles rendering logic
|
|
||||||
- **RenderDevice**: Only creates resources
|
|
||||||
- **RenderSystem**: Only manages frame synchronization
|
|
||||||
|
|
||||||
### **2. Simplified Render Target Design**
|
|
||||||
- Render targets now support **either color OR depth**, not both
|
|
||||||
- Use `RenderTargetDesc.Color()` or `RenderTargetDesc.Depth()` factory methods
|
|
||||||
- Cleaner API, less complex state management
|
|
||||||
|
|
||||||
### **3. D3D12-Native RHI Design**
|
|
||||||
- No abstraction overhead - direct mapping to D3D12 concepts
|
|
||||||
- Command buffers, resource states, pipeline state objects
|
|
||||||
- Descriptor heaps and bindless rendering support
|
|
||||||
- Access to all D3D12 features without compromise
|
|
||||||
|
|
||||||
### **4. Application-Level Frame Management**
|
|
||||||
```csharp
|
|
||||||
// OLD: Frame sync buried in GraphicsPipeline
|
|
||||||
GraphicsPipeline.SignalCPUReady();
|
|
||||||
GraphicsPipeline.WaitForGPUReady();
|
|
||||||
|
|
||||||
// NEW: Clear application-level control
|
|
||||||
renderSystem.SignalCPUReady();
|
|
||||||
renderSystem.WaitForGPUReady();
|
|
||||||
```
|
|
||||||
|
|
||||||
### **5. Multiple Renderer Support**
|
|
||||||
```csharp
|
|
||||||
var renderSystem = new RenderSystem(device);
|
|
||||||
|
|
||||||
// Add multiple renderers
|
|
||||||
renderSystem.AddRenderer(forwardRenderer); // Main scene
|
|
||||||
renderSystem.AddRenderer(shadowRenderer); // Shadow maps
|
|
||||||
renderSystem.AddRenderer(postProcessRenderer); // Post effects
|
|
||||||
|
|
||||||
renderSystem.Start(); // Manages all renderers
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📖 Usage Examples
|
|
||||||
|
|
||||||
### **Modern Approach**
|
|
||||||
```csharp
|
|
||||||
// Create device and render system
|
|
||||||
var device = new D3D12RenderDevice();
|
|
||||||
var renderSystem = new RenderSystem(device);
|
|
||||||
|
|
||||||
// Create renderer with swap chain
|
|
||||||
var renderer = new D3D12Renderer(device);
|
|
||||||
var swapChain = device.CreateSwapChain(swapChainDesc);
|
|
||||||
renderer.SetSwapChain(swapChain);
|
|
||||||
|
|
||||||
// Or render to off-screen target
|
|
||||||
var colorTarget = device.CreateRenderTarget(
|
|
||||||
RenderTargetDesc.Color(1024, 1024, TextureFormat.R8G8B8A8_UNorm));
|
|
||||||
renderer.SetRenderTarget(colorTarget);
|
|
||||||
|
|
||||||
// Start rendering
|
|
||||||
renderSystem.AddRenderer(renderer);
|
|
||||||
renderSystem.Start();
|
|
||||||
```
|
|
||||||
|
|
||||||
### **Legacy Compatibility**
|
|
||||||
```csharp
|
|
||||||
// Legacy code still works (with obsolete warnings)
|
|
||||||
GraphicsPipeline.Initialize(); // ⚠️ Obsolete
|
|
||||||
GraphicsPipeline.Start(); // ⚠️ Obsolete
|
|
||||||
|
|
||||||
// But modern API is preferred
|
|
||||||
var device = GraphicsPipeline.RenderDevice; // ✅ New
|
|
||||||
var renderSystem = GraphicsPipeline.RenderSystem; // ✅ New
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🎯 Benefits Achieved
|
|
||||||
|
|
||||||
1. **Cleaner Code**: High-level rendering code no longer knows about D3D12 specifics
|
|
||||||
2. **Better Testing**: All components can be mocked via interfaces
|
|
||||||
3. **Flexible Rendering**: Easy to support multiple renderers and render targets
|
|
||||||
4. **Future-Proof**: Clean abstraction allows adding Vulkan/Metal later
|
|
||||||
5. **Performance**: Zero abstraction overhead with D3D12-native design
|
|
||||||
6. **Maintainability**: Clear separation of concerns and responsibilities
|
|
||||||
|
|
||||||
The refactoring successfully transforms a monolithic, tightly-coupled graphics system into a clean, modular, and flexible RHI architecture while maintaining backward compatibility and achieving all the stated goals.
|
|
||||||
|
|||||||
@@ -8,17 +8,34 @@ public interface IRenderDevice : IDisposable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Graphics command queue for rendering operations
|
/// Graphics command queue for rendering operations
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ICommandQueue GraphicsQueue { get; }
|
ICommandQueue GraphicsQueue
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Compute command queue for compute shader operations
|
/// Compute command queue for compute shader operations
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ICommandQueue ComputeQueue { get; }
|
ICommandQueue ComputeQueue
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Copy command queue for data transfer operations
|
/// Copy command queue for data transfer operations
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ICommandQueue CopyQueue { get; }
|
ICommandQueue CopyQueue
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the descriptor allocator for managing descriptors
|
||||||
|
/// </summary>
|
||||||
|
IDescriptorAllocator DescriptorAllocator
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a command buffer for recording rendering commands
|
/// Creates a command buffer for recording rendering commands
|
||||||
@@ -33,32 +50,6 @@ public interface IRenderDevice : IDisposable
|
|||||||
/// <param name="desc">Swap chain description</param>
|
/// <param name="desc">Swap chain description</param>
|
||||||
/// <returns>A new swap chain instance</returns>
|
/// <returns>A new swap chain instance</returns>
|
||||||
ISwapChain CreateSwapChain(SwapChainDesc desc);
|
ISwapChain CreateSwapChain(SwapChainDesc desc);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a render target for off-screen rendering
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="desc">Render target description</param>
|
|
||||||
/// <returns>A new render target instance</returns>
|
|
||||||
IRenderTarget CreateRenderTarget(RenderTargetDesc desc);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a texture resource
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="desc">Texture description</param>
|
|
||||||
/// <returns>A new texture instance</returns>
|
|
||||||
ITexture CreateTexture(TextureDesc desc);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a buffer resource
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="desc">Buffer description</param>
|
|
||||||
/// <returns>A new buffer instance</returns>
|
|
||||||
IBuffer CreateBuffer(BufferDesc desc);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the descriptor allocator for managing descriptors
|
|
||||||
/// </summary>
|
|
||||||
IDescriptorAllocator DescriptorAllocator { get; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -71,6 +71,21 @@ public struct RenderTargetDesc
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public TextureFormat Format;
|
public TextureFormat Format;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Texture dimension
|
||||||
|
/// </summary>
|
||||||
|
public TextureDimension Dimension;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creation flags for the render target
|
||||||
|
/// </summary>
|
||||||
|
public RenderTargetCreationFlags CreationFlags;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Number of mip levels. 0 to generate full mip chain
|
||||||
|
/// </summary>
|
||||||
|
public uint MipLevels;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Number of samples for MSAA
|
/// Number of samples for MSAA
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -79,7 +94,10 @@ public struct RenderTargetDesc
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a color render target
|
/// Creates a color render target
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static RenderTargetDesc Color(uint width, uint height, TextureFormat format, uint sampleCount = 1)
|
public static RenderTargetDesc Color(uint width, uint height,
|
||||||
|
TextureFormat format, TextureDimension dimension = TextureDimension.Texture2D,
|
||||||
|
RenderTargetCreationFlags creationFlags = RenderTargetCreationFlags.AllowUAV | RenderTargetCreationFlags.DynamicallyScalable | RenderTargetCreationFlags.GenerateMips,
|
||||||
|
uint mipLevels = 0u, uint sampleCount = 1)
|
||||||
{
|
{
|
||||||
return new RenderTargetDesc
|
return new RenderTargetDesc
|
||||||
{
|
{
|
||||||
@@ -87,6 +105,9 @@ public struct RenderTargetDesc
|
|||||||
Height = height,
|
Height = height,
|
||||||
Type = RenderTargetType.Color,
|
Type = RenderTargetType.Color,
|
||||||
Format = format,
|
Format = format,
|
||||||
|
Dimension = dimension,
|
||||||
|
CreationFlags = creationFlags,
|
||||||
|
MipLevels = mipLevels,
|
||||||
SampleCount = sampleCount
|
SampleCount = sampleCount
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -94,7 +115,10 @@ public struct RenderTargetDesc
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a depth render target
|
/// Creates a depth render target
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static RenderTargetDesc Depth(uint width, uint height, TextureFormat format = TextureFormat.D24_UNorm_S8_UInt, uint sampleCount = 1)
|
public static RenderTargetDesc Depth(uint width, uint height,
|
||||||
|
TextureFormat format = TextureFormat.D24_UNorm_S8_UInt, TextureDimension dimension = TextureDimension.Texture2D,
|
||||||
|
RenderTargetCreationFlags creationFlags = RenderTargetCreationFlags.AllowUAV | RenderTargetCreationFlags.DynamicallyScalable,
|
||||||
|
uint mipLevels = 0u, uint sampleCount = 1)
|
||||||
{
|
{
|
||||||
return new RenderTargetDesc
|
return new RenderTargetDesc
|
||||||
{
|
{
|
||||||
@@ -102,6 +126,9 @@ public struct RenderTargetDesc
|
|||||||
Height = height,
|
Height = height,
|
||||||
Type = RenderTargetType.Depth,
|
Type = RenderTargetType.Depth,
|
||||||
Format = format,
|
Format = format,
|
||||||
|
Dimension = dimension,
|
||||||
|
CreationFlags = creationFlags,
|
||||||
|
MipLevels = mipLevels,
|
||||||
SampleCount = sampleCount
|
SampleCount = sampleCount
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -128,7 +155,12 @@ public struct TextureDesc
|
|||||||
public TextureFormat Format;
|
public TextureFormat Format;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Number of mip levels
|
/// Texture dimension
|
||||||
|
/// </summary>
|
||||||
|
public TextureDimension Dimension;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Number of mip levels. 0 to generate full mip chain
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint MipLevels;
|
public uint MipLevels;
|
||||||
|
|
||||||
@@ -137,11 +169,12 @@ public struct TextureDesc
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public TextureUsage Usage;
|
public TextureUsage Usage;
|
||||||
|
|
||||||
public TextureDesc(uint width, uint height, TextureFormat format, uint mipLevels = 1, TextureUsage usage = TextureUsage.ShaderResource)
|
public TextureDesc(uint width, uint height, TextureFormat format, TextureDimension dimension = TextureDimension.Texture2D, uint mipLevels = 0u, TextureUsage usage = TextureUsage.ShaderResource)
|
||||||
{
|
{
|
||||||
Width = width;
|
Width = width;
|
||||||
Height = height;
|
Height = height;
|
||||||
Format = format;
|
Format = format;
|
||||||
|
Dimension = dimension;
|
||||||
MipLevels = mipLevels;
|
MipLevels = mipLevels;
|
||||||
Usage = usage;
|
Usage = usage;
|
||||||
}
|
}
|
||||||
@@ -188,6 +221,33 @@ public enum TextureUsage
|
|||||||
UnorderedAccess = 1 << 3
|
UnorderedAccess = 1 << 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dimensions of the texture
|
||||||
|
/// </summary>
|
||||||
|
public enum TextureDimension
|
||||||
|
{
|
||||||
|
Unknown = -1,
|
||||||
|
None = 0,
|
||||||
|
Texture2D = 1,
|
||||||
|
Texture3D = 2,
|
||||||
|
TextureCube = 3,
|
||||||
|
Texture2DArray = 4,
|
||||||
|
TextureCubeArray = 5
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Render target creation flags
|
||||||
|
/// </summary>
|
||||||
|
[Flags]
|
||||||
|
public enum RenderTargetCreationFlags
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
AllowUAV = 1 << 0,
|
||||||
|
AllowMSAA = 1 << 1,
|
||||||
|
DynamicallyScalable = 1 << 2,
|
||||||
|
GenerateMips = 1 << 3
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Memory types for resources
|
/// Memory types for resources
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
41
Ghost.Graphics/RHI/IResourceFactory.cs
Normal file
41
Ghost.Graphics/RHI/IResourceFactory.cs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
using Ghost.Graphics.Data;
|
||||||
|
|
||||||
|
namespace Ghost.Graphics.RHI;
|
||||||
|
|
||||||
|
public interface IResourceFactory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a render target for off-screen rendering
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="desc">Render target description</param>
|
||||||
|
/// <returns>A new render target instance</returns>
|
||||||
|
public IRenderTarget CreateRenderTarget(ref readonly RenderTargetDesc desc);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a texture resource
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="desc">Texture description</param>
|
||||||
|
/// <returns>A new texture handle point to the resource</returns>
|
||||||
|
public TextureHandle CreateTextureHandle(ref readonly TextureDesc desc);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a texture resource
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="desc">Texture description</param>
|
||||||
|
/// <returns>A new texture instance</returns>
|
||||||
|
public ITexture CreateTexture(ref readonly TextureDesc desc);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a buffer resource
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="desc">Buffer description</param>
|
||||||
|
/// <returns>A new buffer handle point to the resource</returns>
|
||||||
|
public BufferHandle CreateBufferHandle(ref readonly BufferDesc desc);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a buffer resource
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="desc">Buffer description</param>
|
||||||
|
/// <returns>A new buffer instance</returns>
|
||||||
|
public IBuffer CreateBuffer(ref readonly BufferDesc desc);
|
||||||
|
}
|
||||||
@@ -28,6 +28,12 @@ public interface ISwapChain : IDisposable
|
|||||||
/// <returns>Current back buffer texture</returns>
|
/// <returns>Current back buffer texture</returns>
|
||||||
ITexture GetCurrentBackBuffer();
|
ITexture GetCurrentBackBuffer();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the current back buffer as a render target
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Current back buffer render target</returns>
|
||||||
|
IRenderTarget GetCurrentBackBufferRenderTarget();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Presents the rendered frame
|
/// Presents the rendered frame
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
0
Ghost.Graphics/SWAP_CHAIN_IMPROVEMENTS.md
Normal file
0
Ghost.Graphics/SWAP_CHAIN_IMPROVEMENTS.md
Normal file
Reference in New Issue
Block a user