feat(resource): refactor heap management & suballocation

Major overhaul of GPU resource/heap management:
- Replace resource pooling and upload buffer logic with transient heap/page-based suballocation in ResourceManager.
- Add support for suballocation and heap flags/types, with D3D12 helpers.
- Remove ICommandBuffer.UploadBuffer/UploadTexture; add UpdateSubResources and CopyBuffer, move upload logic to RenderingContext.
- Refactor D3D12ResourceAllocator/Database for suballocation, heap flags, and mapping.
- Standardize on Handle<GPUBuffer> usage.
- Update meshlet/mesh utilities for new allocation handles and memory pools.
- Refactor RenderGraph and docs to use "heap" terminology.
- Use cpuFrame/gpuFrame consistently for frame sync.
- Add s2h.hlsl, s2h_3d.hlsl, s2h_scatter.hlsl shader debug libs.
- Miscellaneous fixes, cleanup, and dependency updates.

BREAKING CHANGE: Resource pooling and upload APIs replaced with new heap/page-based suballocation system. Update all buffer/texture creation and upload code to use new ResourceManager and ICommandBuffer methods.
This commit is contained in:
2026-04-03 01:48:49 +09:00
parent d03eb659fa
commit 6321b36ef5
47 changed files with 2552 additions and 526 deletions

View File

@@ -152,6 +152,19 @@ public record struct Vertex
public float2 uv;
}
public struct ResourceRange
{
public nuint Start
{
get; set;
}
public nuint End
{
get; set;
}
}
public readonly struct ShaderVariant;
public readonly struct GraphicsPipeline;

View File

@@ -185,24 +185,6 @@ public interface ICommandBuffer : IDisposable
// TODO: This method is not supported yet.
void DispatchRay();
/// <summary>
/// Uploads the specified data to the buffer represented by the given handle.
/// </summary>
/// <typeparam name="T">The unmanaged Value space of the elements to upload to the buffer.</typeparam>
/// <param name="buffer">A handle to the buffer that will receive the uploaded data.</param>
/// <param name="data">A read-only span containing the data to upload to the buffer. The span must contain elements of space
/// <typeparamref name="T"/>.</param>
void UploadBuffer<T>(Handle<GPUBuffer> buffer, params ReadOnlySpan<T> data)
where T : unmanaged;
/// <summary>
/// Uploads texture data to the specified texture resource starting at the given subresource index.
/// </summary>
/// <param name="texture">The texture resource to which the subresource data will be uploaded. Must be a valid, initialized texture handle.</param>
/// <param name="subresources">A reference to the structure containing the subresource data to upload. The data must match the Format and layout expected by the texture.</param>
/// Must be greater than zero and not exceed the remaining subresources in the texture.</param>
void UploadTexture(Handle<GPUTexture> texture, params ReadOnlySpan<SubResourceData> subresources);
/// <summary>
/// Copies a specified number of bytes from the source graphics buffer to the destination graphics buffer.
/// </summary>
@@ -221,4 +203,6 @@ public interface ICommandBuffer : IDisposable
/// <param name="src">The handle to the source texture from which data will be read.</param>
/// <param name="srcRegion">The region of the source texture to copy from. If null, the entire texture will be used.</param>
void CopyTexture(Handle<GPUTexture> dst, TextureRegion? dstRegion, Handle<GPUTexture> src, TextureRegion? srcRegion);
void UpdateSubResources(Handle<GPUResource> resource, Handle<GPUResource> intermediate, params ReadOnlySpan<SubResourceData> subResources);
}

View File

@@ -74,14 +74,14 @@ public interface IGraphicsEngine : IDisposable
/// <summary>
/// Begin the current frame.
/// </summary>
/// <param name="currentFrame">CPU fence value for synchronization</param>
/// <param name="cpuFrame">CPU fence value for synchronization</param>
/// <returns>Result of the begin frame operation</returns>
Result BeginFrame(ulong currentFrame);
Result BeginFrame(ulong cpuFrame);
/// <summary>
/// End the current frame.
/// </summary>
/// <param name="completedFrame">GPU fence value for synchronization</param>
/// <param name="gpuFrame">GPU fence value for synchronization</param>
/// <returns>Result of the end frame operation</returns>
Result EndFrame(ulong completedFrame);
Result EndFrame(ulong gpuFrame);
}

View File

@@ -1,3 +1,5 @@
using Ghost.Core;
namespace Ghost.Graphics.RHI;
[Flags]

View File

