Files
GhostEngine/src/Runtime/Ghost.Graphics/SwapChainManager.cs
Misaki d8a7b07624 feat(graphics): improve rendering pipeline and docs
- Refactor D3D12 backend and RenderGraph module
- Update graphics RHI and core rendering components
- Add Random.hlsl shader include
- Regenerate API documentation and update user guides
2026-03-27 22:23:44 +09:00

174 lines
4.3 KiB
C#

using Ghost.Graphics.RHI;
using System.Diagnostics.CodeAnalysis;
namespace Ghost.Graphics;
internal sealed class SwapChainRecord
{
private int _refCount;
public ISwapChain SwapChain { get; }
public SwapChainRecord(ISwapChain swapChain)
{
SwapChain = swapChain;
_refCount = 1;
}
public bool TryAddRef()
{
while (true)
{
var current = Volatile.Read(ref _refCount);
if (current == 0)
{
return false; // It's dead, let it go.
}
if (Interlocked.CompareExchange(ref _refCount, current + 1, current) == current)
{
return true; // Successfully atomically incremented
}
}
}
public bool ReleaseRef()
{
while (true)
{
var current = Volatile.Read(ref _refCount);
if (current == 0)
{
return false;
}
if (Interlocked.CompareExchange(ref _refCount, current - 1, current) == current)
{
return (current - 1) == 0;
}
}
}
}
internal class SwapChainManager : IDisposable
{
public const int MAX_SWAP_CHAINS = 8;
private readonly IGraphicsEngine _graphicsEngine;
private readonly SwapChainRecord?[] _swapChains = new SwapChainRecord?[MAX_SWAP_CHAINS];
public SwapChainManager(IGraphicsEngine graphicsEngine)
{
_graphicsEngine = graphicsEngine;
}
public ISwapChain EnsureSwapChain(int index, SwapChainDesc desc)
{
while (true)
{
var record = Volatile.Read(ref _swapChains[index]);
if (record != null)
{
if (record.TryAddRef())
{
return record.SwapChain;
}
Thread.Yield();
continue;
}
var newRecord = new SwapChainRecord(_graphicsEngine.CreateSwapChain(desc));
var previous = Interlocked.CompareExchange(ref _swapChains[index], newRecord, null);
if (previous == null)
{
return newRecord.SwapChain;
}
else
{
newRecord.SwapChain.Dispose();
}
}
}
public bool TryGetSwapChain(int index, [MaybeNullWhen(false)] out ISwapChain swapChain)
{
var record = Volatile.Read(ref _swapChains[index]);
if (record != null && record.TryAddRef())
{
swapChain = record.SwapChain;
return true;
}
swapChain = null;
return false;
}
public void ReleaseSwapChain(int index)
{
var record = Volatile.Read(ref _swapChains[index]);
if (record != null && record.ReleaseRef())
{
record.SwapChain.Dispose();
Interlocked.CompareExchange(ref _swapChains[index], null, record);
}
}
public void TransitionToPresent(ICommandBuffer commandBuffer)
{
for (int i = 0; i < MAX_SWAP_CHAINS; i++)
{
var record = Volatile.Read(ref _swapChains[i]);
if (record == null)
{
continue;
}
commandBuffer.Barrier(BarrierDesc.Texture(record.SwapChain.GetCurrentBackBuffer().AsResource(),
null, BarrierSync.None,
null, BarrierAccess.NoAccess,
null, BarrierLayout.Present));
}
}
public void Present(int index, ICommandBuffer commandBuffer)
{
var record = Volatile.Read(ref _swapChains[index]);
if (record == null)
{
return;
}
record.SwapChain.Present();
}
public void PresentAll(ICommandBuffer commandBuffer)
{
for (int i = 0; i < MAX_SWAP_CHAINS; i++)
{
var record = Volatile.Read(ref _swapChains[i]);
if (record == null)
{
continue;
}
record?.SwapChain.Present();
}
}
public void Dispose()
{
for (int i = 0; i < MAX_SWAP_CHAINS; i++)
{
var record = Volatile.Read(ref _swapChains[i]);
if (record != null)
{
record.SwapChain.Dispose();
Interlocked.CompareExchange(ref _swapChains[i], null, record);
}
}
}
}