feat(d3d12): unify resource mgmt & add pooling system
Refactored D3D12 resource and command management with a new D3D12Object<T> base class for unified lifetime and naming of COM objects. Introduced pooled command buffer and resource management in D3D12GraphicsEngine and ResourceManager, using frame-based return queues for safe reuse. Updated RenderSystem to use pooled command buffers and render requests, and to properly dispose of per-frame resources. Changed frame synchronization and resource release logic to use ulong fence/frame values for improved robustness. Refactored swap chain to DXGISwapChain and improved error handling and code clarity. Removed renderer management from IGraphicsEngine. Changed ResourceDesc, TextureDesc, and BufferDesc to record structs with equality and hashing for pooling. BREAKING CHANGE: Renderer management APIs removed from IGraphicsEngine. Frame and resource synchronization now use ulong instead of uint. Resource pooling and command buffer pooling are now required for correct usage.
This commit is contained in:
@@ -34,12 +34,12 @@ public struct Frustum
|
||||
|
||||
public static void CalculateFrustumPlanes(float4x4 finalMatrix, ref plane_array outPlanes)
|
||||
{
|
||||
const int kPlaneFrustumLeft = 0;
|
||||
const int kPlaneFrustumRight = 1;
|
||||
const int kPlaneFrustumBottom = 2;
|
||||
const int kPlaneFrustumTop = 3;
|
||||
const int kPlaneFrustumNear = 4;
|
||||
const int kPlaneFrustumFar = 5;
|
||||
const int planeFrustumLeft = 0;
|
||||
const int planeFrustumRight = 1;
|
||||
const int planeFrustumBottom = 2;
|
||||
const int planeFrustumTop = 3;
|
||||
const int planeFrustumNear = 4;
|
||||
const int planeFrustumFar = 5;
|
||||
|
||||
float4 tmpVec = default;
|
||||
float4 otherVec = default;
|
||||
@@ -66,8 +66,8 @@ public struct Frustum
|
||||
leftNormalY *= leftInvMagnitude;
|
||||
leftNormalZ *= leftInvMagnitude;
|
||||
leftDistance *= leftInvMagnitude;
|
||||
outPlanes[kPlaneFrustumLeft].xyz = new float3(leftNormalX, leftNormalY, leftNormalZ);
|
||||
outPlanes[kPlaneFrustumLeft].w = leftDistance;
|
||||
outPlanes[planeFrustumLeft].xyz = new float3(leftNormalX, leftNormalY, leftNormalZ);
|
||||
outPlanes[planeFrustumLeft].w = leftDistance;
|
||||
|
||||
var rightNormalX = -otherVec[0] + tmpVec[0];
|
||||
var rightNormalY = -otherVec[1] + tmpVec[1];
|
||||
@@ -80,8 +80,8 @@ public struct Frustum
|
||||
rightNormalY *= rightInvMagnitude;
|
||||
rightNormalZ *= rightInvMagnitude;
|
||||
rightDistance *= rightInvMagnitude;
|
||||
outPlanes[kPlaneFrustumRight].xyz = new float3(rightNormalX, rightNormalY, rightNormalZ);
|
||||
outPlanes[kPlaneFrustumRight].w = rightDistance;
|
||||
outPlanes[planeFrustumRight].xyz = new float3(rightNormalX, rightNormalY, rightNormalZ);
|
||||
outPlanes[planeFrustumRight].w = rightDistance;
|
||||
|
||||
// bottom & top
|
||||
otherVec[0] = finalMatrix[0][1];
|
||||
@@ -100,8 +100,8 @@ public struct Frustum
|
||||
bottomNormalY *= bottomInvMagnitude;
|
||||
bottomNormalZ *= bottomInvMagnitude;
|
||||
bottomDistance *= bottomInvMagnitude;
|
||||
outPlanes[kPlaneFrustumBottom].xyz = new float3(bottomNormalX, bottomNormalY, bottomNormalZ);
|
||||
outPlanes[kPlaneFrustumBottom].w = bottomDistance;
|
||||
outPlanes[planeFrustumBottom].xyz = new float3(bottomNormalX, bottomNormalY, bottomNormalZ);
|
||||
outPlanes[planeFrustumBottom].w = bottomDistance;
|
||||
|
||||
var topNormalX = -otherVec[0] + tmpVec[0];
|
||||
var topNormalY = -otherVec[1] + tmpVec[1];
|
||||
@@ -114,8 +114,8 @@ public struct Frustum
|
||||
topNormalY *= topInvMagnitude;
|
||||
topNormalZ *= topInvMagnitude;
|
||||
topDistance *= topInvMagnitude;
|
||||
outPlanes[kPlaneFrustumTop].xyz = new float3(topNormalX, topNormalY, topNormalZ);
|
||||
outPlanes[kPlaneFrustumTop].w = topDistance;
|
||||
outPlanes[planeFrustumTop].xyz = new float3(topNormalX, topNormalY, topNormalZ);
|
||||
outPlanes[planeFrustumTop].w = topDistance;
|
||||
|
||||
// near & far
|
||||
otherVec[0] = finalMatrix[0][2];
|
||||
@@ -134,8 +134,8 @@ public struct Frustum
|
||||
nearNormalY *= nearInvMagnitude;
|
||||
nearNormalZ *= nearInvMagnitude;
|
||||
nearDistance *= nearInvMagnitude;
|
||||
outPlanes[kPlaneFrustumNear].xyz = new float3(nearNormalX, nearNormalY, nearNormalZ);
|
||||
outPlanes[kPlaneFrustumNear].w = nearDistance;
|
||||
outPlanes[planeFrustumNear].xyz = new float3(nearNormalX, nearNormalY, nearNormalZ);
|
||||
outPlanes[planeFrustumNear].w = nearDistance;
|
||||
|
||||
var farNormalX = -otherVec[0] + tmpVec[0];
|
||||
var farNormalY = -otherVec[1] + tmpVec[1];
|
||||
@@ -148,8 +148,8 @@ public struct Frustum
|
||||
farNormalY *= farInvMagnitude;
|
||||
farNormalZ *= farInvMagnitude;
|
||||
farDistance *= farInvMagnitude;
|
||||
outPlanes[kPlaneFrustumFar].xyz = new float3(farNormalX, farNormalY, farNormalZ);
|
||||
outPlanes[kPlaneFrustumFar].w = farDistance;
|
||||
outPlanes[planeFrustumFar].xyz = new float3(farNormalX, farNormalY, farNormalZ);
|
||||
outPlanes[planeFrustumFar].w = farDistance;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ public struct RenderView
|
||||
public RenderingLayerMask renderingLayerMask;
|
||||
}
|
||||
|
||||
public unsafe struct RenderRequest
|
||||
public unsafe struct RenderRequest: IDisposable
|
||||
{
|
||||
public RenderView view;
|
||||
|
||||
@@ -189,4 +189,11 @@ public unsafe struct RenderRequest
|
||||
public RenderList shadowCasterRenderList;
|
||||
|
||||
public delegate*<ref readonly RenderContext, ref readonly RenderRequest, void> renderFunc;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
opaqueRenderList.Dispose();
|
||||
transparentRenderList.Dispose();
|
||||
shadowCasterRenderList.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ public partial class GhostRenderPipeline
|
||||
private readonly uint _padding1;
|
||||
private readonly uint _padding2;
|
||||
}
|
||||
|
||||
#if flase
|
||||
private void RenderTest(RenderGraph graph, Identifier<RGTexture> backbuffer)
|
||||
{
|
||||
Identifier<RGTexture> renderTarget;
|
||||
@@ -107,4 +107,5 @@ public partial class GhostRenderPipeline
|
||||
});
|
||||
}
|
||||
}
|
||||
# endif
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.D3D12;
|
||||
using Ghost.Graphics.RenderPipeline;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
@@ -70,14 +73,16 @@ public class RenderSystem : IDisposable
|
||||
private readonly FrameResource[] _frameResources;
|
||||
private readonly Thread _renderThread;
|
||||
private readonly AutoResetEvent _shutdownEvent;
|
||||
|
||||
private UnsafeArray<UnsafeList<RenderRequest>> _renderRequests;
|
||||
private readonly ConcurrentDictionary<ISwapChain, uint2> _resizeRequest;
|
||||
|
||||
private IRenderPipelineSettings _renderPipelineSettings;
|
||||
private IRenderPipeline _renderPipeline;
|
||||
|
||||
private uint _frameIndex;
|
||||
private uint _cpuFenceValue;
|
||||
private uint _gpuFenceValue;
|
||||
private ulong _cpuFenceValue;
|
||||
private ulong _gpuFenceValue;
|
||||
|
||||
private bool _isRunning;
|
||||
private bool _disposed;
|
||||
@@ -86,8 +91,8 @@ public class RenderSystem : IDisposable
|
||||
public ResourceManager ResourceManager => _resourceManager;
|
||||
public bool IsRunning => _isRunning;
|
||||
|
||||
public uint CPUFenceValue => _cpuFenceValue;
|
||||
public uint GPUFenceValue => _gpuFenceValue;
|
||||
public ulong CPUFenceValue => _cpuFenceValue;
|
||||
public ulong GPUFenceValue => _gpuFenceValue;
|
||||
public uint FrameIndex => _frameIndex;
|
||||
public uint MaxFrameLatency => _config.FrameBufferCount;
|
||||
|
||||
@@ -160,6 +165,11 @@ public class RenderSystem : IDisposable
|
||||
|
||||
_shutdownEvent = new AutoResetEvent(false);
|
||||
_resizeRequest = new ConcurrentDictionary<ISwapChain, uint2>();
|
||||
_renderRequests = new UnsafeArray<UnsafeList<RenderRequest>>((int)desc.FrameBufferCount, Allocator.Persistent);
|
||||
for (var i = 0; i < desc.FrameBufferCount; i++)
|
||||
{
|
||||
_renderRequests[i] = new UnsafeList<RenderRequest>(2, Allocator.Persistent);
|
||||
}
|
||||
|
||||
_renderPipelineSettings = new GhostRenderPipelineSettings();
|
||||
_renderPipeline = _renderPipelineSettings.CreatePipeline(this);
|
||||
@@ -191,7 +201,7 @@ public class RenderSystem : IDisposable
|
||||
|
||||
while (_isRunning)
|
||||
{
|
||||
_frameIndex = _gpuFenceValue % _config.FrameBufferCount;
|
||||
_frameIndex = (uint)(_gpuFenceValue % _config.FrameBufferCount);
|
||||
ref var frameResource = ref _frameResources[_frameIndex];
|
||||
|
||||
// Wait for either CPU ready signal or shutdown signal
|
||||
@@ -214,7 +224,6 @@ public class RenderSystem : IDisposable
|
||||
|
||||
if (!_resizeRequest.IsEmpty)
|
||||
{
|
||||
//WaitIdle();
|
||||
_gpuFenceValue++;
|
||||
var flushFence = _graphicsEngine.Device.GraphicsQueue.Signal(_gpuFenceValue);
|
||||
_graphicsEngine.Device.GraphicsQueue.WaitForValue(flushFence);
|
||||
@@ -241,28 +250,65 @@ public class RenderSystem : IDisposable
|
||||
continue; // Skip rendering this frame since we just resized and may have invalid render targets
|
||||
}
|
||||
|
||||
// Begin rendering for this frame
|
||||
frameResource.CommandAllocator.Reset();
|
||||
|
||||
var r = _graphicsEngine.BeginFrame(_cpuFenceValue, _gpuFenceValue);
|
||||
_resourceManager.BeginFrame(_cpuFenceValue);
|
||||
var r = _graphicsEngine.BeginFrame(_cpuFenceValue);
|
||||
|
||||
if (r.IsFailure)
|
||||
{
|
||||
StopRenderLoop(r);
|
||||
break;
|
||||
}
|
||||
|
||||
var cmd = _graphicsEngine.CreateCommandBuffer(CommandBufferType.Graphics);
|
||||
// Start recording commands
|
||||
|
||||
// TODO: How can we support async compute and async copy?
|
||||
var cmd = _graphicsEngine.GetPooledCommandBuffer(CommandBufferType.Graphics);
|
||||
cmd.Begin(frameResource.CommandAllocator);
|
||||
|
||||
var renderCtx = new RenderContext
|
||||
{
|
||||
CommandBuffer = cmd,
|
||||
CommandBuffer = cmd
|
||||
};
|
||||
|
||||
_renderPipeline.Render(renderCtx, default);
|
||||
ref var renderRequests = ref _renderRequests[_frameIndex];
|
||||
_renderPipeline.Render(renderCtx, renderRequests.AsSpan());
|
||||
|
||||
// End recording commands and submit
|
||||
r = cmd.End();
|
||||
if (r.IsFailure)
|
||||
{
|
||||
_graphicsEngine.ReturnPooledCommandBuffer(cmd);
|
||||
StopRenderLoop(r);
|
||||
break;
|
||||
}
|
||||
|
||||
_graphicsEngine.Device.GraphicsQueue.Submit(cmd);
|
||||
_graphicsEngine.ReturnPooledCommandBuffer(cmd);
|
||||
|
||||
// End the frame and present
|
||||
_resourceManager.EndFrame(_cpuFenceValue);
|
||||
r = _graphicsEngine.EndFrame(_gpuFenceValue);
|
||||
|
||||
r = _graphicsEngine.EndFrame(_cpuFenceValue, _gpuFenceValue);
|
||||
if (r.IsFailure)
|
||||
{
|
||||
StopRenderLoop(r);
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: Present here.
|
||||
|
||||
|
||||
// Prepare for the next frame.
|
||||
for (var i = 0; i < renderRequests.Count; i++)
|
||||
{
|
||||
renderRequests[i].Dispose();
|
||||
}
|
||||
|
||||
renderRequests.Clear();
|
||||
|
||||
_gpuFenceValue++;
|
||||
|
||||
frameResource.GpuReadyEvent.Set();
|
||||
@@ -312,6 +358,14 @@ public class RenderSystem : IDisposable
|
||||
_resizeRequest.AddOrUpdate(swapChain, newSize, (_, _) => newSize);
|
||||
}
|
||||
|
||||
public void AddRenderRequest(in RenderRequest request)
|
||||
{
|
||||
Debug.Assert(!_disposed, "Cannot add render request to a disposed RenderSystem.");
|
||||
|
||||
var frameIndex = (int)(_cpuFenceValue % _config.FrameBufferCount);
|
||||
_renderRequests[frameIndex].Add(request);
|
||||
}
|
||||
|
||||
public bool WaitForGPUReady(int timeOut = -1)
|
||||
{
|
||||
Debug.Assert(!_disposed, "Cannot wait for GPU ready on a disposed RenderSystem.");
|
||||
@@ -346,6 +400,16 @@ public class RenderSystem : IDisposable
|
||||
frameResource.Dispose();
|
||||
}
|
||||
|
||||
foreach (ref var renderRequestList in _renderRequests)
|
||||
{
|
||||
foreach (ref var request in renderRequestList)
|
||||
{
|
||||
request.Dispose();
|
||||
}
|
||||
|
||||
renderRequestList.Dispose();
|
||||
}
|
||||
|
||||
_graphicsEngine.Dispose();
|
||||
_shutdownEvent.Dispose();
|
||||
|
||||
|
||||
@@ -4,11 +4,24 @@ using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Ghost.Graphics;
|
||||
|
||||
public sealed class ResourceManager : IDisposable
|
||||
{
|
||||
private readonly struct ResourceReturnEntry
|
||||
{
|
||||
public readonly Handle<GPUResource> handle;
|
||||
public readonly ulong returnFrame;
|
||||
|
||||
public ResourceReturnEntry(Handle<GPUResource> handle, ulong returnFrame)
|
||||
{
|
||||
this.handle = handle;
|
||||
this.returnFrame = returnFrame;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly IResourceAllocator _resourceAllocator;
|
||||
private readonly IResourceDatabase _resourceDatabase;
|
||||
|
||||
@@ -18,6 +31,11 @@ public sealed class ResourceManager : IDisposable
|
||||
|
||||
private readonly MaterialPaletteStore _materialPalettes;
|
||||
|
||||
private ulong _currentFrame;
|
||||
|
||||
private UnsafeHashMap<ResourceDesc, UnsafeQueue<Handle<GPUResource>>> _resourceCache;
|
||||
private UnsafeQueue<ResourceReturnEntry> _resourceReturnQueue;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public ResourceManager(IResourceAllocator resourceAllocator, IResourceDatabase resourceDatabase)
|
||||
@@ -28,7 +46,11 @@ public sealed class ResourceManager : IDisposable
|
||||
_meshes = new UnsafeSlotMap<Mesh>(64, Allocator.Persistent);
|
||||
_materials = new UnsafeSlotMap<Material>(64, Allocator.Persistent);
|
||||
_shaders = new UnsafeList<Shader>(16, Allocator.Persistent);
|
||||
|
||||
_materialPalettes = new MaterialPaletteStore();
|
||||
|
||||
_resourceCache = new UnsafeHashMap<ResourceDesc, UnsafeQueue<Handle<GPUResource>>>(32, Allocator.Persistent);
|
||||
_resourceReturnQueue = new UnsafeQueue<ResourceReturnEntry>(32, Allocator.Persistent);
|
||||
}
|
||||
|
||||
~ResourceManager()
|
||||
@@ -36,6 +58,32 @@ public sealed class ResourceManager : IDisposable
|
||||
Dispose();
|
||||
}
|
||||
|
||||
internal void BeginFrame(ulong currentFrame)
|
||||
{
|
||||
Debug.Assert(!_disposed);
|
||||
_currentFrame = currentFrame;
|
||||
}
|
||||
|
||||
internal void EndFrame(ulong completedFrame)
|
||||
{
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
while (_resourceReturnQueue.TryPeek(out var entry) && entry.returnFrame <= completedFrame)
|
||||
{
|
||||
_resourceReturnQueue.Dequeue();
|
||||
var result = _resourceDatabase.GetResourceDescription(entry.handle);
|
||||
Debug.Assert(result.IsSuccess);
|
||||
|
||||
ref var queue = ref _resourceCache.GetValueRefOrAddDefault(result.Value, out var exist);
|
||||
if (!exist)
|
||||
{
|
||||
queue = new UnsafeQueue<Handle<GPUResource>>(4, Allocator.Persistent);
|
||||
}
|
||||
|
||||
queue.Enqueue(entry.handle);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new mesh from the specified vertex and index data.
|
||||
/// </summary>
|
||||
@@ -44,7 +92,7 @@ public sealed class ResourceManager : IDisposable
|
||||
/// <returns>An <see cref="Identifier{Mesh}"/> representing the newly created mesh.</returns>
|
||||
public unsafe Handle<Mesh> CreateMesh(UnsafeList<Vertex> vertices, UnsafeList<uint> indices)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
var vertexBufferDesc = new BufferDesc
|
||||
{
|
||||
@@ -94,7 +142,7 @@ public sealed class ResourceManager : IDisposable
|
||||
/// <returns>An <see cref="Identifier{Material}"/> representing the newly created material.</returns>
|
||||
public Handle<Material> CreateMaterial(Identifier<Shader> shader)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
var material = new Material();
|
||||
if (material.SetShader(shader, this, _resourceDatabase, _resourceAllocator) != Error.None)
|
||||
@@ -113,7 +161,7 @@ public sealed class ResourceManager : IDisposable
|
||||
/// <param name="descriptor">The viewGroup containing the shader's properties and passes.</param>
|
||||
public Identifier<Shader> CreateGraphicsShader(ShaderDescriptor descriptor)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
var shader = new Shader(descriptor);
|
||||
|
||||
@@ -129,7 +177,7 @@ public sealed class ResourceManager : IDisposable
|
||||
/// <returns>true if a mesh with the specified Handle exists; otherwise, false.</returns>
|
||||
public bool HasMesh(Handle<Mesh> handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
return _meshes.Contains(handle.ID, handle.Generation);
|
||||
}
|
||||
|
||||
@@ -155,7 +203,7 @@ public sealed class ResourceManager : IDisposable
|
||||
/// <param name="handle">The handle of the mesh to release. Must refer to a mesh that was previously created and not already released.</param>
|
||||
public void ReleaseMesh(Handle<Mesh> handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
if (!_meshes.TryGetElementAt(handle.ID, handle.Generation, out var mesh))
|
||||
{
|
||||
@@ -173,7 +221,7 @@ public sealed class ResourceManager : IDisposable
|
||||
/// <returns>true if a material with the specified handle exists; otherwise, false.</returns>
|
||||
public bool HasMaterial(Handle<Material> handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
return _materials.Contains(handle.ID, handle.Generation);
|
||||
}
|
||||
|
||||
@@ -199,7 +247,7 @@ public sealed class ResourceManager : IDisposable
|
||||
/// <param name="handle">The handle of the material to release. Must refer to a material that has been previously acquired.</param>
|
||||
public void ReleaseMaterial(Handle<Material> handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
var material = _materials.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
|
||||
if (!exist)
|
||||
@@ -218,7 +266,7 @@ public sealed class ResourceManager : IDisposable
|
||||
/// <returns>The palette index. Index 0 represents an empty palette.</returns>
|
||||
public int GetOrCreateMaterialPalette(ReadOnlySpan<Handle<Material>> materials)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
foreach (var material in materials)
|
||||
{
|
||||
@@ -237,7 +285,7 @@ public sealed class ResourceManager : IDisposable
|
||||
/// <param name="paletteID">The palette index to validate.</param>
|
||||
public bool HasMaterialPalette(Identifier<MaterialPalette> paletteID)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
return _materialPalettes.IsValid(paletteID);
|
||||
}
|
||||
|
||||
@@ -247,7 +295,7 @@ public sealed class ResourceManager : IDisposable
|
||||
/// <param name="paletteID">The palette index to query.</param>
|
||||
public MaterialPalette GetMaterialPaletteInfo(Identifier<MaterialPalette> paletteID)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
return _materialPalettes.GetInfo(paletteID);
|
||||
}
|
||||
|
||||
@@ -258,7 +306,7 @@ public sealed class ResourceManager : IDisposable
|
||||
/// <param name="localMaterialIndex">The material slot inside the palette.</param>
|
||||
public Handle<Material> GetMaterialPaletteMaterial(Identifier<MaterialPalette> paletteID, int localMaterialIndex)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
return _materialPalettes.GetMaterial(paletteID, localMaterialIndex);
|
||||
}
|
||||
|
||||
@@ -268,7 +316,7 @@ public sealed class ResourceManager : IDisposable
|
||||
/// <param name="paletteID">The palette index to release.</param>
|
||||
public void ReleaseMaterialPalette(Identifier<MaterialPalette> paletteID)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
_materialPalettes.Release(paletteID);
|
||||
}
|
||||
|
||||
@@ -279,7 +327,7 @@ public sealed class ResourceManager : IDisposable
|
||||
/// <returns>true if a shader with the specified identifier exists; otherwise, false.</returns>
|
||||
public bool HasShader(Identifier<Shader> id)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
return id.Value >= 0 && id.Value < _shaders.Count;
|
||||
}
|
||||
|
||||
@@ -304,7 +352,7 @@ public sealed class ResourceManager : IDisposable
|
||||
/// <param name="id">The identifier of the shader to release. Must refer to a valid, previously created shader.</param>
|
||||
public void ReleaseShader(Identifier<Shader> id)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
if (!HasShader(id))
|
||||
{
|
||||
@@ -315,6 +363,38 @@ public sealed class ResourceManager : IDisposable
|
||||
shader.ReleaseResource(_resourceDatabase);
|
||||
}
|
||||
|
||||
public Handle<GPUResource> GetPooledResource(in ResourceDesc desc)
|
||||
{
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
ref var queue = ref _resourceCache.GetValueRef(desc, out var exist);
|
||||
if (exist && queue.TryDequeue(out Handle<GPUResource> handle))
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
|
||||
handle = desc.Type switch
|
||||
{
|
||||
ResourceType.Buffer => _resourceAllocator.CreateBuffer(in desc.BufferDescription, "PooledBuffer").AsResource(),
|
||||
ResourceType.Texture => _resourceAllocator.CreateTexture(in desc.TextureDescription, "PooledTexture").AsResource(),
|
||||
_ => throw new ArgumentException("Invalid resource type.", nameof(desc)),
|
||||
};
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
public void ReturnPooledResource(Handle<GPUResource> handle)
|
||||
{
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
if (handle.IsInvalid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_resourceReturnQueue.Enqueue(new ResourceReturnEntry(handle, _currentFrame));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
@@ -342,6 +422,20 @@ public sealed class ResourceManager : IDisposable
|
||||
_shaders.Dispose();
|
||||
_materialPalettes.Dispose();
|
||||
|
||||
foreach (var kvp in _resourceCache)
|
||||
{
|
||||
var queue = kvp.Value;
|
||||
while (queue.TryDequeue(out var handle))
|
||||
{
|
||||
_resourceDatabase.ReleaseResource(handle);
|
||||
}
|
||||
|
||||
queue.Dispose();
|
||||
}
|
||||
|
||||
_resourceCache.Dispose();
|
||||
_resourceReturnQueue.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user