@@ -5,7 +5,6 @@ namespace Ghost.Graphics.RHI;
public enum ResourceAllocationType
{
Default,
Temporary,
Suballocation,
}
@@ -36,11 +35,12 @@ public enum HeapType
public enum HeapFlags
{
None = 0,
AllowBuffers,
AllowTextures,
AllowRTAndDS,
AlowBufferAndTexture,
None,
Shared,
AllowOnlyBuffers,
AllowOnlyTextures,
AllowOnlyRTAndDS,
AllowAllBufferAndTexture,
}
public struct AllocationDesc
@@ -89,7 +89,7 @@ public interface IResourceAllocator : IDisposable
/// <param name="desc">Allocation description</param>
/// <param name="name">Debug name of the allocation</param>
/// <returns>An <see cref="Handle{GPUResource}"/> point to the allocated memory</returns>
Handle<GPUResource> Allocate(ref readonly AllocationDesc desc, string name);
Handle<GPUResource> Allocate(ref readonly AllocationDesc desc, string? name = null);
/// <summary>
/// Creates a texture resource
@@ -98,7 +98,7 @@ public interface IResourceAllocator : IDisposable
/// <param name="name">Debug name of the resource</param>
/// <param name="options">Additional options of the resource allocation</param>
/// <returns>An <see cref="Handle{Texture}"/> point to the resource</returns>
Handle<GPUTexture> CreateTexture(ref readonly TextureDesc desc, string name, CreationOptions options = default);
Handle<GPUTexture> CreateTexture(ref readonly TextureDesc desc, string? name = null, CreationOptions options = default);
/// <summary>
/// Creates a render Target for off-screen rendering
@@ -107,7 +107,7 @@ public interface IResourceAllocator : IDisposable
/// <param name="name">Debug name of the resource</param>
/// <param name="options">Additional options of the resource allocation</param>
/// <returns>An <see cref="Handle{Texture}"/> point to the resource</returns>
Handle<GPUTexture> CreateRenderTarget(ref readonly RenderTargetDesc desc, string name, CreationOptions options = default);
Handle<GPUTexture> CreateRenderTarget(ref readonly RenderTargetDesc desc, string? name = null, CreationOptions options = default);
/// <summary>
/// Creates a buffer resource
@@ -116,18 +116,7 @@ public interface IResourceAllocator : IDisposable
/// <param name="name">Debug name of the resource</param>
/// <param name="options">Additional options of the resource allocation</param>
/// <returns>An <see cref="Handle{GraphicsBuffer}"/> point to the resource</returns>
Handle<GPUBuffer> CreateBuffer(ref readonly BufferDesc desc, string name, CreationOptions options = default);
/// <summary>
/// Creates a temporary upload buffer of the specified size in bytes.
/// </summary>
/// <remarks>
/// This method has been optimized for frequent calls during frame updates. It efficiently manages memory to minimize fragmentation and overhead.
/// </remarks>
/// <param name="sizeInBytes">The size of the upload buffer to create, in bytes.</param>
/// <param name="offset">The offset within the upload buffer where the allocation begins.</param>
/// <returns>An <see cref="Handle{GraphicsBuffer}"/> pointing to the created upload buffer.</returns>
Handle<GPUBuffer> CreateTempUploadBuffer(ulong sizeInBytes, out ulong offset);
Handle<GPUBuffer> CreateBuffer(ref readonly BufferDesc desc, string? name = null, CreationOptions options = default);
/// <summary>
/// Creates a new sampler object using the specified sampler description.

View File

@@ -31,22 +31,8 @@ public enum BindlessAccess
UnorderedAccess,
}
// TODO: Consider adding methods for resource enumeration, statistics, and bulk operations.
// TODO: Consider adding async resource loading and streaming support.
public interface IResourceDatabase : IDisposable
public unsafe interface IResourceDatabase : IDisposable
{
/*
/// <summary>
/// Imports an external unmanaged resource and returns a handle for use within the resource management system.
/// </summary>
/// <typeparam name="T">The space of the unmanaged resource pointer to import.</typeparam>
/// <param name="resourcePtr">A pointer to the external unmanaged resource to be imported. Must remain valid for the duration of the resource's usage.</param>
/// <param name="initialState">The initial state to assign to the imported resource.</param>
/// <returns>A handle representing the imported resource, which can be used for subsequent operations.</returns>
unsafe Handle<GPUResource> ImportExternalResource<T>(T resourcePtr, ResourceState initialState, string? name = null)
where T : unmanaged;
*/
void EnterParallelRead();
void ExitParallelRead();
@@ -139,4 +125,8 @@ public interface IResourceDatabase : IDisposable
/// <param name="handleB">The second handle whose associated resource is to be swapped.</param>
/// <returns>An Error indicating the success or failure of the swap operation.</returns>
Error Swap(Handle<GPUResource> handleA, Handle<GPUResource> handleB);
Error Map(Handle<GPUResource> handle, uint subResource, ResourceRange? readRange, ResourceRange? writeRange, void* pData, nuint size);
ulong GetIntermediateResourceSize(Handle<GPUResource> resource, uint firstSubResource, uint numSubResources);
}

View File

@@ -48,7 +48,7 @@ public interface ISwapChain : IDisposable
/// <summary>
/// Gets all back buffer textures
/// </summary>
/// <returns>AlowBufferAndTexture back buffer textures</returns>
/// <returns>AllowAllBufferAndTexture back buffer textures</returns>
ReadOnlySpan<Handle<GPUTexture>> GetBackBuffers();
/// <summary>

View File

@@ -25,7 +25,7 @@ public static class ResourceHandleExtensions
return new Handle<GPUTexture>(resource.ID, resource.Generation);
}
public static Handle<GPUBuffer> AsGraphicsBuffer(this Handle<GPUResource> resource)
public static Handle<GPUBuffer> AsBuffer(this Handle<GPUResource> resource)
{
return new Handle<GPUBuffer>(resource.ID, resource.Generation);
}