feat(meshlet): refactor meshlet pipeline and add render pass
Refactor meshlet data structures to use packed uint triangle indices, update meshlet cooking and upload logic, and align HLSL mesh shader. Add MeshRenderPass with bindless rendering and blit support. Improve RenderExtractionSystem, RootSignatureLayout, and TestRenderPipeline. Update GraphicsTestWindow for new pipeline and meshlet logic. Includes code cleanups and comments.
This commit is contained in:
@@ -54,7 +54,7 @@ public struct MeshletMeshData : IDisposable
|
||||
public UnsafeList<MeshletGroup> groups;
|
||||
public UnsafeList<MeshletHierarchyNode> hierarchyNodes;
|
||||
public UnsafeList<uint> meshletVertices;
|
||||
public UnsafeList<byte> meshletTriangles;
|
||||
public UnsafeList<uint> meshletTriangles;
|
||||
public int lodLevelCount;
|
||||
public int materialSlotCount;
|
||||
|
||||
@@ -247,7 +247,7 @@ public struct Mesh : IResourceReleasable
|
||||
if (!data.groups.IsCreated) data.groups = new UnsafeList<MeshletGroup>(16, Allocator.Persistent);
|
||||
if (!data.meshlets.IsCreated) data.meshlets = new UnsafeList<Meshlet>(64, Allocator.Persistent);
|
||||
if (!data.meshletVertices.IsCreated) data.meshletVertices = new UnsafeList<uint>(128, Allocator.Persistent);
|
||||
if (!data.meshletTriangles.IsCreated) data.meshletTriangles = new UnsafeList<byte>(128, Allocator.Persistent);
|
||||
if (!data.meshletTriangles.IsCreated) data.meshletTriangles = new UnsafeList<uint>(128, Allocator.Persistent);
|
||||
|
||||
var meshletGroup = new MeshletGroup
|
||||
{
|
||||
@@ -264,23 +264,27 @@ public struct Mesh : IResourceReleasable
|
||||
var meshlet = new Meshlet
|
||||
{
|
||||
vertexCount = (byte)cluster.vertexCount,
|
||||
triangleCount = (byte)(cluster.indexCount / 3),
|
||||
triangleCount = (byte)(cluster.localIndexCount / 3),
|
||||
vertexOffset = (uint)data.meshletVertices.Count,
|
||||
triangleOffset = (uint)data.meshletTriangles.Count,
|
||||
groupIndex = (uint)data.groups.Count - 1
|
||||
};
|
||||
data.meshlets.Add(meshlet);
|
||||
|
||||
// Add indices
|
||||
for (nuint j = 0; j < cluster.indexCount; j++)
|
||||
// Add unique vertices
|
||||
for (nuint j = 0; j < cluster.vertexCount; j++)
|
||||
{
|
||||
data.meshletVertices.Add(cluster.indices[j]);
|
||||
data.meshletVertices.Add(cluster.uniqueVertices[j]);
|
||||
}
|
||||
// Add triangles (packed indices or byte offsets)
|
||||
// Assuming 8-bit local indices for meshlets as per standard convention
|
||||
for (nuint j = 0; j < cluster.indexCount; j++)
|
||||
// Add local triangles (packed into uints)
|
||||
nuint triangleCount = cluster.localIndexCount / 3;
|
||||
for (nuint j = 0; j < triangleCount; j++)
|
||||
{
|
||||
data.meshletTriangles.Add((byte)j);
|
||||
uint i0 = cluster.localIndices[j * 3 + 0];
|
||||
uint i1 = cluster.localIndices[j * 3 + 1];
|
||||
uint i2 = cluster.localIndices[j * 3 + 2];
|
||||
uint packedTriangle = i0 | (i1 << 8) | (i2 << 16);
|
||||
data.meshletTriangles.Add(packedTriangle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ using Misaki.HighPerformance.Mathematics;
|
||||
|
||||
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.
|
||||
public readonly unsafe ref struct RenderingContext
|
||||
{
|
||||
private readonly IGraphicsEngine _engine;
|
||||
@@ -185,12 +186,11 @@ public readonly unsafe ref struct RenderingContext
|
||||
MemoryType = ResourceMemoryType.Default,
|
||||
};
|
||||
// Ensure size is multiple of 4 for Raw buffer
|
||||
var trianglesSize = (uint)meshletData.meshletTriangles.Count;
|
||||
trianglesSize = (trianglesSize + 3u) & ~3u;
|
||||
var trianglesSize = (uint)meshletData.meshletTriangles.Count * sizeof(uint);
|
||||
var trianglesDesc = new BufferDesc
|
||||
{
|
||||
Size = trianglesSize,
|
||||
Stride = sizeof(byte),
|
||||
Stride = sizeof(uint),
|
||||
Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
|
||||
MemoryType = ResourceMemoryType.Default,
|
||||
};
|
||||
@@ -208,7 +208,7 @@ public readonly unsafe ref struct RenderingContext
|
||||
// Padding for triangle data if needed
|
||||
if (trianglesSize > meshletData.meshletTriangles.Count)
|
||||
{
|
||||
var paddedData = new byte[trianglesSize];
|
||||
var paddedData = new uint[trianglesSize];
|
||||
meshletData.meshletTriangles.AsSpan().CopyTo(paddedData);
|
||||
_directCmd.UploadBuffer(meshRef.MeshletTrianglesBuffer, paddedData.AsSpan());
|
||||
}
|
||||
@@ -222,7 +222,7 @@ public readonly unsafe ref struct RenderingContext
|
||||
TransitionBarrier(meshRef.MeshletTrianglesBuffer.AsResource(), false, BarrierLayout.Undefined, BarrierAccess.ShaderResource, BarrierSync.NonPixelShading | BarrierSync.PixelShading);
|
||||
}
|
||||
|
||||
public void UpdateObjectData(Handle<Mesh> mesh, float4x4 localToWorld)
|
||||
public void UpdateObjectData(Handle<Mesh> mesh)
|
||||
{
|
||||
var r = _resourceManager.GetMeshReference(mesh);
|
||||
if (r.IsFailure)
|
||||
@@ -233,7 +233,6 @@ public readonly unsafe ref struct RenderingContext
|
||||
ref readonly var meshData = ref r.Value;
|
||||
var data = new PerObjectData
|
||||
{
|
||||
localToWorld = localToWorld,
|
||||
worldBoundsMin = meshData.BoundingBox.Min,
|
||||
worldBoundsMax = meshData.BoundingBox.Max,
|
||||
vertexBuffer = _engine.ResourceDatabase.GetBindlessIndex(meshData.VertexBuffer.AsResource()),
|
||||
|
||||
337
src/Runtime/Ghost.Graphics/Obsolete/MeshRenderPass.cs
Normal file
337
src/Runtime/Ghost.Graphics/Obsolete/MeshRenderPass.cs
Normal file
@@ -0,0 +1,337 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Core.Graphics;
|
||||
using Ghost.DSL.ShaderCompiler;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.Core.Contracts;
|
||||
using Ghost.Graphics.RenderGraphModule;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Ghost.Graphics.Utilities;
|
||||
using Misaki.HighPerformance.Image;
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
using Misaki.HighPerformance.Utilities;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ghost.Graphics;
|
||||
|
||||
internal class MeshRenderPassData
|
||||
{
|
||||
public Handle<Mesh> mesh;
|
||||
public Handle<Material> material;
|
||||
public Identifier<RGTexture> renderTarget;
|
||||
}
|
||||
|
||||
internal class BlitPassData
|
||||
{
|
||||
public Identifier<RGTexture> source;
|
||||
public Identifier<RGTexture> destination;
|
||||
|
||||
public Handle<Material> blitMaterial;
|
||||
public Identifier<Sampler> sampler;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simplified bindless mesh render pass using high-level bindless APIs with fully bindless vertex/index buffer access
|
||||
/// </summary>
|
||||
internal class MeshRenderPass : IRenderPass
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct ShaderProperties_MyShader_Standard
|
||||
{
|
||||
public float4 color;
|
||||
public uint texture1;
|
||||
public uint texture2;
|
||||
public uint texture3;
|
||||
public uint texture4;
|
||||
public uint tex_sampler;
|
||||
|
||||
private readonly uint _padding1;
|
||||
private readonly uint _padding2;
|
||||
private readonly uint _padding3;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct ShaderProperties_Hidden_Blit
|
||||
{
|
||||
public uint mainTex;
|
||||
public uint sampler_mainTex;
|
||||
private readonly uint _padding1;
|
||||
private readonly uint _padding2;
|
||||
}
|
||||
|
||||
private Handle<Mesh> _mesh;
|
||||
private Identifier<Shader> _shader;
|
||||
private Handle<Material> _material;
|
||||
private Handle<Texture>[]? _textures;
|
||||
private Identifier<Sampler> _sampler;
|
||||
|
||||
private Identifier<Shader> _blitShader;
|
||||
private Handle<Material> _blitMaterial;
|
||||
|
||||
// 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"
|
||||
];
|
||||
|
||||
private static IEnumerable<ReadOnlyMemory<string>> GetAllVariantCombination(KeywordsGroup[] keywordsGroups)
|
||||
{
|
||||
if (keywordsGroups.Length == 0)
|
||||
{
|
||||
yield return ReadOnlyMemory<string>.Empty;
|
||||
yield break;
|
||||
}
|
||||
|
||||
var firstGroup = keywordsGroups[0];
|
||||
var remainingGroups = keywordsGroups[1..];
|
||||
|
||||
foreach (var combination in GetAllVariantCombination(remainingGroups))
|
||||
{
|
||||
yield return combination;
|
||||
}
|
||||
|
||||
foreach (var keyword in firstGroup.keywords)
|
||||
{
|
||||
foreach (var combination in GetAllVariantCombination(remainingGroups))
|
||||
{
|
||||
var array = new string[combination.Length + 1];
|
||||
array[0] = keyword;
|
||||
combination.Span.CopyTo(array.AsSpan(1));
|
||||
|
||||
yield return array;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CompileBlitShader(ref readonly RenderingContext ctx)
|
||||
{
|
||||
var shaderDescriptor = DSLShaderCompiler.CompileShader("F:/csharp/GhostEngine/src/Runtime/Ghost.Graphics/Shaders/Blit.gshdr", "C:/Users/Misaki/Downloads/Archive").GetValueOrThrow();
|
||||
_blitShader = ctx.ResourceManager.CreateGraphicsShader(shaderDescriptor);
|
||||
_blitMaterial = ctx.ResourceManager.CreateMaterial(_blitShader);
|
||||
|
||||
var config = new ShaderCompilationConfig
|
||||
{
|
||||
optimizeLevel = CompilerOptimizeLevel.O3,
|
||||
options = CompilerOption.KeepReflections,
|
||||
tier = CompilerTier.Tier2
|
||||
};
|
||||
|
||||
var pass = shaderDescriptor.passes[0];
|
||||
var emptyKeywords = new LocalKeywordSet();
|
||||
var variantKey = RHIUtility.CreateShaderVariantKey(
|
||||
RHIUtility.CreateShaderPassKey(pass.identifier),
|
||||
in emptyKeywords);
|
||||
|
||||
ctx.ShaderCompiler.CompilePass(in pass, in config, variantKey).GetValueOrThrow();
|
||||
}
|
||||
|
||||
public void Initialize(ref readonly RenderingContext ctx)
|
||||
{
|
||||
CompileBlitShader(in ctx);
|
||||
|
||||
var shaderDescriptor = DSLShaderCompiler.CompileShader("F:/csharp/GhostEngine/src/Runtime/Ghost.Graphics/test.gshdr", "C:/Users/Misaki/Downloads/Archive").GetValueOrThrow();
|
||||
|
||||
_shader = ctx.ResourceManager.CreateGraphicsShader(shaderDescriptor);
|
||||
_material = ctx.ResourceManager.CreateMaterial(_shader);
|
||||
|
||||
for (var i = 0; i < shaderDescriptor.passes.Length; i++)
|
||||
{
|
||||
ref var pass = ref shaderDescriptor.passes[i];
|
||||
var config = new ShaderCompilationConfig
|
||||
{
|
||||
optimizeLevel = CompilerOptimizeLevel.O3,
|
||||
options = CompilerOption.KeepReflections,
|
||||
tier = CompilerTier.Tier2
|
||||
};
|
||||
|
||||
// TODO: Ideally, in editor mode, we compile a single variant when it's needed during rendering. Before the compilation is done, we fallback to a special "compilation in progress" shader.
|
||||
// During the build process, we can precompile all the variants and store them in the cache for fast loading in runtime.
|
||||
// After the compilation, we should store the compiled result in the disk cache even in editor mode. This allows us to avoid recompiling the same variant, same code hash and same version) multiple times.
|
||||
if (pass.keywords.Length == 0)
|
||||
{
|
||||
var emptyKeywords = new LocalKeywordSet();
|
||||
var variantKey = RHIUtility.CreateShaderVariantKey(
|
||||
RHIUtility.CreateShaderPassKey(pass.identifier),
|
||||
in emptyKeywords);
|
||||
|
||||
ctx.ShaderCompiler.CompilePass(in pass, in config, variantKey).GetValueOrThrow();
|
||||
}
|
||||
else
|
||||
{
|
||||
var shaderResult = ctx.ResourceManager.GetShaderReference(_shader);
|
||||
if (shaderResult.IsFailure)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to get shader reference.");
|
||||
}
|
||||
|
||||
ref readonly var shaderRef = ref shaderResult.Value;
|
||||
foreach (var keyGroup in GetAllVariantCombination(pass.keywords))
|
||||
{
|
||||
config.defines = keyGroup.Span;
|
||||
var keywordsSet = new LocalKeywordSet();
|
||||
|
||||
foreach (var key in keyGroup.Span)
|
||||
{
|
||||
var localIndex = shaderRef.GetLocalKeywordIndex(Shader.GetKeywordID(key));
|
||||
if (localIndex == -1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
keywordsSet.SetKeyword(localIndex, true);
|
||||
}
|
||||
|
||||
var variantKey = RHIUtility.CreateShaderVariantKey(
|
||||
RHIUtility.CreateShaderPassKey(pass.identifier),
|
||||
in keywordsSet);
|
||||
|
||||
ctx.ShaderCompiler.CompilePass(in pass, in config, variantKey).GetValueOrThrow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MeshBuilder.CreateCube(0.75f, default, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent, out var vertices, out var indices);
|
||||
|
||||
_mesh = ctx.CreateMesh(vertices, indices, true);
|
||||
|
||||
// Cook meshlets for the mesh
|
||||
var meshRef = ctx.ResourceManager.GetMeshReference(_mesh);
|
||||
if (meshRef.IsSuccess)
|
||||
{
|
||||
meshRef.Value.CookMeshlets();
|
||||
}
|
||||
|
||||
ctx.UploadMeshlets(_mesh);
|
||||
|
||||
ctx.UpdateObjectData(_mesh);
|
||||
|
||||
_textures = new Handle<Texture>[_textureFiles.Length];
|
||||
for (var i = 0; i < _textureFiles.Length; i++)
|
||||
{
|
||||
using var stream = File.OpenRead(_textureFiles[i]);
|
||||
using var imageData = ImageResult.FromStream(stream, ColorComponents.RGBA);
|
||||
var desc = new TextureDesc
|
||||
{
|
||||
Width = imageData.Width,
|
||||
Height = imageData.Height,
|
||||
Dimension = TextureDimension.Texture2D,
|
||||
Format = TextureFormat.R8G8B8A8_UNorm,
|
||||
MipLevels = 1,
|
||||
Slice = 1,
|
||||
Usage = TextureUsage.ShaderResource,
|
||||
};
|
||||
|
||||
_textures[i] = ctx.CreateTexture<byte>(in desc, imageData.AsSpan(), $"Texture_{i}");
|
||||
}
|
||||
|
||||
var samplerDesc = new SamplerDesc
|
||||
{
|
||||
AddressU = TextureAddressMode.Repeat,
|
||||
AddressV = TextureAddressMode.Repeat,
|
||||
AddressW = TextureAddressMode.Repeat,
|
||||
FilterMode = TextureFilterMode.Bilinear,
|
||||
MaxAnisotropy = 16,
|
||||
};
|
||||
|
||||
_sampler = ctx.ResourceAllocator.CreateSampler(in samplerDesc);
|
||||
|
||||
var meshResult = ctx.ResourceManager.GetMaterialReference(_material);
|
||||
if (meshResult.IsFailure)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to get material reference.");
|
||||
}
|
||||
|
||||
ref var matRef = ref meshResult.Value;
|
||||
var matProps = new ShaderProperties_MyShader_Standard
|
||||
{
|
||||
color = new float4(1.0f, 1.0f, 1.0f, 1.0f),
|
||||
texture1 = ctx.ResourceDatabase.GetBindlessIndex(_textures[0].AsResource()),
|
||||
texture2 = ctx.ResourceDatabase.GetBindlessIndex(_textures[1].AsResource()),
|
||||
texture3 = ctx.ResourceDatabase.GetBindlessIndex(_textures[2].AsResource()),
|
||||
texture4 = ctx.ResourceDatabase.GetBindlessIndex(_textures[3].AsResource()),
|
||||
tex_sampler = (uint)_sampler.Value,
|
||||
};
|
||||
|
||||
matRef.SetPropertyCache(in matProps).ThrowIfFailed();
|
||||
matRef.UploadData(ctx.DirectCommandBuffer, ctx.ResourceDatabase);
|
||||
}
|
||||
|
||||
public void Build(RenderGraph graph, Identifier<RGTexture> backbuffer)
|
||||
{
|
||||
Identifier<RGTexture> renderTarget;
|
||||
using (var builder = graph.AddRasterRenderPass<MeshRenderPassData>("Mesh Render Pass", out var passData))
|
||||
{
|
||||
passData.mesh = _mesh;
|
||||
passData.material = _material;
|
||||
|
||||
passData.renderTarget = builder.CreateTexture(RGTextureDesc.Relative(1.0f, TextureFormat.R8G8B8A8_UNorm), "Render Target");
|
||||
builder.SetColorAttachment(passData.renderTarget, 0);
|
||||
|
||||
renderTarget = passData.renderTarget;
|
||||
|
||||
builder.SetRenderFunc<MeshRenderPassData>(static (data, ctx) =>
|
||||
{
|
||||
ctx.SetActiveMaterial(data.material);
|
||||
ctx.SetActiveMesh(data.mesh);
|
||||
|
||||
var threadGroupCountX = ((uint)ctx.ActiveMeshIndexCount + 2u) / 3u;
|
||||
ctx.DispatchMesh(new uint3(threadGroupCountX, 1u, 1u));
|
||||
});
|
||||
}
|
||||
|
||||
using (var builder = graph.AddUnsafeRenderPass<BlitPassData>("Blit Pass", out var passData))
|
||||
{
|
||||
passData.source = renderTarget;
|
||||
passData.destination = backbuffer;
|
||||
passData.blitMaterial = _blitMaterial;
|
||||
passData.sampler = _sampler;
|
||||
|
||||
builder.UseTexture(passData.source, AccessFlags.Read);
|
||||
builder.UseTexture(passData.destination, AccessFlags.WriteAll);
|
||||
|
||||
builder.SetRenderFunc<BlitPassData>(static (data, ctx) =>
|
||||
{
|
||||
var r = ctx.ResourceManager.GetMaterialReference(data.blitMaterial);
|
||||
if (r.IsFailure)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ref var matRef = ref r.Value;
|
||||
var blitProps = new ShaderProperties_Hidden_Blit
|
||||
{
|
||||
mainTex = ctx.ResourceDatabase.GetBindlessIndex(ctx.GetActualResource(data.source.AsResource())),
|
||||
sampler_mainTex = (uint)data.sampler.Value,
|
||||
};
|
||||
|
||||
matRef.SetPropertyCache(in blitProps).ThrowIfFailed();
|
||||
matRef.UploadData(ctx.CommandBuffer, ctx.ResourceDatabase);
|
||||
|
||||
ctx.CommandBuffer.SetRenderTargets([ctx.GetActualTexture(data.destination)], Handle<Texture>.Invalid);
|
||||
|
||||
ctx.SetActiveMaterial(data.blitMaterial);
|
||||
ctx.SetActiveMesh(Handle<Mesh>.Invalid); // Generate a full-screen triangle dynamically in mesh shader.
|
||||
ctx.DispatchMesh(new uint3(1, 1, 1));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void Cleanup(ResourceManager resourceManager, IResourceDatabase resourceDatabase)
|
||||
{
|
||||
resourceManager.ReleaseMaterial(_blitMaterial);
|
||||
|
||||
resourceManager.ReleaseMaterial(_material);
|
||||
resourceManager.ReleaseShader(_shader);
|
||||
resourceManager.ReleaseMesh(_mesh);
|
||||
resourceDatabase.ReleaseSampler(_sampler);
|
||||
|
||||
if (_textures != null)
|
||||
{
|
||||
foreach (var texture in _textures)
|
||||
{
|
||||
resourceDatabase.ReleaseResource(texture.AsResource());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ struct Meshlet
|
||||
uint packedCounts; // byte vertexCount, byte triangleCount, byte localMaterialIndex, byte lodLevel
|
||||
};
|
||||
|
||||
[numthreads(64, 1, 1)] // 64 threads for max 64 vertices and up to 124 triangles
|
||||
[numthreads(128, 1, 1)] // 128 threads to cover max 64 vertices and 124 triangles
|
||||
[outputtopology("triangle")]
|
||||
void MSMain(
|
||||
uint3 groupThreadID : SV_GroupThreadID,
|
||||
@@ -59,23 +59,12 @@ void MSMain(
|
||||
}
|
||||
|
||||
// Write triangle output (1 thread processes 1 triangle)
|
||||
// We could pack 3 indices in a uint or just use byte offset
|
||||
// In our CPU code, we packed it as individual bytes, so 3 bytes per triangle.
|
||||
// For 124 triangles, we have 372 bytes.
|
||||
if (groupThreadID.x < triangleCount)
|
||||
{
|
||||
uint triangleIndex = groupThreadID.x;
|
||||
uint baseOffset = m.triangleOffset + triangleIndex * 3;
|
||||
|
||||
// Load 4 bytes to get the 3 index bytes
|
||||
// Needs byte-aligned loading
|
||||
uint wordOffset = baseOffset & ~3;
|
||||
uint shift = (baseOffset & 3) * 8;
|
||||
uint packedIndices1 = meshletTrianglesBuffer.Load(wordOffset);
|
||||
uint packedIndices2 = meshletTrianglesBuffer.Load(wordOffset + 4);
|
||||
|
||||
uint64_t combined = ((uint64_t)packedIndices2 << 32) | packedIndices1;
|
||||
uint packedIndices = (uint)(combined >> shift);
|
||||
|
||||
// Load the packed 32-bit integer containing the 3 local indices
|
||||
uint packedIndices = meshletTrianglesBuffer.Load((m.triangleOffset + triangleIndex) * 4);
|
||||
|
||||
uint i0 = packedIndices & 0xFF;
|
||||
uint i1 = (packedIndices >> 8) & 0xFF;
|
||||
|
||||
@@ -28,6 +28,11 @@ internal struct RenderSystemDesc
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public IRenderPipelineSettings? InitialRenderPipelineSettings
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -180,7 +185,7 @@ public class RenderSystem : IDisposable
|
||||
_shutdownEvent = new AutoResetEvent(false);
|
||||
_resizeRequest = new ConcurrentDictionary<ISwapChain, uint2>();
|
||||
|
||||
_renderPipelineSettings = new GhostRenderPipelineSettings();
|
||||
_renderPipelineSettings = _config.InitialRenderPipelineSettings ?? new GhostRenderPipelineSettings();
|
||||
_renderPipeline = _renderPipelineSettings.CreatePipeline(this);
|
||||
|
||||
_isRunning = false;
|
||||
@@ -416,6 +421,7 @@ public class RenderSystem : IDisposable
|
||||
frameResource.Dispose();
|
||||
}
|
||||
|
||||
_renderPipeline.Dispose();
|
||||
_graphicsEngine.Dispose();
|
||||
_shutdownEvent.Dispose();
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
#ifndef BUILTIN_PROPERTIES_HLSL
|
||||
#define BUILTIN_PROPERTIES_HLSL
|
||||
|
||||
#include "F:/csharp/GhostEngine/src/Runtime//Ghost.Graphics/Shaders/Includes/Common.hlsl"
|
||||
#include "F:/csharp/GhostEngine/src/Runtime/Ghost.Graphics/Shaders/Includes/Common.hlsl"
|
||||
|
||||
struct PushConstantData
|
||||
{
|
||||
BYTE_ADDRESS_BUFFER globalBuffer;
|
||||
BYTE_ADDRESS_BUFFER perViewBuffer;
|
||||
BYTE_ADDRESS_BUFFER perObjectBuffer;
|
||||
BYTE_ADDRESS_BUFFER perInstanceBuffer;
|
||||
BYTE_ADDRESS_BUFFER perMaterialBuffer;
|
||||
};
|
||||
|
||||
@@ -24,7 +25,6 @@ struct PerViewData
|
||||
|
||||
struct PerObjectData
|
||||
{
|
||||
float4x4 localToWorld;
|
||||
float3 worldBoundsMin;
|
||||
BYTE_ADDRESS_BUFFER vertexBuffer;
|
||||
float3 worldBoundsMax;
|
||||
|
||||
@@ -10,6 +10,8 @@ namespace Ghost.Graphics.Utilities;
|
||||
internal struct Cluster : IDisposable
|
||||
{
|
||||
public UnsafeList<uint> indices;
|
||||
public UnsafeList<uint> uniqueVertices;
|
||||
public UnsafeList<byte> localIndices;
|
||||
public ClodBounds bounds;
|
||||
public nuint vertices;
|
||||
public int group;
|
||||
@@ -17,7 +19,9 @@ internal struct Cluster : IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
indices.Dispose();
|
||||
if (indices.IsCreated) indices.Dispose();
|
||||
if (uniqueVertices.IsCreated) uniqueVertices.Dispose();
|
||||
if (localIndices.IsCreated) localIndices.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,8 +140,14 @@ public unsafe struct ClodCluster
|
||||
public uint* indices;
|
||||
/// <summary> Number of indices. </summary>
|
||||
public nuint indexCount;
|
||||
/// <summary> Number of vertices in the cluster. </summary>
|
||||
/// <summary> Pointer to unique vertices for this cluster. </summary>
|
||||
public uint* uniqueVertices;
|
||||
/// <summary> Number of unique vertices in the cluster. </summary>
|
||||
public nuint vertexCount;
|
||||
/// <summary> Pointer to local triangle indices for this cluster. </summary>
|
||||
public byte* localIndices;
|
||||
/// <summary> Number of local indices. </summary>
|
||||
public nuint localIndexCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -244,13 +254,22 @@ public static unsafe class MeshletUtility
|
||||
{
|
||||
vertices = meshlet.vertex_count,
|
||||
indices = new UnsafeList<uint>((int)(meshlet.triangle_count * 3), Allocator.Persistent),
|
||||
uniqueVertices = new UnsafeList<uint>((int)meshlet.vertex_count, Allocator.Persistent),
|
||||
localIndices = new UnsafeList<byte>((int)(meshlet.triangle_count * 3), Allocator.Persistent),
|
||||
group = -1,
|
||||
refined = -1
|
||||
};
|
||||
|
||||
for (nuint j = 0; j < meshlet.vertex_count; j++)
|
||||
{
|
||||
cluster.uniqueVertices.Add(pMeshletVertices[meshlet.vertex_offset + j]);
|
||||
}
|
||||
|
||||
for (nuint j = 0; j < meshlet.triangle_count * 3; j++)
|
||||
{
|
||||
cluster.indices.Add(pMeshletVertices[meshlet.vertex_offset + pMeshletTriangles[meshlet.triangle_offset + j]]);
|
||||
byte localIdx = pMeshletTriangles[meshlet.triangle_offset + j];
|
||||
cluster.localIndices.Add(localIdx);
|
||||
cluster.indices.Add(pMeshletVertices[meshlet.vertex_offset + localIdx]);
|
||||
}
|
||||
|
||||
clusters.Add(cluster);
|
||||
@@ -377,7 +396,10 @@ public static unsafe class MeshletUtility
|
||||
: srcCluster.bounds,
|
||||
indices = (uint*)srcCluster.indices.GetUnsafePtr(),
|
||||
indexCount = (nuint)srcCluster.indices.Count,
|
||||
vertexCount = srcCluster.vertices
|
||||
uniqueVertices = (uint*)srcCluster.uniqueVertices.GetUnsafePtr(),
|
||||
vertexCount = srcCluster.vertices,
|
||||
localIndices = (byte*)srcCluster.localIndices.GetUnsafePtr(),
|
||||
localIndexCount = (nuint)srcCluster.localIndices.Count
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user