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:
@@ -209,7 +209,7 @@ public class ShaderVisitor : GhostShaderParserBaseVisitor<object>
|
|||||||
if (stop >= start)
|
if (stop >= start)
|
||||||
{
|
{
|
||||||
var input = context.Start.InputStream;
|
var input = context.Start.InputStream;
|
||||||
hlsl.Code = input.GetText(new Antlr4.Runtime.Misc.Interval(start, stop));
|
hlsl.Code = input.GetText(new Interval(start, stop));
|
||||||
}
|
}
|
||||||
|
|
||||||
return hlsl;
|
return hlsl;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ namespace Ghost.Editor.Properties {
|
|||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
/// A strongly-typed heap class, for looking up localized strings, etc.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||||
// class via a tool like ResGen or Visual Studio.
|
// class via a tool like ResGen or Visual Studio.
|
||||||
@@ -48,7 +48,7 @@ namespace Ghost.Editor.Properties {
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Overrides the current thread's CurrentUICulture property for all
|
/// Overrides the current thread's CurrentUICulture property for all
|
||||||
/// resource lookups using this strongly typed resource class.
|
/// heap lookups using this strongly typed heap class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
internal static global::System.Globalization.CultureInfo Culture {
|
internal static global::System.Globalization.CultureInfo Culture {
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
<ResourceDictionary
|
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
|
||||||
|
|
||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
<ResourceDictionary Source="/View/Controls/Docking/DockingLayout.xaml" />
|
<ResourceDictionary Source="/View/Controls/Docking/DockingLayout.xaml" />
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public partial class AssetPathToGlyphConverter : IValueConverter
|
|||||||
|
|
||||||
var extension = Path.GetExtension(path).ToLowerInvariant();
|
var extension = Path.GetExtension(path).ToLowerInvariant();
|
||||||
|
|
||||||
// TODO: Use resource dictionary for icons.
|
// TODO: Use heap dictionary for icons.
|
||||||
return extension switch
|
return extension switch
|
||||||
{
|
{
|
||||||
".ghostscene" => "\uF159",
|
".ghostscene" => "\uF159",
|
||||||
|
|||||||
@@ -10,9 +10,10 @@
|
|||||||
<Setter.Value>
|
<Setter.Value>
|
||||||
<ControlTemplate TargetType="local:DockDocument">
|
<ControlTemplate TargetType="local:DockDocument">
|
||||||
<Border Background="Transparent">
|
<Border Background="Transparent">
|
||||||
<ContentPresenter Content="{TemplateBinding Content}"
|
<ContentPresenter
|
||||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
|
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||||
|
Content="{TemplateBinding Content}" />
|
||||||
</Border>
|
</Border>
|
||||||
</ControlTemplate>
|
</ControlTemplate>
|
||||||
</Setter.Value>
|
</Setter.Value>
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ public partial class DockGroup : DockContainer
|
|||||||
Content = doc
|
Content = doc
|
||||||
};
|
};
|
||||||
|
|
||||||
existingTab.SetBinding(TabViewItem.HeaderProperty, new Microsoft.UI.Xaml.Data.Binding
|
existingTab.SetBinding(TabViewItem.HeaderProperty, new Binding
|
||||||
{
|
{
|
||||||
Source = doc,
|
Source = doc,
|
||||||
Path = new PropertyPath(nameof(DockDocument.Title)),
|
Path = new PropertyPath(nameof(DockDocument.Title)),
|
||||||
|
|||||||
@@ -7,7 +7,12 @@
|
|||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
<Setter.Value>
|
<Setter.Value>
|
||||||
<ControlTemplate TargetType="local:DockRegionHighlight">
|
<ControlTemplate TargetType="local:DockRegionHighlight">
|
||||||
<Border Background="{ThemeResource SystemControlHighlightAccentBrush}" Opacity="0.25" BorderBrush="{ThemeResource SystemControlHighlightAccentBrush}" BorderThickness="2" CornerRadius="4" />
|
<Border
|
||||||
|
Background="{ThemeResource SystemControlHighlightAccentBrush}"
|
||||||
|
BorderBrush="{ThemeResource SystemControlHighlightAccentBrush}"
|
||||||
|
BorderThickness="2"
|
||||||
|
CornerRadius="4"
|
||||||
|
Opacity="0.25" />
|
||||||
</ControlTemplate>
|
</ControlTemplate>
|
||||||
</Setter.Value>
|
</Setter.Value>
|
||||||
</Setter>
|
</Setter>
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ internal sealed partial class ProjectBrowser : UserControl
|
|||||||
_isUpdatingSelection = false;
|
_isUpdatingSelection = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void PART_FilesView_DoubleTapped(object sender, Microsoft.UI.Xaml.Input.DoubleTappedRoutedEventArgs e)
|
private async void PART_FilesView_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (PART_FilesView.SelectedItem is ExplorerItem selectedItem)
|
if (PART_FilesView.SelectedItem is ExplorerItem selectedItem)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Misaki.HighPerformance" Version="1.0.6" />
|
<PackageReference Include="Misaki.HighPerformance" Version="1.0.6" />
|
||||||
<PackageReference Include="Misaki.HighPerformance.Jobs" Version="1.5.8" />
|
<PackageReference Include="Misaki.HighPerformance.Jobs" Version="1.5.8" />
|
||||||
<PackageReference Include="Misaki.HighPerformance.LowLevel" Version="1.6.3">
|
<PackageReference Include="Misaki.HighPerformance.LowLevel" Version="1.6.9">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using Misaki.HighPerformance.LowLevel.Utilities;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using TerraFX.Interop.DirectX;
|
using TerraFX.Interop.DirectX;
|
||||||
|
using TerraFX.Interop.Gdiplus;
|
||||||
using TerraFX.Interop.Windows;
|
using TerraFX.Interop.Windows;
|
||||||
|
|
||||||
using static TerraFX.Aliases.D3D_Alias;
|
using static TerraFX.Aliases.D3D_Alias;
|
||||||
@@ -635,7 +636,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
|
|||||||
pNativeObject->SetPipelineState(psor.Value);
|
pNativeObject->SetPipelineState(psor.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetConstantBufferView(uint slot, Handle<RHI.GPUBuffer> buffer)
|
public void SetConstantBufferView(uint slot, Handle<GPUBuffer> buffer)
|
||||||
{
|
{
|
||||||
AssertNotDisposed();
|
AssertNotDisposed();
|
||||||
ThrowIfNotRecording();
|
ThrowIfNotRecording();
|
||||||
@@ -651,7 +652,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
|
|||||||
pNativeObject->SetGraphicsRootConstantBufferView(slot, resource.Get()->GetGPUVirtualAddress());
|
pNativeObject->SetGraphicsRootConstantBufferView(slot, resource.Get()->GetGPUVirtualAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetVertexBuffer(uint slot, Handle<RHI.GPUBuffer> buffer, ulong offset = 0)
|
public void SetVertexBuffer(uint slot, Handle<GPUBuffer> buffer, ulong offset = 0)
|
||||||
{
|
{
|
||||||
AssertNotDisposed();
|
AssertNotDisposed();
|
||||||
ThrowIfNotRecording();
|
ThrowIfNotRecording();
|
||||||
@@ -681,7 +682,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
|
|||||||
pNativeObject->IASetVertexBuffers(slot, 1, &vbView);
|
pNativeObject->IASetVertexBuffers(slot, 1, &vbView);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetIndexBuffer(Handle<RHI.GPUBuffer> buffer, IndexType type, ulong offset = 0)
|
public void SetIndexBuffer(Handle<GPUBuffer> buffer, IndexType type, ulong offset = 0)
|
||||||
{
|
{
|
||||||
AssertNotDisposed();
|
AssertNotDisposed();
|
||||||
ThrowIfNotRecording();
|
ThrowIfNotRecording();
|
||||||
@@ -821,7 +822,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ExecuteIndirect(Handle<RHI.GPUBuffer> argumentBuffer, ulong argumentOffset, Handle<RHI.GPUBuffer> countBuffer, ulong countBufferOffset)
|
public void ExecuteIndirect(Handle<GPUBuffer> argumentBuffer, ulong argumentOffset, Handle<GPUBuffer> countBuffer, ulong countBufferOffset)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
|
|
||||||
@@ -845,57 +846,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UploadBuffer<T>(Handle<RHI.GPUBuffer> buffer, params ReadOnlySpan<T> data)
|
public void CopyBuffer(Handle<GPUBuffer> dst, Handle<GPUBuffer> src, ulong dstOffset = 0, ulong srcOffset = 0, ulong numBytes = 0)
|
||||||
where T : unmanaged
|
|
||||||
{
|
|
||||||
static void Map(T* pData, nuint size, ulong offset, SharedPtr<ID3D12Resource> resource)
|
|
||||||
{
|
|
||||||
void* pMappedData;
|
|
||||||
resource.Get()->Map(0, null, &pMappedData);
|
|
||||||
MemoryUtility.MemCpy((byte*)pMappedData + offset, pData, size);
|
|
||||||
resource.Get()->Unmap(0, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
AssertNotDisposed();
|
|
||||||
ThrowIfNotRecording();
|
|
||||||
#if !DEBUG
|
|
||||||
if (_lastError.Status != Error.None)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
IncrementCommandCount();
|
|
||||||
|
|
||||||
var pResource = _resourceDatabase.GetResource(buffer.AsResource());
|
|
||||||
|
|
||||||
D3D12_HEAP_PROPERTIES properties;
|
|
||||||
D3D12_HEAP_FLAGS flags;
|
|
||||||
ThrowIfFailed(pResource.Get()->GetHeapProperties(&properties, &flags));
|
|
||||||
|
|
||||||
if (properties.Type == D3D12_HEAP_TYPE_UPLOAD)
|
|
||||||
{
|
|
||||||
fixed (T* pData = data)
|
|
||||||
{
|
|
||||||
Map(pData, (nuint)(data.Length * sizeof(T)), 0, pResource);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var sizeInBytes = (uint)(data.Length * sizeof(T));
|
|
||||||
|
|
||||||
var uploadHandle = _resourceAllocator.CreateTempUploadBuffer(sizeInBytes, out var offset);
|
|
||||||
var uploadResource = _resourceDatabase.GetResource(uploadHandle.AsResource());
|
|
||||||
|
|
||||||
fixed (T* pData = data)
|
|
||||||
{
|
|
||||||
Map(pData, sizeInBytes, offset, uploadResource);
|
|
||||||
}
|
|
||||||
|
|
||||||
pNativeObject->CopyBufferRegion(pResource, 0, uploadResource, offset, sizeInBytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UploadTexture(Handle<GPUTexture> texture, params ReadOnlySpan<SubResourceData> subresources)
|
|
||||||
{
|
{
|
||||||
AssertNotDisposed();
|
AssertNotDisposed();
|
||||||
ThrowIfNotRecording();
|
ThrowIfNotRecording();
|
||||||
@@ -905,47 +856,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
IncrementCommandCount();
|
|
||||||
|
|
||||||
var resource = _resourceDatabase.GetResource(texture.AsResource());
|
|
||||||
|
|
||||||
var resourceDesc = resource.Get()->GetDesc();
|
|
||||||
var requiredSize = GetRequiredIntermediateSize(resource, 0, (uint)subresources.Length);
|
|
||||||
|
|
||||||
var uploadHandle = _resourceAllocator.CreateTempUploadBuffer(requiredSize, out var offset);
|
|
||||||
var pUploadResource = _resourceDatabase.GetResource(uploadHandle.AsResource());
|
|
||||||
|
|
||||||
var d3d12Subresources = stackalloc D3D12_SUBRESOURCE_DATA[subresources.Length];
|
|
||||||
for (var i = 0; i < subresources.Length; i++)
|
|
||||||
{
|
|
||||||
d3d12Subresources[i] = new D3D12_SUBRESOURCE_DATA
|
|
||||||
{
|
|
||||||
pData = subresources[i].pData,
|
|
||||||
RowPitch = (nint)subresources[i].rowPitch,
|
|
||||||
SlicePitch = (nint)subresources[i].slicePitch
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateSubresources(
|
|
||||||
(ID3D12GraphicsCommandList*)pNativeObject,
|
|
||||||
resource,
|
|
||||||
pUploadResource,
|
|
||||||
offset,
|
|
||||||
0,
|
|
||||||
(uint)subresources.Length,
|
|
||||||
d3d12Subresources);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CopyBuffer(Handle<RHI.GPUBuffer> dst, Handle<RHI.GPUBuffer> src, ulong dstOffset = 0, ulong srcOffset = 0, ulong numBytes = 0)
|
|
||||||
{
|
|
||||||
AssertNotDisposed();
|
|
||||||
ThrowIfNotRecording();
|
|
||||||
#if !DEBUG
|
|
||||||
if (_lastError.Status != Error.None)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (dst == src)
|
if (dst == src)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -971,6 +882,48 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateSubResources(Handle<GPUResource> resource, Handle<GPUResource> intermediate, params ReadOnlySpan<SubResourceData> subResources)
|
||||||
|
{
|
||||||
|
AssertNotDisposed();
|
||||||
|
ThrowIfNotRecording();
|
||||||
|
#if !DEBUG
|
||||||
|
if (_lastError.Status != Error.None)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
IncrementCommandCount();
|
||||||
|
|
||||||
|
var d3d12Resource = _resourceDatabase.GetResource(resource);
|
||||||
|
var d3d12Intermediate = _resourceDatabase.GetResource(intermediate);
|
||||||
|
if (d3d12Intermediate == null || d3d12Resource == null)
|
||||||
|
{
|
||||||
|
RecordError(nameof(UpdateSubResources), Error.InvalidArgument);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var d3d12Subresources = stackalloc D3D12_SUBRESOURCE_DATA[subResources.Length];
|
||||||
|
for (var i = 0; i < subResources.Length; i++)
|
||||||
|
{
|
||||||
|
d3d12Subresources[i] = new D3D12_SUBRESOURCE_DATA
|
||||||
|
{
|
||||||
|
pData = subResources[i].pData,
|
||||||
|
RowPitch = (nint)subResources[i].rowPitch,
|
||||||
|
SlicePitch = (nint)subResources[i].slicePitch
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateSubresources(
|
||||||
|
(ID3D12GraphicsCommandList*)pNativeObject,
|
||||||
|
d3d12Resource,
|
||||||
|
d3d12Intermediate,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
(uint)subResources.Length,
|
||||||
|
d3d12Subresources);
|
||||||
|
}
|
||||||
|
|
||||||
private D3D12_TEXTURE_COPY_LOCATION GetTextureCopyLocation(SharedPtr<ID3D12Resource> texture, TextureSubresource subres)
|
private D3D12_TEXTURE_COPY_LOCATION GetTextureCopyLocation(SharedPtr<ID3D12Resource> texture, TextureSubresource subres)
|
||||||
{
|
{
|
||||||
var flatIndex = subres.MipLevel + subres.ArrayLayer * texture.Get()->GetDesc().MipLevels;
|
var flatIndex = subres.MipLevel + subres.ArrayLayer * texture.Get()->GetDesc().MipLevels;
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
|||||||
private readonly Queue<ICommandBuffer> _commandBufferPool;
|
private readonly Queue<ICommandBuffer> _commandBufferPool;
|
||||||
private readonly Queue<CommandBufferReturnEntry> _commandBufferReturnQueue;
|
private readonly Queue<CommandBufferReturnEntry> _commandBufferReturnQueue;
|
||||||
|
|
||||||
private ulong _currentFrame;
|
private ulong _cpuFrame;
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
public IRenderDevice Device => _device;
|
public IRenderDevice Device => _device;
|
||||||
@@ -147,7 +147,7 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
|||||||
|
|
||||||
public void ReturnPooledCommandBuffer(ICommandBuffer commandBuffer)
|
public void ReturnPooledCommandBuffer(ICommandBuffer commandBuffer)
|
||||||
{
|
{
|
||||||
_commandBufferReturnQueue.Enqueue(new CommandBufferReturnEntry(commandBuffer, _currentFrame));
|
_commandBufferReturnQueue.Enqueue(new CommandBufferReturnEntry(commandBuffer, _cpuFrame));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ISwapChain CreateSwapChain(SwapChainDesc desc)
|
public ISwapChain CreateSwapChain(SwapChainDesc desc)
|
||||||
@@ -156,22 +156,22 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
|||||||
return new DXGISwapChain(_resourceDatabase, _descriptorAllocator, _device, desc, _desc.FrameBufferCount);
|
return new DXGISwapChain(_resourceDatabase, _descriptorAllocator, _device, desc, _desc.FrameBufferCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result BeginFrame(ulong currentFrame)
|
public Result BeginFrame(ulong cpuFrame)
|
||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
|
|
||||||
_currentFrame = currentFrame;
|
_cpuFrame = cpuFrame;
|
||||||
_resourceDatabase.BeginFrame(currentFrame);
|
_resourceDatabase.BeginFrame(cpuFrame);
|
||||||
return Result.Success();
|
return Result.Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result EndFrame(ulong completedFrame)
|
public Result EndFrame(ulong gpuFrame)
|
||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
|
|
||||||
_resourceDatabase.EndFrame(completedFrame);
|
_resourceDatabase.EndFrame(gpuFrame);
|
||||||
|
|
||||||
while (_commandBufferReturnQueue.TryPeek(out var entry) && entry.returnFrame <= completedFrame)
|
while (_commandBufferReturnQueue.TryPeek(out var entry) && entry.returnFrame <= gpuFrame)
|
||||||
{
|
{
|
||||||
_commandBufferPool.Enqueue(entry.commandBuffer);
|
_commandBufferPool.Enqueue(entry.commandBuffer);
|
||||||
_commandBufferReturnQueue.Dequeue();
|
_commandBufferReturnQueue.Dequeue();
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Ghost.Core;
|
||||||
using Ghost.Graphics.D3D12.Utilities;
|
using Ghost.Graphics.D3D12.Utilities;
|
||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
using Misaki.HighPerformance.LowLevel;
|
using Misaki.HighPerformance.LowLevel;
|
||||||
|
|||||||
@@ -462,10 +462,6 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
private readonly D3D12ResourceDatabase _resourceDatabase;
|
private readonly D3D12ResourceDatabase _resourceDatabase;
|
||||||
private readonly D3D12PipelineLibrary _pipelineLibrary;
|
private readonly D3D12PipelineLibrary _pipelineLibrary;
|
||||||
|
|
||||||
// TODO: We should use ring buffer pool in d3d12ma for upload buffer.
|
|
||||||
private readonly Handle<RHI.GPUBuffer> _uploadBatch;
|
|
||||||
private ulong _uploadBatchOffset;
|
|
||||||
|
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
public D3D12ResourceAllocator(
|
public D3D12ResourceAllocator(
|
||||||
@@ -489,17 +485,6 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
_descriptorAllocator = descriptorAllocator;
|
_descriptorAllocator = descriptorAllocator;
|
||||||
_resourceDatabase = resourceDatabase;
|
_resourceDatabase = resourceDatabase;
|
||||||
_pipelineLibrary = pipelineLibrary;
|
_pipelineLibrary = pipelineLibrary;
|
||||||
|
|
||||||
// Create an upload batch
|
|
||||||
var uploadDesc = new BufferDesc
|
|
||||||
{
|
|
||||||
Size = _UPLOAD_BATCH_SIZE,
|
|
||||||
Usage = BufferUsage.Upload,
|
|
||||||
MemoryType = ResourceMemoryType.Upload,
|
|
||||||
};
|
|
||||||
|
|
||||||
_uploadBatch = CreateBuffer(in uploadDesc, "D3D12ResourceAllocator_UploadBatch");
|
|
||||||
_uploadBatchOffset = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~D3D12ResourceAllocator()
|
~D3D12ResourceAllocator()
|
||||||
@@ -507,18 +492,6 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
Dispose();
|
Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private Handle<GPUResource> TrackAllocation(D3D12MA_Allocation* allocation, ResourceBarrierData barrierData, ResourceViewGroup resourceDescriptor, ResourceDesc desc, string name, bool isTemp)
|
|
||||||
{
|
|
||||||
var handle = _resourceDatabase.AddAllocation(allocation, barrierData, resourceDescriptor, desc, name);
|
|
||||||
if (isTemp)
|
|
||||||
{
|
|
||||||
_resourceDatabase.ReleaseResource(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
private HRESULT CreateResource(D3D12MA_ALLOCATION_DESC* pAllocationDesc, D3D12_RESOURCE_DESC* pResourceDesc, D3D12_RESOURCE_STATES initialState, CreationOptions options, Guid* riid, void** ppv)
|
private HRESULT CreateResource(D3D12MA_ALLOCATION_DESC* pAllocationDesc, D3D12_RESOURCE_DESC* pResourceDesc, D3D12_RESOURCE_STATES initialState, CreationOptions options, Guid* riid, void** ppv)
|
||||||
{
|
{
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
@@ -563,27 +536,13 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public Handle<GPUResource> Allocate(ref readonly AllocationDesc desc, string name)
|
public Handle<GPUResource> Allocate(ref readonly AllocationDesc desc, string? name = null)
|
||||||
{
|
{
|
||||||
var allocDesc = new D3D12MA_ALLOCATION_DESC
|
var allocDesc = new D3D12MA_ALLOCATION_DESC
|
||||||
{
|
{
|
||||||
HeapType = desc.HeapType switch
|
HeapType = desc.HeapType.ToD3D12HeapType(),
|
||||||
{
|
|
||||||
HeapType.Default => D3D12_HEAP_TYPE_DEFAULT,
|
|
||||||
HeapType.Upload => D3D12_HEAP_TYPE_UPLOAD,
|
|
||||||
HeapType.Readback => D3D12_HEAP_TYPE_READBACK,
|
|
||||||
_ => D3D12_HEAP_TYPE_DEFAULT
|
|
||||||
},
|
|
||||||
Flags = D3D12MA_ALLOCATION_FLAG_COMMITTED,
|
Flags = D3D12MA_ALLOCATION_FLAG_COMMITTED,
|
||||||
ExtraHeapFlags = desc.HeapFlags switch
|
ExtraHeapFlags = desc.HeapFlags.ToD3D12HeapFlags()
|
||||||
{
|
|
||||||
HeapFlags.None => D3D12_HEAP_FLAG_NONE,
|
|
||||||
HeapFlags.AllowBuffers => D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS,
|
|
||||||
HeapFlags.AllowTextures => D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES,
|
|
||||||
HeapFlags.AllowRTAndDS => D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES,
|
|
||||||
HeapFlags.AlowBufferAndTexture => D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES,
|
|
||||||
_ => D3D12_HEAP_FLAG_NONE
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// SizeInBytes must be aligned to 64KB for committed resources
|
// SizeInBytes must be aligned to 64KB for committed resources
|
||||||
@@ -606,10 +565,10 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
sync = BarrierSync.None
|
sync = BarrierSync.None
|
||||||
};
|
};
|
||||||
|
|
||||||
return TrackAllocation(alloc, barrierData, ResourceViewGroup.Invalid, default, name, false);
|
return _resourceDatabase.AddAllocation(alloc, barrierData, ResourceViewGroup.Invalid, default, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Handle<GPUTexture> CreateTexture(ref readonly TextureDesc desc, string name, CreationOptions options = default)
|
public Handle<GPUTexture> CreateTexture(ref readonly TextureDesc desc, string? name = null, CreationOptions options = default)
|
||||||
{
|
{
|
||||||
Debug.Assert(!_disposed);
|
Debug.Assert(!_disposed);
|
||||||
|
|
||||||
@@ -634,7 +593,10 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
hr = CreateResource(&allocationDesc, &resourceDesc, D3D12_RESOURCE_STATE_COMMON, options, null, (void**)&pAllocation);
|
hr = CreateResource(&allocationDesc, &resourceDesc, D3D12_RESOURCE_STATE_COMMON, options, null, (void**)&pAllocation);
|
||||||
pResource = pAllocation->GetResource();
|
if (hr.SUCCEEDED)
|
||||||
|
{
|
||||||
|
pResource = pAllocation->GetResource();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hr.FAILED)
|
if (hr.FAILED)
|
||||||
@@ -645,7 +607,6 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
return Handle<GPUTexture>.Invalid;
|
return Handle<GPUTexture>.Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
var isTemp = options.AllocationType == ResourceAllocationType.Temporary;
|
|
||||||
var resourceDescriptor = ResourceViewGroup.Invalid;
|
var resourceDescriptor = ResourceViewGroup.Invalid;
|
||||||
if (desc.Usage.HasFlag(TextureUsage.ShaderResource))
|
if (desc.Usage.HasFlag(TextureUsage.ShaderResource))
|
||||||
{
|
{
|
||||||
@@ -701,13 +662,13 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
resource = TrackAllocation(pAllocation, barrierData, resourceDescriptor, ResourceDesc.Texture(desc), name, isTemp);
|
resource = _resourceDatabase.AddAllocation(pAllocation, barrierData, resourceDescriptor, ResourceDesc.Texture(desc), name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return resource.AsTexture();
|
return resource.AsTexture();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Handle<GPUTexture> CreateRenderTarget(ref readonly RenderTargetDesc desc, string name, CreationOptions options = default)
|
public Handle<GPUTexture> CreateRenderTarget(ref readonly RenderTargetDesc desc, string? name = null, CreationOptions options = default)
|
||||||
{
|
{
|
||||||
Debug.Assert(!_disposed);
|
Debug.Assert(!_disposed);
|
||||||
|
|
||||||
@@ -715,7 +676,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
return CreateTexture(in textureDesc, name, options);
|
return CreateTexture(in textureDesc, name, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Handle<RHI.GPUBuffer> CreateBuffer(ref readonly BufferDesc desc, string name, CreationOptions options = default)
|
public Handle<GPUBuffer> CreateBuffer(ref readonly BufferDesc desc, string? name = null, CreationOptions options = default)
|
||||||
{
|
{
|
||||||
Debug.Assert(!_disposed);
|
Debug.Assert(!_disposed);
|
||||||
CheckBufferSize(desc.Size);
|
CheckBufferSize(desc.Size);
|
||||||
@@ -760,10 +721,9 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
#if DEBUG
|
#if DEBUG
|
||||||
ThrowIfFailed(hr);
|
ThrowIfFailed(hr);
|
||||||
#endif
|
#endif
|
||||||
return Handle<RHI.GPUBuffer>.Invalid;
|
return Handle<GPUBuffer>.Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
var isTemp = options.AllocationType == ResourceAllocationType.Temporary;
|
|
||||||
var resourceDescriptor = ResourceViewGroup.Invalid;
|
var resourceDescriptor = ResourceViewGroup.Invalid;
|
||||||
|
|
||||||
if (desc.Usage.HasFlag(BufferUsage.Constant))
|
if (desc.Usage.HasFlag(BufferUsage.Constant))
|
||||||
@@ -814,40 +774,10 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
resource = TrackAllocation(pAllocation, barrierData, resourceDescriptor, ResourceDesc.Buffer(desc), name, isTemp);
|
resource = _resourceDatabase.AddAllocation(pAllocation, barrierData, resourceDescriptor, ResourceDesc.Buffer(desc), name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return resource.AsGraphicsBuffer();
|
return resource.AsBuffer();
|
||||||
}
|
|
||||||
|
|
||||||
public Handle<RHI.GPUBuffer> CreateTempUploadBuffer(ulong sizeInBytes, out ulong offset)
|
|
||||||
{
|
|
||||||
if (sizeInBytes <= _MAX_RESOURCE_SIZE_TO_FIT_IN_UPLOAD_BATCH && sizeInBytes + _uploadBatchOffset <= _UPLOAD_BATCH_SIZE)
|
|
||||||
{
|
|
||||||
offset = _uploadBatchOffset;
|
|
||||||
_uploadBatchOffset += sizeInBytes;
|
|
||||||
return _uploadBatch;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var bufferDesc = new BufferDesc
|
|
||||||
{
|
|
||||||
Size = (uint)sizeInBytes,
|
|
||||||
Usage = BufferUsage.Upload,
|
|
||||||
MemoryType = ResourceMemoryType.Upload,
|
|
||||||
};
|
|
||||||
|
|
||||||
var options = new CreationOptions
|
|
||||||
{
|
|
||||||
AllocationType = ResourceAllocationType.Temporary,
|
|
||||||
};
|
|
||||||
|
|
||||||
offset = 0;
|
|
||||||
var handle = CreateBuffer(in bufferDesc, "TempUploadBuffer", options);
|
|
||||||
|
|
||||||
_resourceDatabase.ReleaseResource(handle.AsResource());
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Identifier<Sampler> CreateSampler(ref readonly SamplerDesc desc)
|
public Identifier<Sampler> CreateSampler(ref readonly SamplerDesc desc)
|
||||||
@@ -887,7 +817,6 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_resourceDatabase.ReleaseResourceImmediately(_uploadBatch.AsResource());
|
|
||||||
_d3d12MA.Dispose();
|
_d3d12MA.Dispose();
|
||||||
|
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ using Ghost.Graphics.RHI;
|
|||||||
using Misaki.HighPerformance.LowLevel;
|
using Misaki.HighPerformance.LowLevel;
|
||||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
using Misaki.HighPerformance.LowLevel.Collections;
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
|
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using TerraFX.Interop.DirectX;
|
using TerraFX.Interop.DirectX;
|
||||||
|
|
||||||
namespace Ghost.Graphics.D3D12;
|
namespace Ghost.Graphics.D3D12;
|
||||||
|
|
||||||
// TODO: Thread safety
|
internal unsafe class D3D12ResourceDatabase : IResourceDatabase
|
||||||
internal class D3D12ResourceDatabase : IResourceDatabase
|
|
||||||
{
|
{
|
||||||
internal unsafe record struct ResourceRecord
|
internal unsafe record struct ResourceRecord
|
||||||
{
|
{
|
||||||
@@ -109,7 +109,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
|||||||
|
|
||||||
private int _writeLock;
|
private int _writeLock;
|
||||||
|
|
||||||
private ulong _currentFrame;
|
private ulong _cpuFrame;
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
public D3D12ResourceDatabase(D3D12DescriptorAllocator descriptorAllocator)
|
public D3D12ResourceDatabase(D3D12DescriptorAllocator descriptorAllocator)
|
||||||
@@ -323,12 +323,12 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
|||||||
{
|
{
|
||||||
Debug.Assert(!_disposed);
|
Debug.Assert(!_disposed);
|
||||||
|
|
||||||
if (_resources.TryGetElementAt(handle.ID, handle.Generation, out var record))
|
if (!_resources.TryGetElementAt(handle.ID, handle.Generation, out var record))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var entry = new ReleaseEntry(record, _currentFrame);
|
var entry = new ReleaseEntry(record, _cpuFrame);
|
||||||
|
|
||||||
_releaseQueue.Enqueue(entry);
|
_releaseQueue.Enqueue(entry);
|
||||||
_resources.Remove(handle.ID, handle.Generation);
|
_resources.Remove(handle.ID, handle.Generation);
|
||||||
@@ -399,20 +399,52 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
|||||||
return Error.None;
|
return Error.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BeginFrame(ulong currentFrame)
|
public Error Map(Handle<GPUResource> handle, uint subResource, ResourceRange? readRange, ResourceRange? writeRange, void* pData, nuint size)
|
||||||
{
|
{
|
||||||
Debug.Assert(!_disposed);
|
var r = GetResourceRecord(handle);
|
||||||
_currentFrame = currentFrame;
|
if (r.IsFailure)
|
||||||
|
{
|
||||||
|
return r.Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
var resource = r.Value.ResourcePtr;
|
||||||
|
|
||||||
|
var rRange = readRange.HasValue ? new D3D12_RANGE { Begin = readRange.Value.Start, End = readRange.Value.End } : default;
|
||||||
|
var wRange = writeRange.HasValue ? new D3D12_RANGE { Begin = writeRange.Value.Start, End = writeRange.Value.End } : default;
|
||||||
|
|
||||||
|
void * mappedData = null;
|
||||||
|
resource.Get()->Map(subResource, readRange.HasValue ? &rRange : null, &mappedData);
|
||||||
|
MemoryUtility.MemCpy(mappedData, pData, size);
|
||||||
|
resource.Get()->Unmap(subResource, writeRange.HasValue ? &wRange : null);
|
||||||
|
|
||||||
|
return Error.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EndFrame(ulong completedFrame)
|
public ulong GetIntermediateResourceSize(Handle<GPUResource> resource, uint firstSubResource, uint numSubResources)
|
||||||
|
{
|
||||||
|
var r = GetResourceRecord(resource);
|
||||||
|
if (r.IsFailure)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetRequiredIntermediateSize(r.Value.ResourcePtr.Get(), firstSubResource, numSubResources);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BeginFrame(ulong cpuFrame)
|
||||||
|
{
|
||||||
|
Debug.Assert(!_disposed);
|
||||||
|
_cpuFrame = cpuFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EndFrame(ulong gpuFrame)
|
||||||
{
|
{
|
||||||
Debug.Assert(!_disposed);
|
Debug.Assert(!_disposed);
|
||||||
|
|
||||||
while (_releaseQueue.Count > 0)
|
while (_releaseQueue.Count > 0)
|
||||||
{
|
{
|
||||||
var toRelease = _releaseQueue.Peek();
|
var toRelease = _releaseQueue.Peek();
|
||||||
if (toRelease.fenceValue > completedFrame)
|
if (toRelease.fenceValue > gpuFrame)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -431,6 +463,8 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
|||||||
{
|
{
|
||||||
record.Release(_descriptorAllocator);
|
record.Release(_descriptorAllocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_resources.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|||||||
@@ -386,6 +386,31 @@ internal static unsafe class D3D12Utility
|
|||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static D3D12_HEAP_TYPE ToD3D12HeapType(this HeapType heapType)
|
||||||
|
{
|
||||||
|
return heapType switch
|
||||||
|
{
|
||||||
|
HeapType.Default => D3D12_HEAP_TYPE_DEFAULT,
|
||||||
|
HeapType.Upload => D3D12_HEAP_TYPE_UPLOAD,
|
||||||
|
HeapType.Readback => D3D12_HEAP_TYPE_READBACK,
|
||||||
|
_ => throw new ArgumentException($"Unknown heap type: {heapType}")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static D3D12_HEAP_FLAGS ToD3D12HeapFlags(this HeapFlags flags)
|
||||||
|
{
|
||||||
|
return flags switch
|
||||||
|
{
|
||||||
|
HeapFlags.None => D3D12_HEAP_FLAG_NONE,
|
||||||
|
HeapFlags.Shared => D3D12_HEAP_FLAG_SHARED,
|
||||||
|
HeapFlags.AllowOnlyBuffers => D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS,
|
||||||
|
HeapFlags.AllowOnlyTextures => D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES,
|
||||||
|
HeapFlags.AllowOnlyRTAndDS => D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES,
|
||||||
|
HeapFlags.AllowAllBufferAndTexture => D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES,
|
||||||
|
_ => throw new ArgumentException($"Unknown heap flags: {flags}")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public static D3D12_RESOURCE_DESC ToD3D12ResourceDesc(this in TextureDesc desc)
|
public static D3D12_RESOURCE_DESC ToD3D12ResourceDesc(this in TextureDesc desc)
|
||||||
{
|
{
|
||||||
var dxgiFormat = desc.Format.ToDXGIFormat();
|
var dxgiFormat = desc.Format.ToDXGIFormat();
|
||||||
|
|||||||
@@ -152,6 +152,19 @@ public record struct Vertex
|
|||||||
public float2 uv;
|
public float2 uv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct ResourceRange
|
||||||
|
{
|
||||||
|
public nuint Start
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public nuint End
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public readonly struct ShaderVariant;
|
public readonly struct ShaderVariant;
|
||||||
public readonly struct GraphicsPipeline;
|
public readonly struct GraphicsPipeline;
|
||||||
|
|
||||||
|
|||||||
@@ -185,24 +185,6 @@ public interface ICommandBuffer : IDisposable
|
|||||||
// TODO: This method is not supported yet.
|
// TODO: This method is not supported yet.
|
||||||
void DispatchRay();
|
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>
|
/// <summary>
|
||||||
/// Copies a specified number of bytes from the source graphics buffer to the destination graphics buffer.
|
/// Copies a specified number of bytes from the source graphics buffer to the destination graphics buffer.
|
||||||
/// </summary>
|
/// </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="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>
|
/// <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 CopyTexture(Handle<GPUTexture> dst, TextureRegion? dstRegion, Handle<GPUTexture> src, TextureRegion? srcRegion);
|
||||||
|
|
||||||
|
void UpdateSubResources(Handle<GPUResource> resource, Handle<GPUResource> intermediate, params ReadOnlySpan<SubResourceData> subResources);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,14 +74,14 @@ public interface IGraphicsEngine : IDisposable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Begin the current frame.
|
/// Begin the current frame.
|
||||||
/// </summary>
|
/// </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>
|
/// <returns>Result of the begin frame operation</returns>
|
||||||
Result BeginFrame(ulong currentFrame);
|
Result BeginFrame(ulong cpuFrame);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// End the current frame.
|
/// End the current frame.
|
||||||
/// </summary>
|
/// </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>
|
/// <returns>Result of the end frame operation</returns>
|
||||||
Result EndFrame(ulong completedFrame);
|
Result EndFrame(ulong gpuFrame);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using Ghost.Core;
|
||||||
|
|
||||||
namespace Ghost.Graphics.RHI;
|
namespace Ghost.Graphics.RHI;
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ namespace Ghost.Graphics.RHI;
|
|||||||
public enum ResourceAllocationType
|
public enum ResourceAllocationType
|
||||||
{
|
{
|
||||||
Default,
|
Default,
|
||||||
Temporary,
|
|
||||||
Suballocation,
|
Suballocation,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,11 +35,12 @@ public enum HeapType
|
|||||||
|
|
||||||
public enum HeapFlags
|
public enum HeapFlags
|
||||||
{
|
{
|
||||||
None = 0,
|
None,
|
||||||
AllowBuffers,
|
Shared,
|
||||||
AllowTextures,
|
AllowOnlyBuffers,
|
||||||
AllowRTAndDS,
|
AllowOnlyTextures,
|
||||||
AlowBufferAndTexture,
|
AllowOnlyRTAndDS,
|
||||||
|
AllowAllBufferAndTexture,
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct AllocationDesc
|
public struct AllocationDesc
|
||||||
@@ -89,7 +89,7 @@ public interface IResourceAllocator : IDisposable
|
|||||||
/// <param name="desc">Allocation description</param>
|
/// <param name="desc">Allocation description</param>
|
||||||
/// <param name="name">Debug name of the allocation</param>
|
/// <param name="name">Debug name of the allocation</param>
|
||||||
/// <returns>An <see cref="Handle{GPUResource}"/> point to the allocated memory</returns>
|
/// <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>
|
/// <summary>
|
||||||
/// Creates a texture resource
|
/// Creates a texture resource
|
||||||
@@ -98,7 +98,7 @@ public interface IResourceAllocator : IDisposable
|
|||||||
/// <param name="name">Debug name of the resource</param>
|
/// <param name="name">Debug name of the resource</param>
|
||||||
/// <param name="options">Additional options of the resource allocation</param>
|
/// <param name="options">Additional options of the resource allocation</param>
|
||||||
/// <returns>An <see cref="Handle{Texture}"/> point to the resource</returns>
|
/// <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>
|
/// <summary>
|
||||||
/// Creates a render Target for off-screen rendering
|
/// 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="name">Debug name of the resource</param>
|
||||||
/// <param name="options">Additional options of the resource allocation</param>
|
/// <param name="options">Additional options of the resource allocation</param>
|
||||||
/// <returns>An <see cref="Handle{Texture}"/> point to the resource</returns>
|
/// <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>
|
/// <summary>
|
||||||
/// Creates a buffer resource
|
/// Creates a buffer resource
|
||||||
@@ -116,18 +116,7 @@ public interface IResourceAllocator : IDisposable
|
|||||||
/// <param name="name">Debug name of the resource</param>
|
/// <param name="name">Debug name of the resource</param>
|
||||||
/// <param name="options">Additional options of the resource allocation</param>
|
/// <param name="options">Additional options of the resource allocation</param>
|
||||||
/// <returns>An <see cref="Handle{GraphicsBuffer}"/> point to the resource</returns>
|
/// <returns>An <see cref="Handle{GraphicsBuffer}"/> point to the resource</returns>
|
||||||
Handle<GPUBuffer> CreateBuffer(ref readonly BufferDesc desc, string name, CreationOptions options = default);
|
Handle<GPUBuffer> CreateBuffer(ref readonly BufferDesc desc, string? name = null, 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);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new sampler object using the specified sampler description.
|
/// Creates a new sampler object using the specified sampler description.
|
||||||
|
|||||||
@@ -31,22 +31,8 @@ public enum BindlessAccess
|
|||||||
UnorderedAccess,
|
UnorderedAccess,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Consider adding methods for resource enumeration, statistics, and bulk operations.
|
public unsafe interface IResourceDatabase : IDisposable
|
||||||
// TODO: Consider adding async resource loading and streaming support.
|
|
||||||
public 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 EnterParallelRead();
|
||||||
|
|
||||||
void ExitParallelRead();
|
void ExitParallelRead();
|
||||||
@@ -139,4 +125,8 @@ public interface IResourceDatabase : IDisposable
|
|||||||
/// <param name="handleB">The second handle whose associated resource is to be swapped.</param>
|
/// <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>
|
/// <returns>An Error indicating the success or failure of the swap operation.</returns>
|
||||||
Error Swap(Handle<GPUResource> handleA, Handle<GPUResource> handleB);
|
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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ public interface ISwapChain : IDisposable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets all back buffer textures
|
/// Gets all back buffer textures
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>AlowBufferAndTexture back buffer textures</returns>
|
/// <returns>AllowAllBufferAndTexture back buffer textures</returns>
|
||||||
ReadOnlySpan<Handle<GPUTexture>> GetBackBuffers();
|
ReadOnlySpan<Handle<GPUTexture>> GetBackBuffers();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ public static class ResourceHandleExtensions
|
|||||||
return new Handle<GPUTexture>(resource.ID, resource.Generation);
|
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);
|
return new Handle<GPUBuffer>(resource.ID, resource.Generation);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,16 +11,16 @@ namespace Ghost.Graphics.Core;
|
|||||||
internal struct CBufferCache : IResourceReleasable
|
internal struct CBufferCache : IResourceReleasable
|
||||||
{
|
{
|
||||||
private UnsafeArray<byte> _cpuData;
|
private UnsafeArray<byte> _cpuData;
|
||||||
private Handle<RHI.GPUBuffer> _gpuResource;
|
private Handle<GPUBuffer> _gpuResource;
|
||||||
private uint _size;
|
private uint _size;
|
||||||
|
|
||||||
public readonly UnsafeArray<byte> CpuData => _cpuData;
|
public readonly UnsafeArray<byte> CpuData => _cpuData;
|
||||||
public readonly Handle<RHI.GPUBuffer> GpuResource => _gpuResource;
|
public readonly Handle<GPUBuffer> GpuResource => _gpuResource;
|
||||||
public readonly uint Size => _size;
|
public readonly uint Size => _size;
|
||||||
|
|
||||||
public readonly bool IsCreated => _size != 0 && _gpuResource.IsValid && _cpuData.IsCreated;
|
public readonly bool IsCreated => _size != 0 && _gpuResource.IsValid && _cpuData.IsCreated;
|
||||||
|
|
||||||
public CBufferCache(Handle<RHI.GPUBuffer> buffer, uint bufferSize)
|
public CBufferCache(Handle<GPUBuffer> buffer, uint bufferSize)
|
||||||
{
|
{
|
||||||
_size = bufferSize;
|
_size = bufferSize;
|
||||||
_cpuData = new UnsafeArray<byte>((int)bufferSize, Allocator.Persistent);
|
_cpuData = new UnsafeArray<byte>((int)bufferSize, Allocator.Persistent);
|
||||||
@@ -37,7 +37,7 @@ internal struct CBufferCache : IResourceReleasable
|
|||||||
_cpuData.Dispose();
|
_cpuData.Dispose();
|
||||||
database.ReleaseResource(_gpuResource.AsResource());
|
database.ReleaseResource(_gpuResource.AsResource());
|
||||||
|
|
||||||
_gpuResource = Handle<RHI.GPUBuffer>.Invalid;
|
_gpuResource = Handle<GPUBuffer>.Invalid;
|
||||||
_size = 0;
|
_size = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using Ghost.Core;
|
using Ghost.Core;
|
||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
using Ghost.Graphics.Utilities;
|
using Ghost.Graphics.Utilities;
|
||||||
using Misaki.HighPerformance.Jobs;
|
|
||||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
using Misaki.HighPerformance.LowLevel.Collections;
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
using Misaki.HighPerformance.Mathematics;
|
using Misaki.HighPerformance.Mathematics;
|
||||||
@@ -138,7 +137,7 @@ public struct Mesh : IResourceReleasable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the handle to the vertex buffer on the GPU.
|
/// Gets the handle to the vertex buffer on the GPU.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Handle<RHI.GPUBuffer> VertexBuffer
|
public Handle<GPUBuffer> VertexBuffer
|
||||||
{
|
{
|
||||||
get; internal set;
|
get; internal set;
|
||||||
}
|
}
|
||||||
@@ -146,7 +145,7 @@ public struct Mesh : IResourceReleasable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the handle to the index buffer on the GPU.
|
/// Gets the handle to the index buffer on the GPU.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Handle<RHI.GPUBuffer> IndexBuffer
|
public Handle<GPUBuffer> IndexBuffer
|
||||||
{
|
{
|
||||||
get; internal set;
|
get; internal set;
|
||||||
}
|
}
|
||||||
@@ -154,7 +153,7 @@ public struct Mesh : IResourceReleasable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the handle to the meshlet buffer on the GPU.
|
/// Gets the handle to the meshlet buffer on the GPU.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Handle<RHI.GPUBuffer> MeshLetBuffer
|
public Handle<GPUBuffer> MeshLetBuffer
|
||||||
{
|
{
|
||||||
get; internal set;
|
get; internal set;
|
||||||
}
|
}
|
||||||
@@ -162,7 +161,7 @@ public struct Mesh : IResourceReleasable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the handle to the meshlet vertices buffer on the GPU.
|
/// Gets the handle to the meshlet vertices buffer on the GPU.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Handle<RHI.GPUBuffer> MeshletVerticesBuffer
|
public Handle<GPUBuffer> MeshletVerticesBuffer
|
||||||
{
|
{
|
||||||
get; internal set;
|
get; internal set;
|
||||||
}
|
}
|
||||||
@@ -170,7 +169,7 @@ public struct Mesh : IResourceReleasable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the handle to the meshlet triangles buffer on the GPU.
|
/// Gets the handle to the meshlet triangles buffer on the GPU.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Handle<RHI.GPUBuffer> MeshletTrianglesBuffer
|
public Handle<GPUBuffer> MeshletTrianglesBuffer
|
||||||
{
|
{
|
||||||
get; internal set;
|
get; internal set;
|
||||||
}
|
}
|
||||||
@@ -178,12 +177,12 @@ public struct Mesh : IResourceReleasable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the handle to the mesh data buffer on the GPU.
|
/// Gets the handle to the mesh data buffer on the GPU.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Handle<RHI.GPUBuffer> ObjectDataBuffer
|
public Handle<GPUBuffer> ObjectDataBuffer
|
||||||
{
|
{
|
||||||
get; internal set;
|
get; internal set;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Mesh(ReadOnlySpan<Vertex> vertices, ReadOnlySpan<uint> indices, Handle<RHI.GPUBuffer> vertexBuffer, Handle<RHI.GPUBuffer> indexBuffer)
|
internal Mesh(ReadOnlySpan<Vertex> vertices, ReadOnlySpan<uint> indices, Handle<GPUBuffer> vertexBuffer, Handle<GPUBuffer> indexBuffer)
|
||||||
{
|
{
|
||||||
Vertices = new UnsafeList<Vertex>(vertices.Length, Allocator.Persistent);
|
Vertices = new UnsafeList<Vertex>(vertices.Length, Allocator.Persistent);
|
||||||
Indices = new UnsafeList<uint>(indices.Length, Allocator.Persistent);
|
Indices = new UnsafeList<uint>(indices.Length, Allocator.Persistent);
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ using Ghost.Core;
|
|||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
using Misaki.HighPerformance.LowLevel.Collections;
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Ghost.Graphics.Core;
|
namespace Ghost.Graphics.Core;
|
||||||
|
|
||||||
// TODO: Temporary rendering context for resource creation and data upload. We will refactor it later when we have a better understanding of the engine architecture.
|
// TODO: Temporary rendering context for heap creation and data upload. We will refactor it later when we have a better understanding of the engine architecture.
|
||||||
public readonly unsafe ref struct RenderingContext
|
public readonly unsafe ref struct RenderingContext
|
||||||
{
|
{
|
||||||
private readonly IGraphicsEngine _engine;
|
private readonly IGraphicsEngine _engine;
|
||||||
@@ -82,6 +83,49 @@ public readonly unsafe ref struct RenderingContext
|
|||||||
ResourceDatabase.SetResourceBarrierData(resource, new ResourceBarrierData(newLayout, newAccess, newSync));
|
ResourceDatabase.SetResourceBarrierData(resource, new ResourceBarrierData(newLayout, newAccess, newSync));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UploadBuffer<T>(Handle<GPUBuffer> buffer, params ReadOnlySpan<T> data)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
var r = _engine.ResourceDatabase.GetResourceDescription(buffer.AsResource());
|
||||||
|
if (r.IsFailure)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Assert(r.Value.Type == ResourceType.Buffer);
|
||||||
|
|
||||||
|
var sizeInBytes = (nuint)(data.Length * sizeof(T));
|
||||||
|
var memoryType = r.Value.BufferDescription.MemoryType;
|
||||||
|
|
||||||
|
if (memoryType == ResourceMemoryType.Upload)
|
||||||
|
{
|
||||||
|
fixed (T* pData = data)
|
||||||
|
{
|
||||||
|
ResourceDatabase.Map(buffer.AsResource(), 0, null, null, pData, sizeInBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//var uploadHandle = _resourceAllocator.CreateTempUploadBuffer(sizeInBytes, out var offset);
|
||||||
|
//var uploadResource = _resourceDatabase.GetResource(uploadHandle.AsResource());
|
||||||
|
var uploadDesc = new BufferDesc
|
||||||
|
{
|
||||||
|
Size = sizeInBytes,
|
||||||
|
Usage = BufferUsage.Upload,
|
||||||
|
MemoryType = ResourceMemoryType.Upload,
|
||||||
|
};
|
||||||
|
|
||||||
|
var uploadHandle = _resourceManager.CreateTransientBuffer(in uploadDesc);
|
||||||
|
|
||||||
|
fixed (T* pData = data)
|
||||||
|
{
|
||||||
|
ResourceDatabase.Map(uploadHandle.AsResource(), 0, null, null, pData, sizeInBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
_directCmd.CopyBuffer(buffer, uploadHandle, 0, 0, sizeInBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Handle<Mesh> CreateMesh(UnsafeList<Vertex> vertices, UnsafeList<uint> indices, bool staticMesh)
|
public Handle<Mesh> CreateMesh(UnsafeList<Vertex> vertices, UnsafeList<uint> indices, bool staticMesh)
|
||||||
{
|
{
|
||||||
var mesh = _resourceManager.CreateMesh(vertices, indices);
|
var mesh = _resourceManager.CreateMesh(vertices, indices);
|
||||||
@@ -98,8 +142,8 @@ public readonly unsafe ref struct RenderingContext
|
|||||||
TransitionBarrier(vertexHandle, false, BarrierLayout.Undefined, BarrierAccess.CopyDest, BarrierSync.Copy);
|
TransitionBarrier(vertexHandle, false, BarrierLayout.Undefined, BarrierAccess.CopyDest, BarrierSync.Copy);
|
||||||
TransitionBarrier(indexHandle, false, BarrierLayout.Undefined, BarrierAccess.CopyDest, BarrierSync.Copy);
|
TransitionBarrier(indexHandle, false, BarrierLayout.Undefined, BarrierAccess.CopyDest, BarrierSync.Copy);
|
||||||
|
|
||||||
_directCmd.UploadBuffer(meshData.VertexBuffer, meshData.Vertices.AsSpan());
|
UploadBuffer(meshData.VertexBuffer, meshData.Vertices.AsSpan());
|
||||||
_directCmd.UploadBuffer(meshData.IndexBuffer, meshData.Indices.AsSpan());
|
UploadBuffer(meshData.IndexBuffer, meshData.Indices.AsSpan());
|
||||||
|
|
||||||
TransitionBarrier(vertexHandle, false, BarrierLayout.Undefined, BarrierAccess.ShaderResource, BarrierSync.VertexShading);
|
TransitionBarrier(vertexHandle, false, BarrierLayout.Undefined, BarrierAccess.ShaderResource, BarrierSync.VertexShading);
|
||||||
TransitionBarrier(indexHandle, false, BarrierLayout.Undefined, BarrierAccess.IndexBuffer, BarrierSync.IndexInput);
|
TransitionBarrier(indexHandle, false, BarrierLayout.Undefined, BarrierAccess.IndexBuffer, BarrierSync.IndexInput);
|
||||||
@@ -147,8 +191,8 @@ public readonly unsafe ref struct RenderingContext
|
|||||||
TransitionBarrier(vertexHandle, false, BarrierLayout.Undefined, BarrierAccess.CopyDest, BarrierSync.Copy);
|
TransitionBarrier(vertexHandle, false, BarrierLayout.Undefined, BarrierAccess.CopyDest, BarrierSync.Copy);
|
||||||
TransitionBarrier(indexHandle, false, BarrierLayout.Undefined, BarrierAccess.CopyDest, BarrierSync.Copy);
|
TransitionBarrier(indexHandle, false, BarrierLayout.Undefined, BarrierAccess.CopyDest, BarrierSync.Copy);
|
||||||
|
|
||||||
_directCmd.UploadBuffer(meshRef.VertexBuffer, meshRef.Vertices.AsSpan());
|
UploadBuffer(meshRef.VertexBuffer, meshRef.Vertices.AsSpan());
|
||||||
_directCmd.UploadBuffer(meshRef.IndexBuffer, meshRef.Indices.AsSpan());
|
UploadBuffer(meshRef.IndexBuffer, meshRef.Indices.AsSpan());
|
||||||
|
|
||||||
TransitionBarrier(vertexHandle, false, BarrierLayout.Undefined, BarrierAccess.ShaderResource, BarrierSync.VertexShading);
|
TransitionBarrier(vertexHandle, false, BarrierLayout.Undefined, BarrierAccess.ShaderResource, BarrierSync.VertexShading);
|
||||||
TransitionBarrier(indexHandle, false, BarrierLayout.Undefined, BarrierAccess.IndexBuffer, BarrierSync.IndexInput);
|
TransitionBarrier(indexHandle, false, BarrierLayout.Undefined, BarrierAccess.IndexBuffer, BarrierSync.IndexInput);
|
||||||
@@ -204,9 +248,9 @@ public readonly unsafe ref struct RenderingContext
|
|||||||
TransitionBarrier(meshRef.MeshletVerticesBuffer.AsResource(), false, BarrierLayout.Undefined, BarrierAccess.CopyDest, BarrierSync.Copy);
|
TransitionBarrier(meshRef.MeshletVerticesBuffer.AsResource(), false, BarrierLayout.Undefined, BarrierAccess.CopyDest, BarrierSync.Copy);
|
||||||
TransitionBarrier(meshRef.MeshletTrianglesBuffer.AsResource(), false, BarrierLayout.Undefined, BarrierAccess.CopyDest, BarrierSync.Copy);
|
TransitionBarrier(meshRef.MeshletTrianglesBuffer.AsResource(), false, BarrierLayout.Undefined, BarrierAccess.CopyDest, BarrierSync.Copy);
|
||||||
|
|
||||||
_directCmd.UploadBuffer(meshRef.MeshLetBuffer, meshletData.meshlets.AsSpan());
|
UploadBuffer(meshRef.MeshLetBuffer, meshletData.meshlets.AsSpan());
|
||||||
_directCmd.UploadBuffer(meshRef.MeshletVerticesBuffer, meshletData.meshletVertices.AsSpan());
|
UploadBuffer(meshRef.MeshletVerticesBuffer, meshletData.meshletVertices.AsSpan());
|
||||||
_directCmd.UploadBuffer(meshRef.MeshletTrianglesBuffer, meshletData.meshletTriangles.AsSpan());
|
UploadBuffer(meshRef.MeshletTrianglesBuffer, meshletData.meshletTriangles.AsSpan());
|
||||||
|
|
||||||
TransitionBarrier(meshRef.MeshLetBuffer.AsResource(), false, BarrierLayout.Undefined, BarrierAccess.ShaderResource, BarrierSync.NonPixelShading | BarrierSync.PixelShading);
|
TransitionBarrier(meshRef.MeshLetBuffer.AsResource(), false, BarrierLayout.Undefined, BarrierAccess.ShaderResource, BarrierSync.NonPixelShading | BarrierSync.PixelShading);
|
||||||
TransitionBarrier(meshRef.MeshletVerticesBuffer.AsResource(), false, BarrierLayout.Undefined, BarrierAccess.ShaderResource, BarrierSync.NonPixelShading | BarrierSync.PixelShading);
|
TransitionBarrier(meshRef.MeshletVerticesBuffer.AsResource(), false, BarrierLayout.Undefined, BarrierAccess.ShaderResource, BarrierSync.NonPixelShading | BarrierSync.PixelShading);
|
||||||
@@ -236,7 +280,7 @@ public readonly unsafe ref struct RenderingContext
|
|||||||
var bufferHandle = meshData.ObjectDataBuffer.AsResource();
|
var bufferHandle = meshData.ObjectDataBuffer.AsResource();
|
||||||
|
|
||||||
TransitionBarrier(bufferHandle, false, BarrierLayout.Undefined, BarrierAccess.CopyDest, BarrierSync.Copy);
|
TransitionBarrier(bufferHandle, false, BarrierLayout.Undefined, BarrierAccess.CopyDest, BarrierSync.Copy);
|
||||||
_directCmd.UploadBuffer(meshData.ObjectDataBuffer, data);
|
UploadBuffer(meshData.ObjectDataBuffer, data);
|
||||||
TransitionBarrier(bufferHandle, false, BarrierLayout.Undefined, BarrierAccess.ShaderResource, BarrierSync.PixelShading | BarrierSync.NonPixelShading);
|
TransitionBarrier(bufferHandle, false, BarrierLayout.Undefined, BarrierAccess.ShaderResource, BarrierSync.PixelShading | BarrierSync.NonPixelShading);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,16 +296,29 @@ public readonly unsafe ref struct RenderingContext
|
|||||||
public void UploadTexture<T>(Handle<GPUTexture> texture, ReadOnlySpan<T> data)
|
public void UploadTexture<T>(Handle<GPUTexture> texture, ReadOnlySpan<T> data)
|
||||||
where T : unmanaged
|
where T : unmanaged
|
||||||
{
|
{
|
||||||
var desc = ResourceDatabase.GetResourceDescription(texture.AsResource()).GetValueOrThrow();
|
|
||||||
|
|
||||||
//var size = ResourceAllocator.GetSizeInfo(desc).Size;
|
//var size = ResourceAllocator.GetSizeInfo(desc).Size;
|
||||||
//if ((ulong)(data.Length * sizeof(T)) != ResourceAllocator.GetSizeInfo(desc).Size)
|
//if ((ulong)(data.Length * sizeof(T)) != ResourceAllocator.GetSizeInfo(desc).Size)
|
||||||
//{
|
//{
|
||||||
// throw new ArgumentException("Data size does not match texture size.");
|
// throw new ArgumentException("Data size does not match texture size.");
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
var desc = ResourceDatabase.GetResourceDescription(texture.AsResource()).GetValueOrThrow();
|
||||||
desc.TextureDescription.Format.GetSurfaceInfo(desc.TextureDescription.Width, desc.TextureDescription.Height, out var rowPitch, out var slicePitch, out _);
|
desc.TextureDescription.Format.GetSurfaceInfo(desc.TextureDescription.Width, desc.TextureDescription.Height, out var rowPitch, out var slicePitch, out _);
|
||||||
|
|
||||||
|
var requiredSize = ResourceDatabase.GetIntermediateResourceSize(texture.AsResource(), 0, 1);
|
||||||
|
var uploadDesc = new BufferDesc
|
||||||
|
{
|
||||||
|
Size = requiredSize,
|
||||||
|
Usage = BufferUsage.Upload,
|
||||||
|
MemoryType = ResourceMemoryType.Upload,
|
||||||
|
};
|
||||||
|
|
||||||
|
var uploadHandle = _resourceManager.CreateTransientBuffer(in uploadDesc);
|
||||||
|
if (uploadHandle.IsInvalid)
|
||||||
|
{
|
||||||
|
throw new OutOfMemoryException("Failed to create upload buffer for texture data.");
|
||||||
|
}
|
||||||
|
|
||||||
TransitionBarrier(texture.AsResource(), true, BarrierLayout.CopyDest, BarrierAccess.CopyDest, BarrierSync.Copy);
|
TransitionBarrier(texture.AsResource(), true, BarrierLayout.CopyDest, BarrierAccess.CopyDest, BarrierSync.Copy);
|
||||||
|
|
||||||
fixed (T* pData = data)
|
fixed (T* pData = data)
|
||||||
@@ -273,7 +330,7 @@ public readonly unsafe ref struct RenderingContext
|
|||||||
slicePitch = slicePitch
|
slicePitch = slicePitch
|
||||||
};
|
};
|
||||||
|
|
||||||
_directCmd.UploadTexture(texture, subresourceData);
|
_directCmd.UpdateSubResources(texture.AsResource(), uploadHandle.AsResource(), subresourceData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ using System.Diagnostics;
|
|||||||
namespace Ghost.Graphics.RenderGraphModule;
|
namespace Ghost.Graphics.RenderGraphModule;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Main render graph class that manages resource allocation and pass execution.
|
/// Main render graph class that manages heap allocation and pass execution.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class RenderGraph : IDisposable
|
public sealed class RenderGraph : IDisposable
|
||||||
{
|
{
|
||||||
@@ -125,7 +125,7 @@ public sealed class RenderGraph : IDisposable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="buffer">The external buffer handle.</param>
|
/// <param name="buffer">The external buffer handle.</param>
|
||||||
/// <returns>The identifier of the imported render graph buffer. Invalid if import fails.</returns>
|
/// <returns>The identifier of the imported render graph buffer. Invalid if import fails.</returns>
|
||||||
public Identifier<RGBuffer> ImportBuffer(Handle<RHI.GPUBuffer> buffer, string name)
|
public Identifier<RGBuffer> ImportBuffer(Handle<GPUBuffer> buffer, string name)
|
||||||
{
|
{
|
||||||
var r = _resourceDatabase.GetResourceDescription(buffer.AsResource());
|
var r = _resourceDatabase.GetResourceDescription(buffer.AsResource());
|
||||||
if (r.IsFailure)
|
if (r.IsFailure)
|
||||||
@@ -178,7 +178,7 @@ public sealed class RenderGraph : IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Compiles the render graph by culling unused passes and determining resource lifetimes.
|
/// Compiles the render graph by culling unused passes and determining heap lifetimes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Error Compile(ViewState viewState)
|
public Error Compile(ViewState viewState)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ internal struct MemoryBlock
|
|||||||
public bool isFree;
|
public bool isFree;
|
||||||
public int firstUsePass;
|
public int firstUsePass;
|
||||||
public int lastUsePass;
|
public int lastUsePass;
|
||||||
public int logicalResourceIndex; // Which logical resource is currently using this block
|
public int logicalResourceIndex; // Which logical heap is currently using this block
|
||||||
|
|
||||||
public MemoryBlock(ulong offset, ulong size)
|
public MemoryBlock(ulong offset, ulong size)
|
||||||
{
|
{
|
||||||
@@ -267,7 +267,7 @@ internal sealed class ResourceAliasingManager
|
|||||||
|
|
||||||
private readonly ResourceHeap _heap;
|
private readonly ResourceHeap _heap;
|
||||||
private readonly List<PlacedResource> _placedResources;
|
private readonly List<PlacedResource> _placedResources;
|
||||||
// Mapping from logical resource index to placed resource index
|
// Mapping from logical heap index to placed heap index
|
||||||
private readonly Dictionary<int, int> _logicalToPlaced;
|
private readonly Dictionary<int, int> _logicalToPlaced;
|
||||||
|
|
||||||
private const ulong _DEFAULT_TEXTURE_ALIGNMENT = 65536; // 64KB
|
private const ulong _DEFAULT_TEXTURE_ALIGNMENT = 65536; // 64KB
|
||||||
@@ -373,7 +373,7 @@ internal sealed class ResourceAliasingManager
|
|||||||
_heap.size = peakMemoryUsage;
|
_heap.size = peakMemoryUsage;
|
||||||
_heap.Reset();
|
_heap.Reset();
|
||||||
|
|
||||||
// Allocate each logical resource in the heap
|
// Allocate each logical heap in the heap
|
||||||
foreach (var (logicalIndex, logicalResource) in logicalResources)
|
foreach (var (logicalIndex, logicalResource) in logicalResources)
|
||||||
{
|
{
|
||||||
// TODO: Currently we are recalculating the aliasing candidates in the real allocation pass.
|
// TODO: Currently we are recalculating the aliasing candidates in the real allocation pass.
|
||||||
@@ -414,7 +414,7 @@ internal sealed class ResourceAliasingManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Second pass: Populate aliasedLogicalResources lists
|
// Second pass: Populate aliasedLogicalResources lists
|
||||||
// For each placed resource, find all OTHER placed resources at the same offset
|
// For each placed heap, find all OTHER placed resources at the same offset
|
||||||
for (var i = 0; i < _placedResources.Count; i++)
|
for (var i = 0; i < _placedResources.Count; i++)
|
||||||
{
|
{
|
||||||
var placed = _placedResources[i];
|
var placed = _placedResources[i];
|
||||||
@@ -432,7 +432,7 @@ internal sealed class ResourceAliasingManager
|
|||||||
// Check if they're at the same offset
|
// Check if they're at the same offset
|
||||||
if (other.heapOffset == placed.heapOffset)
|
if (other.heapOffset == placed.heapOffset)
|
||||||
{
|
{
|
||||||
// Add the other's logical resource to this one's aliased list
|
// Add the other's logical heap to this one's aliased list
|
||||||
var otherLogicalIndex = other.aliasedLogicalResources[0]; // Each has exactly one at this point
|
var otherLogicalIndex = other.aliasedLogicalResources[0]; // Each has exactly one at this point
|
||||||
if (!placed.aliasedLogicalResources.Contains(otherLogicalIndex))
|
if (!placed.aliasedLogicalResources.Contains(otherLogicalIndex))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ internal enum BarrierFlags
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a resource barrier requirement that needs to be resolved at runtime.
|
/// Represents a heap barrier requirement that needs to be resolved at runtime.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal struct ResourceBarrier
|
internal struct ResourceBarrier
|
||||||
{
|
{
|
||||||
@@ -55,7 +55,7 @@ internal struct ResourceBarrier
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tracks the current state of a resource across passes during compilation.
|
/// Tracks the current state of a heap across passes during compilation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal sealed class ResourceStateTracker
|
internal sealed class ResourceStateTracker
|
||||||
{
|
{
|
||||||
@@ -124,7 +124,7 @@ internal static class RenderGraphBarriers
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Inserts aliasing barriers when a placed resource is reused.
|
/// Inserts aliasing barriers when a placed heap is reused.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static void InsertAliasingBarriers(
|
private static void InsertAliasingBarriers(
|
||||||
RenderGraphPassBase pass,
|
RenderGraphPassBase pass,
|
||||||
@@ -148,20 +148,20 @@ internal static class RenderGraphBarriers
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this is the first use of this logical resource
|
// Check if this is the first use of this logical heap
|
||||||
if (resource.firstUsePass == pass.index)
|
if (resource.firstUsePass == pass.index)
|
||||||
{
|
{
|
||||||
// Get the placed resource
|
// Get the placed heap
|
||||||
var placedIndex = aliasingManager.GetPlacedResourceIndex(id.Value);
|
var placedIndex = aliasingManager.GetPlacedResourceIndex(id.Value);
|
||||||
if (placedIndex >= 0)
|
if (placedIndex >= 0)
|
||||||
{
|
{
|
||||||
var placed = aliasingManager.GetPlacedResource(placedIndex);
|
var placed = aliasingManager.GetPlacedResource(placedIndex);
|
||||||
|
|
||||||
// If this placed resource has multiple aliased resources,
|
// If this placed heap has multiple aliased resources,
|
||||||
// we need an aliasing barrier when switching between them
|
// we need an aliasing barrier when switching between them
|
||||||
if (placed != null && placed.aliasedLogicalResources.Count > 1)
|
if (placed != null && placed.aliasedLogicalResources.Count > 1)
|
||||||
{
|
{
|
||||||
// Find the resource that used this placed memory most recently before this pass
|
// Find the heap that used this placed memory most recently before this pass
|
||||||
Identifier<RGResource> resourceBefore = default;
|
Identifier<RGResource> resourceBefore = default;
|
||||||
var mostRecentLastUse = -1;
|
var mostRecentLastUse = -1;
|
||||||
|
|
||||||
@@ -169,10 +169,10 @@ internal static class RenderGraphBarriers
|
|||||||
{
|
{
|
||||||
if (otherLogicalIndex != id.Value)
|
if (otherLogicalIndex != id.Value)
|
||||||
{
|
{
|
||||||
// Get resource by global index
|
// Get heap by global index
|
||||||
var otherResource = resources.GetResourceByIndex(otherLogicalIndex);
|
var otherResource = resources.GetResourceByIndex(otherLogicalIndex);
|
||||||
|
|
||||||
// Check if this resource finished before our resource starts
|
// Check if this heap finished before our heap starts
|
||||||
if (otherResource.lastUsePass < pass.index &&
|
if (otherResource.lastUsePass < pass.index &&
|
||||||
otherResource.lastUsePass > mostRecentLastUse)
|
otherResource.lastUsePass > mostRecentLastUse)
|
||||||
{
|
{
|
||||||
@@ -182,7 +182,7 @@ internal static class RenderGraphBarriers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we found a previous resource, insert aliasing barrier
|
// If we found a previous heap, insert aliasing barrier
|
||||||
if (mostRecentLastUse >= 0)
|
if (mostRecentLastUse >= 0)
|
||||||
{
|
{
|
||||||
// Aliasing Requirement: Transition to Undefined, Sync with Predecessor
|
// Aliasing Requirement: Transition to Undefined, Sync with Predecessor
|
||||||
@@ -215,7 +215,7 @@ internal static class RenderGraphBarriers
|
|||||||
List<CompiledBarrier> compiledBarriers,
|
List<CompiledBarrier> compiledBarriers,
|
||||||
RenderGraphResourceRegistry resources)
|
RenderGraphResourceRegistry resources)
|
||||||
{
|
{
|
||||||
// Helper to add a compiled barrier for a resource transition
|
// Helper to add a compiled barrier for a heap transition
|
||||||
void AddTransition(Identifier<RGResource> id, ResourceBarrierData targetState)
|
void AddTransition(Identifier<RGResource> id, ResourceBarrierData targetState)
|
||||||
{
|
{
|
||||||
var resource = resources.GetResource(id);
|
var resource = resources.GetResource(id);
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public enum ResourceExtractionFlags : byte
|
|||||||
{
|
{
|
||||||
None = 0,
|
None = 0,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Releases the old resource after extraction.
|
/// Releases the old heap after extraction.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ReleaseAfterExtract = 1 << 0,
|
ReleaseAfterExtract = 1 << 0,
|
||||||
}
|
}
|
||||||
@@ -36,19 +36,19 @@ public interface IRenderGraphBuilder : IDisposable
|
|||||||
void AllowPassCulling(bool value);
|
void AllowPassCulling(bool value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new texture resource based on the specified desc.
|
/// Creates a new texture heap based on the specified desc.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="desc">A structure that defines the properties and configuration of the texture to create.</param>
|
/// <param name="desc">A structure that defines the properties and configuration of the texture to create.</param>
|
||||||
/// <param name="name">The name of the texture resource.</param>
|
/// <param name="name">The name of the texture heap.</param>
|
||||||
/// <returns>An identifier for the newly created texture resource.</returns>
|
/// <returns>An identifier for the newly created texture heap.</returns>
|
||||||
Identifier<RGTexture> CreateTexture(in RGTextureDesc desc, string name);
|
Identifier<RGTexture> CreateTexture(in RGTextureDesc desc, string name);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new buffer resource based on the specified desc.
|
/// Creates a new buffer heap based on the specified desc.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="desc">A structure that defines the properties and configuration of the buffer to create.</param>
|
/// <param name="desc">A structure that defines the properties and configuration of the buffer to create.</param>
|
||||||
/// <param name="name">The name of the buffer resource.</param>
|
/// <param name="name">The name of the buffer heap.</param>
|
||||||
/// <returns>An identifier for the newly created buffer resource.</returns>
|
/// <returns>An identifier for the newly created buffer heap.</returns>
|
||||||
Identifier<RGBuffer> CreateBuffer(in BufferDesc desc, string name);
|
Identifier<RGBuffer> CreateBuffer(in BufferDesc desc, string name);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -69,17 +69,17 @@ public interface IRenderGraphBuilder : IDisposable
|
|||||||
Identifier<RGBuffer> UseBuffer(Identifier<RGBuffer> buffer, AccessFlags accessMode);
|
Identifier<RGBuffer> UseBuffer(Identifier<RGBuffer> buffer, AccessFlags accessMode);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Extracts the actual texture resource associated with the given identifier for use in outside of the render graph execution context.
|
/// Extracts the actual texture heap associated with the given identifier for use in outside of the render graph execution context.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="src">The identifier of the texture to be extracted.</param>
|
/// <param name="src">The identifier of the texture to be extracted.</param>
|
||||||
/// <param name="dst">A handle to receive the actual GPU texture resource.</param>
|
/// <param name="dst">A handle to receive the actual GPU texture heap.</param>
|
||||||
void QueryTextureExtraction(Identifier<RGTexture> src, Handle<GPUTexture> dst, ResourceExtractionFlags flags = ResourceExtractionFlags.ReleaseAfterExtract);
|
void QueryTextureExtraction(Identifier<RGTexture> src, Handle<GPUTexture> dst, ResourceExtractionFlags flags = ResourceExtractionFlags.ReleaseAfterExtract);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Extracts the actual buffer resource associated with the given identifier for use in outside of the render graph execution context.
|
/// Extracts the actual buffer heap associated with the given identifier for use in outside of the render graph execution context.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="src">The identifier of the buffer to be extracted.</param>
|
/// <param name="src">The identifier of the buffer to be extracted.</param>
|
||||||
/// <param name="dst">A handle to receive the actual GPU buffer resource.</param>
|
/// <param name="dst">A handle to receive the actual GPU buffer heap.</param>
|
||||||
void QueryBufferExtraction(Identifier<RGBuffer> src, Handle<GPUBuffer> dst, ResourceExtractionFlags flags = ResourceExtractionFlags.ReleaseAfterExtract);
|
void QueryBufferExtraction(Identifier<RGBuffer> src, Handle<GPUBuffer> dst, ResourceExtractionFlags flags = ResourceExtractionFlags.ReleaseAfterExtract);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +94,7 @@ public interface IRasterRenderGraphBuilder : IRenderGraphBuilder
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Specifies that the given buffer will be used for random access operations with the specified access mode within the current context.
|
/// Specifies that the given buffer will be used for random access operations with the specified access mode within the current context.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="buffer">An identifier for the buffer to be used for random access. Must reference a valid buffer resource.</param>
|
/// <param name="buffer">An identifier for the buffer to be used for random access. Must reference a valid buffer heap.</param>
|
||||||
/// <returns>An identifier for the buffer.</returns>
|
/// <returns>An identifier for the buffer.</returns>
|
||||||
Identifier<RGBuffer> UseRandomAccessBuffer(Identifier<RGBuffer> buffer);
|
Identifier<RGBuffer> UseRandomAccessBuffer(Identifier<RGBuffer> buffer);
|
||||||
|
|
||||||
@@ -150,7 +150,7 @@ public interface IUnsafeRenderGraphBuilder : IRenderGraphBuilder
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Specifies that the given buffer will be used for random access operations with the specified access mode within the current context.
|
/// Specifies that the given buffer will be used for random access operations with the specified access mode within the current context.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="buffer">An identifier for the buffer to be used for random access. Must reference a valid buffer resource.</param>
|
/// <param name="buffer">An identifier for the buffer to be used for random access. Must reference a valid buffer heap.</param>
|
||||||
/// <returns>An identifier for the buffer.</returns>
|
/// <returns>An identifier for the buffer.</returns>
|
||||||
Identifier<RGBuffer> UseRandomAccessBuffer(Identifier<RGBuffer> buffer);
|
Identifier<RGBuffer> UseRandomAccessBuffer(Identifier<RGBuffer> buffer);
|
||||||
|
|
||||||
|
|||||||
@@ -16,16 +16,16 @@ internal sealed class CachedCompilation
|
|||||||
// Culling decisions for each pass
|
// Culling decisions for each pass
|
||||||
public readonly List<bool> passCulledFlags = new(64);
|
public readonly List<bool> passCulledFlags = new(64);
|
||||||
|
|
||||||
// Physical resource aliasing mappings (logical index -> physical index)
|
// Physical heap aliasing mappings (logical index -> physical index)
|
||||||
public readonly Dictionary<int, int> logicalToPhysical = new(128);
|
public readonly Dictionary<int, int> logicalToPhysical = new(128);
|
||||||
|
|
||||||
// Placed resource metadata
|
// Placed heap metadata
|
||||||
public readonly List<PlacedResourceData> placedResources = new(32);
|
public readonly List<PlacedResourceData> placedResources = new(32);
|
||||||
|
|
||||||
// Compiled barriers (stores only target states, queries before state from ResourceManager)
|
// Compiled barriers (stores only target states, queries before state from ResourceManager)
|
||||||
public readonly List<CompiledBarrier> compiledBarriers = new(128);
|
public readonly List<CompiledBarrier> compiledBarriers = new(128);
|
||||||
|
|
||||||
// Real gpu resource
|
// Real gpu heap
|
||||||
public readonly List<Handle<GPUResource>> backingResources = new(32);
|
public readonly List<Handle<GPUResource>> backingResources = new(32);
|
||||||
|
|
||||||
// View state used for this compilation
|
// View state used for this compilation
|
||||||
@@ -44,7 +44,7 @@ internal sealed class CachedCompilation
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Placed resource data for caching.
|
/// Placed heap data for caching.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal struct PlacedResourceData
|
internal struct PlacedResourceData
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Ghost.Graphics.RHI;
|
|||||||
namespace Ghost.Graphics.RenderGraphModule;
|
namespace Ghost.Graphics.RenderGraphModule;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles compilation of the render graph including pass culling, resource allocation,
|
/// Handles compilation of the render graph including pass culling, heap allocation,
|
||||||
/// barrier compilation, and cache management.
|
/// barrier compilation, and cache management.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal sealed class RenderGraphCompiler
|
internal sealed class RenderGraphCompiler
|
||||||
@@ -229,7 +229,7 @@ internal sealed class RenderGraphCompiler
|
|||||||
{
|
{
|
||||||
Size = _aliasingManager.Heap.size + 64 * 1024, // Add 64KB padding to avoid potential overflows
|
Size = _aliasingManager.Heap.size + 64 * 1024, // Add 64KB padding to avoid potential overflows
|
||||||
Alignment = ResourceHeap.DEFAULT_ALIGNMENT,
|
Alignment = ResourceHeap.DEFAULT_ALIGNMENT,
|
||||||
HeapFlags = HeapFlags.AlowBufferAndTexture,
|
HeapFlags = HeapFlags.AllowAllBufferAndTexture,
|
||||||
HeapType = HeapType.Default
|
HeapType = HeapType.Default
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ public interface IRenderGraphContext
|
|||||||
|
|
||||||
Handle<GPUResource> GetActualResource(Identifier<RGResource> resource);
|
Handle<GPUResource> GetActualResource(Identifier<RGResource> resource);
|
||||||
Handle<GPUTexture> GetActualTexture(Identifier<RGTexture> texture);
|
Handle<GPUTexture> GetActualTexture(Identifier<RGTexture> texture);
|
||||||
Handle<RHI.GPUBuffer> GetActualBuffer(Identifier<RGBuffer> buffer);
|
Handle<GPUBuffer> GetActualBuffer(Identifier<RGBuffer> buffer);
|
||||||
|
|
||||||
Handle<GPUTexture> GetHistoryTexture(ReadOnlySpan<Identifier<RGTexture>> texture, int historyOffset);
|
Handle<GPUTexture> GetHistoryTexture(ReadOnlySpan<Identifier<RGTexture>> texture, int historyOffset);
|
||||||
Handle<RHI.GPUBuffer> GetHistoryBuffer(ReadOnlySpan<Identifier<RGBuffer>> buffer, int historyOffset);
|
Handle<GPUBuffer> GetHistoryBuffer(ReadOnlySpan<Identifier<RGBuffer>> buffer, int historyOffset);
|
||||||
|
|
||||||
ICommandBuffer GetCommandBufferUnsafe();
|
ICommandBuffer GetCommandBufferUnsafe();
|
||||||
}
|
}
|
||||||
@@ -60,8 +60,8 @@ internal sealed class RenderGraphContext : IUnsafeRenderContext
|
|||||||
private TextureFormat _dsvFormat;
|
private TextureFormat _dsvFormat;
|
||||||
private int _rtvCount;
|
private int _rtvCount;
|
||||||
|
|
||||||
private Handle<RHI.GPUBuffer> _activePerMaterialData;
|
private Handle<GPUBuffer> _activePerMaterialData;
|
||||||
private Handle<RHI.GPUBuffer> _activePerMeshData;
|
private Handle<GPUBuffer> _activePerMeshData;
|
||||||
private int _activeMeshIndexCount;
|
private int _activeMeshIndexCount;
|
||||||
|
|
||||||
private uint _activeFrameBuffer;
|
private uint _activeFrameBuffer;
|
||||||
@@ -120,9 +120,9 @@ internal sealed class RenderGraphContext : IUnsafeRenderContext
|
|||||||
return _resources.GetResource(texture.AsResource()).backingResource.AsTexture();
|
return _resources.GetResource(texture.AsResource()).backingResource.AsTexture();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Handle<RHI.GPUBuffer> GetActualBuffer(Identifier<RGBuffer> buffer)
|
public Handle<GPUBuffer> GetActualBuffer(Identifier<RGBuffer> buffer)
|
||||||
{
|
{
|
||||||
return _resources.GetResource(buffer.AsResource()).backingResource.AsGraphicsBuffer();
|
return _resources.GetResource(buffer.AsResource()).backingResource.AsBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Handle<GPUTexture> GetHistoryTexture(ReadOnlySpan<Identifier<RGTexture>> textures, int historyOffset)
|
public Handle<GPUTexture> GetHistoryTexture(ReadOnlySpan<Identifier<RGTexture>> textures, int historyOffset)
|
||||||
@@ -141,11 +141,11 @@ internal sealed class RenderGraphContext : IUnsafeRenderContext
|
|||||||
return GetActualTexture(textures[index]);
|
return GetActualTexture(textures[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Handle<RHI.GPUBuffer> GetHistoryBuffer(ReadOnlySpan<Identifier<RGBuffer>> buffers, int historyOffset)
|
public Handle<GPUBuffer> GetHistoryBuffer(ReadOnlySpan<Identifier<RGBuffer>> buffers, int historyOffset)
|
||||||
{
|
{
|
||||||
if (historyOffset < 0 || historyOffset >= buffers.Length)
|
if (historyOffset < 0 || historyOffset >= buffers.Length)
|
||||||
{
|
{
|
||||||
return Handle<RHI.GPUBuffer>.Invalid;
|
return Handle<GPUBuffer>.Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
var index = (int)(_frameIndex % buffers.Length) - historyOffset;
|
var index = (int)(_frameIndex % buffers.Length) - historyOffset;
|
||||||
@@ -172,7 +172,7 @@ internal sealed class RenderGraphContext : IUnsafeRenderContext
|
|||||||
var r = _resourceManager.GetMaterialReference(material);
|
var r = _resourceManager.GetMaterialReference(material);
|
||||||
if (r.IsFailure)
|
if (r.IsFailure)
|
||||||
{
|
{
|
||||||
_activePerMaterialData = Handle<RHI.GPUBuffer>.Invalid;
|
_activePerMaterialData = Handle<GPUBuffer>.Invalid;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,7 +185,7 @@ internal sealed class RenderGraphContext : IUnsafeRenderContext
|
|||||||
var shaderResult = _resourceManager.GetShaderReference(material.Shader);
|
var shaderResult = _resourceManager.GetShaderReference(material.Shader);
|
||||||
if (shaderResult.IsFailure)
|
if (shaderResult.IsFailure)
|
||||||
{
|
{
|
||||||
_activePerMaterialData = Handle<RHI.GPUBuffer>.Invalid;
|
_activePerMaterialData = Handle<GPUBuffer>.Invalid;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,7 +230,7 @@ internal sealed class RenderGraphContext : IUnsafeRenderContext
|
|||||||
var r = _resourceManager.GetMeshReference(mesh);
|
var r = _resourceManager.GetMeshReference(mesh);
|
||||||
if (r.IsFailure)
|
if (r.IsFailure)
|
||||||
{
|
{
|
||||||
_activePerMeshData = Handle<RHI.GPUBuffer>.Invalid;
|
_activePerMeshData = Handle<GPUBuffer>.Invalid;
|
||||||
_activeMeshIndexCount = 0;
|
_activeMeshIndexCount = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ internal static class RenderGraphHasher
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Computes a hash of a texture resource's structural properties.
|
/// Computes a hash of a texture heap's structural properties.
|
||||||
/// For imported textures, hashes the backing handle.
|
/// For imported textures, hashes the backing handle.
|
||||||
/// For transient textures, hashes the descriptor (respecting size mode).
|
/// For transient textures, hashes the descriptor (respecting size mode).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -94,7 +94,7 @@ internal static class RenderGraphHasher
|
|||||||
// Hash imported flag
|
// Hash imported flag
|
||||||
writer.Write(resource.isImported);
|
writer.Write(resource.isImported);
|
||||||
|
|
||||||
// For imported textures, hash the backing resource handle
|
// For imported textures, hash the backing heap handle
|
||||||
if (resource.isImported)
|
if (resource.isImported)
|
||||||
{
|
{
|
||||||
writer.Write(resource.backingResource.GetHashCode());
|
writer.Write(resource.backingResource.GetHashCode());
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ internal sealed class RenderGraphNativePassBuilder
|
|||||||
{
|
{
|
||||||
var laterPass = compiledPasses[passB];
|
var laterPass = compiledPasses[passB];
|
||||||
|
|
||||||
// Build a set of render target resource IDs (color + depth)
|
// Build a set of render target heap IDs (color + depth)
|
||||||
var renderTargets = new HashSet<Identifier<RGResource>>();
|
var renderTargets = new HashSet<Identifier<RGResource>>();
|
||||||
for (var i = 0; i <= laterPass.maxColorIndex; i++)
|
for (var i = 0; i <= laterPass.maxColorIndex; i++)
|
||||||
{
|
{
|
||||||
@@ -241,7 +241,7 @@ internal sealed class RenderGraphNativePassBuilder
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Infers optimal load/store operations for all attachments in a native render pass.
|
/// Infers optimal load/store operations for all attachments in a native render pass.
|
||||||
/// Uses resource lifetime information to minimize memory bandwidth (critical for TBDR GPUs).
|
/// Uses heap lifetime information to minimize memory bandwidth (critical for TBDR GPUs).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void InferLoadStoreOps(NativeRenderPass nativePass)
|
private void InferLoadStoreOps(NativeRenderPass nativePass)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ internal sealed class RenderGraphResourceRegistry
|
|||||||
return new Identifier<RGTexture>(resource.index);
|
return new Identifier<RGTexture>(resource.index);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Identifier<RGBuffer> ImportBuffer(ref readonly BufferDesc desc, Handle<RHI.GPUBuffer> buffer, string name)
|
public Identifier<RGBuffer> ImportBuffer(ref readonly BufferDesc desc, Handle<GPUBuffer> buffer, string name)
|
||||||
{
|
{
|
||||||
var resource = _pool.Rent<RenderGraphResource>();
|
var resource = _pool.Rent<RenderGraphResource>();
|
||||||
resource.name = name;
|
resource.name = name;
|
||||||
@@ -256,7 +256,7 @@ internal sealed class RenderGraphResourceRegistry
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets resource by global index. Use this when iterating over all resources.
|
/// Gets heap by global index. Use this when iterating over all resources.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public RenderGraphResource GetResourceByIndex(int index)
|
public RenderGraphResource GetResourceByIndex(int index)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ public class RenderSystem : IDisposable
|
|||||||
throw new NotSupportedException($"The specified graphics API '{desc.GraphicsAPI}' is not supported.");
|
throw new NotSupportedException($"The specified graphics API '{desc.GraphicsAPI}' is not supported.");
|
||||||
}
|
}
|
||||||
|
|
||||||
_resourceManager = new ResourceManager(_graphicsEngine.ResourceAllocator, _graphicsEngine.ResourceDatabase);
|
_resourceManager = new ResourceManager(_graphicsEngine.Device, _graphicsEngine.ResourceAllocator, _graphicsEngine.ResourceDatabase);
|
||||||
_swapChainManager = new SwapChainManager(_graphicsEngine);
|
_swapChainManager = new SwapChainManager(_graphicsEngine);
|
||||||
|
|
||||||
// Create frame resources for synchronization
|
// Create frame resources for synchronization
|
||||||
@@ -246,7 +246,7 @@ public class RenderSystem : IDisposable
|
|||||||
var flushFence = _graphicsEngine.Device.GraphicsQueue.Signal(_gpuFenceValue);
|
var flushFence = _graphicsEngine.Device.GraphicsQueue.Signal(_gpuFenceValue);
|
||||||
_graphicsEngine.Device.GraphicsQueue.WaitForValue(flushFence);
|
_graphicsEngine.Device.GraphicsQueue.WaitForValue(flushFence);
|
||||||
|
|
||||||
// Sync the current frame resource to this new fence to keep state consistent
|
// Sync the current frame heap to this new fence to keep state consistent
|
||||||
frameResource.FenceValue = flushFence;
|
frameResource.FenceValue = flushFence;
|
||||||
|
|
||||||
foreach (var resource in _frameResources)
|
foreach (var resource in _frameResources)
|
||||||
@@ -322,7 +322,7 @@ public class RenderSystem : IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// End the frame and present
|
// End the frame and present
|
||||||
_resourceManager.EndFrame(_cpuFenceValue);
|
_resourceManager.EndFrame(_gpuFenceValue);
|
||||||
r = _graphicsEngine.EndFrame(_gpuFenceValue);
|
r = _graphicsEngine.EndFrame(_gpuFenceValue);
|
||||||
|
|
||||||
if (r.IsFailure)
|
if (r.IsFailure)
|
||||||
@@ -426,9 +426,11 @@ public class RenderSystem : IDisposable
|
|||||||
|
|
||||||
_renderPipeline.Dispose();
|
_renderPipeline.Dispose();
|
||||||
|
|
||||||
_swapChainManager.Dispose();
|
|
||||||
_resourceManager.Dispose();
|
_resourceManager.Dispose();
|
||||||
_graphicsEngine.Dispose();
|
_graphicsEngine.Dispose();
|
||||||
|
|
||||||
|
_swapChainManager.Dispose();
|
||||||
|
|
||||||
_shutdownEvent.Dispose();
|
_shutdownEvent.Dispose();
|
||||||
|
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
|
|||||||
271
src/Runtime/Ghost.Graphics/ResourceManager.Pool.cs
Normal file
271
src/Runtime/Ghost.Graphics/ResourceManager.Pool.cs
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
using Ghost.Core;
|
||||||
|
using Ghost.Graphics.RHI;
|
||||||
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Ghost.Graphics;
|
||||||
|
|
||||||
|
public partial class ResourceManager
|
||||||
|
{
|
||||||
|
private const ulong _DEFAULT_TRANSIENT_PAGE_SIZE = 16 * 1024 * 1024; // 16MB
|
||||||
|
|
||||||
|
private struct Page
|
||||||
|
{
|
||||||
|
public Handle<GPUResource> heap;
|
||||||
|
public ulong offset;
|
||||||
|
|
||||||
|
public HeapFlags heapFlags;
|
||||||
|
public HeapType heapType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct RetiringPage
|
||||||
|
{
|
||||||
|
public Page page;
|
||||||
|
public ulong retireFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
private UnsafeList<Page> _activePages;
|
||||||
|
private UnsafeQueue<RetiringPage> _retiringPages;
|
||||||
|
|
||||||
|
private UnsafeList<Handle<GPUResource>> _oversizedTransientResources;
|
||||||
|
|
||||||
|
private void InitializePool()
|
||||||
|
{
|
||||||
|
_activePages = new UnsafeList<Page>(4, Allocator.Persistent);
|
||||||
|
_retiringPages = new UnsafeQueue<RetiringPage>(4, Allocator.Persistent);
|
||||||
|
_oversizedTransientResources = new UnsafeList<Handle<GPUResource>>(4, Allocator.Persistent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Error CreateNewActivePage(HeapType heapType, HeapFlags heapFlags)
|
||||||
|
{
|
||||||
|
var allocationDesc = new AllocationDesc
|
||||||
|
{
|
||||||
|
Size = _DEFAULT_TRANSIENT_PAGE_SIZE,
|
||||||
|
Alignment = 65536, // 64KB
|
||||||
|
HeapType = heapType,
|
||||||
|
HeapFlags = heapFlags,
|
||||||
|
};
|
||||||
|
|
||||||
|
var buffer = _resourceAllocator.Allocate(in allocationDesc, $"Page {_activePages.Count + _retiringPages.Count}");
|
||||||
|
if (buffer.IsInvalid)
|
||||||
|
{
|
||||||
|
return Error.OutOfMemory;
|
||||||
|
}
|
||||||
|
|
||||||
|
_activePages.Add(new Page
|
||||||
|
{
|
||||||
|
heap = buffer,
|
||||||
|
offset = 0,
|
||||||
|
heapFlags = heapFlags,
|
||||||
|
heapType = heapType,
|
||||||
|
});
|
||||||
|
|
||||||
|
return Error.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Handle<GPUTexture> CreateTransientTexture(ref readonly TextureDesc desc, string? name = null)
|
||||||
|
{
|
||||||
|
var isRTOrDS = desc.Usage.HasFlag(TextureUsage.DepthStencil) || desc.Usage.HasFlag(TextureUsage.RenderTarget);
|
||||||
|
var size = _resourceAllocator.GetSizeInfo(ResourceDesc.Texture(desc));
|
||||||
|
|
||||||
|
if (size.Size > _DEFAULT_TRANSIENT_PAGE_SIZE)
|
||||||
|
{
|
||||||
|
var texHandle = _resourceAllocator.CreateTexture(in desc, name);
|
||||||
|
if (texHandle.IsValid)
|
||||||
|
{
|
||||||
|
_oversizedTransientResources.Add(texHandle.AsResource());
|
||||||
|
}
|
||||||
|
|
||||||
|
return texHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
var requiredHeapFlags = _renderDevice.FeatureSupport.HasFlag(FeatureSupport.AliasBuffersAndTextures) ?
|
||||||
|
HeapFlags.AllowAllBufferAndTexture :
|
||||||
|
isRTOrDS ? HeapFlags.AllowOnlyRTAndDS : HeapFlags.AllowOnlyTextures;
|
||||||
|
|
||||||
|
var foundPageIndex = -1;
|
||||||
|
var alignedOffset = 0UL;
|
||||||
|
|
||||||
|
for (var i = 0; i < _activePages.Count; i++)
|
||||||
|
{
|
||||||
|
ref var p = ref _activePages[i];
|
||||||
|
|
||||||
|
if (p.heapType != HeapType.Default)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.heapFlags != requiredHeapFlags && p.heapFlags != HeapFlags.AllowAllBufferAndTexture)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var proposedOffset = (p.offset + (size.Alignment - 1)) & ~(size.Alignment - 1);
|
||||||
|
|
||||||
|
if (proposedOffset + size.Size <= _DEFAULT_TRANSIENT_PAGE_SIZE)
|
||||||
|
{
|
||||||
|
foundPageIndex = i;
|
||||||
|
alignedOffset = proposedOffset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundPageIndex == -1)
|
||||||
|
{
|
||||||
|
var error = CreateNewActivePage(HeapType.Default, requiredHeapFlags);
|
||||||
|
if (error != Error.None)
|
||||||
|
{
|
||||||
|
Debug.Fail($"Failed to create a new page for transient texture: {error}");
|
||||||
|
return Handle<GPUTexture>.Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
foundPageIndex = _activePages.Count - 1;
|
||||||
|
alignedOffset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref var page = ref _activePages[foundPageIndex];
|
||||||
|
|
||||||
|
var handle = _resourceAllocator.CreateTexture(in desc, name, new CreationOptions
|
||||||
|
{
|
||||||
|
AllocationType = ResourceAllocationType.Suballocation,
|
||||||
|
Heap = page.heap,
|
||||||
|
Offset = alignedOffset,
|
||||||
|
});
|
||||||
|
|
||||||
|
page.offset = alignedOffset + size.Size;
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Handle<GPUBuffer> CreateTransientBuffer(ref readonly BufferDesc desc, string? name = null)
|
||||||
|
{
|
||||||
|
var size = _resourceAllocator.GetSizeInfo(ResourceDesc.Buffer(desc));
|
||||||
|
if (size.Size > _DEFAULT_TRANSIENT_PAGE_SIZE)
|
||||||
|
{
|
||||||
|
var bufHandle = _resourceAllocator.CreateBuffer(in desc, name);
|
||||||
|
if (bufHandle.IsValid)
|
||||||
|
{
|
||||||
|
_oversizedTransientResources.Add(bufHandle.AsResource());
|
||||||
|
}
|
||||||
|
|
||||||
|
return bufHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
var requiredHeapType = desc.MemoryType switch
|
||||||
|
{
|
||||||
|
ResourceMemoryType.Upload => HeapType.Upload,
|
||||||
|
ResourceMemoryType.Readback => HeapType.Readback,
|
||||||
|
_ => HeapType.Default
|
||||||
|
};
|
||||||
|
|
||||||
|
var requiredHeapFlags = _renderDevice.FeatureSupport.HasFlag(FeatureSupport.AliasBuffersAndTextures) ?
|
||||||
|
HeapFlags.AllowAllBufferAndTexture : HeapFlags.AllowOnlyBuffers;
|
||||||
|
|
||||||
|
var foundPageIndex = -1;
|
||||||
|
var alignedOffset = 0UL;
|
||||||
|
|
||||||
|
for (var i = 0; i < _activePages.Count; i++)
|
||||||
|
{
|
||||||
|
ref var p = ref _activePages[i];
|
||||||
|
|
||||||
|
if (p.heapType != requiredHeapType)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.heapFlags != requiredHeapFlags && p.heapFlags != HeapFlags.AllowAllBufferAndTexture)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var proposedOffset = (p.offset + (size.Alignment - 1)) & ~(size.Alignment - 1);
|
||||||
|
|
||||||
|
if (proposedOffset + size.Size <= _DEFAULT_TRANSIENT_PAGE_SIZE)
|
||||||
|
{
|
||||||
|
foundPageIndex = i;
|
||||||
|
alignedOffset = proposedOffset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundPageIndex == -1)
|
||||||
|
{
|
||||||
|
var error = CreateNewActivePage(requiredHeapType, requiredHeapFlags);
|
||||||
|
if (error != Error.None)
|
||||||
|
{
|
||||||
|
Debug.Fail($"Failed to create a new page for transient buffer: {error}");
|
||||||
|
return Handle<GPUBuffer>.Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
foundPageIndex = _activePages.Count - 1;
|
||||||
|
alignedOffset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref var page = ref _activePages[foundPageIndex];
|
||||||
|
|
||||||
|
var handle = _resourceAllocator.CreateBuffer(in desc, name, new CreationOptions
|
||||||
|
{
|
||||||
|
AllocationType = ResourceAllocationType.Suballocation,
|
||||||
|
Heap = page.heap,
|
||||||
|
Offset = alignedOffset,
|
||||||
|
});
|
||||||
|
|
||||||
|
page.offset = alignedOffset + size.Size;
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EndFramePool(ulong gpuFrame)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < _activePages.Count; i++)
|
||||||
|
{
|
||||||
|
ref var page = ref _activePages[i];
|
||||||
|
_retiringPages.Enqueue(new RetiringPage
|
||||||
|
{
|
||||||
|
page = page,
|
||||||
|
retireFrame = _cpuFrame
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_activePages.Clear();
|
||||||
|
|
||||||
|
while (_retiringPages.TryPeek(out var retiringPage) && retiringPage.retireFrame <= gpuFrame)
|
||||||
|
{
|
||||||
|
_retiringPages.Dequeue();
|
||||||
|
|
||||||
|
// Reset the page for reuse
|
||||||
|
retiringPage.page.offset = 0;
|
||||||
|
_activePages.Add(retiringPage.page);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < _oversizedTransientResources.Count; i++)
|
||||||
|
{
|
||||||
|
_resourceDatabase.ReleaseResource(_oversizedTransientResources[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
_oversizedTransientResources.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DisposePool()
|
||||||
|
{
|
||||||
|
foreach (var page in _activePages)
|
||||||
|
{
|
||||||
|
_resourceDatabase.ReleaseResourceImmediately(page.heap);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var page in _retiringPages)
|
||||||
|
{
|
||||||
|
_resourceDatabase.ReleaseResourceImmediately(page.page.heap);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var resource in _oversizedTransientResources)
|
||||||
|
{
|
||||||
|
_resourceDatabase.ReleaseResourceImmediately(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
_activePages.Dispose();
|
||||||
|
_retiringPages.Dispose();
|
||||||
|
_oversizedTransientResources.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ using System.Diagnostics;
|
|||||||
|
|
||||||
namespace Ghost.Graphics;
|
namespace Ghost.Graphics;
|
||||||
|
|
||||||
public sealed class ResourceManager : IDisposable
|
public sealed partial class ResourceManager : IDisposable
|
||||||
{
|
{
|
||||||
private readonly struct ResourceReturnEntry
|
private readonly struct ResourceReturnEntry
|
||||||
{
|
{
|
||||||
@@ -22,6 +22,7 @@ public sealed class ResourceManager : IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly IRenderDevice _renderDevice;
|
||||||
private readonly IResourceAllocator _resourceAllocator;
|
private readonly IResourceAllocator _resourceAllocator;
|
||||||
private readonly IResourceDatabase _resourceDatabase;
|
private readonly IResourceDatabase _resourceDatabase;
|
||||||
|
|
||||||
@@ -31,15 +32,13 @@ public sealed class ResourceManager : IDisposable
|
|||||||
|
|
||||||
private readonly MaterialPaletteStore _materialPalettes;
|
private readonly MaterialPaletteStore _materialPalettes;
|
||||||
|
|
||||||
private ulong _currentFrame;
|
private ulong _cpuFrame;
|
||||||
|
|
||||||
private UnsafeHashMap<ResourceDesc, UnsafeQueue<Handle<GPUResource>>> _resourceCache;
|
|
||||||
private UnsafeQueue<ResourceReturnEntry> _resourceReturnQueue;
|
|
||||||
|
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
public ResourceManager(IResourceAllocator resourceAllocator, IResourceDatabase resourceDatabase)
|
public ResourceManager(IRenderDevice renderDevice, IResourceAllocator resourceAllocator, IResourceDatabase resourceDatabase)
|
||||||
{
|
{
|
||||||
|
_renderDevice = renderDevice;
|
||||||
_resourceAllocator = resourceAllocator;
|
_resourceAllocator = resourceAllocator;
|
||||||
_resourceDatabase = resourceDatabase;
|
_resourceDatabase = resourceDatabase;
|
||||||
|
|
||||||
@@ -49,8 +48,7 @@ public sealed class ResourceManager : IDisposable
|
|||||||
|
|
||||||
_materialPalettes = new MaterialPaletteStore();
|
_materialPalettes = new MaterialPaletteStore();
|
||||||
|
|
||||||
_resourceCache = new UnsafeHashMap<ResourceDesc, UnsafeQueue<Handle<GPUResource>>>(32, Allocator.Persistent);
|
InitializePool();
|
||||||
_resourceReturnQueue = new UnsafeQueue<ResourceReturnEntry>(32, Allocator.Persistent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~ResourceManager()
|
~ResourceManager()
|
||||||
@@ -58,30 +56,16 @@ public sealed class ResourceManager : IDisposable
|
|||||||
Dispose();
|
Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void BeginFrame(ulong currentFrame)
|
internal void BeginFrame(ulong cpuFrame)
|
||||||
{
|
{
|
||||||
Debug.Assert(!_disposed);
|
Debug.Assert(!_disposed);
|
||||||
_currentFrame = currentFrame;
|
_cpuFrame = cpuFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void EndFrame(ulong completedFrame)
|
internal void EndFrame(ulong gpuFrame)
|
||||||
{
|
{
|
||||||
Debug.Assert(!_disposed);
|
Debug.Assert(!_disposed);
|
||||||
|
EndFramePool(gpuFrame);
|
||||||
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>
|
/// <summary>
|
||||||
@@ -198,7 +182,7 @@ public sealed class ResourceManager : IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Releases the mesh resource associated with the specified handle, freeing any resources held by it. Includes both CPU and GPU resources.
|
/// Releases the mesh heap associated with the specified handle, freeing any resources held by it. Includes both CPU and GPU resources.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="handle">The handle of the mesh to release. Must refer to a mesh that was previously created and not already released.</param>
|
/// <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)
|
public void ReleaseMesh(Handle<Mesh> handle)
|
||||||
@@ -364,38 +348,6 @@ public sealed class ResourceManager : IDisposable
|
|||||||
shader.ReleaseResource(_resourceDatabase);
|
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()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (_disposed)
|
if (_disposed)
|
||||||
@@ -423,19 +375,7 @@ public sealed class ResourceManager : IDisposable
|
|||||||
_shaders.Dispose();
|
_shaders.Dispose();
|
||||||
_materialPalettes.Dispose();
|
_materialPalettes.Dispose();
|
||||||
|
|
||||||
foreach (var kvp in _resourceCache)
|
DisposePool();
|
||||||
{
|
|
||||||
var queue = kvp.Value;
|
|
||||||
while (queue.TryDequeue(out var handle))
|
|
||||||
{
|
|
||||||
_resourceDatabase.ReleaseResource(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
queue.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
_resourceCache.Dispose();
|
|
||||||
_resourceReturnQueue.Dispose();
|
|
||||||
|
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
|
|||||||
1228
src/Runtime/Ghost.Graphics/Shaders/Includes/S2H/s2h.hlsl
Normal file
1228
src/Runtime/Ghost.Graphics/Shaders/Includes/S2H/s2h.hlsl
Normal file
File diff suppressed because it is too large
Load Diff
354
src/Runtime/Ghost.Graphics/Shaders/Includes/S2H/s2h_3d.hlsl
Normal file
354
src/Runtime/Ghost.Graphics/Shaders/Includes/S2H/s2h_3d.hlsl
Normal file
@@ -0,0 +1,354 @@
|
|||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Shader To Human (S2H) - HLSL/GLSL library for debugging shaders //
|
||||||
|
// Copyright (c) 2024-2025 Electronic Arts Inc. All rights reserved. //
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Example:
|
||||||
|
// #include "s2h.h"
|
||||||
|
// #include "s2h_3d.h"
|
||||||
|
// {
|
||||||
|
// struct Context3D context;
|
||||||
|
// // todo
|
||||||
|
// s2h_init(context);
|
||||||
|
// }
|
||||||
|
|
||||||
|
#ifndef S2H_3D_INCLUDE
|
||||||
|
#define S2H_3D_INCLUDE
|
||||||
|
|
||||||
|
struct Context3D
|
||||||
|
{
|
||||||
|
// ray origin
|
||||||
|
float3 ro;
|
||||||
|
// ray direction, normalized?
|
||||||
|
float3 rd;
|
||||||
|
// surfacePos = ro + rd * depth
|
||||||
|
float depth;
|
||||||
|
//
|
||||||
|
float4 dstColor;
|
||||||
|
};
|
||||||
|
//
|
||||||
|
void s2h_init(out Context3D context, float3 ro, float3 rd);
|
||||||
|
// @param thickness e.g. 0.09f
|
||||||
|
void s2h_drawLineWS(inout Context3D context, float3 from, float3 to, float4 color, float thickness);
|
||||||
|
// AABB: Axis Aligned Bounding Box
|
||||||
|
void s2h_drawAABB(inout Context3D context, float3 center, float3 halfSize, float4 color);
|
||||||
|
//
|
||||||
|
void s2h_drawArrowWS(inout Context3D context, float3 from, float3 to, float4 color, float thickness);
|
||||||
|
// @param worldFromObject aka objectToWorld
|
||||||
|
// @param r in world space units
|
||||||
|
void s2h_drawBasis(inout Context3D context, float4x4 worldFromObject, float r);
|
||||||
|
// @param radius e.g. 0.1f
|
||||||
|
void s2h_drawSphereWS(inout Context3D context, float3 pos, float4 color, float radius);
|
||||||
|
// 8x8 checker board with X (red) and Z (blue) around offset pointing up (Y+)
|
||||||
|
void s2h_drawCheckerBoard(inout Context3D context, float3 offset);
|
||||||
|
// infinitely far, +/- X/Z and horizon, useful to having some background for the user to orient themselves
|
||||||
|
void s2h_drawSkybox(inout Context3D context);
|
||||||
|
|
||||||
|
// implementation ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
// @param ro ray origin
|
||||||
|
// @param ro ray direction
|
||||||
|
void s2h_init(out Context3D context, float3 ro, float3 rd)
|
||||||
|
{
|
||||||
|
context.ro = ro;
|
||||||
|
context.rd = rd;
|
||||||
|
context.depth = S2H_FLT_MAX;
|
||||||
|
context.dstColor = float4(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inigo Quilez sphere ray intersection https://iquilezles.org/articles/intersectors
|
||||||
|
// sphere of size ra centered at point ce
|
||||||
|
float2 s2h_sphIntersect( in float3 ro, in float3 rd, in float3 ce, float ra )
|
||||||
|
{
|
||||||
|
float3 oc = ro - ce;
|
||||||
|
float b = dot( oc, rd );
|
||||||
|
float c = dot( oc, oc ) - ra*ra;
|
||||||
|
float h = b*b - c;
|
||||||
|
if( h<0.0 ) return float2(-1.0, -1.0); // no intersection
|
||||||
|
h = sqrt( h );
|
||||||
|
return float2( -b-h, -b+h );
|
||||||
|
}
|
||||||
|
// Inigo Quilez box ray intersection https://iquilezles.org/articles/intersectors
|
||||||
|
// axis aligned box centered at the origin, with size boxSize
|
||||||
|
float2 s2h_boxIntersection( in float3 ro, in float3 rd, float3 boxSize, out float3 outNormal )
|
||||||
|
{
|
||||||
|
float3 m = 1.0/rd; // can precompute if traversing a set of aligned boxes
|
||||||
|
float3 n = m*ro; // can precompute if traversing a set of aligned boxes
|
||||||
|
float3 k = abs(m)*boxSize;
|
||||||
|
float3 t1 = -n - k;
|
||||||
|
float3 t2 = -n + k;
|
||||||
|
float tN = max( max( t1.x, t1.y ), t1.z );
|
||||||
|
float tF = min( min( t2.x, t2.y ), t2.z );
|
||||||
|
if( tN>tF || tF<0.0) return float2(-1.0, -1.0); // no intersection
|
||||||
|
outNormal = (tN>0.0) ? step(float3(tN, tN, tN),t1) : // ro outside the box
|
||||||
|
step(t2,float3(tF, tF, tF)); // ro inside the box
|
||||||
|
outNormal *= -sign(rd);
|
||||||
|
return float2( tN, tF );
|
||||||
|
}
|
||||||
|
// Inigo Quilez box cylinder intersection https://iquilezles.org/articles/intersectors
|
||||||
|
// cylinder defined by extremes a and b, and radious ra
|
||||||
|
float4 s2h_cylIntersect( in float3 ro, in float3 rd, in float3 a, in float3 b, float ra )
|
||||||
|
{
|
||||||
|
float3 ba = b - a;
|
||||||
|
float3 oc = ro - a;
|
||||||
|
float baba = dot(ba,ba);
|
||||||
|
float bard = dot(ba,rd);
|
||||||
|
float baoc = dot(ba,oc);
|
||||||
|
float k2 = baba - bard*bard;
|
||||||
|
float k1 = baba*dot(oc,rd) - baoc*bard;
|
||||||
|
float k0 = baba*dot(oc,oc) - baoc*baoc - ra*ra*baba;
|
||||||
|
float h = k1*k1 - k2*k0;
|
||||||
|
if( h<0.0 ) return float4(-1.0, -1.0, -1.0, -1.0);//no intersection
|
||||||
|
h = sqrt(h);
|
||||||
|
float t = (-k1-h)/k2;
|
||||||
|
// body
|
||||||
|
float y = baoc + t*bard;
|
||||||
|
if( y>0.0 && y<baba ) return float4( t, (oc+t*rd - ba*y/baba)/ra );
|
||||||
|
// caps
|
||||||
|
t = ( ((y<0.0) ? 0.0 : baba) - baoc)/bard;
|
||||||
|
if( abs(k1+k2*t)<h )
|
||||||
|
{
|
||||||
|
return float4( t, ba*sign(y)/sqrt(baba) );
|
||||||
|
}
|
||||||
|
return float4(-1.0, -1.0, -1.0, -1.0);//no intersection
|
||||||
|
}
|
||||||
|
// normal at point p of cylinder (a,b,ra), see above
|
||||||
|
float3 s2h_cylNormal( in float3 p, in float3 a, in float3 b, float ra )
|
||||||
|
{
|
||||||
|
float3 pa = p - a;
|
||||||
|
float3 ba = b - a;
|
||||||
|
float baba = dot(ba,ba);
|
||||||
|
float paba = dot(pa,ba);
|
||||||
|
float h = dot(pa,ba)/baba;
|
||||||
|
return (pa - ba*h)/ra;
|
||||||
|
}
|
||||||
|
float s2h_dot2(float3 p)
|
||||||
|
{
|
||||||
|
return dot(p,p);
|
||||||
|
}
|
||||||
|
// cone defined by extremes pa and pb, and radious ra and rb
|
||||||
|
// Only one square root and one division is emplyed in the worst case. s2h_dot2(v) is dot(v,v)
|
||||||
|
// @param float4(t, normal)
|
||||||
|
float4 s2h_coneIntersect( in float3 ro, in float3 rd, in float3 pa, in float3 pb, in float ra, in float rb )
|
||||||
|
{
|
||||||
|
float3 ba = pb - pa;
|
||||||
|
float3 oa = ro - pa;
|
||||||
|
float3 ob = ro - pb;
|
||||||
|
float m0 = dot(ba,ba);
|
||||||
|
float m1 = dot(oa,ba);
|
||||||
|
float m2 = dot(rd,ba);
|
||||||
|
float m3 = dot(rd,oa);
|
||||||
|
float m5 = dot(oa,oa);
|
||||||
|
float m9 = dot(ob,ba);
|
||||||
|
|
||||||
|
// caps
|
||||||
|
if( m1<0.0 )
|
||||||
|
{
|
||||||
|
if( s2h_dot2(oa*m2-rd*m1)<(ra*ra*m2*m2) ) // delayed division
|
||||||
|
return float4(-m1/m2,-ba*rsqrt(m0));
|
||||||
|
}
|
||||||
|
else if( m9>0.0 )
|
||||||
|
{
|
||||||
|
float t = -m9/m2; // NOT delayed division
|
||||||
|
if( s2h_dot2(ob+rd*t)<(rb*rb) )
|
||||||
|
return float4(t,ba*rsqrt(m0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// body
|
||||||
|
float rr = ra - rb;
|
||||||
|
float hy = m0 + rr*rr;
|
||||||
|
float k2 = m0*m0 - m2*m2*hy;
|
||||||
|
float k1 = m0*m0*m3 - m1*m2*hy + m0*ra*(rr*m2*1.0 );
|
||||||
|
float k0 = m0*m0*m5 - m1*m1*hy + m0*ra*(rr*m1*2.0 - m0*ra);
|
||||||
|
float h = k1*k1 - k2*k0;
|
||||||
|
if( h<0.0 ) return float4(-1.0, -1.0, -1.0, -1.0); //no intersection
|
||||||
|
float t = (-k1-sqrt(h))/k2;
|
||||||
|
float y = m1 + t*m2;
|
||||||
|
if( y<0.0 || y>m0 ) return float4(-1.0, -1.0, -1.0, -1.0); //no intersection
|
||||||
|
return float4(t, normalize(m0*(m0*(oa+t*rd)+rr*ba*ra)-ba*hy*y));
|
||||||
|
}
|
||||||
|
|
||||||
|
void s2h_drawAABB(inout Context3D context, float3 center, float3 halfSize, float4 color)
|
||||||
|
{
|
||||||
|
float3 normal;
|
||||||
|
float2 hit = s2h_boxIntersection(context.ro - center, context.rd, halfSize, normal);
|
||||||
|
|
||||||
|
if(hit.y > 0.0f && hit.x < context.depth)
|
||||||
|
{
|
||||||
|
context.depth = hit.x;
|
||||||
|
context.dstColor = color;
|
||||||
|
context.dstColor = lerp(context.dstColor, float4(normal * 0.5f + 0.5f, 1), 0.3f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void s2h_drawLineWS(inout Context3D context, float3 from, float3 to, float4 color, float thickness)
|
||||||
|
{
|
||||||
|
float2 hit = s2h_cylIntersect(context.ro, context.rd, from, to, thickness).xy;
|
||||||
|
|
||||||
|
if(hit.x > 0.0 && hit.x < context.depth)
|
||||||
|
{
|
||||||
|
context.depth = hit.x;
|
||||||
|
float3 p = context.ro + context.depth * context.rd;
|
||||||
|
float3 normal = s2h_cylNormal(p, from, to, thickness);
|
||||||
|
// todo: refine shading
|
||||||
|
color.rgb = lerp(color.rgb, normal * 0.5f + 0.5f, 0.3f);
|
||||||
|
context.dstColor = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void s2h_drawArrowWS(inout Context3D context, float3 from, float3 to, float4 color, float thickness)
|
||||||
|
{
|
||||||
|
float4 hit = s2h_coneIntersect(context.ro, context.rd, from, to, thickness, 0.0f);
|
||||||
|
|
||||||
|
if(hit.x > 0.0 && hit.x < context.depth)
|
||||||
|
{
|
||||||
|
context.depth = hit.x;
|
||||||
|
float3 normal = hit.yzw;
|
||||||
|
// todo: refine shading
|
||||||
|
color.rgb = lerp(color.rgb, normal * 0.5 + 0.5, 0.3);
|
||||||
|
context.dstColor = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void s2h_drawBasis(inout Context3D context, float4x4 worldFromObject, float r)
|
||||||
|
{
|
||||||
|
float4 oHom = mul(worldFromObject, float4(0, 0, 0, 1));
|
||||||
|
float4 xHom = mul(worldFromObject, float4(r, 0, 0, 1));
|
||||||
|
float4 yHom = mul(worldFromObject, float4(0, r, 0, 1));
|
||||||
|
float4 zHom = mul(worldFromObject, float4(0, 0, r, 1));
|
||||||
|
|
||||||
|
float3 o = oHom.xyz / oHom.w;
|
||||||
|
float3 x = xHom.xyz / xHom.w;
|
||||||
|
float3 y = yHom.xyz / yHom.w;
|
||||||
|
float3 z = zHom.xyz / zHom.w;
|
||||||
|
|
||||||
|
s2h_drawArrowWS(context, o, x, float4(1, 0, 0, 1), 0.09f);
|
||||||
|
s2h_drawArrowWS(context, o, y, float4(0, 1, 0, 1), 0.09f);
|
||||||
|
s2h_drawArrowWS(context, o, z, float4(0, 0, 1, 1), 0.09f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void s2h_drawSphereWS(inout Context3D context, float3 pos, float4 color, float radius)
|
||||||
|
{
|
||||||
|
float2 hit = s2h_sphIntersect(context.ro, context.rd, pos, radius);
|
||||||
|
|
||||||
|
if(hit.x > 0.0 && hit.x < context.depth)
|
||||||
|
{
|
||||||
|
float3 hitPos = context.ro + hit.x * context.rd;
|
||||||
|
float3 normal = normalize(hitPos - pos);
|
||||||
|
|
||||||
|
context.depth = hit.x;
|
||||||
|
// todo: refine shading
|
||||||
|
color.rgb = lerp(color.rgb, normal * 0.5 + 0.5, 0.3);
|
||||||
|
context.dstColor = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void s2h_drawCheckerBoard(inout Context3D context, float3 offset)
|
||||||
|
{
|
||||||
|
float3 pos = float3(0, -0.2, 0) + offset;
|
||||||
|
float3 size = float3(4.4, 0.2, 4.4);
|
||||||
|
float3 normal;
|
||||||
|
float2 hit = s2h_boxIntersection(context.ro - pos, context.rd, size, normal);
|
||||||
|
|
||||||
|
if(hit.y > 0.0f && hit.x < context.depth)
|
||||||
|
{
|
||||||
|
context.depth = hit.x;
|
||||||
|
|
||||||
|
float3 hitPos = context.ro + hit.x * context.rd;
|
||||||
|
float2 uv = hitPos.zx;
|
||||||
|
|
||||||
|
float value = 1.0f;
|
||||||
|
|
||||||
|
if(abs(uv.x) < 4.0f && abs(uv.y) < 4.0f)
|
||||||
|
value = frac(floor(uv.x) * 0.5f + floor(uv.y) * 0.5f) > 0.25f ? 0.4f : 0.6f;
|
||||||
|
|
||||||
|
context.dstColor = float4(value, value, value, 1);
|
||||||
|
|
||||||
|
if(abs(uv.x) < (4.0f - uv.y) * 0.1f && uv.y > 0.0f)
|
||||||
|
context.dstColor.rgb = float3(1,0,0);
|
||||||
|
if(abs(uv.y) < (4.0f - uv.x) * 0.1f && uv.x > 0.0f)
|
||||||
|
context.dstColor.rgb = float3(0,0,1);
|
||||||
|
if(dot(uv, uv) < 0.25f)
|
||||||
|
context.dstColor.rgb = float3(0,1,0);
|
||||||
|
|
||||||
|
context.dstColor = lerp(context.dstColor, float4(normal * 0.5f + 0.5f, 1), 0.3f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void s2h_drawSkybox(inout Context3D context)
|
||||||
|
{
|
||||||
|
if(context.depth == S2H_FLT_MAX)
|
||||||
|
{
|
||||||
|
float3 d = context.rd;
|
||||||
|
|
||||||
|
float pi = 3.14159265f;
|
||||||
|
|
||||||
|
// assuming normalized rd
|
||||||
|
float2 uv = float2(-atan2(d.z, d.x) / pi + 1.0, acos(d.y) / pi);
|
||||||
|
|
||||||
|
float2 px = uv * float2(s2h_fontSize() * 8.0, s2h_fontSize() * 4.0);
|
||||||
|
|
||||||
|
float tileX = s2h_fontSize() * 4.0;
|
||||||
|
|
||||||
|
// 4*4 characters around the x axis
|
||||||
|
ContextGather ui;
|
||||||
|
s2h_init(ui, float2(frac(px.x / tileX + 0.5f) * tileX, px.y));
|
||||||
|
|
||||||
|
// horizon
|
||||||
|
ui.dstColor.rgb = float3(1,1,1) * saturate(1.0f - pow(abs(d.y), 0.2f));
|
||||||
|
ui.dstColor.a = 1.0f;
|
||||||
|
|
||||||
|
// grid
|
||||||
|
{
|
||||||
|
float2 gridXY = frac(ui.pxPos);
|
||||||
|
gridXY = min(gridXY, float2(1.0f, 1.0f) - gridXY);
|
||||||
|
// 0 .. 0.5
|
||||||
|
float grid = min(gridXY.x, gridXY.y);
|
||||||
|
ui.dstColor.rgb = lerp(ui.dstColor.rgb, float3(1,1,1), 0.07f * saturate(1.0f - grid * 30.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool xzAxis = abs(d.x) > abs(d.z);
|
||||||
|
|
||||||
|
bool posAxis = xzAxis ? (d.x > 0.0f) : (d.z > 0.0f);
|
||||||
|
|
||||||
|
s2h_setCursor(ui, float2(0, 12));
|
||||||
|
ui.textColor.rgb = xzAxis ? float3(1, 0, 0) : float3(0, 0, 1);
|
||||||
|
ui.textColor.a = 0.4f;
|
||||||
|
s2h_printTxt(ui, _SPACE);
|
||||||
|
s2h_printTxt(ui, posAxis ? _PLUS : _MINUS);
|
||||||
|
s2h_printTxt(ui, xzAxis ? _X : _Z);
|
||||||
|
|
||||||
|
context.dstColor = ui.dstColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scene(inout Context3D context);
|
||||||
|
|
||||||
|
void sceneWithShadows(inout Context3D context)
|
||||||
|
{
|
||||||
|
scene(context);
|
||||||
|
|
||||||
|
float4 litScene = context.dstColor;
|
||||||
|
|
||||||
|
// shadow ray, experiment, todo: expose light direction
|
||||||
|
if(context.depth < S2H_FLT_MAX)
|
||||||
|
{
|
||||||
|
// 0..1
|
||||||
|
float visible;
|
||||||
|
{
|
||||||
|
const float bias = 0.001;
|
||||||
|
Context3D shadowContext;
|
||||||
|
s2h_init(shadowContext, context.ro + context.depth * context.rd, normalize(float3(1.0,3.0,2.0)));
|
||||||
|
shadowContext.ro += bias * shadowContext.rd;
|
||||||
|
|
||||||
|
scene(shadowContext);
|
||||||
|
visible = shadowContext.depth == S2H_FLT_MAX ? 1.0 : 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// shadows are grey, todo: expose ambient color
|
||||||
|
float shadowFactor = 0.5 - visible * 0.5;
|
||||||
|
context.dstColor.rgb = lerp(litScene.rgb, float3(0.0, 0.0, 0.0), shadowFactor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // S2H_3D_INCLUDE
|
||||||
245
src/Runtime/Ghost.Graphics/Shaders/Includes/S2H/s2h_scatter.hlsl
Normal file
245
src/Runtime/Ghost.Graphics/Shaders/Includes/S2H/s2h_scatter.hlsl
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Shader To Human (S2H) - HLSL/GLSL library for debugging shaders //
|
||||||
|
// Copyright (c) 2024-2025 Electronic Arts Inc. All rights reserved. //
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Example:
|
||||||
|
// #include "s2h.h"
|
||||||
|
// #include "s2h_scatter.h"
|
||||||
|
// {
|
||||||
|
// struct ContextScatter ui;
|
||||||
|
// s2h_init(ui);
|
||||||
|
// s2h_printTxt(ui, _A, _B);
|
||||||
|
// }
|
||||||
|
// void onGfxForAllScatter(int2 pxPos, float4 color)
|
||||||
|
// {
|
||||||
|
// g_computeOutput[pxPos] = color;
|
||||||
|
// }
|
||||||
|
|
||||||
|
#ifndef S2H_SCATTER_INCLUDE
|
||||||
|
#define S2H_SCATTER_INCLUDE
|
||||||
|
|
||||||
|
// documentation:
|
||||||
|
struct ContextScatter
|
||||||
|
{
|
||||||
|
// RGBA, alpha 1 is assumed to be opaque
|
||||||
|
float4 textColor;
|
||||||
|
|
||||||
|
// private, for internal use, might change --------
|
||||||
|
|
||||||
|
// in pixels
|
||||||
|
int2 pxCursor;
|
||||||
|
// window left top, set by s2h_init()
|
||||||
|
int pxLeftX;
|
||||||
|
// 1/2/3/4, call s2h_setScale()
|
||||||
|
int scale;
|
||||||
|
};
|
||||||
|
|
||||||
|
// first call this
|
||||||
|
void s2h_init(out ContextScatter ui);
|
||||||
|
// set text cursor position, next printLF() will reset to this x position
|
||||||
|
void s2h_setCursor(inout ContextScatter ui, float2 inpxLeftTop);
|
||||||
|
// @param scale 1:pixel perfect, 2:2x, 3:3x, ..
|
||||||
|
void s2h_setScale(inout ContextScatter ui, uint scale);
|
||||||
|
// e.g. ui.s2h_printTxt('I', ' ', 'a', 'm');
|
||||||
|
// @param a ascii character or 0
|
||||||
|
void s2h_printTxt(inout ContextScatter ui, uint a, uint b, uint c, uint d, uint e, uint f);
|
||||||
|
// jump to next line
|
||||||
|
void s2h_printLF(inout ContextScatter ui);
|
||||||
|
// @param value e.g. 123, 0
|
||||||
|
void s2h_printInt(inout ContextScatter ui, int value);
|
||||||
|
// print hexadecimal e.g. "0000aa34"
|
||||||
|
// @param value 32bit e.g. 0x123, 0xff00
|
||||||
|
void s2h_printHex(inout ContextScatter ui, uint value);
|
||||||
|
// @param output e.g. g_output from RWTexture2D<float3> g_output : register(u0, space0);
|
||||||
|
// @param pos in pixels from left top, left top of the printout
|
||||||
|
// @param value
|
||||||
|
void s2h_printFloat(inout ContextScatter ui, float value);
|
||||||
|
// block in a 8x8 character
|
||||||
|
void s2h_printBlock(inout ContextScatter ui, float4 color);
|
||||||
|
// circle in a 8x8 character
|
||||||
|
void s2h_printDisc(inout ContextScatter ui, float4 color);
|
||||||
|
// don't use directly
|
||||||
|
void s2h_printCharacter(inout ContextScatter ui, uint ascii);
|
||||||
|
// no AA
|
||||||
|
void s2h_drawCrosshair(inout ContextScatter ui, float2 pxCenter, float pxRadius, float4 color);
|
||||||
|
|
||||||
|
// implementation ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void s2h_init(out ContextScatter ui)
|
||||||
|
{
|
||||||
|
// white, opaque
|
||||||
|
ui.textColor = float4(1, 1, 1, 1);
|
||||||
|
ui.pxCursor = int2(0, 0);
|
||||||
|
ui.pxLeftX = ui.pxCursor.x;
|
||||||
|
ui.scale = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void s2h_setCursor(inout ContextScatter ui, float2 inpxLeftTop)
|
||||||
|
{
|
||||||
|
ui.pxCursor = inpxLeftTop;
|
||||||
|
ui.pxLeftX = inpxLeftTop.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
void s2h_setScale(inout ContextScatter ui, uint scale)
|
||||||
|
{
|
||||||
|
ui.scale = scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
// implement this in your code
|
||||||
|
void onGfxForAllScatter(int2 pxPos, float4 color);
|
||||||
|
|
||||||
|
void s2h_printCharacter(inout ContextScatter ui, uint ascii)
|
||||||
|
{
|
||||||
|
[loop] for(int y = 0; y < 8 * ui.scale; ++y)
|
||||||
|
[loop] for(int x = 0; x < 8 * ui.scale; ++x)
|
||||||
|
if(s2h_fontLookup(ascii, int2(x, y) / ui.scale))
|
||||||
|
onGfxForAllScatter(ui.pxCursor + int2(x, y), ui.textColor);
|
||||||
|
|
||||||
|
ui.pxCursor.x += 8 * ui.scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
void s2h_drawCrosshair(inout ContextScatter ui, float2 pxCenter, float pxRadius, float4 color)
|
||||||
|
{
|
||||||
|
// avoiding int math for better performance
|
||||||
|
onGfxForAllScatter(pxCenter, ui.textColor);
|
||||||
|
|
||||||
|
[loop] for(float i = 1; i < pxRadius; ++i)
|
||||||
|
{
|
||||||
|
onGfxForAllScatter(pxCenter + float2( i, 0), color);
|
||||||
|
onGfxForAllScatter(pxCenter+ float2(-i, 0), color);
|
||||||
|
onGfxForAllScatter(pxCenter+ float2( 0, i), color);
|
||||||
|
onGfxForAllScatter(pxCenter + float2( 0, -i), color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void s2h_printTxt(inout ContextScatter ui, uint a)
|
||||||
|
{
|
||||||
|
s2h_printCharacter(ui, a);
|
||||||
|
}
|
||||||
|
// glsl has no default arguments to we implement multiple functions instead making porting easier
|
||||||
|
void s2h_printTxt(inout ContextScatter ui, uint a, uint b)
|
||||||
|
{ s2h_printTxt(ui, a); s2h_printCharacter(ui, b); }
|
||||||
|
void s2h_printTxt(inout ContextScatter ui, uint a, uint b, uint c)
|
||||||
|
{ s2h_printTxt(ui, a, b); s2h_printCharacter(ui, c); }
|
||||||
|
void s2h_printTxt(inout ContextScatter ui, uint a, uint b, uint c, uint d)
|
||||||
|
{ s2h_printTxt(ui, a, b, c); s2h_printCharacter(ui, d); }
|
||||||
|
void s2h_printTxt(inout ContextScatter ui, uint a, uint b, uint c, uint d, uint e)
|
||||||
|
{ s2h_printTxt(ui, a, b, c, d); s2h_printCharacter(ui, e); }
|
||||||
|
void s2h_printTxt(inout ContextScatter ui, uint a, uint b, uint c, uint d, uint e, uint f)
|
||||||
|
{ s2h_printTxt(ui, a, b, c, d, e); s2h_printCharacter(ui, f); }
|
||||||
|
|
||||||
|
void s2h_printLF(inout ContextScatter ui)
|
||||||
|
{
|
||||||
|
ui.pxCursor.x = ui.pxLeftX;
|
||||||
|
ui.pxCursor.y += 8 * ui.scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
void s2h_printInt(inout ContextScatter ui, int value)
|
||||||
|
{
|
||||||
|
// leading '-'
|
||||||
|
if (value < 0)
|
||||||
|
{
|
||||||
|
s2h_printCharacter(ui, '-');
|
||||||
|
value = -value;
|
||||||
|
}
|
||||||
|
if (value == 0)
|
||||||
|
{
|
||||||
|
s2h_printCharacter(ui, '0');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// move to right depending on number length
|
||||||
|
{
|
||||||
|
uint tmp = (uint)value;
|
||||||
|
while (tmp != 0u)
|
||||||
|
{
|
||||||
|
ui.pxCursor.x += 8 * ui.scale;
|
||||||
|
tmp /= 10u;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// digits
|
||||||
|
{
|
||||||
|
float backup = ui.pxCursor.x;
|
||||||
|
uint tmp = (uint)value;
|
||||||
|
while (tmp != 0u)
|
||||||
|
{
|
||||||
|
// 0..9
|
||||||
|
uint digit = tmp % 10u;
|
||||||
|
tmp /= 10u;
|
||||||
|
// go backwards
|
||||||
|
ui.pxCursor.x -= 8 * ui.scale;
|
||||||
|
s2h_printCharacter(ui, '0' + digit);
|
||||||
|
// counter +=8 from printCharacter ()
|
||||||
|
ui.pxCursor.x -= 8 * ui.scale;
|
||||||
|
}
|
||||||
|
ui.pxCursor.x = backup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void s2h_printHex(inout ContextScatter ui, uint value)
|
||||||
|
{
|
||||||
|
// 8 nibbles
|
||||||
|
for(int i = 7; i >= 0; --i)
|
||||||
|
{
|
||||||
|
// 0..15
|
||||||
|
uint nibble = (value >> (i * 4)) & 0xf;
|
||||||
|
uint start = (nibble < 10) ? '0' : ('A' - 10);
|
||||||
|
s2h_printCharacter(ui, start + nibble);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void s2h_printFloat(inout ContextScatter ui, float value)
|
||||||
|
{
|
||||||
|
s2h_printInt(ui, int(value));
|
||||||
|
float fractional = frac(abs(value));
|
||||||
|
|
||||||
|
s2h_printCharacter(ui, '.');
|
||||||
|
|
||||||
|
uint digitCount = 3u;
|
||||||
|
|
||||||
|
// todo: unit tests, this is likely wrong at lower precision
|
||||||
|
|
||||||
|
// fractional digits
|
||||||
|
for(uint i = 0u; i < digitCount; ++i)
|
||||||
|
{
|
||||||
|
fractional *= 10.0f;
|
||||||
|
// 0..9
|
||||||
|
uint digit = uint(fractional);
|
||||||
|
fractional = frac(fractional);
|
||||||
|
s2h_printCharacter(ui, '0' + digit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void s2h_printBlock(inout ContextScatter ui, float4 color)
|
||||||
|
{
|
||||||
|
[loop] for(int y = 0; y < 8 * ui.scale; ++y)
|
||||||
|
[loop] for(int x = 0; x < 8 * ui.scale; ++x)
|
||||||
|
{
|
||||||
|
float2 pxLocal = (float2(x, y) ) / ui.scale - float2(3.5f, 3.5f);
|
||||||
|
|
||||||
|
float mask = saturate(4 - max(abs(pxLocal.x), abs(pxLocal.y)));
|
||||||
|
|
||||||
|
if(mask)
|
||||||
|
onGfxForAllScatter(ui.pxCursor + int2(x,y), color);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.pxCursor.x += 8 * ui.scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
void s2h_printDisc(inout ContextScatter ui, float4 color)
|
||||||
|
{
|
||||||
|
[loop] for(int y = 0; y < 8 * ui.scale; ++y)
|
||||||
|
[loop] for(int x = 0; x < 8 * ui.scale; ++x)
|
||||||
|
{
|
||||||
|
float2 pxLocal = (float2(x, y) ) / ui.scale - float2(3.5f, 3.5f);
|
||||||
|
|
||||||
|
float mask = saturate(4 - length(pxLocal));
|
||||||
|
|
||||||
|
if(mask)
|
||||||
|
onGfxForAllScatter(ui.pxCursor + int2(x,y), color);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.pxCursor.x += 8 * ui.scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // S2H_SCATTER_INCLUDE
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
using Ghost.Graphics.Core;
|
// Source: https://github.com/zeux/meshoptimizer/blob/master/demo/clusterlod.h
|
||||||
|
// Translated from C++ to C#.
|
||||||
|
|
||||||
using Ghost.MeshOptimizer;
|
using Ghost.MeshOptimizer;
|
||||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
using Misaki.HighPerformance.LowLevel.Collections;
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
@@ -172,9 +174,9 @@ public static unsafe class MeshletUtility
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ClodBounds MergeBounds(UnsafeList<Cluster> clusters, UnsafeList<int> group)
|
private static ClodBounds MergeBounds(UnsafeList<Cluster> clusters, UnsafeList<int> group, AllocationHandle handle)
|
||||||
{
|
{
|
||||||
using var boundsList = new UnsafeArray<ClodBounds>(group.Count, Allocator.FreeList);
|
using var boundsList = new UnsafeArray<ClodBounds>(group.Count, handle);
|
||||||
for (var j = 0; j < group.Count; j++)
|
for (var j = 0; j < group.Count; j++)
|
||||||
{
|
{
|
||||||
boundsList[j] = (clusters[group[j]].bounds);
|
boundsList[j] = (clusters[group[j]].bounds);
|
||||||
@@ -202,13 +204,13 @@ public static unsafe class MeshletUtility
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static UnsafeList<Cluster> Clusterize(ref readonly ClodConfig config, ref readonly ClodMesh mesh, uint* indices, nuint indexCount, Allocator allocator)
|
private static UnsafeList<Cluster> Clusterize(ref readonly ClodConfig config, ref readonly ClodMesh mesh, uint* indices, nuint indexCount, AllocationHandle handle)
|
||||||
{
|
{
|
||||||
var maxMeshlets = MeshOptApi.BuildMeshletsBound(indexCount, config.maxVertices, config.minTriangles);
|
var maxMeshlets = MeshOptApi.BuildMeshletsBound(indexCount, config.maxVertices, config.minTriangles);
|
||||||
|
|
||||||
using var meshlets = new UnsafeArray<meshopt_Meshlet>((int)maxMeshlets, Allocator.FreeList);
|
using var meshlets = new UnsafeArray<meshopt_Meshlet>((int)maxMeshlets, handle);
|
||||||
using var meshletVertices = new UnsafeArray<uint>((int)indexCount, Allocator.FreeList);
|
using var meshletVertices = new UnsafeArray<uint>((int)indexCount, handle);
|
||||||
using var meshletTriangles = new UnsafeArray<byte>((int)indexCount, Allocator.FreeList);
|
using var meshletTriangles = new UnsafeArray<byte>((int)indexCount, handle);
|
||||||
|
|
||||||
var pMeshlets = (meshopt_Meshlet*)meshlets.GetUnsafePtr();
|
var pMeshlets = (meshopt_Meshlet*)meshlets.GetUnsafePtr();
|
||||||
var pMeshletVertices = (uint*)meshletVertices.GetUnsafePtr();
|
var pMeshletVertices = (uint*)meshletVertices.GetUnsafePtr();
|
||||||
@@ -236,7 +238,7 @@ public static unsafe class MeshletUtility
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
var clusters = new UnsafeList<Cluster>((int)meshletCount, allocator);
|
var clusters = new UnsafeList<Cluster>((int)meshletCount, handle);
|
||||||
|
|
||||||
for (nuint i = 0; i < meshletCount; i++)
|
for (nuint i = 0; i < meshletCount; i++)
|
||||||
{
|
{
|
||||||
@@ -255,9 +257,9 @@ public static unsafe class MeshletUtility
|
|||||||
var cluster = new Cluster
|
var cluster = new Cluster
|
||||||
{
|
{
|
||||||
vertices = meshlet.vertex_count,
|
vertices = meshlet.vertex_count,
|
||||||
indices = new UnsafeList<uint>((int)(meshlet.triangle_count * 3), Allocator.FreeList),
|
indices = new UnsafeList<uint>((int)(meshlet.triangle_count * 3), handle),
|
||||||
uniqueVertices = new UnsafeList<uint>((int)meshlet.vertex_count, Allocator.FreeList),
|
uniqueVertices = new UnsafeList<uint>((int)meshlet.vertex_count, handle),
|
||||||
localIndices = new UnsafeList<byte>((int)(meshlet.triangle_count * 3), Allocator.FreeList),
|
localIndices = new UnsafeList<byte>((int)(meshlet.triangle_count * 3), handle),
|
||||||
group = -1,
|
group = -1,
|
||||||
refined = -1
|
refined = -1
|
||||||
};
|
};
|
||||||
@@ -324,12 +326,12 @@ public static unsafe class MeshletUtility
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static UnsafeList<UnsafeList<int>> Partition(ref readonly ClodConfig config, ref readonly ClodMesh mesh, UnsafeList<Cluster> clusters, UnsafeList<int> pending, UnsafeArray<uint> remap, Allocator allocator)
|
private static UnsafeList<UnsafeList<int>> Partition(ref readonly ClodConfig config, ref readonly ClodMesh mesh, UnsafeList<Cluster> clusters, UnsafeList<int> pending, UnsafeArray<uint> remap, AllocationHandle handle)
|
||||||
{
|
{
|
||||||
if (pending.Count <= (int)config.partitionSize)
|
if (pending.Count <= (int)config.partitionSize)
|
||||||
{
|
{
|
||||||
var single = new UnsafeList<UnsafeList<int>>(1, allocator);
|
var single = new UnsafeList<UnsafeList<int>>(1, handle);
|
||||||
var pendingcpy = new UnsafeList<int>(pending.Count, Allocator.FreeList);
|
var pendingcpy = new UnsafeList<int>(pending.Count, handle);
|
||||||
|
|
||||||
pendingcpy.AddRange(pending.AsSpan());
|
pendingcpy.AddRange(pending.AsSpan());
|
||||||
single.Add(pendingcpy);
|
single.Add(pendingcpy);
|
||||||
@@ -343,8 +345,8 @@ public static unsafe class MeshletUtility
|
|||||||
totalIndexCount += (nuint)clusters[pending[i]].indices.Count;
|
totalIndexCount += (nuint)clusters[pending[i]].indices.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
using var clusterIndices = new UnsafeList<uint>((int)totalIndexCount, Allocator.FreeList);
|
using var clusterIndices = new UnsafeList<uint>((int)totalIndexCount, handle);
|
||||||
using var clusterCounts = new UnsafeList<uint>(pending.Count, Allocator.FreeList);
|
using var clusterCounts = new UnsafeList<uint>(pending.Count, handle);
|
||||||
|
|
||||||
nuint offset = 0;
|
nuint offset = 0;
|
||||||
for (var i = 0; i < pending.Count; i++)
|
for (var i = 0; i < pending.Count; i++)
|
||||||
@@ -359,7 +361,7 @@ public static unsafe class MeshletUtility
|
|||||||
offset += (nuint)cluster.indices.Count;
|
offset += (nuint)cluster.indices.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
using var clusterPart = new UnsafeArray<uint>(pending.Count, Allocator.FreeList);
|
using var clusterPart = new UnsafeArray<uint>(pending.Count, handle);
|
||||||
|
|
||||||
var partitionCount = MeshOptApi.PartitionClusters(
|
var partitionCount = MeshOptApi.PartitionClusters(
|
||||||
(uint*)clusterPart.GetUnsafePtr(),
|
(uint*)clusterPart.GetUnsafePtr(),
|
||||||
@@ -373,10 +375,10 @@ public static unsafe class MeshletUtility
|
|||||||
config.partitionSize
|
config.partitionSize
|
||||||
);
|
);
|
||||||
|
|
||||||
var partitions = new UnsafeList<UnsafeList<int>>((int)partitionCount, allocator);
|
var partitions = new UnsafeList<UnsafeList<int>>((int)partitionCount, handle);
|
||||||
for (nuint i = 0; i < partitionCount; i++)
|
for (nuint i = 0; i < partitionCount; i++)
|
||||||
{
|
{
|
||||||
partitions.Add(new UnsafeList<int>((int)(config.partitionSize + config.partitionSize / 3), allocator));
|
partitions.Add(new UnsafeList<int>((int)(config.partitionSize + config.partitionSize / 3), handle));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < pending.Count; i++)
|
for (var i = 0; i < pending.Count; i++)
|
||||||
@@ -387,9 +389,9 @@ public static unsafe class MeshletUtility
|
|||||||
return partitions;
|
return partitions;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int OutputGroup(ref readonly ClodConfig config, ref readonly ClodMesh mesh, UnsafeList<Cluster> clusters, UnsafeList<int> group, ClodBounds simplified, int depth, void* outputContext, ClodOutputDelegate? outputCallback)
|
private static int OutputGroup(ref readonly ClodConfig config, ref readonly ClodMesh mesh, UnsafeList<Cluster> clusters, UnsafeList<int> group, ClodBounds simplified, int depth, void* outputContext, ClodOutputDelegate? outputCallback, AllocationHandle handle)
|
||||||
{
|
{
|
||||||
using var groupClusters = new UnsafeList<ClodCluster>(group.Count, Allocator.FreeList);
|
using var groupClusters = new UnsafeList<ClodCluster>(group.Count, handle);
|
||||||
|
|
||||||
for (var i = 0; i < group.Count; i++)
|
for (var i = 0; i < group.Count; i++)
|
||||||
{
|
{
|
||||||
@@ -423,35 +425,35 @@ public static unsafe class MeshletUtility
|
|||||||
public uint id;
|
public uint id;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SimplifyFallback(ref UnsafeArray<uint> lod, ref readonly ClodMesh mesh, ReadOnlyUnsafeCollection<uint> indices, ReadOnlyUnsafeCollection<byte> locks, nuint target_count, float* error)
|
private static void SimplifyFallback(ref UnsafeArray<uint> lod, ref readonly ClodMesh mesh, ReadOnlyUnsafeCollection<uint> indices, ReadOnlyUnsafeCollection<byte> locks, nuint target_count, float* error, AllocationHandle handle)
|
||||||
{
|
{
|
||||||
using var subset = new UnsafeArray<SloppyVertex>(indices.Count, Allocator.FreeList);
|
using var subset = new UnsafeArray<SloppyVertex>(indices.Count, handle);
|
||||||
using var subset_locks = new UnsafeArray<byte>(indices.Count, Allocator.FreeList);
|
using var subset_locks = new UnsafeArray<byte>(indices.Count, handle);
|
||||||
|
|
||||||
lod.Resize(indices.Count);
|
lod.Resize(indices.Count);
|
||||||
|
|
||||||
var positions_stride = mesh.vertexPositionsStride / sizeof(float);
|
var positions_stride = mesh.vertexPositionsStride / sizeof(float);
|
||||||
|
|
||||||
// deindex the mesh subset to avoid calling simplifySloppy on the entire vertex buffer (which is prohibitively expensive without sparsity)
|
// deindex the mesh subset to avoid calling simplifySloppy on the entire vertex buffer (which is prohibitively expensive without sparsity)
|
||||||
for (var i = 0; i<indices.Count; ++i)
|
for (var i = 0; i < indices.Count; ++i)
|
||||||
{
|
{
|
||||||
var v = indices[i];
|
var v = indices[i];
|
||||||
Debug.Assert(v<mesh.vertexCount);
|
Debug.Assert(v < mesh.vertexCount);
|
||||||
|
|
||||||
subset[i].x = mesh.vertexPositions[v * positions_stride + 0];
|
subset[i].x = mesh.vertexPositions[v * positions_stride + 0];
|
||||||
subset[i].y = mesh.vertexPositions[v * positions_stride + 1];
|
subset[i].y = mesh.vertexPositions[v * positions_stride + 1];
|
||||||
subset[i].z = mesh.vertexPositions[v * positions_stride + 2];
|
subset[i].z = mesh.vertexPositions[v * positions_stride + 2];
|
||||||
subset[i].id = v;
|
subset[i].id = v;
|
||||||
|
|
||||||
subset_locks[i] = locks[v];
|
subset_locks[i] = locks[v];
|
||||||
lod[i] = (uint)i;
|
lod[i] = (uint)i;
|
||||||
}
|
}
|
||||||
|
|
||||||
var newSize = MeshOptApi.SimplifySloppy((uint*)lod.GetUnsafePtr(), (uint*)lod.GetUnsafePtr(), (nuint)lod.Count, (float*)subset.GetUnsafePtr(), (nuint)subset.Count, (nuint)sizeof(SloppyVertex), (byte*)subset_locks.GetUnsafePtr(), target_count, float.MaxValue, error);
|
var newSize = MeshOptApi.SimplifySloppy((uint*)lod.GetUnsafePtr(), (uint*)lod.GetUnsafePtr(), (nuint)lod.Count, (float*)subset.GetUnsafePtr(), (nuint)subset.Count, (nuint)sizeof(SloppyVertex), (byte*)subset_locks.GetUnsafePtr(), target_count, float.MaxValue, error);
|
||||||
lod.Resize((int)newSize);
|
lod.Resize((int)newSize);
|
||||||
|
|
||||||
// convert error to absolute
|
// convert error to absolute
|
||||||
* error *= MeshOptApi.SimplifyScale((float*)subset.GetUnsafePtr(), (nuint)subset.Count, (nuint)sizeof(SloppyVertex));
|
*error *= MeshOptApi.SimplifyScale((float*)subset.GetUnsafePtr(), (nuint)subset.Count, (nuint)sizeof(SloppyVertex));
|
||||||
|
|
||||||
// restore original vertex indices
|
// restore original vertex indices
|
||||||
for (var i = 0; i < lod.Count; ++i)
|
for (var i = 0; i < lod.Count; ++i)
|
||||||
@@ -460,9 +462,9 @@ public static unsafe class MeshletUtility
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UnsafeArray<uint> Simplify(ref readonly ClodConfig config, ref readonly ClodMesh mesh, ReadOnlyUnsafeCollection<uint> indices, ReadOnlyUnsafeCollection<byte> locks, nuint targetCount, float* error, Allocator allocator)
|
public static UnsafeArray<uint> Simplify(ref readonly ClodConfig config, ref readonly ClodMesh mesh, ReadOnlyUnsafeCollection<uint> indices, ReadOnlyUnsafeCollection<byte> locks, nuint targetCount, float* error, AllocationHandle handle)
|
||||||
{
|
{
|
||||||
var lod = new UnsafeArray<uint>(indices.Count, allocator);
|
var lod = new UnsafeArray<uint>(indices.Count, handle);
|
||||||
|
|
||||||
if (targetCount >= (nuint)indices.Count)
|
if (targetCount >= (nuint)indices.Count)
|
||||||
{
|
{
|
||||||
@@ -527,7 +529,7 @@ public static unsafe class MeshletUtility
|
|||||||
|
|
||||||
if ((nuint)lod.Length > targetCount && config.simplifyFallbackSloppy)
|
if ((nuint)lod.Length > targetCount && config.simplifyFallbackSloppy)
|
||||||
{
|
{
|
||||||
SimplifyFallback(ref lod, in mesh, indices, locks, targetCount, error);
|
SimplifyFallback(ref lod, in mesh, indices, locks, targetCount, error, handle);
|
||||||
*error *= config.simplifyErrorFactorSloppy;
|
*error *= config.simplifyErrorFactorSloppy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -575,8 +577,13 @@ public static unsafe class MeshletUtility
|
|||||||
{
|
{
|
||||||
Debug.Assert(mesh.vertexAttributesStride % sizeof(float) == 0, "vertexAttributesStride must be a multiple of sizeof(float)");
|
Debug.Assert(mesh.vertexAttributesStride % sizeof(float) == 0, "vertexAttributesStride must be a multiple of sizeof(float)");
|
||||||
|
|
||||||
using var locks = new UnsafeArray<byte>((int)mesh.vertexCount, Allocator.FreeList, AllocationOption.Clear);
|
using var pool = new MemoryPool<VirtualArena, VirtualArena.CreationOptions>(new VirtualArena.CreationOptions
|
||||||
using var remap = new UnsafeArray<uint>((int)mesh.vertexCount, Allocator.FreeList);
|
{
|
||||||
|
reserveCapacity = 256 * 1024 * 1024
|
||||||
|
});
|
||||||
|
|
||||||
|
using var locks = new UnsafeArray<byte>((int)mesh.vertexCount, pool.AllocationHandle, AllocationOption.Clear); ;
|
||||||
|
using var remap = new UnsafeArray<uint>((int)mesh.vertexCount, pool.AllocationHandle);
|
||||||
|
|
||||||
MeshOptApi.GeneratePositionRemap((uint*)remap.GetUnsafePtr(), mesh.vertexPositions, mesh.vertexCount, mesh.vertexPositionsStride);
|
MeshOptApi.GeneratePositionRemap((uint*)remap.GetUnsafePtr(), mesh.vertexPositions, mesh.vertexCount, mesh.vertexPositionsStride);
|
||||||
|
|
||||||
@@ -599,14 +606,14 @@ public static unsafe class MeshletUtility
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
using var clusters = Clusterize(in config, in mesh, mesh.indices, mesh.indexCount, Allocator.FreeList);
|
using var clusters = Clusterize(in config, in mesh, mesh.indices, mesh.indexCount, pool.AllocationHandle);
|
||||||
|
|
||||||
for (var i = 0; i < clusters.Count; i++)
|
for (var i = 0; i < clusters.Count; i++)
|
||||||
{
|
{
|
||||||
clusters[i].bounds = ComputeBounds(in mesh, clusters[i].indices, 0.0f);
|
clusters[i].bounds = ComputeBounds(in mesh, clusters[i].indices, 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
using var pending = new UnsafeList<int>(clusters.Count, Allocator.FreeList);
|
using var pending = new UnsafeList<int>(clusters.Count, pool.AllocationHandle);
|
||||||
for (var i = 0; i < clusters.Count; i++)
|
for (var i = 0; i < clusters.Count; i++)
|
||||||
{
|
{
|
||||||
pending.Add(i);
|
pending.Add(i);
|
||||||
@@ -616,14 +623,14 @@ public static unsafe class MeshletUtility
|
|||||||
|
|
||||||
while (pending.Count > 1)
|
while (pending.Count > 1)
|
||||||
{
|
{
|
||||||
using var groups = Partition(in config, in mesh, clusters, pending, remap, Allocator.FreeList);
|
using var groups = Partition(in config, in mesh, clusters, pending, remap, pool.AllocationHandle);
|
||||||
pending.Clear();
|
pending.Clear();
|
||||||
|
|
||||||
LockBoundary(locks, groups, clusters, remap, mesh.vertexLock);
|
LockBoundary(locks, groups, clusters, remap, mesh.vertexLock);
|
||||||
|
|
||||||
for (var i = 0; i < groups.Count; i++)
|
for (var i = 0; i < groups.Count; i++)
|
||||||
{
|
{
|
||||||
using var merged = new UnsafeList<uint>(groups[i].Count * (int)config.maxTriangles * 3, Allocator.FreeList);
|
using var merged = new UnsafeList<uint>(groups[i].Count * (int)config.maxTriangles * 3, pool.AllocationHandle);
|
||||||
for (var j = 0; j < groups[i].Count; j++)
|
for (var j = 0; j < groups[i].Count; j++)
|
||||||
{
|
{
|
||||||
var clusterIndices = clusters[groups[i][j]].indices;
|
var clusterIndices = clusters[groups[i][j]].indices;
|
||||||
@@ -631,28 +638,28 @@ public static unsafe class MeshletUtility
|
|||||||
}
|
}
|
||||||
|
|
||||||
var targetSize = (nuint)(merged.Count / 3 * config.simplifyRatio * 3.0f);
|
var targetSize = (nuint)(merged.Count / 3 * config.simplifyRatio * 3.0f);
|
||||||
var bounds = MergeBounds(clusters, groups[i]);
|
var bounds = MergeBounds(clusters, groups[i], pool.AllocationHandle);
|
||||||
|
|
||||||
var error = 0.0f;
|
var error = 0.0f;
|
||||||
using var simplified = Simplify(in config, in mesh, merged.AsReadOnly(), locks.AsReadOnly(), targetSize, &error, Allocator.FreeList);
|
using var simplified = Simplify(in config, in mesh, merged.AsReadOnly(), locks.AsReadOnly(), targetSize, &error, pool.AllocationHandle);
|
||||||
|
|
||||||
if ((nuint)simplified.Length > (nuint)(merged.Count * config.simplifyThreshold))
|
if ((nuint)simplified.Length > (nuint)(merged.Count * config.simplifyThreshold))
|
||||||
{
|
{
|
||||||
bounds.error = float.MaxValue;
|
bounds.error = float.MaxValue;
|
||||||
OutputGroup(in config, in mesh, clusters, groups[i], bounds, depth, outputContext, outputCallback);
|
OutputGroup(in config, in mesh, clusters, groups[i], bounds, depth, outputContext, outputCallback, pool.AllocationHandle);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bounds.error = Math.Max(bounds.error * config.simplifyErrorMergePrevious, error) + error * config.simplifyErrorMergeAdditive;
|
bounds.error = Math.Max(bounds.error * config.simplifyErrorMergePrevious, error) + error * config.simplifyErrorMergeAdditive;
|
||||||
|
|
||||||
var refined = OutputGroup(in config, in mesh, clusters, groups[i], bounds, depth, outputContext, outputCallback);
|
var refined = OutputGroup(in config, in mesh, clusters, groups[i], bounds, depth, outputContext, outputCallback, pool.AllocationHandle);
|
||||||
|
|
||||||
for (var j = 0; j < groups[i].Count; j++)
|
for (var j = 0; j < groups[i].Count; j++)
|
||||||
{
|
{
|
||||||
clusters[groups[i][j]].Dispose();
|
clusters[groups[i][j]].Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
using var split = Clusterize(in config, in mesh, (uint*)simplified.GetUnsafePtr(), (nuint)simplified.Length, Allocator.FreeList);
|
using var split = Clusterize(in config, in mesh, (uint*)simplified.GetUnsafePtr(), (nuint)simplified.Length, pool.AllocationHandle);
|
||||||
for (var j = 0; j < split.Count; j++)
|
for (var j = 0; j < split.Count; j++)
|
||||||
{
|
{
|
||||||
split[j].refined = refined;
|
split[j].refined = refined;
|
||||||
@@ -674,7 +681,7 @@ public static unsafe class MeshletUtility
|
|||||||
{
|
{
|
||||||
var bounds = clusters[pending[0]].bounds;
|
var bounds = clusters[pending[0]].bounds;
|
||||||
bounds.error = float.MaxValue;
|
bounds.error = float.MaxValue;
|
||||||
OutputGroup(in config, in mesh, clusters, pending, bounds, depth, outputContext, outputCallback);
|
OutputGroup(in config, in mesh, clusters, pending, bounds, depth, outputContext, outputCallback, pool.AllocationHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
var finalClusterCount = (nuint)clusters.Count;
|
var finalClusterCount = (nuint)clusters.Count;
|
||||||
|
|||||||
@@ -53,11 +53,11 @@ shader "MyShader/Standard"
|
|||||||
MeshData meshData = LoadData<MeshData>(instanceData.meshBuffer, 0);
|
MeshData meshData = LoadData<MeshData>(instanceData.meshBuffer, 0);
|
||||||
|
|
||||||
ByteAddressBuffer meshletBuffer = GET_BUFFER(meshData.meshletBuffer);
|
ByteAddressBuffer meshletBuffer = GET_BUFFER(meshData.meshletBuffer);
|
||||||
Meshlet m = meshletBuffer.Load<Meshlet>(groupID.x * sizeof(Meshlet));
|
Meshlet meshlet = meshletBuffer.Load<Meshlet>(groupID.x * sizeof(Meshlet));
|
||||||
|
|
||||||
s_Payload.meshletIndex = groupID.x;
|
s_Payload.meshletIndex = groupID.x;
|
||||||
|
|
||||||
uint lodLevel = (m.packedCounts >> 24) & 0xFFu;
|
uint lodLevel = (meshlet.packedCounts >> 24) & 0xFFu;
|
||||||
uint emitMeshlet = lodLevel == 0u ? 1u : 0u;
|
uint emitMeshlet = lodLevel == 0u ? 1u : 0u;
|
||||||
DispatchMesh(emitMeshlet, 1u, 1u, s_Payload);
|
DispatchMesh(emitMeshlet, 1u, 1u, s_Payload);
|
||||||
}
|
}
|
||||||
@@ -74,10 +74,10 @@ shader "MyShader/Standard"
|
|||||||
MeshData meshData = LoadData<MeshData>(instanceData.meshBuffer, 0);
|
MeshData meshData = LoadData<MeshData>(instanceData.meshBuffer, 0);
|
||||||
|
|
||||||
ByteAddressBuffer meshletBuffer = GET_BUFFER(meshData.meshletBuffer);
|
ByteAddressBuffer meshletBuffer = GET_BUFFER(meshData.meshletBuffer);
|
||||||
Meshlet m = meshletBuffer.Load<Meshlet>(meshPayload.meshletIndex * sizeof(Meshlet));
|
Meshlet meshlet = meshletBuffer.Load<Meshlet>(meshPayload.meshletIndex * sizeof(Meshlet));
|
||||||
|
|
||||||
uint vertexCount = m.packedCounts & 0xFFu;
|
uint vertexCount = meshlet.packedCounts & 0xFFu;
|
||||||
uint triangleCount = (m.packedCounts >> 8) & 0xFFu;
|
uint triangleCount = (meshlet.packedCounts >> 8) & 0xFFu;
|
||||||
SetMeshOutputCounts(vertexCount, triangleCount);
|
SetMeshOutputCounts(vertexCount, triangleCount);
|
||||||
|
|
||||||
ByteAddressBuffer meshletVerticesBuffer = GET_BUFFER(meshData.meshletVerticesBuffer);
|
ByteAddressBuffer meshletVerticesBuffer = GET_BUFFER(meshData.meshletVerticesBuffer);
|
||||||
@@ -86,7 +86,7 @@ shader "MyShader/Standard"
|
|||||||
// Write vertex output
|
// Write vertex output
|
||||||
if (groupThreadID.x < vertexCount)
|
if (groupThreadID.x < vertexCount)
|
||||||
{
|
{
|
||||||
uint vertexIndex = meshletVerticesBuffer.Load((m.vertexOffset + groupThreadID.x) * 4);
|
uint vertexIndex = meshletVerticesBuffer.Load((meshlet.vertexOffset + groupThreadID.x) * 4);
|
||||||
ByteAddressBuffer vertices = GET_BUFFER(meshData.vertexBuffer);
|
ByteAddressBuffer vertices = GET_BUFFER(meshData.vertexBuffer);
|
||||||
Vertex v = vertices.Load<Vertex>(vertexIndex * sizeof(Vertex));
|
Vertex v = vertices.Load<Vertex>(vertexIndex * sizeof(Vertex));
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ shader "MyShader/Standard"
|
|||||||
uint triangleIndex = groupThreadID.x;
|
uint triangleIndex = groupThreadID.x;
|
||||||
|
|
||||||
// Load the packed 32-bit integer containing the 3 local indices
|
// Load the packed 32-bit integer containing the 3 local indices
|
||||||
uint packedIndices = meshletTrianglesBuffer.Load((m.triangleOffset + triangleIndex) * 4);
|
uint packedIndices = meshletTrianglesBuffer.Load((meshlet.triangleOffset + triangleIndex) * 4);
|
||||||
|
|
||||||
uint i0 = packedIndices & 0xFF;
|
uint i0 = packedIndices & 0xFF;
|
||||||
uint i1 = (packedIndices >> 8) & 0xFF;
|
uint i1 = (packedIndices >> 8) & 0xFF;
|
||||||
|
|||||||
@@ -141,9 +141,9 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<GPUResource> instanceBufferHandle = default;
|
Handle<GPUBuffer> instanceBufferHandle = default;
|
||||||
Handle<GPUResource> viewBufferHandle = default;
|
Handle<GPUBuffer> viewBufferHandle = default;
|
||||||
Handle<GPUResource> frameBufferHandle = default;
|
Handle<GPUBuffer> frameBufferHandle = default;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -220,17 +220,16 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
|
|||||||
//var frustum = CreateFrustum(request.view.nearClipPlane, request.view.farClipPlane, vp, viewDir, viewPos);
|
//var frustum = CreateFrustum(request.view.nearClipPlane, request.view.farClipPlane, vp, viewDir, viewPos);
|
||||||
|
|
||||||
var instanceDataSize = (uint)(instanceCount * sizeof(InstanceData));
|
var instanceDataSize = (uint)(instanceCount * sizeof(InstanceData));
|
||||||
var instanceBufferDesc = ResourceDesc.Buffer(new BufferDesc
|
var instanceBufferDesc = new BufferDesc
|
||||||
{
|
{
|
||||||
Size = instanceDataSize,
|
Size = instanceDataSize,
|
||||||
Stride = (uint)sizeof(InstanceData),
|
Stride = (uint)sizeof(InstanceData),
|
||||||
Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
|
Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
|
||||||
MemoryType = ResourceMemoryType.Upload, // Upload directly for simplicity in testing
|
MemoryType = ResourceMemoryType.Upload, // Upload directly for simplicity in testing
|
||||||
});
|
};
|
||||||
|
|
||||||
// TODO: Optimize by suballocation.
|
instanceBufferHandle = resourceManager.CreateTransientBuffer(in instanceBufferDesc, "Instance Buffer");
|
||||||
instanceBufferHandle = resourceManager.GetPooledResource(instanceBufferDesc);
|
var instanceBufferResource = instanceBufferHandle.AsResource();
|
||||||
var instanceBufferResource = instanceBufferHandle.AsGraphicsBuffer();
|
|
||||||
|
|
||||||
var instanceDataArray = new InstanceData[instanceCount];
|
var instanceDataArray = new InstanceData[instanceCount];
|
||||||
var instanceIdx = 0;
|
var instanceIdx = 0;
|
||||||
@@ -256,21 +255,21 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(instanceBufferHandle, null, BarrierSync.Copy, null, BarrierAccess.CopyDest));
|
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(instanceBufferResource, null, BarrierSync.Copy, null, BarrierAccess.CopyDest));
|
||||||
ctx.CommandBuffer.UploadBuffer(instanceBufferResource, instanceDataArray.AsSpan());
|
ctx.CommandBuffer.UploadBuffer(instanceBufferHandle, instanceDataArray.AsSpan());
|
||||||
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(instanceBufferHandle, BarrierSync.Copy, BarrierSync.AllShading, BarrierAccess.CopyDest, BarrierAccess.ShaderResource));
|
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(instanceBufferResource, BarrierSync.Copy, BarrierSync.AllShading, BarrierAccess.CopyDest, BarrierAccess.ShaderResource));
|
||||||
|
|
||||||
// 2. Allocate and populate View Data buffer
|
// 2. Allocate and populate View Data buffer
|
||||||
var viewDataSize = (uint)sizeof(ViewData);
|
var viewBufferDesc = new BufferDesc
|
||||||
var viewBufferDesc = ResourceDesc.Buffer(new BufferDesc
|
|
||||||
{
|
{
|
||||||
Size = viewDataSize,
|
Size = (uint)sizeof(ViewData),
|
||||||
Stride = viewDataSize,
|
Stride = (uint)sizeof(ViewData),
|
||||||
Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
|
Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
|
||||||
MemoryType = ResourceMemoryType.Upload,
|
MemoryType = ResourceMemoryType.Upload,
|
||||||
});
|
};
|
||||||
|
|
||||||
viewBufferHandle = resourceManager.GetPooledResource(viewBufferDesc);
|
viewBufferHandle = resourceManager.CreateTransientBuffer(in viewBufferDesc, "View Buffer");
|
||||||
|
var viewBufferResource = viewBufferHandle.AsResource();
|
||||||
|
|
||||||
var viewData = new ViewData
|
var viewData = new ViewData
|
||||||
{
|
{
|
||||||
@@ -283,31 +282,32 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
|
|||||||
screenSize = new float4(request.view.sensorSize.x, request.view.sensorSize.y, 1.0f / request.view.sensorSize.x, 1.0f / request.view.sensorSize.y)
|
screenSize = new float4(request.view.sensorSize.x, request.view.sensorSize.y, 1.0f / request.view.sensorSize.x, 1.0f / request.view.sensorSize.y)
|
||||||
};
|
};
|
||||||
|
|
||||||
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(viewBufferHandle, null, BarrierSync.Copy, null, BarrierAccess.CopyDest));
|
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(viewBufferResource, null, BarrierSync.Copy, null, BarrierAccess.CopyDest));
|
||||||
ctx.CommandBuffer.UploadBuffer(viewBufferHandle.AsGraphicsBuffer(), new ReadOnlySpan<ViewData>(in viewData));
|
ctx.CommandBuffer.UploadBuffer(viewBufferHandle, new ReadOnlySpan<ViewData>(in viewData));
|
||||||
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(viewBufferHandle, BarrierSync.Copy, BarrierSync.AllShading, BarrierAccess.CopyDest, BarrierAccess.ShaderResource));
|
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(viewBufferResource, BarrierSync.Copy, BarrierSync.AllShading, BarrierAccess.CopyDest, BarrierAccess.ShaderResource));
|
||||||
|
|
||||||
// 3. Allocate and populate Global Frame Data buffer
|
// 3. Allocate and populate Global Frame Data buffer
|
||||||
var frameDataSize = (uint)sizeof(FrameData);
|
var frameDataSize = (uint)sizeof(FrameData);
|
||||||
var frameBufferDesc = ResourceDesc.Buffer(new BufferDesc
|
var frameBufferDesc = new BufferDesc
|
||||||
{
|
{
|
||||||
Size = frameDataSize,
|
Size = frameDataSize,
|
||||||
Stride = frameDataSize,
|
Stride = frameDataSize,
|
||||||
Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
|
Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
|
||||||
MemoryType = ResourceMemoryType.Upload,
|
MemoryType = ResourceMemoryType.Upload,
|
||||||
});
|
};
|
||||||
|
|
||||||
frameBufferHandle = resourceManager.GetPooledResource(frameBufferDesc);
|
frameBufferHandle = resourceManager.CreateTransientBuffer(in frameBufferDesc, "Frame Buffer");
|
||||||
|
var frameBufferResource = frameBufferHandle.AsResource();
|
||||||
|
|
||||||
var frameData = new FrameData
|
var frameData = new FrameData
|
||||||
{
|
{
|
||||||
viewBufferIndex = resourceDatabase.GetBindlessIndex(viewBufferHandle),
|
viewBufferIndex = resourceDatabase.GetBindlessIndex(viewBufferResource),
|
||||||
instanceBufferIndex = resourceDatabase.GetBindlessIndex(instanceBufferResource.AsResource()),
|
instanceBufferIndex = resourceDatabase.GetBindlessIndex(instanceBufferResource),
|
||||||
};
|
};
|
||||||
|
|
||||||
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(frameBufferHandle, null, BarrierSync.Copy, null, BarrierAccess.CopyDest));
|
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(frameBufferResource, null, BarrierSync.Copy, null, BarrierAccess.CopyDest));
|
||||||
ctx.CommandBuffer.UploadBuffer(frameBufferHandle.AsGraphicsBuffer(), new ReadOnlySpan<FrameData>(in frameData));
|
ctx.CommandBuffer.UploadBuffer(frameBufferHandle, new ReadOnlySpan<FrameData>(in frameData));
|
||||||
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(frameBufferHandle, BarrierSync.Copy, BarrierSync.AllShading, BarrierAccess.CopyDest, BarrierAccess.ShaderResource));
|
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(frameBufferResource, BarrierSync.Copy, BarrierSync.AllShading, BarrierAccess.CopyDest, BarrierAccess.ShaderResource));
|
||||||
|
|
||||||
if (request.renderFunc != null)
|
if (request.renderFunc != null)
|
||||||
{
|
{
|
||||||
@@ -320,7 +320,9 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
|
|||||||
var backBuffer = _renderGraph.ImportTexture(rt, "BackBuffer");
|
var backBuffer = _renderGraph.ImportTexture(rt, "BackBuffer");
|
||||||
|
|
||||||
MeshletDebugPass(backBuffer, request.opaqueRenderList,
|
MeshletDebugPass(backBuffer, request.opaqueRenderList,
|
||||||
resourceDatabase.GetBindlessIndex(frameBufferHandle), resourceDatabase.GetBindlessIndex(viewBufferHandle), resourceDatabase.GetBindlessIndex(instanceBufferHandle));
|
resourceDatabase.GetBindlessIndex(frameBufferResource),
|
||||||
|
resourceDatabase.GetBindlessIndex(viewBufferResource),
|
||||||
|
resourceDatabase.GetBindlessIndex(instanceBufferResource));
|
||||||
|
|
||||||
var viewState = new ViewState(rtSize.x, rtSize.y, rtSize.x, rtSize.y);
|
var viewState = new ViewState(rtSize.x, rtSize.y, rtSize.x, rtSize.y);
|
||||||
_renderGraph.Compile(viewState);
|
_renderGraph.Compile(viewState);
|
||||||
@@ -329,10 +331,9 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
// We must enqueue a return for the pooled resources so they are freed next frame.
|
_renderSystem.GraphicsEngine.ResourceDatabase.ReleaseResource(instanceBufferHandle.AsResource());
|
||||||
resourceManager.ReturnPooledResource(instanceBufferHandle);
|
_renderSystem.GraphicsEngine.ResourceDatabase.ReleaseResource(viewBufferHandle.AsResource());
|
||||||
resourceManager.ReturnPooledResource(viewBufferHandle);
|
_renderSystem.GraphicsEngine.ResourceDatabase.ReleaseResource(frameBufferHandle.AsResource());
|
||||||
resourceManager.ReturnPooledResource(frameBufferHandle);
|
|
||||||
|
|
||||||
if (request.swapChainIndex >= 0)
|
if (request.swapChainIndex >= 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -67,7 +67,6 @@ internal static class MeshUtility
|
|||||||
}
|
}
|
||||||
|
|
||||||
using var flatVertices = new UnsafeList<Vertex>(1024, scope0.AllocationHandle);
|
using var flatVertices = new UnsafeList<Vertex>(1024, scope0.AllocationHandle);
|
||||||
//using var flatIndices = new UnsafeList<uint>(1024, scope0.AllocationHandle);
|
|
||||||
|
|
||||||
var needComputeNormals = false;
|
var needComputeNormals = false;
|
||||||
|
|
||||||
@@ -91,7 +90,7 @@ internal static class MeshUtility
|
|||||||
|
|
||||||
var maxScratchIndices = (int)(pMesh->max_face_triangles * 3u);
|
var maxScratchIndices = (int)(pMesh->max_face_triangles * 3u);
|
||||||
|
|
||||||
using var triIndicesArray = new UnsafeArray<uint>(maxScratchIndices, scope1.AllocationHandle);
|
var triIndicesArray = new UnsafeArray<uint>(maxScratchIndices, scope1.AllocationHandle);
|
||||||
|
|
||||||
for (var j = 0u; j < pMesh->num_faces; j++)
|
for (var j = 0u; j < pMesh->num_faces; j++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ using Ghost.Engine.Utilities;
|
|||||||
using Ghost.Entities;
|
using Ghost.Entities;
|
||||||
using Ghost.Graphics.Core;
|
using Ghost.Graphics.Core;
|
||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
using Ghost.Graphics.Utilities;
|
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using Microsoft.UI.Xaml.Media;
|
using Microsoft.UI.Xaml.Media;
|
||||||
|
|||||||
Reference in New Issue
Block a user