Update rendering architecture and resource management
Added a new `Ref<T>` struct for reference semantics. Added the `RenderGraph` system for managing rendering passes. Added the `RenderTexture` class for encapsulating GPU resources. Added `GraphicsBuffer` class for effective GPU resource management. Changed `CommandList` methods from public to internal for visibility control. Changed `IRenderPass` interface from internal to public for accessibility. Changed `GetData<T>()` in `ComponentObject.cs` to return `CompRef<T>`. Changed `GetComponent<T>()` in `EntityManager.cs` to return `CompRef<T>`. Changed `GetSingleton<T>()` in `World.cs` to use `CompRef<T>`. Changed `IQueryTypeParameter` to use `CompRef<T>` for consistency. Changed `QueryItem<T0>` and related structs to use `CompRef<T>`. Changed `Material` class to support bindless textures. Changed `Shader` class to support bindless rendering. Changed `Mesh` class to support bindless vertex and index buffer access. Updated documentation to reflect the new bindless rendering architecture.
This commit is contained in:
@@ -1,32 +1,39 @@
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.D3D12;
|
||||
using Ghost.Graphics.Data;
|
||||
using Ghost.Graphics.Shading;
|
||||
using Ghost.Graphics.Utilities;
|
||||
using StbImageSharp;
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
using Win32.Graphics.Dxgi.Common;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Ghost.Graphics.RenderPasses;
|
||||
|
||||
/// <summary>
|
||||
/// Simplified bindless mesh render pass using high-level bindless APIs with fully bindless vertex/index buffer access
|
||||
/// </summary>
|
||||
internal unsafe class MeshRenderPass : IRenderPass
|
||||
{
|
||||
private const string _HLSL_SOURCE = @"
|
||||
cbuffer ConstantBuffer : register(b0)
|
||||
{
|
||||
float4 _Color;
|
||||
uint _TextureIndex1;
|
||||
uint _TextureIndex2;
|
||||
uint _TextureIndex3;
|
||||
uint _TextureIndex4;
|
||||
uint _VertexBufferIndex;
|
||||
uint _IndexBufferIndex;
|
||||
};
|
||||
|
||||
Texture2D _MainTex : register(t0);
|
||||
// SM 6.6 approach - direct access to global descriptor heap
|
||||
SamplerState _MainSampler : register(s0);
|
||||
|
||||
struct VertexInput
|
||||
struct Vertex
|
||||
{
|
||||
float4 position : POSITION;
|
||||
float4 color : COLOR;
|
||||
float4 uv : TEXCOORD0;
|
||||
float4 position;
|
||||
float4 normal;
|
||||
float4 tangent;
|
||||
float4 color;
|
||||
float4 uv;
|
||||
};
|
||||
|
||||
struct PixelInput
|
||||
@@ -36,237 +43,105 @@ struct PixelInput
|
||||
float4 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
PixelInput VSMain(VertexInput input)
|
||||
// Bindless vertex shader that fetches vertex data from bindless buffers
|
||||
PixelInput VSMain(uint vertexId : SV_VertexID, uint instanceId : SV_InstanceID)
|
||||
{
|
||||
// Get bindless buffers
|
||||
ByteAddressBuffer vertexBuffer = ResourceDescriptorHeap[_VertexBufferIndex];
|
||||
ByteAddressBuffer indexBuffer = ResourceDescriptorHeap[_IndexBufferIndex];
|
||||
|
||||
// For fully bindless rendering, we use instanced drawing where:
|
||||
// - Each instance represents a triangle (instanceId = triangle index)
|
||||
// - vertexId goes from 0 to 2 (the 3 vertices of the triangle)
|
||||
|
||||
// Calculate the index into the index buffer
|
||||
uint indexOffset = (instanceId * 3 + vertexId) * 4; // 4 bytes per index (uint32)
|
||||
uint vertexIndex = indexBuffer.Load(indexOffset);
|
||||
|
||||
// Calculate the offset into the vertex buffer
|
||||
uint vertexOffset = vertexIndex * 80; // 80 bytes per vertex (5 * float4)
|
||||
|
||||
// Load vertex data from bindless vertex buffer
|
||||
Vertex vertex;
|
||||
vertex.position = asfloat(vertexBuffer.Load4(vertexOffset + 0));
|
||||
vertex.normal = asfloat(vertexBuffer.Load4(vertexOffset + 16));
|
||||
vertex.tangent = asfloat(vertexBuffer.Load4(vertexOffset + 32));
|
||||
vertex.color = asfloat(vertexBuffer.Load4(vertexOffset + 48));
|
||||
vertex.uv = asfloat(vertexBuffer.Load4(vertexOffset + 64));
|
||||
|
||||
// Output transformed vertex
|
||||
PixelInput output;
|
||||
output.position = input.position;
|
||||
output.color = input.color;
|
||||
output.uv = input.uv;
|
||||
output.position = vertex.position;
|
||||
output.color = vertex.color;
|
||||
output.uv = vertex.uv;
|
||||
return output;
|
||||
}
|
||||
|
||||
float4 PSMain(PixelInput input) : SV_TARGET
|
||||
{
|
||||
return _MainTex.Sample(_MainSampler, input.uv.xy);
|
||||
// SM 6.6 Modern Bindless Approach:
|
||||
// ResourceDescriptorHeap[index] directly accesses any texture in the heap
|
||||
Texture2D tex1 = ResourceDescriptorHeap[_TextureIndex1];
|
||||
Texture2D tex2 = ResourceDescriptorHeap[_TextureIndex2];
|
||||
Texture2D tex3 = ResourceDescriptorHeap[_TextureIndex3];
|
||||
Texture2D tex4 = ResourceDescriptorHeap[_TextureIndex4];
|
||||
|
||||
// Sample the textures
|
||||
float4 color1 = tex1.Sample(_MainSampler, input.uv.xy);
|
||||
float4 color2 = tex2.Sample(_MainSampler, input.uv.xy);
|
||||
float4 color3 = tex3.Sample(_MainSampler, input.uv.xy);
|
||||
float4 color4 = tex4.Sample(_MainSampler, input.uv.xy);
|
||||
|
||||
// Blend all textures together (simple average)
|
||||
float4 blendedColor = (color1 + color2 + color3 + color4) * 0.25f;
|
||||
|
||||
return blendedColor * _Color;
|
||||
}
|
||||
";
|
||||
|
||||
// High-level bindless objects
|
||||
private Mesh? _mesh;
|
||||
private Shader? _shader;
|
||||
private Material? _material;
|
||||
private Texture2D[]? _textures;
|
||||
|
||||
// Direct D3D12 resources for texture
|
||||
private ComPtr<ID3D12Resource> _textureResource;
|
||||
private ComPtr<ID3D12DescriptorHeap> _srvHeap;
|
||||
private CpuDescriptorHandle _srvHandle;
|
||||
private GpuDescriptorHandle _srvGpuHandle;
|
||||
private uint _srvDescriptorSize;
|
||||
|
||||
// Additional fields for deferred texture upload
|
||||
private ComPtr<ID3D12Resource> _uploadBuffer;
|
||||
private uint _textureWidth;
|
||||
private uint _textureHeight;
|
||||
private uint _texturePitch;
|
||||
private bool _textureUploaded = false;
|
||||
// Texture file paths for this demo
|
||||
private readonly string[] _textureFiles = [
|
||||
"C:/Users/Misaki/Downloads/Im/Icon.png",
|
||||
"C:/Users/Misaki/Downloads/Im/Backdrop.jpg",
|
||||
"C:/Users/Misaki/Downloads/Im/101167591_p0.png",
|
||||
"C:/Users/Misaki/Downloads/Im/yande.re 1134666 blue_archive nakamasa_ichika sugarhigh.jpg"
|
||||
];
|
||||
|
||||
public void Initialize(CommandList cmd)
|
||||
{
|
||||
_mesh = MeshBuilder.CreateCube(0.25f);
|
||||
_mesh = MeshBuilder.CreateCube(0.75f);
|
||||
_mesh.UploadMeshData();
|
||||
|
||||
_shader = new(_HLSL_SOURCE);
|
||||
_material = new(_shader);
|
||||
_shader = new Shader(_HLSL_SOURCE);
|
||||
_material = new Material(_shader);
|
||||
|
||||
// Create direct D3D12 texture resources
|
||||
CreateTextureDirectly();
|
||||
|
||||
_material.UploadMaterialData();
|
||||
|
||||
// Copy from upload buffer to texture
|
||||
var srcLocation = new TextureCopyLocation(_uploadBuffer.Get(), new PlacedSubresourceFootprint
|
||||
_textures = new Texture2D[_textureFiles.Length];
|
||||
for (var i = 0; i < _textureFiles.Length; i++)
|
||||
{
|
||||
Offset = 0,
|
||||
Footprint = new SubresourceFootprint
|
||||
{
|
||||
Format = Format.R8G8B8A8Unorm,
|
||||
Width = _textureWidth,
|
||||
Height = _textureHeight,
|
||||
Depth = 1,
|
||||
RowPitch = _texturePitch
|
||||
}
|
||||
});
|
||||
|
||||
var dstLocation = new TextureCopyLocation(_textureResource.Get(), 0);
|
||||
|
||||
cmd.NativeCommandList.Ptr->CopyTextureRegion(&dstLocation, 0, 0, 0, &srcLocation, null);
|
||||
|
||||
// Transition texture to shader resource
|
||||
var barrier = new ResourceBarrier
|
||||
{
|
||||
Type = ResourceBarrierType.Transition,
|
||||
Flags = ResourceBarrierFlags.None,
|
||||
Transition = new ResourceTransitionBarrier
|
||||
{
|
||||
pResource = _textureResource.Get(),
|
||||
StateBefore = ResourceStates.CopyDest,
|
||||
StateAfter = ResourceStates.PixelShaderResource,
|
||||
Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES
|
||||
}
|
||||
};
|
||||
cmd.NativeCommandList.Ptr->ResourceBarrier(1, &barrier);
|
||||
}
|
||||
|
||||
private void CreateTextureDirectly()
|
||||
{
|
||||
var device = GraphicsPipeline.GraphicsDevice.NativeDevice.Ptr;
|
||||
|
||||
// Load image data
|
||||
using var stream = new FileStream("C:/Users/Misaki/Downloads/Im/Icon.png", FileMode.Open, FileAccess.Read);
|
||||
var image = ImageResult.FromStream(stream, ColorComponents.RedGreenBlueAlpha);
|
||||
|
||||
var width = (uint)image.Width;
|
||||
var height = (uint)image.Height;
|
||||
uint bytesPerPixel = 4; // RGBA
|
||||
var pitch = width * bytesPerPixel;
|
||||
var textureSize = pitch * height;
|
||||
|
||||
// Create the texture resource
|
||||
var textureDesc = new ResourceDescription
|
||||
{
|
||||
Dimension = ResourceDimension.Texture2D,
|
||||
Alignment = 0,
|
||||
Width = width,
|
||||
Height = height,
|
||||
DepthOrArraySize = 1,
|
||||
MipLevels = 1,
|
||||
Format = Format.R8G8B8A8Unorm,
|
||||
SampleDesc = new SampleDescription(1, 0),
|
||||
Layout = TextureLayout.Unknown,
|
||||
Flags = ResourceFlags.None
|
||||
};
|
||||
|
||||
var heapProps = new HeapProperties(HeapType.Default);
|
||||
device->CreateCommittedResource(
|
||||
&heapProps,
|
||||
HeapFlags.None,
|
||||
&textureDesc,
|
||||
ResourceStates.CopyDest,
|
||||
null,
|
||||
__uuidof<ID3D12Resource>(),
|
||||
_textureResource.GetVoidAddressOf()
|
||||
);
|
||||
|
||||
// Create upload buffer
|
||||
var uploadBufferSize = GetRequiredIntermediateSize(_textureResource.Get(), 0, 1);
|
||||
var uploadHeapProps = new HeapProperties(HeapType.Upload);
|
||||
var uploadBufferDesc = ResourceDescription.Buffer(uploadBufferSize);
|
||||
|
||||
ComPtr<ID3D12Resource> uploadBuffer = default;
|
||||
device->CreateCommittedResource(
|
||||
&uploadHeapProps,
|
||||
HeapFlags.None,
|
||||
&uploadBufferDesc,
|
||||
ResourceStates.GenericRead,
|
||||
null,
|
||||
__uuidof<ID3D12Resource>(),
|
||||
uploadBuffer.GetVoidAddressOf()
|
||||
);
|
||||
|
||||
// Map and copy texture data
|
||||
void* mappedData = null;
|
||||
uploadBuffer.Get()->Map(0, null, &mappedData);
|
||||
|
||||
// Copy image data to upload buffer
|
||||
var srcData = image.Data.AsSpan();
|
||||
var dstSpan = new Span<byte>(mappedData, (int)uploadBufferSize);
|
||||
|
||||
// Copy row by row with proper alignment
|
||||
var alignedPitch = (pitch + 255) & ~255u; // Align to 256 bytes
|
||||
for (var y = 0; y < height; y++)
|
||||
{
|
||||
var srcRow = srcData.Slice(y * (int)pitch, (int)pitch);
|
||||
var dstRow = dstSpan.Slice(y * (int)alignedPitch, (int)pitch);
|
||||
srcRow.CopyTo(dstRow);
|
||||
_textures[i] = Texture2D.FromFile(_textureFiles[i]);
|
||||
_textures[i].UploadTextureData();
|
||||
}
|
||||
|
||||
uploadBuffer.Get()->Unmap(0, null);
|
||||
|
||||
// We'll copy the texture data during Execute phase when we have access to command list
|
||||
// Store the upload buffer for later use
|
||||
_uploadBuffer = uploadBuffer.Move();
|
||||
_textureWidth = width;
|
||||
_textureHeight = height;
|
||||
_texturePitch = alignedPitch;
|
||||
|
||||
// Create SRV descriptor heap
|
||||
var srvHeapDesc = new DescriptorHeapDescription
|
||||
_material.SetVector("_Color", new Vector4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
for (var i = 0; i < _textures.Length; i++)
|
||||
{
|
||||
Type = DescriptorHeapType.CbvSrvUav,
|
||||
NumDescriptors = 1,
|
||||
Flags = DescriptorHeapFlags.ShaderVisible
|
||||
};
|
||||
var texture = _textures[i];
|
||||
_material.SetTextureIndex($"_TextureIndex{i + 1}", texture);
|
||||
}
|
||||
|
||||
device->CreateDescriptorHeap(&srvHeapDesc, __uuidof<ID3D12DescriptorHeap>(), _srvHeap.GetVoidAddressOf());
|
||||
|
||||
// Get descriptor handles
|
||||
_srvHandle = _srvHeap.Get()->GetCPUDescriptorHandleForHeapStart();
|
||||
_srvGpuHandle = _srvHeap.Get()->GetGPUDescriptorHandleForHeapStart();
|
||||
_srvDescriptorSize = device->GetDescriptorHandleIncrementSize(DescriptorHeapType.CbvSrvUav);
|
||||
|
||||
// Create SRV
|
||||
var srvDesc = new ShaderResourceViewDescription
|
||||
{
|
||||
Format = Format.R8G8B8A8Unorm,
|
||||
ViewDimension = Win32.Graphics.Direct3D12.SrvDimension.Texture2D,
|
||||
Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING,
|
||||
Texture2D = new Texture2DSrv
|
||||
{
|
||||
MostDetailedMip = 0,
|
||||
MipLevels = 1,
|
||||
PlaneSlice = 0,
|
||||
ResourceMinLODClamp = 0.0f
|
||||
}
|
||||
};
|
||||
|
||||
device->CreateShaderResourceView(_textureResource.Get(), &srvDesc, _srvHandle);
|
||||
}
|
||||
|
||||
private static ulong GetRequiredIntermediateSize(ID3D12Resource* destinationResource, uint firstSubresource, uint numSubresources)
|
||||
{
|
||||
var device = GraphicsPipeline.GraphicsDevice.NativeDevice.Ptr;
|
||||
var resourceDesc = destinationResource->GetDesc();
|
||||
|
||||
ulong requiredSize = 0;
|
||||
device->GetCopyableFootprints(&resourceDesc, firstSubresource, numSubresources, 0, null, null, null, &requiredSize);
|
||||
|
||||
return requiredSize;
|
||||
_material.SetMeshBufferIndices(_mesh);
|
||||
_material.UploadMaterialData();
|
||||
}
|
||||
|
||||
public void Execute(CommandList cmd)
|
||||
{
|
||||
var commandList = cmd.NativeCommandList.Ptr;
|
||||
|
||||
// Set root signature and pipeline state
|
||||
commandList->SetGraphicsRootSignature(_material!.Shader.RootSignature);
|
||||
commandList->SetPipelineState(_material.Shader.PipelineState);
|
||||
|
||||
// Set descriptor heap
|
||||
var heaps = stackalloc ID3D12DescriptorHeap*[1];
|
||||
heaps[0] = _srvHeap.Get();
|
||||
commandList->SetDescriptorHeaps(1, heaps);
|
||||
|
||||
// Bind texture descriptor table directly
|
||||
if (_material.Shader.Textures.Count > 0)
|
||||
{
|
||||
var textureInfo = _material.Shader.Textures[0];
|
||||
commandList->SetGraphicsRootDescriptorTable(textureInfo.RootParameterIndex, _srvGpuHandle);
|
||||
}
|
||||
|
||||
// Draw mesh
|
||||
commandList->IASetPrimitiveTopology(PrimitiveTopology.TriangleList);
|
||||
commandList->IASetVertexBuffers(0, 1, _mesh!.VertexBufferView);
|
||||
commandList->IASetIndexBuffer(_mesh.IndexBufferView);
|
||||
commandList->DrawIndexedInstanced(_mesh.IndexCount, 1, 0, 0, 0);
|
||||
cmd.DrawMesh(_mesh!, _material!);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@@ -275,8 +150,12 @@ float4 PSMain(PixelInput input) : SV_TARGET
|
||||
_shader?.Dispose();
|
||||
_material?.Dispose();
|
||||
|
||||
_textureResource.Dispose();
|
||||
_uploadBuffer.Dispose();
|
||||
_srvHeap.Dispose();
|
||||
if (_textures != null)
|
||||
{
|
||||
foreach (var texture in _textures)
|
||||
{
|
||||
texture?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user