diff --git a/.idea/.idea.Coplt.Graphics/.idea/vcs.xml b/.idea/.idea.Coplt.Graphics/.idea/vcs.xml index 94a25f7..dd3e634 100644 --- a/.idea/.idea.Coplt.Graphics/.idea/vcs.xml +++ b/.idea/.idea.Coplt.Graphics/.idea/vcs.xml @@ -2,5 +2,13 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/editor.xml b/.idea/editor.xml index 25fcf2b..faedd0b 100644 --- a/.idea/editor.xml +++ b/.idea/editor.xml @@ -1,486 +1,250 @@ - \ No newline at end of file diff --git a/Assets/pattern.png b/Assets/pattern.png new file mode 100644 index 0000000..8de847e Binary files /dev/null and b/Assets/pattern.png differ diff --git a/Coplt.Graphics.Core/Coplt.Graphics.Core.csproj b/Coplt.Graphics.Core/Coplt.Graphics.Core.csproj index 19ce236..39fc16d 100644 --- a/Coplt.Graphics.Core/Coplt.Graphics.Core.csproj +++ b/Coplt.Graphics.Core/Coplt.Graphics.Core.csproj @@ -14,7 +14,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Coplt.Graphics.Core/Core/ComputeShaderPipeline.cs b/Coplt.Graphics.Core/Core/ComputeShaderPipeline.cs new file mode 100644 index 0000000..514772a --- /dev/null +++ b/Coplt.Graphics.Core/Core/ComputeShaderPipeline.cs @@ -0,0 +1,22 @@ +using Coplt.Graphics.Native; +using Coplt.Graphics.States; + +namespace Coplt.Graphics.Core; + +public sealed unsafe class ComputeShaderPipeline : ShaderPipeline +{ + #region Props + + public new FComputeShaderPipeline* Ptr => (FComputeShaderPipeline*)m_ptr; + + #endregion + + #region Ctor + + internal ComputeShaderPipeline( + FComputeShaderPipeline* ptr, string? name, + Shader shader, ShaderBindingLayout binding_layout + ) : base((FShaderPipeline*)ptr, name, shader, binding_layout) { } + + #endregion +} diff --git a/Coplt.Graphics.Core/Core/GpuBuffer.cs b/Coplt.Graphics.Core/Core/GpuBuffer.cs index c2d7ae2..61d5681 100644 --- a/Coplt.Graphics.Core/Core/GpuBuffer.cs +++ b/Coplt.Graphics.Core/Core/GpuBuffer.cs @@ -69,7 +69,7 @@ m_name is null #endregion - #region View + #region Try View public bool TryVbv() => Purpose.HasFlags(ResourcePurpose.VertexBuffer); public bool TryIbv() => Purpose.HasFlags(ResourcePurpose.IndexBuffer); @@ -80,6 +80,13 @@ m_name is null #endregion + #region Create View + + public View View(ulong Offset = 0, int Size = -1, int Stride = -1, GraphicsFormat Format = 0) => + Core.View.MakeBuffer(this, Offset, Size, Stride, Format); + + #endregion + #region Map UnMap public void* Map() => Map(CpuAccess != CpuAccess.Read); diff --git a/Coplt.Graphics.Core/Core/GpuDevice.cs b/Coplt.Graphics.Core/Core/GpuDevice.cs index 4802036..57efd96 100644 --- a/Coplt.Graphics.Core/Core/GpuDevice.cs +++ b/Coplt.Graphics.Core/Core/GpuDevice.cs @@ -382,6 +382,34 @@ public GraphicsShaderPipeline CreateGraphicsShaderPipeline( #endregion + #region CreateComputeShaderPipeline + + public ComputeShaderPipeline CreateComputeShaderPipeline( + Shader Shader, string? Name = null, ReadOnlySpan Name8 = default + ) => CreateComputeShaderPipeline(Shader, Shader.Layout.GetEmptyBindingLayout(), Name, Name8); + + public ComputeShaderPipeline CreateComputeShaderPipeline( + Shader Shader, ShaderBindingLayout BindingLayout, + string? Name = null, ReadOnlySpan Name8 = default + ) + { + fixed (char* p_name = Name) + fixed (byte* p_name8 = Name8) + { + FShaderPipelineCreateOptions f_options = new() + { + Name = new(Name, Name8, p_name, p_name8), + Shader = Shader.Ptr, + Layout = BindingLayout.Ptr, + }; + FComputeShaderPipeline* ptr; + Ptr->CreateComputePipeline(&f_options, &ptr).TryThrow(); + return new(ptr, Name, Shader, BindingLayout); + } + } + + #endregion + #region CreateSampler public Sampler CreateSampler( diff --git a/Coplt.Graphics.Core/Core/GpuImage.cs b/Coplt.Graphics.Core/Core/GpuImage.cs index fd2406e..f1ef2f5 100644 --- a/Coplt.Graphics.Core/Core/GpuImage.cs +++ b/Coplt.Graphics.Core/Core/GpuImage.cs @@ -136,7 +136,7 @@ m_name is null #endregion - #region View + #region Try View public bool TryRtv() => Purpose.HasFlags(ResourcePurpose.RenderTarget); public bool TryDsv() => Purpose.HasFlags(ResourcePurpose.DepthStencil); @@ -144,4 +144,34 @@ m_name is null public bool TrySrv() => Purpose.HasFlags(ResourcePurpose.ShaderResource); #endregion + + #region Create View + + /// Uav 忽略 NumMips + public View View1D(byte Mip = 0, sbyte NumMips = -1, GraphicsFormat Format = 0) + => View.MakeImage1D(this, Mip, NumMips, Format); + /// Uav 忽略 NumMips + public View View1DArray(uint Index = 0, int Size = -1, byte Mip = 0, sbyte NumMips = -1, GraphicsFormat Format = 0) + => View.MakeImage1DArray(this, Index, Size, Mip, NumMips, Format); + /// Uav 忽略 NumMips + public View View2D(byte Mip = 0, sbyte NumMips = -1, byte Plane = 0, GraphicsFormat Format = 0) + => View.MakeImage2D(this, Mip, NumMips, Plane, Format); + /// Uav 忽略 NumMips + public View View2DArray(uint Index = 0, int Size = -1, byte Mip = 0, sbyte NumMips = -1, byte Plane = 0, GraphicsFormat Format = 0) + => View.MakeImage2DArray(this, Index, Size, Mip, NumMips, Plane, Format); + public View View2DMs(GraphicsFormat Format = 0) + => View.MakeImage2DMs(this, Format); + public View View2DMsArray(uint Index = 0, int Size = -1, GraphicsFormat Format = 0) + => View.MakeImage2DMsArray(this, Index, Size, Format); + /// Uav 忽略 NumMips + public View View3D(uint Z = 0, int Depth = -1, byte Mip = 0, sbyte NumMips = -1, GraphicsFormat Format = 0) + => View.MakeImage3D(this, Z, Depth, Mip, NumMips, Format); + /// Uav 忽略 NumMips + public View ViewCube(byte Mip = 0, sbyte NumMips = -1, GraphicsFormat Format = 0) + => View.MakeImageCube(this, Mip, NumMips, Format); + /// Uav 忽略 NumMips + public View ViewCubeArray(uint Index = 0, int Size = -1, byte Mip = 0, sbyte NumMips = -1, GraphicsFormat Format = 0) + => View.MakeImageCubeArray(this, Index, Size, Mip, NumMips, Format); + + #endregion } diff --git a/Coplt.Graphics.Core/Core/GpuIsolate.cs b/Coplt.Graphics.Core/Core/GpuIsolate.cs index 626ab00..514de8e 100644 --- a/Coplt.Graphics.Core/Core/GpuIsolate.cs +++ b/Coplt.Graphics.Core/Core/GpuIsolate.cs @@ -304,8 +304,8 @@ public ShaderBindGroup CreateBindGroup( f_items[i] = new() { View = item.View.ToFFI(), - Slot = item.Slot, - Index = item.Index, + Slot = item.BindIndex, + Index = item.ArrayIndex, }; } fixed (char* p_name = Name) @@ -421,6 +421,12 @@ public GpuImage CreateImage( fixed (char* p_name = Name) fixed (byte* p_name8 = Name8) { + var DepthOrLength = Math.Max(options.DepthOrLength, 1); + if (options.Dimension == ImageDimension.Cube) + { + if (options.DepthOrLength == 0) DepthOrLength = 6; + if (options.DepthOrLength % 6 != 0) throw new ArgumentException("The number of cube texture arrays must be a multiple of 6"); + } FGpuImageCreateOptions f_options = new() { Base = @@ -432,7 +438,7 @@ public GpuImage CreateImage( Format = options.Format.ToFFI(), Width = Math.Max(options.Width, 1), Height = Math.Max(options.Height, 1), - DepthOrLength = Math.Max(options.DepthOrLength, 1), + DepthOrLength = DepthOrLength, MipLevels = (ushort)Math.Max(options.MipLevels, 1), MultisampleCount = (byte)Math.Max(options.MultisampleCount, 1), Dimension = options.Dimension.ToFFI(), diff --git a/Coplt.Graphics.Core/Core/GpuQueue.cs b/Coplt.Graphics.Core/Core/GpuQueue.cs deleted file mode 100644 index be24a80..0000000 --- a/Coplt.Graphics.Core/Core/GpuQueue.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Collections.Concurrent; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Coplt.Dropping; -using Coplt.Graphics.Native; -using Coplt.Graphics.Utilities; - -namespace Coplt.Graphics.Core; - -public enum GpuQueueType : byte -{ - Direct, - Compute, - Copy, - VideoEncode, - VideoDecode, - VideoProcess, -} - -public static partial class GraphicsExtensions -{ - public static FGpuQueueType ToFFI(this GpuQueueType value) => (FGpuQueueType)value; - - public static GpuQueueType FromFFI(this FGpuQueueType value) => (GpuQueueType)value; - - public static ReadOnlySpan ToUtf8String(this GpuQueueType value) => value switch - { - GpuQueueType.Direct => "Direct"u8, - GpuQueueType.Compute => "Compute"u8, - GpuQueueType.Copy => "Copy"u8, - GpuQueueType.VideoEncode => "VideoEncode"u8, - GpuQueueType.VideoDecode => "VideoDecode"u8, - GpuQueueType.VideoProcess => "VideoProcess"u8, - _ => throw new ArgumentOutOfRangeException(nameof(value), value, null) - }; -} diff --git a/Coplt.Graphics.Core/Core/GpuRecord.cs b/Coplt.Graphics.Core/Core/GpuRecord.cs index 8baaefa..b6ac529 100644 --- a/Coplt.Graphics.Core/Core/GpuRecord.cs +++ b/Coplt.Graphics.Core/Core/GpuRecord.cs @@ -14,7 +14,7 @@ public sealed unsafe class GpuRecord : IsolateChild #region Fields internal FGpuRecordData* m_data; - internal readonly Dictionary m_resources = new(); + internal readonly Dictionary m_resources = new(); internal readonly HashSet m_objects = new(); internal uint m_debug_scope_count; internal bool m_in_render_or_compute_scope; @@ -104,6 +104,7 @@ internal void AssertEndOrCanEnd() /// public FSlice AllocUploadMemory(uint Size, out UploadLoc loc, uint Align = 4) { + var r_size = Size; if (Align > 1) Size += Align - 1; ref var upload_buffers = ref Data.Context->m_upload_buffer; for (nuint i = 0; i < upload_buffers.LongLength; i++) @@ -112,9 +113,9 @@ public FSlice AllocUploadMemory(uint Size, out UploadLoc loc, uint Align = if (block.cur_offset + Size < block.size) { var offset = block.cur_offset.Aligned(Align); - var r = new FSlice(block.mapped_ptr, (nuint)block.size).Slice((nuint)offset); - loc = new UploadLoc(Data.Id, Data.Version, i, offset, Size); - block.cur_offset += Size; + var r = new FSlice(block.mapped_ptr, (nuint)block.size).Slice((nuint)offset, r_size); + loc = new UploadLoc(Data.Id, Data.Version, i, offset, r_size); + block.cur_offset += r_size; return r; } } @@ -125,9 +126,9 @@ public FSlice AllocUploadMemory(uint Size, out UploadLoc loc, uint Align = ref var block = ref upload_buffers[i]; if (block.cur_offset + Size >= block.size) throw new OutOfMemoryException(); var offset = block.cur_offset.Aligned(Align); - var r = new FSlice(block.mapped_ptr, (nuint)block.size).Slice((nuint)offset); - loc = new UploadLoc(Data.Id, Data.Version, i, offset, Size); - block.cur_offset += Size; + var r = new FSlice(block.mapped_ptr, (nuint)block.size).Slice((nuint)offset, r_size); + loc = new UploadLoc(Data.Id, Data.Version, i, offset, r_size); + block.cur_offset += r_size; return r; } } @@ -138,6 +139,18 @@ public UploadLoc WriteToUpload(ReadOnlySpan Data, uint Align = 4) return loc; } + public UploadLoc UploadConstants(ReadOnlySpan Data) where T : unmanaged + { + var bytes = MemoryMarshal.AsBytes(Data); + var size = ((uint)bytes.Length).Aligned(256); + var mem = AllocUploadMemory(size, out var loc, 256); + bytes.CopyTo(mem); + return loc; + } + + public UploadLoc UploadConstants(T Data) where T : unmanaged => + UploadConstants(new ReadOnlySpan(in Data)); + /// /// 分配用于上传纹理的 256 对齐的内存 /// @@ -289,7 +302,7 @@ public void Label(ReadOnlySpan Label, Color? Color = null) #region Scope - public DebugScope2 Scope(string Name, Color? Color = null) + public DebugScope Scope(string Name, Color? Color = null) { AssertNotEnded(); if (!DebugEnabled) return new(this); @@ -313,7 +326,7 @@ public DebugScope2 Scope(string Name, Color? Color = null) return new(this); } - public DebugScope2 Scope(ReadOnlySpan Name, Color? Color = null) + public DebugScope Scope(ReadOnlySpan Name, Color? Color = null) { AssertNotEnded(); if (!DebugEnabled) return new(this); @@ -421,6 +434,80 @@ public void Copy( #endregion + #region BufferImageCopy + + public void Copy( + GpuImage Image, + GpuBuffer Buffer, + uint BytesPerRow, + uint RowsPerImage, + uint MipLevel = 0, + uint ImageIndex = 0, + uint ImageCount = 1, + ImagePlane Plane = ImagePlane.All, + ulong BufferOffset = 0, + bool ImageToBuffer = false + ) => Copy( + Image, Buffer, + 0, 0, 0, + Image.Width, Image.Height, Image.DepthOrLength, + BytesPerRow, RowsPerImage, + MipLevel, ImageIndex, ImageCount, Plane, BufferOffset + ); + + public void Copy( + GpuImage Image, + GpuBuffer Buffer, + uint ImageX, + uint ImageY, + uint ImageZ, + uint ImageWidth, + uint ImageHeight, + uint ImageDepth, + uint BytesPerRow, + uint RowsPerImage, + uint MipLevel = 0, + uint ImageIndex = 0, + uint ImageCount = 1, + ImagePlane Plane = ImagePlane.All, + ulong BufferOffset = 0, + bool ImageToBuffer = false + ) + { + AssertNotEnded(); + Image.AssertSameIsolate(Isolate); + Buffer.AssertSameIsolate(Isolate); + var cmd = new FCmdBufferImageCopy + { + Base = { Type = FCmdType.BufferImageCopy }, + RangeIndex = (uint)Data.PayloadBufferImageCopyRange.LongLength, + Image = AddResource(Image), + Buffer = { Buffer = AddResource(Buffer) }, + BufferType = FBufferRefType2.Buffer, + ImageToBuffer = ImageToBuffer, + }; + var range = new FBufferImageCopyRange + { + BufferOffset = BufferOffset, + BytesPerRow = BytesPerRow, + RowsPerImage = RowsPerImage, + ImageIndex = ImageIndex, + ImageCount = ImageCount, + MipLevel = (ushort)MipLevel, + Plane = Plane.ToFFI(), + }; + range.ImageOffset[0] = ImageX; + range.ImageOffset[1] = ImageY; + range.ImageOffset[2] = ImageZ; + range.ImageExtent[0] = ImageWidth; + range.ImageExtent[1] = ImageHeight; + range.ImageExtent[2] = ImageDepth; + Data.PayloadBufferImageCopyRange.Add(range); + Data.Commands.Add(new() { BufferImageCopy = cmd }); + } + + #endregion + #region BufferUpload public void Upload( @@ -494,7 +581,7 @@ public void Upload( } #endregion - + #region ImageUpload public void Upload( @@ -581,17 +668,17 @@ public void Upload( #region Render - public RenderScope2 Render( + public RenderScope Render( ReadOnlySpan Rtvs, RenderInfo.DsvInfo? Dsv = null, bool AutoViewportScissor = true, string? Name = null, ReadOnlySpan Name8 = default ) => Render(new(Rtvs, Dsv), AutoViewportScissor, Name, Name8); - public RenderScope2 Render( + public RenderScope Render( RenderInfo.DsvInfo Dsv, ReadOnlySpan Rtvs = default, bool AutoViewportScissor = true, string? Name = null, ReadOnlySpan Name8 = default ) => Render(new(Rtvs, Dsv), AutoViewportScissor, Name, Name8); - public RenderScope2 Render( + public RenderScope Render( in RenderInfo Info, bool AutoViewportScissor = true, string? Name = null, ReadOnlySpan Name8 = default @@ -756,7 +843,7 @@ public RenderScope2 Render( #region Scope - RenderScope2 scope = new(this, info_index, cmd_index, debug_scope, m_debug_scope_count); + RenderScope scope = new(this, info_index, cmd_index, debug_scope, m_debug_scope_count); #endregion @@ -779,7 +866,7 @@ public RenderScope2 Render( #region Compute - public ComputeScope2 Compute(string? Name = null, ReadOnlySpan Name8 = default) + public ComputeScope Compute(string? Name = null, ReadOnlySpan Name8 = default) { AssertNotEnded(); @@ -824,7 +911,7 @@ public ComputeScope2 Compute(string? Name = null, ReadOnlySpan Name8 = def #region Scope - ComputeScope2 scope = new(this, cmd_index, debug_scope, m_debug_scope_count); + ComputeScope scope = new(this, cmd_index, debug_scope, m_debug_scope_count); #endregion @@ -832,16 +919,94 @@ public ComputeScope2 Compute(string? Name = null, ReadOnlySpan Name8 = def } #endregion + + #region Binding + + internal void SetDynArraySize(ShaderBindGroup Group, uint Size) + { + AssertNotEnded(); + if (Group.Layout.Data.Usage != FBindGroupUsage.Dynamic) + throw new ArgumentException("Dynamic array size can only be set for dynamic groups"); + AddObject(Group); + var cmd = new FCmdSetDynArraySize + { + Base = { Type = FCmdType.SetDynArraySize }, + Group = Group.Ptr, + Size = Size, + }; + Data.Commands.Add(new() { SetDynArraySize = cmd }); + } + + internal void SetConstants(ShaderBindGroup Group, uint BindIndex, ReadOnlySpan Constants, uint Offset = 0) where T : unmanaged + { + if ((sizeof(T) & 3) != 0) throw new ArgumentException("Constant size must be a multiple of 4"); + SetConstants(Group, BindIndex, MemoryMarshal.Cast(Constants), Offset); + } + + internal void SetConstants(ShaderBindGroup Group, uint BindIndex, in T Constant, uint Offset = 0) where T : unmanaged + { + if ((sizeof(T) & 3) != 0) throw new ArgumentException("Constant size must be a multiple of 4"); + SetConstants(Group, BindIndex, MemoryMarshal.Cast(new ReadOnlySpan(in Constant)), Offset); + } + + [OverloadResolutionPriority(1)] + internal void SetConstants(ShaderBindGroup Group, uint BindIndex, ReadOnlySpan Constants, uint Offset = 0) + { + AssertNotEnded(); + if (Constants.Length == 0) return; + AddObject(Group); + var cmd = new FCmdSetConstants + { + Base = { Type = FCmdType.SetConstants }, + Group = Group.Ptr, + Slot = BindIndex, + ValueIndex = (uint)Data.Payload32Bits.LongLength, + Count = (uint)Constants.Length, + Offset = Offset, + }; + Data.Payload32Bits.AddRange(Constants); + Data.Commands.Add(new() { SetConstants = cmd }); + } + + internal void SetBindItem(ShaderBindGroup Group, ReadOnlySpan Items) + { + AssertNotEnded(); + if (Group.Layout.Data.Usage != FBindGroupUsage.Dynamic) + throw new ArgumentException("Only items in a dynamic group can be set dynamically"); + if (Items.Length == 0) return; + AddObject(Group); + var cmd = new FCmdSetBindItem + { + Base = { Type = FCmdType.SetBindItem }, + Group = Group.Ptr, + ItemIndex = (uint)Data.PayloadSetBindItem.LongLength, + Count = (uint)Items.Length, + }; + var dst = Data.PayloadSetBindItem.UnsafeAddRange(Items.Length); + for (var i = 0; i < Items.Length; i++) + { + ref readonly var item = ref Items[i]; + dst[i] = new() + { + View = item.View.ToFFI(), + Slot = item.BindIndex, + Index = item.ArrayIndex, + }; + } + Data.Commands.Add(new() { SetBindItem = cmd }); + } + + #endregion } #region DebugScope -public struct DebugScope2(GpuRecord self) : IDisposable +public struct DebugScope(GpuRecord self) : IDisposable { private bool m_disposed; public void Dispose() { - if (m_disposed) throw new ObjectDisposedException(nameof(DebugScope2)); + if (m_disposed) throw new ObjectDisposedException(nameof(DebugScope)); m_disposed = true; if (!self.DebugEnabled) return; var cmd = new FCmdEndScope @@ -902,7 +1067,7 @@ public void SetBinding(GpuRecord self, ShaderBinding Binding) { Base = { Type = FCmdType.SetBinding }, Binding = binding_index, - Index = self.Data.NumSetBindings++, + // Index = self.Data.NumSetBindings++, }; self.Data.Commands.Add(new() { SetBinding = cmd }); } @@ -914,7 +1079,7 @@ public void SetBinding(GpuRecord self, ShaderBinding Binding) #region RenderScope -public unsafe struct RenderScope2( +public unsafe struct RenderScope( GpuRecord self, uint info_index, uint cmd_index, @@ -940,7 +1105,7 @@ uint debug_scope_count public void Dispose() { - if (m_disposed) throw new ObjectDisposedException(nameof(RenderScope2)); + if (m_disposed) throw new ObjectDisposedException(nameof(RenderScope)); m_disposed = true; if (self.m_debug_scope_count > debug_scope_count) throw new InvalidOperationException( @@ -949,7 +1114,7 @@ public void Dispose() Cmd.CommandCount = (uint)self.m_data->Commands.LongLength - cmd_index; self.m_in_render_or_compute_scope = false; self.Data.Commands.Add(new() { Type = FCmdType.End }); - if (debug_scope) new DebugScope2(self).Dispose(); + if (debug_scope) new DebugScope(self).Dispose(); } #endregion @@ -966,9 +1131,9 @@ public void Dispose() #region Scope - public DebugScope2 Scope(string Name, Color? Color = null) => self.Scope(Name, Color); + public DebugScope Scope(string Name, Color? Color = null) => self.Scope(Name, Color); - public DebugScope2 Scope(ReadOnlySpan Name, Color? Color = null) => self.Scope(Name, Color); + public DebugScope Scope(ReadOnlySpan Name, Color? Color = null) => self.Scope(Name, Color); #endregion @@ -976,7 +1141,7 @@ public void Dispose() #region SetPipeline - public void SetPipeline(ShaderPipeline Pipeline) + public void SetPipeline(GraphicsShaderPipeline Pipeline) { self.AssertNotEnded(); if (m_pipeline_context.m_current_pipeline == Pipeline) return; @@ -995,6 +1160,25 @@ public void SetBinding(ShaderBinding Binding) m_pipeline_context.SetBinding(self, Binding); } + /// + /// 设置动态绑定的动态数组大小,调用后之前的动态数组内容将被丢弃 + /// + public void SetDynArraySize(ShaderBindGroup Group, uint Size) => + self.SetDynArraySize(Group, Size); + + [OverloadResolutionPriority(1)] + public void SetConstants(ShaderBindGroup Group, uint BindIndex, ReadOnlySpan Constants, uint Offset = 0) => + self.SetConstants(Group, BindIndex, Constants, Offset); + + public void SetConstants(ShaderBindGroup Group, uint BindIndex, ReadOnlySpan Constants, uint Offset = 0) where T : unmanaged => + self.SetConstants(Group, BindIndex, Constants, Offset); + + public void SetConstants(ShaderBindGroup Group, uint BindIndex, in T Constant, uint Offset = 0) where T : unmanaged => + self.SetConstants(Group, BindIndex, Constant, Offset); + + public void SetBindItem(ShaderBindGroup Group, ReadOnlySpan Items) => + self.SetBindItem(Group, Items); + #endregion #region SetViewportScissor @@ -1106,7 +1290,7 @@ public void Draw( ) => Draw(null, false, VertexCount, InstanceCount, FirstVertex, FirstInstance, 0, Binding); public void Draw( - ShaderPipeline? Pipeline, + GraphicsShaderPipeline? Pipeline, uint VertexCount, uint InstanceCount = 1, uint FirstVertex = 0, uint FirstInstance = 0, ShaderBinding? Binding = null @@ -1119,14 +1303,14 @@ public void DrawIndexed( ) => Draw(null, true, IndexCount, InstanceCount, FirstIndex, FirstInstance, VertexOffset, Binding); public void DrawIndexed( - ShaderPipeline? Pipeline, + GraphicsShaderPipeline? Pipeline, uint IndexCount, uint InstanceCount = 1, uint FirstIndex = 0, uint FirstInstance = 0, uint VertexOffset = 0, ShaderBinding? Binding = null ) => Draw(Pipeline, true, IndexCount, InstanceCount, FirstIndex, FirstInstance, VertexOffset, Binding); public void Draw( - ShaderPipeline? Pipeline, bool Indexed, + GraphicsShaderPipeline? Pipeline, bool Indexed, uint VertexOrIndexCount, uint InstanceCount = 1, uint FirstVertexOrIndex = 0, uint FirstInstance = 0, uint VertexOffset = 0, ShaderBinding? Binding = null @@ -1148,7 +1332,11 @@ public void Draw( if (Binding != null) SetBinding(Binding); var cmd = new FCmdDraw { - Base = { Type = FCmdType.Draw }, + Base = + { + Base = { Type = FCmdType.Draw }, + SyncBindingIndex = self.Data.NumSyncBindings++, + }, VertexOrIndexCount = VertexOrIndexCount, InstanceCount = InstanceCount, FirstVertexOrIndex = FirstVertexOrIndex, @@ -1169,11 +1357,13 @@ public void DispatchMesh( ) => DispatchMesh(null, GroupCountX, GroupCountY, GroupCountZ, Binding); public void DispatchMesh( - ShaderPipeline? Pipeline, + GraphicsShaderPipeline? Pipeline, uint GroupCountX = 1, uint GroupCountY = 1, uint GroupCountZ = 1, ShaderBinding? Binding = null ) { + if (GroupCountX == 0 || GroupCountY == 0 || GroupCountZ == 0) + throw new ArgumentException("GroupCountX and GroupCountY and GroupCountZ must not be 0"); self.AssertNotEnded(); if (Pipeline != null) { @@ -1190,7 +1380,11 @@ public void DispatchMesh( if (Binding != null) SetBinding(Binding); var cmd = new FCmdDispatch { - Base = { Type = FCmdType.Dispatch }, + Base = + { + Base = { Type = FCmdType.Dispatch }, + SyncBindingIndex = self.Data.NumSyncBindings++, + }, GroupCountX = GroupCountX, GroupCountY = GroupCountY, GroupCountZ = GroupCountZ, @@ -1206,7 +1400,7 @@ public void DispatchMesh( #region ComputeScope -public unsafe struct ComputeScope2( +public unsafe struct ComputeScope( GpuRecord self, uint cmd_index, bool debug_scope, @@ -1230,7 +1424,7 @@ uint debug_scope_count public void Dispose() { - if (m_disposed) throw new ObjectDisposedException(nameof(ComputeScope2)); + if (m_disposed) throw new ObjectDisposedException(nameof(ComputeScope)); m_disposed = true; if (self.m_debug_scope_count > debug_scope_count) throw new InvalidOperationException( @@ -1239,7 +1433,7 @@ public void Dispose() Cmd.CommandCount = (uint)self.m_data->Commands.LongLength - cmd_index; self.m_in_render_or_compute_scope = false; self.Data.Commands.Add(new() { Type = FCmdType.End }); - if (debug_scope) new DebugScope2(self).Dispose(); + if (debug_scope) new DebugScope(self).Dispose(); } #endregion @@ -1256,9 +1450,9 @@ public void Dispose() #region Scope - public DebugScope2 Scope(string Name, Color? Color = null) => self.Scope(Name, Color); + public DebugScope Scope(string Name, Color? Color = null) => self.Scope(Name, Color); - public DebugScope2 Scope(ReadOnlySpan Name, Color? Color = null) => self.Scope(Name, Color); + public DebugScope Scope(ReadOnlySpan Name, Color? Color = null) => self.Scope(Name, Color); #endregion @@ -1266,7 +1460,7 @@ public void Dispose() #region SetPipeline - public void SetPipeline(ShaderPipeline Pipeline) + public void SetPipeline(ComputeShaderPipeline Pipeline) { self.AssertNotEnded(); if (m_pipeline_context.m_current_pipeline == Pipeline) return; @@ -1285,6 +1479,24 @@ public void SetBinding(ShaderBinding Binding) m_pipeline_context.SetBinding(self, Binding); } + /// + /// 设置动态绑定的动态数组大小,调用后之前的动态数组内容将被丢弃 + /// + public void SetDynArraySize(ShaderBindGroup Group, uint Size) => self.SetDynArraySize(Group, Size); + + [OverloadResolutionPriority(1)] + public void SetConstants(ShaderBindGroup Group, uint BindIndex, ReadOnlySpan Constants, uint Offset = 0) => + self.SetConstants(Group, BindIndex, Constants, Offset); + + public void SetConstants(ShaderBindGroup Group, uint BindIndex, ReadOnlySpan Constants, uint Offset = 0) where T : unmanaged => + self.SetConstants(Group, BindIndex, Constants, Offset); + + public void SetConstants(ShaderBindGroup Group, uint BindIndex, in T Constant, uint Offset = 0) where T : unmanaged => + self.SetConstants(Group, BindIndex, Constant, Offset); + + public void SetBindItem(ShaderBindGroup Group, ReadOnlySpan Items) => + self.SetBindItem(Group, Items); + #endregion #region Dispatch @@ -1295,11 +1507,13 @@ public void Dispatch( ) => Dispatch(null, GroupCountX, GroupCountY, GroupCountZ, Binding); public void Dispatch( - ShaderPipeline? Pipeline, + ComputeShaderPipeline? Pipeline, uint GroupCountX = 1, uint GroupCountY = 1, uint GroupCountZ = 1, ShaderBinding? Binding = null ) { + if (GroupCountX == 0 || GroupCountY == 0 || GroupCountZ == 0) + throw new ArgumentException("GroupCountX and GroupCountY and GroupCountZ must not be 0"); self.AssertNotEnded(); if (Pipeline != null) { @@ -1316,7 +1530,11 @@ public void Dispatch( if (Binding != null) SetBinding(Binding); var cmd = new FCmdDispatch { - Base = { Type = FCmdType.Dispatch }, + Base = + { + Base = { Type = FCmdType.Dispatch }, + SyncBindingIndex = self.Data.NumSyncBindings++, + }, GroupCountX = GroupCountX, GroupCountY = GroupCountY, GroupCountZ = GroupCountZ, diff --git a/Coplt.Graphics.Core/Core/GraphicsFormat.cs b/Coplt.Graphics.Core/Core/GraphicsFormat.cs index 67432c2..0561e79 100644 --- a/Coplt.Graphics.Core/Core/GraphicsFormat.cs +++ b/Coplt.Graphics.Core/Core/GraphicsFormat.cs @@ -1,4 +1,6 @@ using Coplt.Graphics.Native; +using Coplt.Union; +using Coplt.Union.Misc; namespace Coplt.Graphics { diff --git a/Coplt.Graphics.Core/Core/GraphicsInstance.cs b/Coplt.Graphics.Core/Core/GraphicsInstance.cs index c02b28a..86653bc 100644 --- a/Coplt.Graphics.Core/Core/GraphicsInstance.cs +++ b/Coplt.Graphics.Core/Core/GraphicsInstance.cs @@ -301,6 +301,7 @@ private SlotId AllocSlotId(string name) return id; } private Func? m_cache_AllocSlotId; + /// 不区分大小写 // todo 改进 SlotId,保留区分大小写的字符串 public SlotId GetSlotId(string name) => m_slot_id_cache.GetOrAdd(name, m_cache_AllocSlotId ??= AllocSlotId); public string? TryGetSlotName(SlotId SlotId) => m_slot_id_name.GetValueOrDefault(SlotId); public String8? TryGetSlotName8(SlotId SlotId) => m_slot_id_name8.GetValueOrDefault(SlotId); diff --git a/Coplt.Graphics.Core/Core/ShaderBindGroup.cs b/Coplt.Graphics.Core/Core/ShaderBindGroup.cs index 98c9f69..b82dcbf 100644 --- a/Coplt.Graphics.Core/Core/ShaderBindGroup.cs +++ b/Coplt.Graphics.Core/Core/ShaderBindGroup.cs @@ -3,14 +3,14 @@ namespace Coplt.Graphics.Core; -/// 绑定槽的索引 -/// 如果绑定是数组的话,数组中的索引 +/// 绑定槽的索引 +/// 如果绑定是数组的话,数组中的索引 /// 绑定内容 -public record struct SetShaderBindItem(uint Slot, uint Index, View View) +public record struct SetShaderBindItem(uint BindIndex, uint ArrayIndex, View View) { - /// 绑定槽的索引 + /// 绑定槽的索引 /// 绑定内容 - public SetShaderBindItem(uint Slot, View View) : this(Slot, 0, View) { } + public SetShaderBindItem(uint BindIndex, View View) : this(BindIndex, 0, View) { } } [Dropping(Unmanaged = true)] @@ -60,9 +60,9 @@ internal static void CheckSet(ShaderBindGroupLayout layout, ReadOnlySpan= defs.Length) throw new IndexOutOfRangeException(); - ref readonly var def = ref defs[(int)item.Slot]; - if (item.Index >= def.Count) throw new IndexOutOfRangeException(); + if (item.BindIndex >= defs.Length) throw new IndexOutOfRangeException(); + ref readonly var def = ref defs[(int)item.BindIndex]; + if (item.ArrayIndex >= def.Count) throw new IndexOutOfRangeException(); } } diff --git a/Coplt.Graphics.Core/Core/ShaderBindGroupLayout.cs b/Coplt.Graphics.Core/Core/ShaderBindGroupLayout.cs index 1825b79..2731f28 100644 --- a/Coplt.Graphics.Core/Core/ShaderBindGroupLayout.cs +++ b/Coplt.Graphics.Core/Core/ShaderBindGroupLayout.cs @@ -29,28 +29,28 @@ public record struct StaticSamplerInfo() public AddressMode W; public StaticSamplerBorderColor BorderColor; - public static readonly SamplerInfo PointRepeat = new() + public static readonly StaticSamplerInfo PointRepeat = new() { Mag = FilterMode.Linear, Min = FilterMode.Linear, Mipmap = FilterMode.Point, }; - public static readonly SamplerInfo LinearRepeat = new() + public static readonly StaticSamplerInfo LinearRepeat = new() { Mag = FilterMode.Linear, Min = FilterMode.Linear, Mipmap = FilterMode.Point, }; - public static readonly SamplerInfo TrilinearRepeat = new() + public static readonly StaticSamplerInfo TrilinearRepeat = new() { Mag = FilterMode.Linear, Min = FilterMode.Linear, Mipmap = FilterMode.Linear, }; - public static readonly SamplerInfo PointClamp = new() + public static readonly StaticSamplerInfo PointClamp = new() { Mag = FilterMode.Linear, Min = FilterMode.Linear, @@ -60,7 +60,7 @@ public record struct StaticSamplerInfo() W = AddressMode.Clamp, }; - public static readonly SamplerInfo LinearClamp = new() + public static readonly StaticSamplerInfo LinearClamp = new() { Mag = FilterMode.Linear, Min = FilterMode.Linear, @@ -70,7 +70,7 @@ public record struct StaticSamplerInfo() W = AddressMode.Clamp, }; - public static readonly SamplerInfo TrilinearClamp = new() + public static readonly StaticSamplerInfo TrilinearClamp = new() { Mag = FilterMode.Linear, Min = FilterMode.Linear, @@ -100,7 +100,7 @@ public unsafe record struct BindGroupItem() /// public ulong Scope; /// - /// 数量,或者 StaticSamplers 中的 Index + /// 数量 /// public uint Count = 1; /// diff --git a/Coplt.Graphics.Core/Core/ShaderLayout.cs b/Coplt.Graphics.Core/Core/ShaderLayout.cs index 6d8baf9..efbed6d 100644 --- a/Coplt.Graphics.Core/Core/ShaderLayout.cs +++ b/Coplt.Graphics.Core/Core/ShaderLayout.cs @@ -25,6 +25,10 @@ public enum ShaderLayoutItemView : byte /// 将使用 Push Const / Root Const, 忽略 Usage,Type 必须是 ConstantBuffer /// Constants, + /// + /// 静态采样器, Type 必须是 Sampler + /// + StaticSampler, } public enum ShaderLayoutItemType : byte diff --git a/Coplt.Graphics.Core/Core/SubmitId.cs b/Coplt.Graphics.Core/Core/SubmitId.cs deleted file mode 100644 index c60717e..0000000 --- a/Coplt.Graphics.Core/Core/SubmitId.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace Coplt.Graphics.Core; - -public record struct SubmitId(ulong Value); diff --git a/Coplt.Graphics.Core/Core/View.cs b/Coplt.Graphics.Core/Core/View.cs index 782db61..e43ccee 100644 --- a/Coplt.Graphics.Core/Core/View.cs +++ b/Coplt.Graphics.Core/Core/View.cs @@ -10,23 +10,187 @@ public unsafe partial struct View private interface Template { void None(); - GpuBuffer Buffer(); - GpuImage Image(); Sampler Sampler(); + void Buffer(GpuBuffer Buffer, ulong Offset = 0, int Size = -1, int Stride = -1, GraphicsFormat Format = 0); + void UploadBuffer(nuint Index, ulong Offset, uint Size); + /// Uav 忽略 NumMips + void Image1D(GpuImage Image, byte Mip = 0, sbyte NumMips = -1, GraphicsFormat Format = 0); + /// Uav 忽略 NumMips + void Image1DArray(GpuImage Image, uint Index = 0, int Size = -1, byte Mip = 0, sbyte NumMips = -1, GraphicsFormat Format = 0); + /// Uav 忽略 NumMips + void Image2D(GpuImage Image, byte Mip = 0, sbyte NumMips = -1, byte Plane = 0, GraphicsFormat Format = 0); + /// Uav 忽略 NumMips + void Image2DArray(GpuImage Image, uint Index = 0, int Size = -1, byte Mip = 0, sbyte NumMips = -1, byte Plane = 0, GraphicsFormat Format = 0); + void Image2DMs(GpuImage Image, GraphicsFormat Format = 0); + void Image2DMsArray(GpuImage Image, uint Index = 0, int Size = -1, GraphicsFormat Format = 0); + /// Uav 忽略 NumMips + void Image3D(GpuImage Image, uint Z = 0, int Depth = -1, byte Mip = 0, sbyte NumMips = -1, GraphicsFormat Format = 0); + /// Uav 忽略 NumMips + void ImageCube(GpuImage Image, byte Mip = 0, sbyte NumMips = -1, GraphicsFormat Format = 0); + /// Uav 忽略 NumMips + void ImageCubeArray(GpuImage Image, uint Index = 0, int Size = -1, byte Mip = 0, sbyte NumMips = -1, GraphicsFormat Format = 0); } public static View None => default; + public static View MakeUploadBuffer(UploadLoc loc) => + MakeUploadBuffer(loc.Index, loc.Offset, loc.Size); + public static implicit operator View(GpuBuffer buffer) => MakeBuffer(buffer); - public static implicit operator View(GpuImage image) => MakeImage(image); + public static implicit operator View(UploadLoc buffer) => MakeUploadBuffer(buffer); + public static implicit operator View(GpuImage image) => image.Dimension switch + { + ImageDimension.One => image.DepthOrLength > 1 ? MakeImage1DArray(image) : MakeImage1D(image), + ImageDimension.Two => image.DepthOrLength > 1 ? MakeImage2DArray(image) : MakeImage2D(image), + ImageDimension.Three => MakeImage3D(image), + ImageDimension.Cube => image.DepthOrLength > 1 ? MakeImageCubeArray(image) : MakeImageCube(image), + _ => throw new ArgumentOutOfRangeException() + }; public static implicit operator View(Sampler sampler) => MakeSampler(sampler); - public FView ToFFI() => Tag switch + public FView ToFFI() => this switch { - Tags.None => new FView { Type = FViewType.None }, - Tags.Buffer => new FView { Type = FViewType.Buffer, Viewable = ((GpuViewable)Buffer).Ptr }, - Tags.Image => new FView { Type = FViewType.Image, Viewable = ((GpuViewable)Image).Ptr }, - Tags.Sampler => new FView { Type = FViewType.Sampler, Viewable = ((GpuViewable)Sampler).Ptr }, - _ => throw new ArgumentOutOfRangeException() + { Tag: Tags.None } => + new() { Data = { Type = FViewType.None } }, + { Tag: Tags.Buffer, Buffer: var (Buffer, Offset, Size, Stride, Format) } => + new() + { + Viewable = ((GpuViewable)Buffer).Ptr, + Data = + { + Buffer = new() + { + Type = FViewType.Buffer, + Offset = Offset, Size = Size < 0 + ? (Stride < 0 && Buffer.Stride > 0) || Stride > 0 ? Buffer.Count : (uint)Buffer.Size + : (uint)Size, + Stride = Stride < 0 ? Buffer.Stride : (uint)Stride, + Format = Format.ToFFI(), + }, + }, + }, + { Tag: Tags.UploadBuffer, UploadBuffer: var (Index, Offset, Size) } => + new() { Data = { UploadBuffer = new() { Type = FViewType.UploadBuffer, Size = Size, Index = Index, Offset = Offset } } }, + { Tag: Tags.Sampler, Sampler: var Sampler } => + new() { Viewable = ((GpuViewable)Sampler).Ptr, Data = { Type = FViewType.Sampler } }, + { Tag: Tags.Image1D, Image1D: var (Image, Mip, NumMips, Format) } => + new() + { + Viewable = ((GpuViewable)Image).Ptr, + Data = + { + Image = new() + { + Type = FViewType.Image1D, + Mip = Mip, NumMips = NumMips < 0 ? (byte)Image.MipLevels : (byte)NumMips, Format = Format.ToFFI() + }, + }, + }, + { Tag: Tags.Image1DArray, Image1DArray: var (Image, Index, Size, Mip, NumMips, Format) } => + new() + { + Viewable = ((GpuViewable)Image).Ptr, + Data = + { + Image = new() + { + Type = FViewType.Image1DArray, + Index = Index, Size = Size < 0 ? Image.DepthOrLength : (uint)Size, + Mip = Mip, NumMips = NumMips < 0 ? (byte)Image.MipLevels : (byte)NumMips, + Format = Format.ToFFI(), + } + }, + }, + { Tag: Tags.Image2D, Image2D: var (Image, Mip, NumMips, Plane, Format) } => + new() + { + Viewable = ((GpuViewable)Image).Ptr, + Data = + { + Image = new() + { + Type = FViewType.Image2D, + Mip = Mip, NumMips = NumMips < 0 ? (byte)Image.MipLevels : (byte)NumMips, Plane = Plane, Format = Format.ToFFI(), + }, + }, + }, + { Tag: Tags.Image2DArray, Image2DArray: var (Image, Index, Size, Mip, NumMips, Plane, Format) } => + new() + { + Viewable = ((GpuViewable)Image).Ptr, + Data = + { + Image = new() + { + Type = FViewType.Image2DArray, + Index = Index, Size = Size < 0 ? Image.DepthOrLength : (uint)Size, + Mip = Mip, NumMips = NumMips < 0 ? (byte)Image.MipLevels : (byte)NumMips, + Plane = Plane, Format = Format.ToFFI() + }, + }, + }, + { Tag: Tags.Image2DMs, Image2DMs: var (Image, Format) } => + new() + { + Viewable = ((GpuViewable)Image).Ptr, + Data = { Image = new() { Type = FViewType.Image2DMs, Format = Format.ToFFI() } }, + }, + { Tag: Tags.Image2DMsArray, Image2DMsArray: var (Image, Index, Size, Format) } => + new() + { + Viewable = ((GpuViewable)Image).Ptr, + Data = + { + Image = new() + { + Type = FViewType.Image2DMsArray, + Index = Index, Size = Size < 0 ? Image.DepthOrLength : (uint)Size, Format = Format.ToFFI(), + }, + }, + }, + { Tag: Tags.Image3D, Image3D : var (Image, Z, Depth, Mip, NumMips, Format) } => + new() + { + Viewable = ((GpuViewable)Image).Ptr, + Data = + { + Image = new() + { + Type = FViewType.Image3D, + Z = Z, Depth = Depth < 0 ? Image.DepthOrLength : (uint)Depth, + Mip = Mip, NumMips = NumMips < 0 ? (byte)Image.MipLevels : (byte)NumMips, + Format = Format.ToFFI(), + }, + }, + }, + { Tag: Tags.ImageCube, ImageCube: var (Image, Mip, NumMips, Format) } => + new() + { + Viewable = ((GpuViewable)Image).Ptr, + Data = + { + Image = new() + { + Type = FViewType.ImageCube, + Mip = Mip, NumMips = NumMips < 0 ? (byte)Image.MipLevels : (byte)NumMips, Format = Format.ToFFI(), + }, + }, + }, + { Tag: Tags.ImageCubeArray, ImageCubeArray: var (Image, Index, Size, Mip, NumMips, Format) } => + new() + { + Viewable = ((GpuViewable)Image).Ptr, + Data = + { + Image = new() + { + Type = FViewType.ImageCubeArray, + Index = Index, Size = Size < 0 ? (Image.DepthOrLength + 5) / 6 : (uint)Size, + Mip = Mip, NumMips = NumMips < 0 ? (byte)Image.MipLevels : (byte)NumMips, + Format = Format.ToFFI(), + }, + }, + }, + _ => throw new ArgumentOutOfRangeException() }; } diff --git a/Coplt.Graphics.Core/Native/Native.cs b/Coplt.Graphics.Core/Native/Native.cs index c0f2f20..1dd5635 100644 --- a/Coplt.Graphics.Core/Native/Native.cs +++ b/Coplt.Graphics.Core/Native/Native.cs @@ -1347,12 +1347,20 @@ public FResult CreateGraphicsPipeline([NativeTypeName("const FGraphicsShaderPipe return *((delegate* unmanaged[Thiscall])(lpVtbl[21]))((FGpuDevice*)Unsafe.AsPointer(ref this), &result, options, @out); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [return: NativeTypeName("Coplt::FResult")] + public FResult CreateComputePipeline([NativeTypeName("const FShaderPipelineCreateOptions &")] FShaderPipelineCreateOptions* options, FComputeShaderPipeline** @out) + { + FResult result; + return *((delegate* unmanaged[Thiscall])(lpVtbl[22]))((FGpuDevice*)Unsafe.AsPointer(ref this), &result, options, @out); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] [return: NativeTypeName("Coplt::FResult")] public FResult CreateBuffer([NativeTypeName("const FGpuBufferCreateOptions &")] FGpuBufferCreateOptions* options, FGpuBuffer** @out) { FResult result; - return *((delegate* unmanaged[Thiscall])(lpVtbl[22]))((FGpuDevice*)Unsafe.AsPointer(ref this), &result, options, @out); + return *((delegate* unmanaged[Thiscall])(lpVtbl[23]))((FGpuDevice*)Unsafe.AsPointer(ref this), &result, options, @out); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -1360,7 +1368,7 @@ public FResult CreateBuffer([NativeTypeName("const FGpuBufferCreateOptions &")] public FResult CreateImage([NativeTypeName("const FGpuImageCreateOptions &")] FGpuImageCreateOptions* options, FGpuImage** @out) { FResult result; - return *((delegate* unmanaged[Thiscall])(lpVtbl[23]))((FGpuDevice*)Unsafe.AsPointer(ref this), &result, options, @out); + return *((delegate* unmanaged[Thiscall])(lpVtbl[24]))((FGpuDevice*)Unsafe.AsPointer(ref this), &result, options, @out); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -1368,7 +1376,7 @@ public FResult CreateImage([NativeTypeName("const FGpuImageCreateOptions &")] FG public FResult CreateSampler([NativeTypeName("const FGpuSamplerCreateOptions &")] FGpuSamplerCreateOptions* options, FGpuSampler** @out) { FResult result; - return *((delegate* unmanaged[Thiscall])(lpVtbl[24]))((FGpuDevice*)Unsafe.AsPointer(ref this), &result, options, @out); + return *((delegate* unmanaged[Thiscall])(lpVtbl[25]))((FGpuDevice*)Unsafe.AsPointer(ref this), &result, options, @out); } public interface Interface : FGpuObject.Interface @@ -1420,6 +1428,9 @@ public interface Interface : FGpuObject.Interface [return: NativeTypeName("Coplt::FResult")] FResult CreateGraphicsPipeline([NativeTypeName("const FGraphicsShaderPipelineCreateOptions &")] FGraphicsShaderPipelineCreateOptions* options, FGraphicsShaderPipeline** @out); + [return: NativeTypeName("Coplt::FResult")] + FResult CreateComputePipeline([NativeTypeName("const FShaderPipelineCreateOptions &")] FShaderPipelineCreateOptions* options, FComputeShaderPipeline** @out); + [return: NativeTypeName("Coplt::FResult")] FResult CreateBuffer([NativeTypeName("const FGpuBufferCreateOptions &")] FGpuBufferCreateOptions* options, FGpuBuffer** @out); @@ -1762,11 +1773,19 @@ public unsafe partial struct FGpuRecordData public FList PayloadVertexBufferRange; + [NativeTypeName("FList")] + public FList Payload32Bits; + + public FList PayloadSetBindItem; + [NativeTypeName("FList")] public FList Blob; [NativeTypeName("Coplt::u32")] - public uint NumSetBindings; + public uint NumSyncBindings; + + [NativeTypeName("Coplt::u32")] + public uint SumDynArraySize; [NativeTypeName("Coplt::b8")] public B8 Ended; @@ -1792,6 +1811,9 @@ public enum FCmdType : uint Compute, SetPipeline, SetBinding, + SetConstants, + SetDynArraySize, + SetBindItem, SetViewportScissor, SetMeshBuffers, Draw, @@ -1808,7 +1830,7 @@ public enum FCmdResType : byte public unsafe partial struct FCmdRes { - [NativeTypeName("__AnonymousRecord_Cmd_L71_C9")] + [NativeTypeName("__AnonymousRecord_Cmd_L74_C9")] public _Anonymous_e__Union Anonymous; [NativeTypeName("Coplt::FCmdResType")] @@ -2222,6 +2244,66 @@ public partial struct FCmdSetBinding public uint Index; } + [NativeTypeName("struct FCmdSyncBinding : Coplt::FCmdBase")] + public partial struct FCmdSyncBinding + { + public FCmdBase Base; + + [NativeTypeName("Coplt::u32")] + public uint SyncBindingIndex; + } + + [NativeTypeName("struct FCmdSetConstants : Coplt::FCmdBase")] + public unsafe partial struct FCmdSetConstants + { + public FCmdBase Base; + + [NativeTypeName("Coplt::FShaderBindGroup *")] + public FShaderBindGroup* Group; + + [NativeTypeName("Coplt::u32")] + public uint SetConstantsIndex; + + [NativeTypeName("Coplt::u32")] + public uint Slot; + + [NativeTypeName("Coplt::u32")] + public uint ValueIndex; + + [NativeTypeName("Coplt::u32")] + public uint Count; + + [NativeTypeName("Coplt::u32")] + public uint Offset; + } + + [NativeTypeName("struct FCmdSetDynArraySize : Coplt::FCmdBase")] + public unsafe partial struct FCmdSetDynArraySize + { + public FCmdBase Base; + + [NativeTypeName("Coplt::FShaderBindGroup *")] + public FShaderBindGroup* Group; + + [NativeTypeName("Coplt::u32")] + public uint Size; + } + + [NativeTypeName("struct FCmdSetBindItem : Coplt::FCmdBase")] + public unsafe partial struct FCmdSetBindItem + { + public FCmdBase Base; + + [NativeTypeName("Coplt::FShaderBindGroup *")] + public FShaderBindGroup* Group; + + [NativeTypeName("Coplt::u32")] + public uint ItemIndex; + + [NativeTypeName("Coplt::u32")] + public uint Count; + } + [NativeTypeName("struct FCmdSetViewportScissor : Coplt::FCmdBase")] public partial struct FCmdSetViewportScissor { @@ -2291,10 +2373,10 @@ public partial struct FCmdSetMeshBuffers public uint PayloadIndex; } - [NativeTypeName("struct FCmdDraw : Coplt::FCmdBase")] + [NativeTypeName("struct FCmdDraw : Coplt::FCmdSyncBinding")] public partial struct FCmdDraw { - public FCmdBase Base; + public FCmdSyncBinding Base; [NativeTypeName("Coplt::u32")] public uint VertexOrIndexCount; @@ -2315,10 +2397,10 @@ public partial struct FCmdDraw public B8 Indexed; } - [NativeTypeName("struct FCmdDispatch : Coplt::FCmdBase")] + [NativeTypeName("struct FCmdDispatch : Coplt::FCmdSyncBinding")] public partial struct FCmdDispatch { - public FCmdBase Base; + public FCmdSyncBinding Base; [NativeTypeName("Coplt::u32")] public uint GroupCountX; @@ -2335,7 +2417,7 @@ public partial struct FCmdDispatch public partial struct FCmdItem { - [NativeTypeName("__AnonymousRecord_Cmd_L360_C9")] + [NativeTypeName("__AnonymousRecord_Cmd_L399_C9")] public _Anonymous_e__Union Anonymous; [UnscopedRef] @@ -2468,6 +2550,36 @@ public ref FCmdSetBinding SetBinding } } + [UnscopedRef] + public ref FCmdSetConstants SetConstants + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return ref Anonymous.SetConstants; + } + } + + [UnscopedRef] + public ref FCmdSetDynArraySize SetDynArraySize + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return ref Anonymous.SetDynArraySize; + } + } + + [UnscopedRef] + public ref FCmdSetBindItem SetBindItem + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return ref Anonymous.SetBindItem; + } + } + [UnscopedRef] public ref FCmdSetViewportScissor SetViewportScissor { @@ -2573,6 +2685,18 @@ public partial struct _Anonymous_e__Union [NativeTypeName("Coplt::FCmdSetBinding")] public FCmdSetBinding SetBinding; + [FieldOffset(0)] + [NativeTypeName("Coplt::FCmdSetConstants")] + public FCmdSetConstants SetConstants; + + [FieldOffset(0)] + [NativeTypeName("Coplt::FCmdSetDynArraySize")] + public FCmdSetDynArraySize SetDynArraySize; + + [FieldOffset(0)] + [NativeTypeName("Coplt::FCmdSetBindItem")] + public FCmdSetBindItem SetBindItem; + [FieldOffset(0)] [NativeTypeName("Coplt::FCmdSetViewportScissor")] public FCmdSetViewportScissor SetViewportScissor; @@ -3491,6 +3615,176 @@ public interface Interface : FGpuObject.Interface } } + public partial struct FGpuBuffer + { + } + + [Guid("283740E3-FE96-41D0-830A-0A4C6A725336")] + [NativeTypeName("struct FGpuBuffer : Coplt::FGpuResource")] + public unsafe partial struct FGpuBuffer : FGpuBuffer.Interface, INativeGuid + { + static Guid* INativeGuid.NativeGuid => (Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in IID_FGpuBuffer)); + + public void** lpVtbl; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() + { + ((delegate* unmanaged[Thiscall])(lpVtbl[0]))((FGpuBuffer*)Unsafe.AsPointer(ref this)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [return: NativeTypeName("size_t")] + public nuint Release() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[1]))((FGpuBuffer*)Unsafe.AsPointer(ref this)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [return: NativeTypeName("size_t")] + public nuint AddRef() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[2]))((FGpuBuffer*)Unsafe.AsPointer(ref this)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void* QueryInterface([NativeTypeName("const Guid &")] Guid* id) + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[3]))((FGpuBuffer*)Unsafe.AsPointer(ref this), id); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [return: NativeTypeName("Coplt::u64")] + public ulong ObjectId() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[4]))((FGpuBuffer*)Unsafe.AsPointer(ref this)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [return: NativeTypeName("Coplt::FResult")] + public FResult SetName([NativeTypeName("const FStr8or16 &")] FStr8or16* name) + { + FResult result; + return *((delegate* unmanaged[Thiscall])(lpVtbl[5]))((FGpuBuffer*)Unsafe.AsPointer(ref this), &result, name); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [return: NativeTypeName("Coplt::FGpuResourceData *")] + public FGpuResourceData* GpuResourceData() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[6]))((FGpuBuffer*)Unsafe.AsPointer(ref this)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [return: NativeTypeName("Coplt::FGpuBufferData *")] + public FGpuBufferData* GpuBufferData() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[7]))((FGpuBuffer*)Unsafe.AsPointer(ref this)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [return: NativeTypeName("Coplt::FResult")] + public FResult Map(void** ptr, [NativeTypeName("Coplt::b8")] B8 discard) + { + FResult result; + return *((delegate* unmanaged[Thiscall])(lpVtbl[8]))((FGpuBuffer*)Unsafe.AsPointer(ref this), &result, ptr, discard); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [return: NativeTypeName("Coplt::FResult")] + public FResult Unmap([NativeTypeName("Coplt::b8")] B8 discard) + { + FResult result; + return *((delegate* unmanaged[Thiscall])(lpVtbl[9]))((FGpuBuffer*)Unsafe.AsPointer(ref this), &result, discard); + } + + public interface Interface : FGpuResource.Interface + { + [return: NativeTypeName("Coplt::FGpuBufferData *")] + FGpuBufferData* GpuBufferData(); + + [return: NativeTypeName("Coplt::FResult")] + FResult Map(void** ptr, [NativeTypeName("Coplt::b8")] B8 discard); + + [return: NativeTypeName("Coplt::FResult")] + FResult Unmap([NativeTypeName("Coplt::b8")] B8 discard); + } + } + + public partial struct FGpuImage + { + } + + [Guid("667EFA36-21C7-4561-ABAD-85780FA4929E")] + [NativeTypeName("struct FGpuImage : Coplt::FGpuResource")] + public unsafe partial struct FGpuImage : FGpuImage.Interface, INativeGuid + { + static Guid* INativeGuid.NativeGuid => (Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in IID_FGpuImage)); + + public void** lpVtbl; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() + { + ((delegate* unmanaged[Thiscall])(lpVtbl[0]))((FGpuImage*)Unsafe.AsPointer(ref this)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [return: NativeTypeName("size_t")] + public nuint Release() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[1]))((FGpuImage*)Unsafe.AsPointer(ref this)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [return: NativeTypeName("size_t")] + public nuint AddRef() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[2]))((FGpuImage*)Unsafe.AsPointer(ref this)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void* QueryInterface([NativeTypeName("const Guid &")] Guid* id) + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[3]))((FGpuImage*)Unsafe.AsPointer(ref this), id); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [return: NativeTypeName("Coplt::u64")] + public ulong ObjectId() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[4]))((FGpuImage*)Unsafe.AsPointer(ref this)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [return: NativeTypeName("Coplt::FResult")] + public FResult SetName([NativeTypeName("const FStr8or16 &")] FStr8or16* name) + { + FResult result; + return *((delegate* unmanaged[Thiscall])(lpVtbl[5]))((FGpuImage*)Unsafe.AsPointer(ref this), &result, name); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [return: NativeTypeName("Coplt::FGpuResourceData *")] + public FGpuResourceData* GpuResourceData() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[6]))((FGpuImage*)Unsafe.AsPointer(ref this)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [return: NativeTypeName("Coplt::FGpuImageData *")] + public FGpuImageData* GpuImageData() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[7]))((FGpuImage*)Unsafe.AsPointer(ref this)); + } + + public interface Interface : FGpuResource.Interface + { + [return: NativeTypeName("Coplt::FGpuImageData *")] + FGpuImageData* GpuImageData(); + } + } + [Guid("B3AEB8A5-1FA6-4866-97EF-1A5FA401E18F")] [NativeTypeName("struct FGpuViewable : Coplt::FGpuObject")] public unsafe partial struct FGpuViewable : FGpuViewable.Interface, INativeGuid @@ -3549,9 +3843,205 @@ public interface Interface : FGpuObject.Interface public enum FViewType : byte { None = 0, - Buffer = 1, - Image = 2, - Sampler = 3, + Sampler = 1, + Buffer = 2, + Image1D = 3, + Image1DArray = 4, + Image2D = 5, + Image2DArray = 6, + Image2DMs = 7, + Image2DMsArray = 8, + Image3D = 9, + ImageCube = 10, + ImageCubeArray = 11, + UploadBuffer = 12, + } + + public partial struct FViewData + { + [NativeTypeName("__AnonymousRecord_View_L34_C9")] + public _Anonymous_e__Union Anonymous; + + [UnscopedRef] + public ref FViewType Type + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return ref Anonymous.Type; + } + } + + [UnscopedRef] + public ref _Anonymous_e__Union._Buffer_e__Struct Buffer + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return ref Anonymous.Buffer; + } + } + + [UnscopedRef] + public ref _Anonymous_e__Union._UploadBuffer_e__Struct UploadBuffer + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return ref Anonymous.UploadBuffer; + } + } + + [UnscopedRef] + public ref _Anonymous_e__Union._Image_e__Struct Image + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return ref Anonymous.Image; + } + } + + [StructLayout(LayoutKind.Explicit)] + public partial struct _Anonymous_e__Union + { + [FieldOffset(0)] + [NativeTypeName("Coplt::FViewType")] + public FViewType Type; + + [FieldOffset(0)] + [NativeTypeName("__AnonymousRecord_View_L38_C13")] + public _Buffer_e__Struct Buffer; + + [FieldOffset(0)] + [NativeTypeName("__AnonymousRecord_View_L47_C13")] + public _UploadBuffer_e__Struct UploadBuffer; + + [FieldOffset(0)] + [NativeTypeName("__AnonymousRecord_View_L55_C13")] + public _Image_e__Struct Image; + + public partial struct _Buffer_e__Struct + { + [NativeTypeName("Coplt::FViewType")] + public FViewType Type; + + [NativeTypeName("Coplt::FGraphicsFormat")] + public FGraphicsFormat Format; + + [NativeTypeName("Coplt::u64")] + public ulong Offset; + + [NativeTypeName("Coplt::u32")] + public uint Size; + + [NativeTypeName("Coplt::u32")] + public uint Stride; + } + + public partial struct _UploadBuffer_e__Struct + { + [NativeTypeName("Coplt::FViewType")] + public FViewType Type; + + [NativeTypeName("Coplt::u32")] + public uint Size; + + [NativeTypeName("Coplt::u64")] + public ulong Index; + + [NativeTypeName("Coplt::u64")] + public ulong Offset; + } + + public partial struct _Image_e__Struct + { + [NativeTypeName("Coplt::FViewType")] + public FViewType Type; + + [NativeTypeName("Coplt::u8")] + public byte Mip; + + [NativeTypeName("Coplt::u8")] + public byte NumMips; + + [NativeTypeName("Coplt::u8")] + public byte Plane; + + [NativeTypeName("Coplt::FGraphicsFormat")] + public FGraphicsFormat Format; + + [NativeTypeName("__AnonymousRecord_View_L63_C17")] + public _Anonymous1_e__Union Anonymous1; + + [NativeTypeName("__AnonymousRecord_View_L69_C17")] + public _Anonymous2_e__Union Anonymous2; + + [UnscopedRef] + public ref uint Index + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return ref Anonymous1.Index; + } + } + + [UnscopedRef] + public ref uint Z + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return ref Anonymous1.Z; + } + } + + [UnscopedRef] + public ref uint Size + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return ref Anonymous2.Size; + } + } + + [UnscopedRef] + public ref uint Depth + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return ref Anonymous2.Depth; + } + } + + [StructLayout(LayoutKind.Explicit)] + public partial struct _Anonymous1_e__Union + { + [FieldOffset(0)] + [NativeTypeName("Coplt::u32")] + public uint Index; + + [FieldOffset(0)] + [NativeTypeName("Coplt::u32")] + public uint Z; + } + + [StructLayout(LayoutKind.Explicit)] + public partial struct _Anonymous2_e__Union + { + [FieldOffset(0)] + [NativeTypeName("Coplt::u32")] + public uint Size; + + [FieldOffset(0)] + [NativeTypeName("Coplt::u32")] + public uint Depth; + } + } + } } public unsafe partial struct FView @@ -3559,8 +4049,8 @@ public unsafe partial struct FView [NativeTypeName("Coplt::FGpuViewable *")] public FGpuViewable* Viewable; - [NativeTypeName("Coplt::FViewType")] - public FViewType Type; + [NativeTypeName("Coplt::FViewData")] + public FViewData Data; } [NativeTypeName("Coplt::u8")] @@ -3730,98 +4220,6 @@ public partial struct FGpuBufferData public FBufferUsage m_usage; } - [Guid("283740E3-FE96-41D0-830A-0A4C6A725336")] - [NativeTypeName("struct FGpuBuffer : Coplt::FGpuResource")] - public unsafe partial struct FGpuBuffer : FGpuBuffer.Interface, INativeGuid - { - static Guid* INativeGuid.NativeGuid => (Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in IID_FGpuBuffer)); - - public void** lpVtbl; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Dispose() - { - ((delegate* unmanaged[Thiscall])(lpVtbl[0]))((FGpuBuffer*)Unsafe.AsPointer(ref this)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [return: NativeTypeName("size_t")] - public nuint Release() - { - return ((delegate* unmanaged[Thiscall])(lpVtbl[1]))((FGpuBuffer*)Unsafe.AsPointer(ref this)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [return: NativeTypeName("size_t")] - public nuint AddRef() - { - return ((delegate* unmanaged[Thiscall])(lpVtbl[2]))((FGpuBuffer*)Unsafe.AsPointer(ref this)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void* QueryInterface([NativeTypeName("const Guid &")] Guid* id) - { - return ((delegate* unmanaged[Thiscall])(lpVtbl[3]))((FGpuBuffer*)Unsafe.AsPointer(ref this), id); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [return: NativeTypeName("Coplt::u64")] - public ulong ObjectId() - { - return ((delegate* unmanaged[Thiscall])(lpVtbl[4]))((FGpuBuffer*)Unsafe.AsPointer(ref this)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [return: NativeTypeName("Coplt::FResult")] - public FResult SetName([NativeTypeName("const FStr8or16 &")] FStr8or16* name) - { - FResult result; - return *((delegate* unmanaged[Thiscall])(lpVtbl[5]))((FGpuBuffer*)Unsafe.AsPointer(ref this), &result, name); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [return: NativeTypeName("Coplt::FGpuResourceData *")] - public FGpuResourceData* GpuResourceData() - { - return ((delegate* unmanaged[Thiscall])(lpVtbl[6]))((FGpuBuffer*)Unsafe.AsPointer(ref this)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [return: NativeTypeName("Coplt::FGpuBufferData *")] - public FGpuBufferData* GpuBufferData() - { - return ((delegate* unmanaged[Thiscall])(lpVtbl[7]))((FGpuBuffer*)Unsafe.AsPointer(ref this)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [return: NativeTypeName("Coplt::FResult")] - public FResult Map(void** ptr, [NativeTypeName("Coplt::b8")] B8 discard) - { - FResult result; - return *((delegate* unmanaged[Thiscall])(lpVtbl[8]))((FGpuBuffer*)Unsafe.AsPointer(ref this), &result, ptr, discard); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [return: NativeTypeName("Coplt::FResult")] - public FResult Unmap([NativeTypeName("Coplt::b8")] B8 discard) - { - FResult result; - return *((delegate* unmanaged[Thiscall])(lpVtbl[9]))((FGpuBuffer*)Unsafe.AsPointer(ref this), &result, discard); - } - - public interface Interface : FGpuResource.Interface - { - [return: NativeTypeName("Coplt::FGpuBufferData *")] - FGpuBufferData* GpuBufferData(); - - [return: NativeTypeName("Coplt::FResult")] - FResult Map(void** ptr, [NativeTypeName("Coplt::b8")] B8 discard); - - [return: NativeTypeName("Coplt::FResult")] - FResult Unmap([NativeTypeName("Coplt::b8")] B8 discard); - } - } - public partial struct FOptimizedClearColor { [NativeTypeName("Coplt::FGraphicsFormat")] @@ -3975,76 +4373,6 @@ public partial struct FGpuImageData public FImageLayout m_layout; } - [Guid("667EFA36-21C7-4561-ABAD-85780FA4929E")] - [NativeTypeName("struct FGpuImage : Coplt::FGpuResource")] - public unsafe partial struct FGpuImage : FGpuImage.Interface, INativeGuid - { - static Guid* INativeGuid.NativeGuid => (Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in IID_FGpuImage)); - - public void** lpVtbl; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Dispose() - { - ((delegate* unmanaged[Thiscall])(lpVtbl[0]))((FGpuImage*)Unsafe.AsPointer(ref this)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [return: NativeTypeName("size_t")] - public nuint Release() - { - return ((delegate* unmanaged[Thiscall])(lpVtbl[1]))((FGpuImage*)Unsafe.AsPointer(ref this)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [return: NativeTypeName("size_t")] - public nuint AddRef() - { - return ((delegate* unmanaged[Thiscall])(lpVtbl[2]))((FGpuImage*)Unsafe.AsPointer(ref this)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void* QueryInterface([NativeTypeName("const Guid &")] Guid* id) - { - return ((delegate* unmanaged[Thiscall])(lpVtbl[3]))((FGpuImage*)Unsafe.AsPointer(ref this), id); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [return: NativeTypeName("Coplt::u64")] - public ulong ObjectId() - { - return ((delegate* unmanaged[Thiscall])(lpVtbl[4]))((FGpuImage*)Unsafe.AsPointer(ref this)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [return: NativeTypeName("Coplt::FResult")] - public FResult SetName([NativeTypeName("const FStr8or16 &")] FStr8or16* name) - { - FResult result; - return *((delegate* unmanaged[Thiscall])(lpVtbl[5]))((FGpuImage*)Unsafe.AsPointer(ref this), &result, name); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [return: NativeTypeName("Coplt::FGpuResourceData *")] - public FGpuResourceData* GpuResourceData() - { - return ((delegate* unmanaged[Thiscall])(lpVtbl[6]))((FGpuImage*)Unsafe.AsPointer(ref this)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [return: NativeTypeName("Coplt::FGpuImageData *")] - public FGpuImageData* GpuImageData() - { - return ((delegate* unmanaged[Thiscall])(lpVtbl[7]))((FGpuImage*)Unsafe.AsPointer(ref this)); - } - - public interface Interface : FGpuResource.Interface - { - [return: NativeTypeName("Coplt::FGpuImageData *")] - FGpuImageData* GpuImageData(); - } - } - [NativeTypeName("Coplt::u8")] public enum FPresentMode : byte { @@ -4746,6 +5074,7 @@ public enum FBindGroupUsage : byte { Common = 0, Dynamic = 1, + Freeze = 2, } public unsafe partial struct FBindGroupLayoutCreateOptions @@ -4783,6 +5112,9 @@ public unsafe partial struct FBindGroupLayoutData [NativeTypeName("Coplt::u32")] public uint NumStaticSamplers; + [NativeTypeName("Coplt::u32")] + public uint DynamicArrayIndex; + [NativeTypeName("Coplt::FBindGroupUsage")] public FBindGroupUsage Usage; } diff --git a/Coplt.Graphics.Native/Api/FFI/Cmd.h b/Coplt.Graphics.Native/Api/FFI/Cmd.h index 267dea8..1e55cd3 100644 --- a/Coplt.Graphics.Native/Api/FFI/Cmd.h +++ b/Coplt.Graphics.Native/Api/FFI/Cmd.h @@ -31,6 +31,9 @@ namespace Coplt // Render / Compute SetPipeline, SetBinding, + SetConstants, + SetDynArraySize, + SetBindItem, // Render SetViewportScissor, @@ -286,6 +289,42 @@ namespace Coplt u32 Index{}; }; + struct FCmdSyncBinding : FCmdBase + { + u32 SyncBindingIndex{}; + }; + + struct FCmdSetConstants : FCmdBase + { + // Payload 中的索引,类型为 FCmdBindGroup + FShaderBindGroup* Group{}; + // 第几个 SetConstants + u32 SetConstantsIndex{}; + u32 Slot{}; + // Payload 中的索引,类型为 u32 + u32 ValueIndex{}; + u32 Count{}; + u32 Offset{}; + }; + + // 设置动态绑定的动态数组大小 + struct FCmdSetDynArraySize : FCmdBase + { + // Payload 中的索引,类型为 FCmdBindGroup + FShaderBindGroup* Group{}; + // 动态数组大小 + u32 Size{}; + }; + + struct FCmdSetBindItem : FCmdBase + { + // Payload 中的索引,类型为 FCmdBindGroup + FShaderBindGroup* Group{}; + // Payload 中的索引,类型为 FSetBindItem + u32 ItemIndex{}; + u32 Count{}; + }; + //////////////////////////////////////////////////////////////////////////////////////////////////// struct FCmdSetViewportScissor : FCmdBase @@ -332,7 +371,7 @@ namespace Coplt u32 PayloadIndex{}; }; - struct FCmdDraw : FCmdBase + struct FCmdDraw : FCmdSyncBinding { u32 VertexOrIndexCount{}; u32 InstanceCount{}; @@ -345,7 +384,7 @@ namespace Coplt //////////////////////////////////////////////////////////////////////////////////////////////////// - struct FCmdDispatch : FCmdBase + struct FCmdDispatch : FCmdSyncBinding { u32 GroupCountX{}; u32 GroupCountY{}; @@ -378,6 +417,9 @@ namespace Coplt FCmdSetPipeline SetPipeline; FCmdSetBinding SetBinding; + FCmdSetConstants SetConstants; + FCmdSetDynArraySize SetDynArraySize; + FCmdSetBindItem SetBindItem; FCmdSetViewportScissor SetViewportScissor; FCmdSetMeshBuffers SetMeshBuffers; diff --git a/Coplt.Graphics.Native/Api/FFI/Device.h b/Coplt.Graphics.Native/Api/FFI/Device.h index 4da9b41..14faaae 100644 --- a/Coplt.Graphics.Native/Api/FFI/Device.h +++ b/Coplt.Graphics.Native/Api/FFI/Device.h @@ -95,6 +95,7 @@ namespace Coplt virtual FResult CreateShaderBinding(const FShaderBindingCreateOptions& options, FShaderBindingCreateResult* out) noexcept = 0; virtual FResult CreateGraphicsPipeline(const FGraphicsShaderPipelineCreateOptions& options, FGraphicsShaderPipeline** out) noexcept = 0; + virtual FResult CreateComputePipeline(const FShaderPipelineCreateOptions& options, FComputeShaderPipeline** out) noexcept = 0; virtual FResult CreateBuffer(const FGpuBufferCreateOptions& options, FGpuBuffer** out) noexcept = 0; virtual FResult CreateImage(const FGpuImageCreateOptions& options, FGpuImage** out) noexcept = 0; diff --git a/Coplt.Graphics.Native/Api/FFI/Layout.h b/Coplt.Graphics.Native/Api/FFI/Layout.h index 8bc2541..2a8104a 100644 --- a/Coplt.Graphics.Native/Api/FFI/Layout.h +++ b/Coplt.Graphics.Native/Api/FFI/Layout.h @@ -79,7 +79,9 @@ namespace Coplt u32 Slot{}; // dx 的 space,vk 的 set,vk 建议尽可能多的拆分 set,dx 的 space 写不写都一样 // todo 引入 spv 修改器运行时修改 vk 的 set u32 Space{}; - // 数量,View 是 StaticSampler 时必须是 1,其他必须最少是 1, View 是 Constants 是 32 位值的数量,而不是 byte 的数量 + // 数量,View 是 StaticSampler 时必须是 1,其他必须最少是 1, View 是 Constants 时是 32 位值的数量,而不是 byte 的数量 + // u32::max 表示是动态数组,只有 Cbv|Srv|Uav View 支持动态数组 + // 采样器不支持动态数组,因为一般采样器没几个,也不会动态使用 u32 Count{}; FGraphicsFormat Format{}; FShaderStage Stage{}; @@ -185,6 +187,8 @@ namespace Coplt // 绑定点所属范围,和 Id 共同组成唯一定位 u64 Scope{}; // 数量 + // u32::max 表示是动态数组,只有 Dynamic Usage 且 Cbv|Srv|Uav View 支持动态数组,每个组只能有 Cbv|Srv|Uav 中的一个 + // 采样器不支持动态数组,因为一般采样器没几个,也不会动态使用; u32 Count{}; // StaticSamplers 中的 Index u32 StaticSamplerIndex{}; @@ -199,9 +203,11 @@ namespace Coplt { // 一般组 Common = 0, - // 提示组的使用是频繁更改的,d3d12 将会尽可能使用根描述符 - // 只有缓冲区可以使用根描述符,且建议小缓冲区(几个 32 位值)尽量使用根常量 + // 动态组不保留绑定状态,每帧都需要在 cmd 上设置 + // d3d12 将会尽可能使用根描述符,但只有缓冲区可以使用根描述符,且建议小缓冲区(几个 32 位值)尽量使用根常量 Dynamic = 1, + // 冻结组中的资源状态将被冻结,不允许改变,可以节省屏障检测开销, 建议将静态资源归与冻结组中 // todo 未实现 + Freeze = 2, }; struct FBindGroupLayoutCreateOptions @@ -220,6 +226,7 @@ namespace Coplt const FStaticSamplerInfo* StaticSamplers{}; u32 NumItems{}; u32 NumStaticSamplers{}; + u32 DynamicArrayIndex{COPLT_U32_MAX}; FBindGroupUsage Usage{}; }; diff --git a/Coplt.Graphics.Native/Api/FFI/Record.h b/Coplt.Graphics.Native/Api/FFI/Record.h index 1d7f9ba..b851d7b 100644 --- a/Coplt.Graphics.Native/Api/FFI/Record.h +++ b/Coplt.Graphics.Native/Api/FFI/Record.h @@ -32,7 +32,7 @@ namespace Coplt FList Commands; FList Resources; FList Bindings; - FList BindingChange; + FList BindingChange; // todo remove FList PayloadRect; FList PayloadViewport; FList PayloadRenderInfo; @@ -41,16 +41,20 @@ namespace Coplt FList PayloadBufferImageCopyRange; FList PayloadMeshBuffers; FList PayloadVertexBufferRange; + FList Payload32Bits; + FList PayloadSetBindItem; FList Blob; - u32 NumSetBindings{}; + u32 NumSyncBindings{}; + u32 SumDynArraySize{}; b8 Ended{}; FGpuRecordMode Mode{}; #ifdef FFI_SRC explicit FGpuRecordData(FAllocator* allocator) - : Commands(allocator), Resources(allocator), Bindings(allocator), BindingChange(allocator), PayloadRect(allocator), - PayloadViewport(allocator), PayloadRenderInfo(allocator), PayloadResolveInfo(allocator), PayloadBufferCopyRange(allocator), - PayloadBufferImageCopyRange(allocator), PayloadMeshBuffers(allocator), PayloadVertexBufferRange(allocator), Blob(allocator) + : Commands(allocator), Resources(allocator), Bindings(allocator), BindingChange(allocator), + PayloadRect(allocator), PayloadViewport(allocator), PayloadRenderInfo(allocator), PayloadResolveInfo(allocator), + PayloadBufferCopyRange(allocator), PayloadBufferImageCopyRange(allocator), PayloadMeshBuffers(allocator), + PayloadVertexBufferRange(allocator), Payload32Bits(allocator), PayloadSetBindItem(allocator), Blob(allocator) { } @@ -68,8 +72,10 @@ namespace Coplt PayloadBufferImageCopyRange.Clear(); PayloadMeshBuffers.Clear(); PayloadVertexBufferRange.Clear(); + Payload32Bits.Clear(); + PayloadSetBindItem.Clear(); Blob.Clear(); - NumSetBindings = 0; + NumSyncBindings = 0; } #endif }; diff --git a/Coplt.Graphics.Native/Api/FFI/View.h b/Coplt.Graphics.Native/Api/FFI/View.h index ee9ddbc..81e93ff 100644 --- a/Coplt.Graphics.Native/Api/FFI/View.h +++ b/Coplt.Graphics.Native/Api/FFI/View.h @@ -1,9 +1,13 @@ #pragma once #include "GpuObject.h" +#include "GraphicsFormat.h" namespace Coplt { + struct FGpuBuffer; + struct FGpuImage; + COPLT_INTERFACE_DEFINE(FGpuViewable, "b3aeb8a5-1fa6-4866-97ef-1a5fa401e18f", FGpuObject) { }; @@ -11,14 +15,69 @@ namespace Coplt enum class FViewType : u8 { None = 0, - Buffer = 1, - Image = 2, - Sampler = 3, + Sampler = 1, + Buffer = 2, + Image1D = 3, + Image1DArray = 4, + Image2D = 5, + Image2DArray = 6, + Image2DMs = 7, + Image2DMsArray = 8, + Image3D = 9, + ImageCube = 10, + ImageCubeArray = 11, + UploadBuffer = 12, + }; + + struct FViewData + { + union + { + FViewType Type{}; + + struct + { + FViewType Type{}; + FGraphicsFormat Format; + u64 Offset; + u32 Size; + u32 Stride; + } Buffer; + + struct + { + FViewType Type{}; + u32 Size; + u64 Index; // todo 32 + u64 Offset; + } UploadBuffer; + + struct + { + FViewType Type{}; + u8 Mip; + u8 NumMips; + u8 Plane; + FGraphicsFormat Format{}; + + union + { + u32 Index; + u32 Z; + }; + + union + { + u32 Size{}; + u32 Depth; + }; + } Image; + }; }; struct FView { FGpuViewable* Viewable{}; - FViewType Type{}; + FViewData Data{}; }; } diff --git a/Coplt.Graphics.Native/Api/Include/HashMap.h b/Coplt.Graphics.Native/Api/Include/HashMap.h index 887306a..b78e232 100644 --- a/Coplt.Graphics.Native/Api/Include/HashMap.h +++ b/Coplt.Graphics.Native/Api/Include/HashMap.h @@ -700,6 +700,28 @@ namespace Coplt return GetOrAddEntry(key, already_exist, fove(create_value)).second; } + Value& GetOrAdd(const Key& key, Value&& value, bool& already_exist) + { + return GetOrAddEntry(key, already_exist, [&](UP p) { new(p.unsafe_put()) Value(std::forward(value)); }).second; + } + + Value& GetOrAdd(const Key& key, Value&& value) + { + bool already_exist{}; + return GetOrAdd(key, std::forward(value), already_exist); + } + + Value& GetOrAdd(const Key& key, const Value& value, bool& already_exist) + { + return GetOrAddEntry(key, already_exist, [&](UP p) { new(p.unsafe_put()) Value(value); }).second; + } + + Value& GetOrAdd(const Key& key, const Value& value) + { + bool already_exist{}; + return GetOrAdd(key, value, already_exist); + } + Value& GetOrAddDefault(Key&& key, bool& already_exist) { return GetOrAdd(std::forward(key), already_exist, [&](UP p) { new(p.unsafe_put()) Value(); }); diff --git a/Coplt.Graphics.Native/Api/Include/Uninit.h b/Coplt.Graphics.Native/Api/Include/Uninit.h index 253d1c4..73501f1 100644 --- a/Coplt.Graphics.Native/Api/Include/Uninit.h +++ b/Coplt.Graphics.Native/Api/Include/Uninit.h @@ -45,7 +45,7 @@ namespace Coplt } } - template requires std::convertible_to + template requires std::convertible_to void put(U&& value) { if (m_initialized) *m_ptr = std::forward(value); @@ -67,7 +67,7 @@ namespace Coplt return *m_ptr; } - template requires std::convertible_to + template requires std::convertible_to T& operator=(U&& p) noexcept(std::is_nothrow_move_constructible_v) { if (m_initialized) *m_ptr = std::forward(p); @@ -90,7 +90,7 @@ namespace Coplt return *m_ptr; } - template requires std::convertible_to + template requires std::convertible_to T& operator=(const U& p) noexcept(std::is_nothrow_copy_constructible_v) { if (m_initialized) *m_ptr = p; @@ -107,7 +107,7 @@ namespace Coplt return operator=(std::forward(p)); } - template requires std::convertible_to + template requires std::convertible_to T& operator<<(U&& p) noexcept(std::is_nothrow_move_constructible_v) { return operator=(std::forward(p)); @@ -118,7 +118,7 @@ namespace Coplt return operator=(p); } - template requires std::convertible_to + template requires std::convertible_to T& operator<<(const U& p) noexcept(std::is_nothrow_copy_constructible_v) { return operator=(p); diff --git a/Coplt.Graphics.Native/D3d12/CMakeLists.txt b/Coplt.Graphics.Native/D3d12/CMakeLists.txt index aea41f4..c947db5 100644 --- a/Coplt.Graphics.Native/D3d12/CMakeLists.txt +++ b/Coplt.Graphics.Native/D3d12/CMakeLists.txt @@ -58,6 +58,8 @@ add_library(${target_name} SHARED Src/DescriptorManager.h Src/DescriptorManager.cc Src/Pipeline.h + Src/ComputePipeline.h + Src/ComputePipeline.cc ) set_target_properties(${target_name} PROPERTIES LINKER_LANGUAGE CXX) set_target_properties(${target_name} PROPERTIES OUTPUT_NAME "Coplt.Graphics.Native.D3d12") diff --git a/Coplt.Graphics.Native/D3d12/Include/ResState.h b/Coplt.Graphics.Native/D3d12/Include/ResState.h index 5461932..6bca50d 100644 --- a/Coplt.Graphics.Native/D3d12/Include/ResState.h +++ b/Coplt.Graphics.Native/D3d12/Include/ResState.h @@ -107,6 +107,8 @@ namespace Coplt bool IsReadOnly(ResAccess access); + bool IsNonUavReadOnly(ResAccess access); + bool IsCompatible(ResAccess Old, ResAccess New); D3D12_RESOURCE_STATES GetResourceState(ResAccess access); diff --git a/Coplt.Graphics.Native/D3d12/Include/Sampler.h b/Coplt.Graphics.Native/D3d12/Include/Sampler.h index fb79d08..fdd6134 100644 --- a/Coplt.Graphics.Native/D3d12/Include/Sampler.h +++ b/Coplt.Graphics.Native/D3d12/Include/Sampler.h @@ -44,7 +44,7 @@ namespace Coplt desc.BorderColor[3] = info.BorderColor[3]; } - inline void SetBorderColor(const FStaticSamplerInfo& info, D3D12_STATIC_SAMPLER_DESC& desc) + inline void SetBorderColor(const FStaticSamplerInfo& info, D3D12_STATIC_SAMPLER_DESC1& desc) { switch (info.BorderColor) { diff --git a/Coplt.Graphics.Native/D3d12/Include/View.h b/Coplt.Graphics.Native/D3d12/Include/View.h index e8be9c8..6b15ac3 100644 --- a/Coplt.Graphics.Native/D3d12/Include/View.h +++ b/Coplt.Graphics.Native/D3d12/Include/View.h @@ -7,6 +7,7 @@ #include "../../Api/FFI/Resource.h" #include "../../Api/FFI/Binding.h" +#include "../Src/Context.h" namespace Coplt { @@ -22,7 +23,7 @@ namespace Coplt struct View final { Rc m_viewable{}; - FViewType m_type{}; + FViewData m_data{}; View() = default; @@ -43,14 +44,22 @@ namespace Coplt bool IsBuffer() const; bool IsImage() const; bool IsSampler() const; + bool IsUploadBuffer() const; + bool NeedBarrier() const; + NonNull GetViewable() const; Ptr TryGetBuffer() const; Ptr TryGetImage() const; Ptr TryGetSampler() const; - void CreateDescriptor(NonNull device, const FBindGroupItem& def, CD3DX12_CPU_DESCRIPTOR_HANDLE handle) const; + void CreateDescriptor( + NonNull device, Ptr context, const FBindGroupItem& def, CD3DX12_CPU_DESCRIPTOR_HANDLE handle + ) const; static void CreateNullDescriptor(NonNull device, const FBindGroupItem& def, CD3DX12_CPU_DESCRIPTOR_HANDLE handle); void CreateBufferDescriptor(NonNull device, const FBindGroupItem& def, CD3DX12_CPU_DESCRIPTOR_HANDLE handle) const; + void CreateUploadBufferDescriptor( + NonNull device, NonNull context, const FBindGroupItem& def, CD3DX12_CPU_DESCRIPTOR_HANDLE handle + ) const; void CreateImageDescriptor(NonNull device, const FBindGroupItem& def, CD3DX12_CPU_DESCRIPTOR_HANDLE handle) const; void CreateSamplerDescriptor(NonNull device, const FBindGroupItem& def, CD3DX12_CPU_DESCRIPTOR_HANDLE handle) const; }; diff --git a/Coplt.Graphics.Native/D3d12/Src/Barrier.cc b/Coplt.Graphics.Native/D3d12/Src/Barrier.cc index bba3d8a..f5074ae 100644 --- a/Coplt.Graphics.Native/D3d12/Src/Barrier.cc +++ b/Coplt.Graphics.Native/D3d12/Src/Barrier.cc @@ -145,9 +145,11 @@ namespace Coplt::Enhanced { if (info.State.IsCompatible(new_state, true)) { + if (HasFlags(info.State.Access, ResAccess::UnorderedAccessWrite) && m_last_cmd_count > 0) goto False0; info.State = info.State.Merge(new_state); goto End; } + False0: info.InfoState = InfoState::Used; m_inputs.push_back(IOResState{.ResIndex = ResIndex, .LstGroup = info.LstGroup, .State = info.State}); if (info.CurGroup == CurGroupIndex() || need_split) Split(); @@ -157,9 +159,11 @@ namespace Coplt::Enhanced { if (info.State.IsCompatible(new_state, true)) { + if (HasFlags(info.State.Access, ResAccess::UnorderedAccessWrite) && m_last_cmd_count > 0) goto False1; info.State = info.State.Merge(new_state); goto End; } + False1: } if (info.CurGroup == CurGroupIndex() || need_split) Split(); CreateBarrier(info); @@ -399,49 +403,29 @@ namespace Coplt::Enhanced }); if (!exist) { - if (sr.State->Layout != input.State.Layout) + if (res.IsImage() && sr.State->Layout != input.State.Layout) { auto& group = result_list ? m_tmp_group : barrier_analyzer->Groups()[0]; - if (res.IsImage()) - { - const auto img_data = res.GetImageData(); - const auto SyncAfter = GetBarrierSync(input.State.Access, input.State.Usage); - D3D12_TEXTURE_BARRIER barrier{ - .SyncBefore = D3D12_BARRIER_SYNC_NONE, - .SyncAfter = SyncAfter, - .AccessBefore = D3D12_BARRIER_ACCESS_NO_ACCESS, - .AccessAfter = GetBarrierAccess(input.State.Access), - .LayoutBefore = GetBarrierLayout(sr.State->Layout), - .LayoutAfter = GetBarrierLayout(input.State.Layout), - .pResource = res.GetResource(), - .Subresources = { - .IndexOrFirstMipLevel = 0, - .NumMipLevels = img_data->m_mip_levels, - .FirstArraySlice = 0, - .NumArraySlices = img_data->m_depth_or_length, - .FirstPlane = 0, - .NumPlanes = img_data->m_planes, - }, - .Flags = D3D12_TEXTURE_BARRIER_FLAG_NONE, - }; - group.Push(barrier); - } - else - { - const auto buffer_data = res.GetBufferData(); - const auto SyncAfter = GetBarrierSync(input.State.Access, input.State.Usage); - D3D12_BUFFER_BARRIER barrier{ - .SyncBefore = D3D12_BARRIER_SYNC_NONE, - .SyncAfter = D3D12_BARRIER_SYNC_SPLIT, - .AccessBefore = D3D12_BARRIER_ACCESS_NO_ACCESS, - .AccessAfter = GetBarrierAccess(input.State.Access), - .pResource = res.GetResource(), - .Offset = 0, - .Size = buffer_data->m_size, - }; - barrier.SyncAfter = SyncAfter; - group.Push(barrier); - } + const auto img_data = res.GetImageData(); + D3D12_TEXTURE_BARRIER barrier{ + .SyncBefore = D3D12_BARRIER_SYNC_NONE, + .SyncAfter = GetBarrierSync(input.State.Access, input.State.Usage), + .AccessBefore = D3D12_BARRIER_ACCESS_NO_ACCESS, + .AccessAfter = GetBarrierAccess(input.State.Access), + .LayoutBefore = GetBarrierLayout(sr.State->Layout), + .LayoutAfter = GetBarrierLayout(input.State.Layout), + .pResource = res.GetResource(), + .Subresources = { + .IndexOrFirstMipLevel = 0, + .NumMipLevels = img_data->m_mip_levels, + .FirstArraySlice = 0, + .NumArraySlices = img_data->m_depth_or_length, + .FirstPlane = 0, + .NumPlanes = img_data->m_planes, + }, + .Flags = D3D12_TEXTURE_BARRIER_FLAG_NONE, + }; + group.Push(barrier); } } // todo @@ -529,7 +513,7 @@ bool AD3d12BarrierCombiner::Submit(NonNull isolate, std::span 0; + const auto has_submit = !m_list_node.empty(); if (has_submit) { const auto& main_queue = isolate->m_main_queue; diff --git a/Coplt.Graphics.Native/D3d12/Src/Binding.cc b/Coplt.Graphics.Native/D3d12/Src/Binding.cc index 6788291..7351649 100644 --- a/Coplt.Graphics.Native/D3d12/Src/Binding.cc +++ b/Coplt.Graphics.Native/D3d12/Src/Binding.cc @@ -8,62 +8,84 @@ using namespace Coplt; +DescriptorHeapPair::operator bool() const +{ + return ResourceHeap || SamplerHeap; +} + D3d12ShaderBindGroup::D3d12ShaderBindGroup(Rc&& device, const FShaderBindGroupCreateOptions& options) : m_device(std::move(device)) { const auto layout = NonNull(options.Layout)->QueryInterface(); if (layout == nullptr) COPLT_THROW("Layout from different backends"); m_layout = Rc::UnsafeClone(layout); + const auto& layout_data = m_layout->Data(); + m_resource_heap_inc = m_device->m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + m_sampler_heap_inc = m_device->m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); const auto items = m_layout->GetItems(); usize sum_count = 0; for (const auto& item : items) { - sum_count += std::max(1u, item.Count); + if (item.Count == COPLT_U32_MAX) sum_count += 1; + else sum_count += std::max(1u, item.Count); } CountSlots = sum_count; - m_views = std::vector(sum_count, View{}); - m_changed_marks = std::vector(sum_count, false); m_item_indexes.reserve(items.size()); m_define_indexes.reserve(sum_count); for (u32 i = 0, off = 0; i < items.size(); ++i) { m_item_indexes.push_back(off); const auto count = std::max(1u, items[i].Count); + if (count == COPLT_U32_MAX) + { + m_define_indexes.push_back(i); + break; + } off += count; for (int j = 0; j < count; ++j) { m_define_indexes.push_back(i); } } - const auto& layout_data = m_layout->Data(); - m_resource_heap_inc = m_device->m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); - m_sampler_heap_inc = m_device->m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); - if (layout_data.ResourceTableSize > 0) - { - D3D12_DESCRIPTOR_HEAP_DESC desc{}; - desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; - desc.NumDescriptors = layout_data.ResourceTableSize; - chr | m_device->m_device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&m_resource_heap)); - } - if (layout_data.SamplerTableSize > 0) + if (layout_data.Usage == FBindGroupUsage::Dynamic) { - D3D12_DESCRIPTOR_HEAP_DESC desc{}; - desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER; - desc.NumDescriptors = layout_data.SamplerTableSize; - chr | m_device->m_device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&m_sampler_heap)); + if (options.NumBindings > 0) + { + COPLT_THROW("Dynamic groups are not allowed to set items"); + } } - if (!options.Name.is_null()) + else { - m_name = options.Name.ToString(); - DoSetName(m_name->GetStr()); + m_views = std::vector(sum_count, View{}); + m_changed_marks = std::vector(sum_count, false); + if (layout_data.ResourceTableSize > 0) + { + D3D12_DESCRIPTOR_HEAP_DESC desc{}; + desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; + desc.NumDescriptors = layout_data.ResourceTableSize; + chr | m_device->m_device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&m_resource_heap)); + } + if (layout_data.SamplerTableSize > 0) + { + D3D12_DESCRIPTOR_HEAP_DESC desc{}; + desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER; + desc.NumDescriptors = layout_data.SamplerTableSize; + chr | m_device->m_device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&m_sampler_heap)); + } + if (m_device->Debug() && !options.Name.is_null()) + { + m_name = options.Name.ToString(); + DoSetName(m_name->GetStr()); + } + Set(std::span(options.Bindings, options.Bindings + options.NumBindings)); } - Set(std::span(options.Bindings, options.Bindings + options.NumBindings)); } FResult D3d12ShaderBindGroup::SetName(const FStr8or16& name) noexcept { return feb([&] { + if (!m_device->Debug()) return; m_name = name.ToString(); DoSetName(name); }); @@ -106,6 +128,43 @@ void D3d12ShaderBindGroup::DoSetName(const FStr8or16& name) const } } +void D3d12ShaderBindGroup::DoSetName(const FStr8or16& name, const DescriptorHeapPair& pair) +{ + if (name.is_null()) return; + if (pair.ResourceHeap) + { + if (name.is8()) + { + const auto str = fmt::format("[{}]::ResourceDescriptorHeap", name.str8); + chr | pair.ResourceHeap >> SetNameEx(FStr8or16(str)); + } + else + { + const auto str = fmt::format( + L"[{}]::ResourceDescriptorHeap", + reinterpret_cast(name.str16) + ); + chr | pair.ResourceHeap >> SetNameEx(FStr8or16(str)); + } + } + if (pair.SamplerHeap) + { + if (name.is8()) + { + const auto str = fmt::format("[{}]::SamplerDescriptorHeap", name.str8); + chr | pair.SamplerHeap >> SetNameEx(FStr8or16(str)); + } + else + { + const auto str = fmt::format( + L"[{}]::SamplerDescriptorHeap", + reinterpret_cast(name.str16) + ); + chr | pair.SamplerHeap >> SetNameEx(FStr8or16(str)); + } + } +} + FShaderBindGroupData* D3d12ShaderBindGroup::ShaderBindGroupData() noexcept { return this; @@ -143,6 +202,10 @@ RwLock& D3d12ShaderBindGroup::SelfLock() noexcept void D3d12ShaderBindGroup::Set(const std::span items) { + if (m_layout->Data().Usage == FBindGroupUsage::Dynamic) + { + COPLT_THROW("Dynamic groups are not allowed to set items"); + } if (items.empty()) return; const auto defs = m_layout->GetItems(); const auto slots = m_layout->Slots(); @@ -188,13 +251,63 @@ void D3d12ShaderBindGroup::Set(const std::span items) m_version++; } -bool D3d12ShaderBindGroup::EnsureAvailable() +DescriptorHeapPair D3d12ShaderBindGroup::Cow() const +{ + DescriptorHeapPair out{}; + const auto& layout_data = m_layout->Data(); + if (layout_data.ResourceTableSize > 0) + { + D3D12_DESCRIPTOR_HEAP_DESC desc{}; + desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; + desc.NumDescriptors = layout_data.ResourceTableSize; + chr | m_device->m_device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&out.ResourceHeap)); + m_device->m_device->CopyDescriptorsSimple( + layout_data.ResourceTableSize, + out.ResourceHeap->GetCPUDescriptorHandleForHeapStart(), + m_resource_heap->GetCPUDescriptorHandleForHeapStart(), + desc.Type + ); + out.ResourceVersion = m_resource_version + 1; + } + if (layout_data.SamplerTableSize > 0) + { + D3D12_DESCRIPTOR_HEAP_DESC desc{}; + desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER; + desc.NumDescriptors = layout_data.SamplerTableSize; + chr | m_device->m_device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&out.SamplerHeap)); + m_device->m_device->CopyDescriptorsSimple( + layout_data.SamplerTableSize, + out.SamplerHeap->GetCPUDescriptorHandleForHeapStart(), + m_sampler_heap->GetCPUDescriptorHandleForHeapStart(), + desc.Type + ); + out.SamplerVersion = m_sampler_version + 1; + } + if (m_device->Debug() && m_name) + { + DoSetName(m_name->GetStr(), out); + } + return out; +} + +bool D3d12ShaderBindGroup::EnsureAvailable(DescriptorHeapPair& out) { - if (!m_changed) return false; + if (!m_changed) + { + out.ResourceHeap = m_resource_heap; + out.SamplerHeap = m_sampler_heap; + return false; + } std::lock_guard guard(m_desc_lock); - if (!m_changed) return false; + if (!m_changed) + { + out.ResourceHeap = m_resource_heap; + out.SamplerHeap = m_sampler_heap; + return false; + } const auto defs = m_layout->GetItems(); const auto slots = m_layout->Slots(); + auto new_heap = Cow(); for (usize i = 0; i < m_views.size(); ++i) { const auto def_index = m_define_indexes[i]; @@ -203,13 +316,20 @@ bool D3d12ShaderBindGroup::EnsureAvailable() if (const auto& mark = m_changed_marks[i]) mark = false; else continue; const auto& view = m_views[i]; + if (view.IsUploadBuffer()) + COPLT_THROW_FMT("[{}] The upload buffer can only be set dynamically in the dynamic buffer.", i); const auto& def = defs[def_index]; - const auto& heap = slot.Place == Layout::BindSlotPlace::ResourceTable ? m_resource_heap : m_sampler_heap; + const auto& heap = slot.Place == Layout::BindSlotPlace::ResourceTable ? new_heap.ResourceHeap : new_heap.SamplerHeap; COPLT_DEBUG_ASSERT(heap != nullptr); CD3DX12_CPU_DESCRIPTOR_HANDLE handle(heap->GetCPUDescriptorHandleForHeapStart(), slot.OffsetInTable, m_resource_heap_inc); - view.CreateDescriptor(m_device->m_device.Get(), def, handle); + view.CreateDescriptor(m_device->m_device.Get(), nullptr, def, handle); } m_changed = false; + m_resource_heap = new_heap.ResourceHeap; + m_sampler_heap = new_heap.SamplerHeap; + m_resource_version = std::max(m_resource_version, new_heap.ResourceVersion); + m_sampler_version = std::max(m_sampler_version, new_heap.SamplerVersion); + out = std::move(new_heap); return true; } diff --git a/Coplt.Graphics.Native/D3d12/Src/Binding.h b/Coplt.Graphics.Native/D3d12/Src/Binding.h index 0c58245..9fafeff 100644 --- a/Coplt.Graphics.Native/D3d12/Src/Binding.h +++ b/Coplt.Graphics.Native/D3d12/Src/Binding.h @@ -15,16 +15,28 @@ namespace Coplt struct ID3d12ShaderBinding; struct DescriptorAllocation; + struct DescriptorHeapPair + { + u64 ResourceVersion{}; + u64 SamplerVersion{}; + ComPtr ResourceHeap{}; + ComPtr SamplerHeap{}; + + explicit operator bool() const; + }; + COPLT_INTERFACE_DEFINE(ID3d12ShaderBindGroup, "5bb948b9-ad31-4eb8-b881-98017e048259", FShaderBindGroup) { virtual u64 Version() const noexcept = 0; virtual const Rc& Layout() const noexcept = 0; virtual std::span Views() const noexcept = 0; + // 布局 item index => view index virtual std::span ItemIndexes() const noexcept = 0; + // view index => 布局 item index virtual std::span DefineIndexes() const noexcept = 0; virtual RwLock& SelfLock() noexcept = 0; // 返回是否有改变 - virtual bool EnsureAvailable() = 0; + virtual bool EnsureAvailable(DescriptorHeapPair& out) = 0; virtual const ComPtr& ResourceHeap() noexcept = 0; virtual const ComPtr& SamplerHeap() noexcept = 0; @@ -36,8 +48,11 @@ namespace Coplt Rc m_name{}; Rc m_device{}; Rc m_layout{}; + // 动态组永远不会创建描述符暂存堆 ComPtr m_resource_heap{}; ComPtr m_sampler_heap{}; + u64 m_resource_version{}; + u64 m_sampler_version{}; u32 m_resource_heap_inc{}; u32 m_sampler_heap_inc{}; std::vector m_views{}; @@ -56,6 +71,7 @@ namespace Coplt explicit D3d12ShaderBindGroup(Rc&& device, const FShaderBindGroupCreateOptions& options); FResult SetName(const FStr8or16& name) noexcept override; void DoSetName(const FStr8or16& name) const; + static void DoSetName(const FStr8or16& name, const DescriptorHeapPair& pair); FShaderBindGroupData* ShaderBindGroupData() noexcept override; @@ -69,8 +85,10 @@ namespace Coplt // 需要外部锁 m_self_lock 必须是 Write 锁定状态 void Set(std::span items); + DescriptorHeapPair Cow() const; + // 需要外部锁 m_self_lock 必须是 Read 锁定状态 - bool EnsureAvailable() override; + bool EnsureAvailable(DescriptorHeapPair& out) override; const ComPtr& ResourceHeap() noexcept override; const ComPtr& SamplerHeap() noexcept override; diff --git a/Coplt.Graphics.Native/D3d12/Src/ComputePipeline.cc b/Coplt.Graphics.Native/D3d12/Src/ComputePipeline.cc new file mode 100644 index 0000000..d987375 --- /dev/null +++ b/Coplt.Graphics.Native/D3d12/Src/ComputePipeline.cc @@ -0,0 +1,96 @@ +#include "ComputePipeline.h" + +using namespace Coplt; + +D3d12ComputeShaderPipeline::D3d12ComputeShaderPipeline(Rc&& device, const FShaderPipelineCreateOptions& options) + : m_device(std::move(device)) +{ + if (options.Shader == nullptr) + COPLT_THROW("Shader is null"); + m_shader = Rc::UnsafeClone(options.Shader); + if (!HasAnyFlags(m_shader->Stages(), FShaderStageFlags::Compute)) // | FShaderStageFlags::Mesh + COPLT_THROW("The shader is not compute"); + + if (m_shader->Layout()) + { + auto layout = NonNull(options.Layout)->QueryInterface(); + if (layout == nullptr) + COPLT_THROW("Shader layout from different backends"); + m_layout = Rc::UnsafeClone(layout); + if (m_layout->ShaderLayout()->ObjectId() != m_shader->Layout()->ObjectId()) + COPLT_THROW("Differences between binding layout and shader layout"); + } + else + { + m_layout = m_device->GetEmptyBindingLayout(FShaderLayoutFlags::None); + } + + auto root_signature = m_layout->RootSignature().Get(); + + CD3DX12_PIPELINE_STATE_STREAM2 stream{}; + if (HasFlags(m_shader->Stages(), FShaderStageFlags::Compute)) + { + D3D12_COMPUTE_PIPELINE_STATE_DESC desc{}; + desc.pRootSignature = root_signature; + NonNull cs = m_shader->Compute(); + desc.CS = D3D12_SHADER_BYTECODE{cs->Data().Data, cs->Data().Size}; + stream = CD3DX12_PIPELINE_STATE_STREAM2(desc); + } + // else if (HasFlags(m_shader->Stages(), FShaderStageFlags::Mesh)) // 当前不确定是否应该在计算中支持 Mesh Shader + // { + // D3DX12_MESH_SHADER_PIPELINE_STATE_DESC desc{}; + // desc.pRootSignature = root_signature; + // NonNull ms = m_shader->Mesh(); + // auto ts = m_shader->Task(); + // desc.MS = D3D12_SHADER_BYTECODE{ms->Data().Data, ms->Data().Size}; + // if (ts != nullptr) + // desc.AS = D3D12_SHADER_BYTECODE{ts->Data().Data, ts->Data().Size}; + // stream = CD3DX12_PIPELINE_STATE_STREAM2(desc); + // } + else + COPLT_THROW("Unknown shader stage combination"); + + D3D12_PIPELINE_STATE_STREAM_DESC stream_desc{}; + stream_desc.SizeInBytes = sizeof(stream); + stream_desc.pPipelineStateSubobjectStream = &stream; + chr | m_device->m_device->CreatePipelineState(&stream_desc, IID_PPV_ARGS(&m_pipeline)); + + if (m_device->Debug()) + { + chr | m_pipeline >> SetNameEx(options.Name); + } +} + +FResult D3d12ComputeShaderPipeline::SetName(const FStr8or16& name) noexcept +{ + return feb([&] + { + if (!m_device->Debug()) return; + chr | m_pipeline >> SetNameEx(name); + }); +} + +const Rc& D3d12ComputeShaderPipeline::Layout() const noexcept +{ + return m_layout; +} + +const ComPtr& D3d12ComputeShaderPipeline::GetPipelineState() const noexcept +{ + return m_pipeline; +} + +FShader* D3d12ComputeShaderPipeline::GetShader() noexcept +{ + return m_shader.get(); +} + +FBindingLayout* D3d12ComputeShaderPipeline::GetLayout() noexcept +{ + return m_layout.get(); +} + +FShaderStageFlags D3d12ComputeShaderPipeline::GetStages() noexcept +{ + return m_shader->Stages(); +} diff --git a/Coplt.Graphics.Native/D3d12/Src/ComputePipeline.h b/Coplt.Graphics.Native/D3d12/Src/ComputePipeline.h new file mode 100644 index 0000000..1d22360 --- /dev/null +++ b/Coplt.Graphics.Native/D3d12/Src/ComputePipeline.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include "../../Api/Include/Object.h" +#include "Device.h" +#include "Layout.h" +#include "Pipeline.h" + +namespace Coplt +{ + struct D3d12ComputeShaderPipeline final : GpuObject + { + Rc m_device{}; + Rc m_shader{}; + Rc m_layout{}; + ComPtr m_pipeline{}; + + explicit D3d12ComputeShaderPipeline(Rc&& device, const FShaderPipelineCreateOptions& options); + + FResult SetName(const FStr8or16& name) noexcept override; + + const Rc& Layout() const noexcept override; + const ComPtr& GetPipelineState() const noexcept override; + + FShader* GetShader() noexcept override; + FBindingLayout* GetLayout() noexcept override; + FShaderStageFlags GetStages() noexcept override; + }; +} diff --git a/Coplt.Graphics.Native/D3d12/Src/Context.cc b/Coplt.Graphics.Native/D3d12/Src/Context.cc index abb11df..df7e7f0 100644 --- a/Coplt.Graphics.Native/D3d12/Src/Context.cc +++ b/Coplt.Graphics.Native/D3d12/Src/Context.cc @@ -329,9 +329,11 @@ D3d12RentedCommandList::D3d12RentedCommandList(const Rc& pool, { } -D3d12RentedCommandList::~D3d12RentedCommandList() +D3d12RentedCommandList::~D3d12RentedCommandList() noexcept(false) { - if (!m_pool) return; + if (!m_pool || !m_list) return; + if (std::uncaught_exceptions()) + return; m_pool->ReturnCommandList(std::move(m_list)); } @@ -369,9 +371,11 @@ D3d12RentedCommandAllocator::D3d12RentedCommandAllocator(const RcReturnCommandAllocator(std::move(m_allocator)); } diff --git a/Coplt.Graphics.Native/D3d12/Src/Context.h b/Coplt.Graphics.Native/D3d12/Src/Context.h index f02ea63..db1fcf1 100644 --- a/Coplt.Graphics.Native/D3d12/Src/Context.h +++ b/Coplt.Graphics.Native/D3d12/Src/Context.h @@ -122,7 +122,7 @@ namespace Coplt D3d12RentedCommandList() = default; explicit D3d12RentedCommandList(const Rc& pool, Rc&& list); - ~D3d12RentedCommandList(); + ~D3d12RentedCommandList() noexcept(false); D3d12RentedCommandList(D3d12RentedCommandList&&) = default; D3d12RentedCommandList& operator=(D3d12RentedCommandList&&) = default; @@ -146,7 +146,7 @@ namespace Coplt D3d12RentedCommandAllocator() = default; explicit D3d12RentedCommandAllocator(const Rc& pool, ComPtr&& allocator); - ~D3d12RentedCommandAllocator(); + ~D3d12RentedCommandAllocator() noexcept(false); D3d12RentedCommandAllocator(D3d12RentedCommandAllocator&& other) = default; D3d12RentedCommandAllocator& operator=(D3d12RentedCommandAllocator&& other) = default; diff --git a/Coplt.Graphics.Native/D3d12/Src/DescriptorManager.cc b/Coplt.Graphics.Native/D3d12/Src/DescriptorManager.cc index c44d2ef..4312ed6 100644 --- a/Coplt.Graphics.Native/D3d12/Src/DescriptorManager.cc +++ b/Coplt.Graphics.Native/D3d12/Src/DescriptorManager.cc @@ -26,8 +26,8 @@ DescriptorAllocation::operator bool() const return m_heap != nullptr; } -DescriptorHeap::DescriptorHeap(NonNull device, D3D12_DESCRIPTOR_HEAP_TYPE type, u32 init_size) - : m_device(device->m_device), m_half_size(init_size), m_type(type) +DescriptorHeap::DescriptorHeap(NonNull device, D3D12_DESCRIPTOR_HEAP_TYPE type, u32 init_size, u32 max_size) + : m_device(device->m_device), m_half_size(init_size), m_type(type), m_max_size(max_size / 2) { { D3D12_DESCRIPTOR_HEAP_DESC desc{}; @@ -58,8 +58,11 @@ void DescriptorHeap::ReadyFrame(const u32 cap) void DescriptorHeap::EnsureCapacity(u32 cap) { - cap = std::bit_ceil(cap); - if (m_need_grow) cap = std::max(cap, m_half_size * 2); + const auto new_cap = std::min(std::bit_ceil(cap), m_max_size); + if (cap > new_cap) + COPLT_THROW_FMT("Out of memory; The required descriptor heap capacity is {}, but the maximum allowed is {}", cap * 2, m_max_size * 2); + cap = new_cap; + if (m_need_grow) cap = std::max(cap, std::min(m_half_size * 2, m_max_size)); else if (cap <= m_half_size) return; m_half_size = cap; @@ -86,9 +89,10 @@ void DescriptorHeap::EnsureCapacity(u32 cap) } } -DescriptorAllocation DescriptorHeap::Allocate(const u64 ObjectId, const u32 Size, bool& IsOld) +DescriptorAllocation DescriptorHeap::Allocate(const u64 ObjectId, const u64 Version, const u32 Size, bool& NeedUpload) { - const auto& allocation = m_allocations.GetOrAdd(ObjectId, IsOld, [this, Size](auto& p) + bool exists = false; + auto& allocation = m_allocations.GetOrAdd(ObjectId, exists, [this, Version, Size](auto& p) { D3D12MA::VIRTUAL_ALLOCATION_DESC desc{}; desc.Alignment = 1; @@ -100,14 +104,20 @@ DescriptorAllocation DescriptorHeap::Allocate(const u64 ObjectId, const u32 Size { const auto a = AllocateTmp(Size); m_need_grow = true; - p.put(Allocation{{}, a.m_offset}); + p.put(Allocation{{}, a.m_offset, Version}); } else { chr | r; - p.put(Allocation{allocation, offset}); + p.put(Allocation{allocation, offset, Version}); } }); + if (!exists) NeedUpload = true; + if (allocation.m_version < Version) + { + allocation.m_version = Version; + NeedUpload = true; + } return DescriptorAllocation(this, allocation.m_offset); } @@ -123,8 +133,8 @@ DescriptorAllocation DescriptorHeap::AllocateTmp(const u32 Size) DescriptorManager::DescriptorManager(NonNull device) { - m_res = new DescriptorHeap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, InitResHeapSize); - m_smp = new DescriptorHeap(device, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, InitSmpHeapSize); + m_res = new DescriptorHeap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, InitResHeapSize, MaxResHeapSize); + m_smp = new DescriptorHeap(device, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, InitSmpHeapSize, MaxSmpHeapSize); } void DescriptorManager::ReadyFrame(const u32 res_cap, const u32 smp_cap) const diff --git a/Coplt.Graphics.Native/D3d12/Src/DescriptorManager.h b/Coplt.Graphics.Native/D3d12/Src/DescriptorManager.h index 7074cb3..22b51a7 100644 --- a/Coplt.Graphics.Native/D3d12/Src/DescriptorManager.h +++ b/Coplt.Graphics.Native/D3d12/Src/DescriptorManager.h @@ -35,6 +35,7 @@ namespace Coplt { D3D12MA::VirtualAllocation m_allocation{}; u64 m_offset{}; + u64 m_version{}; }; // 堆版本,每次扩容增加版本,每次扩容所有持久分配都会失效 @@ -45,6 +46,7 @@ namespace Coplt HashMap m_allocations{}; // todo 释放绑组时排队到隔离,释放分配 // 描述符半容量,永远保证一半容量可以完整容纳每帧需求 u32 m_half_size{}; + u32 m_max_size{}; // 临时分配的偏移 u32 m_tmp_offset{}; // heap 类型的描述符增量 @@ -53,12 +55,12 @@ namespace Coplt // 下帧是否需要扩容,即使半容量足够 bool m_need_grow{}; - explicit DescriptorHeap(NonNull device, D3D12_DESCRIPTOR_HEAP_TYPE type, u32 init_size); + explicit DescriptorHeap(NonNull device, D3D12_DESCRIPTOR_HEAP_TYPE type, u32 init_size, u32 max_size); void ReadyFrame(u32 cap); void EnsureCapacity(u32 cap); - DescriptorAllocation Allocate(u64 ObjectId, u32 Size, bool& IsOld); + DescriptorAllocation Allocate(u64 ObjectId, u64 Version, u32 Size, bool& NeedUpload); DescriptorAllocation AllocateTmp(u32 Size); }; @@ -66,6 +68,8 @@ namespace Coplt { constexpr static u32 InitResHeapSize = 1024; constexpr static u32 InitSmpHeapSize = 64; + constexpr static u32 MaxResHeapSize = COPLT_U32_MAX; + constexpr static u32 MaxSmpHeapSize = 4096; Rc m_res{}; Rc m_smp{}; diff --git a/Coplt.Graphics.Native/D3d12/Src/Device.cc b/Coplt.Graphics.Native/D3d12/Src/Device.cc index 1cdcccd..eb545b3 100644 --- a/Coplt.Graphics.Native/D3d12/Src/Device.cc +++ b/Coplt.Graphics.Native/D3d12/Src/Device.cc @@ -5,6 +5,7 @@ #include "../../Api/Src/Shader.h" #include "Adapter.h" #include "Buffer.h" +#include "ComputePipeline.h" #include "GraphicsPipeline.h" #include "Image.h" #include "Isolate.h" @@ -328,6 +329,14 @@ FResult D3d12GpuDevice::CreateGraphicsPipeline(const FGraphicsShaderPipelineCrea }); } +FResult D3d12GpuDevice::CreateComputePipeline(const FShaderPipelineCreateOptions& options, FComputeShaderPipeline** out) noexcept +{ + return feb([&] + { + *out = new D3d12ComputeShaderPipeline(this->CloneThis(), options); + }); +} + FResult D3d12GpuDevice::CreateBuffer(const FGpuBufferCreateOptions& options, FGpuBuffer** out) noexcept { return feb([&] diff --git a/Coplt.Graphics.Native/D3d12/Src/Device.h b/Coplt.Graphics.Native/D3d12/Src/Device.h index 4d3e0ee..e27e4de 100644 --- a/Coplt.Graphics.Native/D3d12/Src/Device.h +++ b/Coplt.Graphics.Native/D3d12/Src/Device.h @@ -74,6 +74,7 @@ namespace Coplt FResult CreateShaderBinding(const FShaderBindingCreateOptions& options, FShaderBindingCreateResult* out) noexcept override; FResult CreateGraphicsPipeline(const FGraphicsShaderPipelineCreateOptions& options, FGraphicsShaderPipeline** out) noexcept override; + FResult CreateComputePipeline(const FShaderPipelineCreateOptions& options, FComputeShaderPipeline** out) noexcept override; FResult CreateBuffer(const FGpuBufferCreateOptions& options, FGpuBuffer** out) noexcept override; FResult CreateImage(const FGpuImageCreateOptions& options, FGpuImage** out) noexcept override; diff --git a/Coplt.Graphics.Native/D3d12/Src/GraphicsPipeline.cc b/Coplt.Graphics.Native/D3d12/Src/GraphicsPipeline.cc index 67f6131..6f2b9e8 100644 --- a/Coplt.Graphics.Native/D3d12/Src/GraphicsPipeline.cc +++ b/Coplt.Graphics.Native/D3d12/Src/GraphicsPipeline.cc @@ -131,7 +131,6 @@ D3d12GraphicsShaderPipeline::D3d12GraphicsShaderPipeline( Rc&& device, const FGraphicsShaderPipelineCreateOptions& options ) : m_device(std::move(device)), m_graphics_state(options.GraphicsState) { - m_dx_device = m_device->m_device; if (options.Shader == nullptr) COPLT_THROW("Shader is null"); m_shader = Rc::UnsafeClone(options.Shader); @@ -212,7 +211,7 @@ D3d12GraphicsShaderPipeline::D3d12GraphicsShaderPipeline( D3D12_PIPELINE_STATE_STREAM_DESC stream_desc{}; stream_desc.SizeInBytes = sizeof(stream); stream_desc.pPipelineStateSubobjectStream = &stream; - chr | m_dx_device->CreatePipelineState(&stream_desc, IID_PPV_ARGS(&m_pipeline)); + chr | m_device->m_device->CreatePipelineState(&stream_desc, IID_PPV_ARGS(&m_pipeline)); if (m_device->Debug()) { diff --git a/Coplt.Graphics.Native/D3d12/Src/GraphicsPipeline.h b/Coplt.Graphics.Native/D3d12/Src/GraphicsPipeline.h index 56d7d58..da69a79 100644 --- a/Coplt.Graphics.Native/D3d12/Src/GraphicsPipeline.h +++ b/Coplt.Graphics.Native/D3d12/Src/GraphicsPipeline.h @@ -18,7 +18,6 @@ namespace Coplt Rc m_input_layout{}; // 可选 Rc m_mesh_layout{}; - ComPtr m_dx_device{}; ComPtr m_pipeline{}; std::vector m_input_slots{}; FGraphicsPipelineState m_graphics_state{}; diff --git a/Coplt.Graphics.Native/D3d12/Src/Layout.cc b/Coplt.Graphics.Native/D3d12/Src/Layout.cc index febe186..05adeec 100644 --- a/Coplt.Graphics.Native/D3d12/Src/Layout.cc +++ b/Coplt.Graphics.Native/D3d12/Src/Layout.cc @@ -69,7 +69,7 @@ D3d12BindGroupLayout::D3d12BindGroupLayout(const FBindGroupLayoutCreateOptions& { m_items = std::vector(options.Items, options.Items + options.NumItems); m_static_samplers = std::vector(options.StaticSamplers, options.StaticSamplers + options.NumStaticSamplers); - Items = m_items.data(); + FBindGroupLayoutData::Items = m_items.data(); StaticSamplers = m_static_samplers.data(); NumItems = m_items.size(); NumStaticSamplers = m_static_samplers.size(); @@ -81,19 +81,65 @@ D3d12BindGroupLayout::D3d12BindGroupLayout(const FBindGroupLayoutCreateOptions& BindSlotInfo info{}; const auto& item = m_items[i]; const auto count = std::max(1u, item.Count); + if (item.View == FShaderLayoutItemView::Constants) + { + if (Usage != FBindGroupUsage::Dynamic) + { + COPLT_THROW_FMT( + "Invalid binding define {{ Id = {}, Scope = {}, Stages = {} }} at [{}]; Only dynamic usage groups support constants", + item.Id, item.Scope, static_cast(item.Stages), i + ); + } + } + if (count == COPLT_U32_MAX) + { + if (Usage != FBindGroupUsage::Dynamic) + { + COPLT_THROW_FMT( + "Invalid binding define {{ Id = {}, Scope = {}, Stages = {} }} at [{}]; Only dynamic usage groups support unlimited length arrays", + item.Id, item.Scope, static_cast(item.Stages), i + ); + } + switch (item.View) + { + case FShaderLayoutItemView::Cbv: + case FShaderLayoutItemView::Srv: + case FShaderLayoutItemView::Uav: + if (i + 1 != m_items.size()) + { + COPLT_THROW_FMT( + "Invalid binding define {{ Id = {}, Scope = {}, Stages = {} }} at [{}]; Dynamic length array must be the last item in the group", + item.Id, item.Scope, static_cast(item.Stages), i + ); + } + DynamicArrayIndex = i; + break; + case FShaderLayoutItemView::Sampler: + case FShaderLayoutItemView::Constants: + case FShaderLayoutItemView::StaticSampler: + COPLT_THROW_FMT( + "Invalid binding define {{ Id = {}, Scope = {}, Stages = {} }} at [{}]; Only Cbv|Srv|Uav View support unlimited length arrays", + item.Id, item.Scope, static_cast(item.Stages), i + ); + } + } switch (item.View) { case FShaderLayoutItemView::Cbv: case FShaderLayoutItemView::Srv: case FShaderLayoutItemView::Uav: - if (Usage == FBindGroupUsage::Dynamic && item.Count <= 1 && IsBuffer(item.Type)) - { - info.Place = BindSlotPlace::NonTable; - break; - } + // if (Usage == FBindGroupUsage::Dynamic && item.Count <= 1 && IsBuffer(item.Type)) + // { + // info.Place = BindSlotPlace::NonTable; + // break; + // } + // todo 暂时不支持直接资源 info.OffsetInTable = ResourceTableSize; info.Place = BindSlotPlace::ResourceTable; - ResourceTableSize += count; + if (count != COPLT_U32_MAX) + { + ResourceTableSize += count; + } break; case FShaderLayoutItemView::Sampler: info.OffsetInTable = SamplerTableSize; @@ -119,6 +165,16 @@ FBindGroupLayoutData* D3d12BindGroupLayout::BindGroupLayoutData() noexcept return this; } +std::span D3d12BindGroupLayout::Items() const noexcept +{ + return m_items; +} + +std::span D3d12BindGroupLayout::StaticSamplerInfos() const noexcept +{ + return m_static_samplers; +} + const D3d12BindGroupLayoutData& D3d12BindGroupLayout::Data() const noexcept { return *this; @@ -196,16 +252,22 @@ D3d12BindingLayout::D3d12BindingLayout(Rc&& device, const FBindi } const auto& def = defines[info.Index]; if ( - def.View != item.View || (item.View == FShaderLayoutItemView::StaticSampler ? false : def.Count != item.Count) + !IsCompatible(def.View, item.View) || + (item.View == FShaderLayoutItemView::StaticSampler || def.Count == COPLT_U32_MAX ? false : def.Count != item.Count) || def.Type != item.Type || def.Format != item.Format ) { + if (item.View == FShaderLayoutItemView::Constants && def.View == FShaderLayoutItemView::Cbv) + { + if (def.Count == 1) goto Compatible; + } COPLT_THROW_FMT( "Incompatible binding slot {{ Id = {}, Scope = {}, Stage = {} }} at Group {} [{}]", slot.Id, slot.Scope, static_cast(slot.Stage), g, i ); + Compatible:; } - if (item.Count < def.Count) + if (def.Count != COPLT_U32_MAX && item.Count < def.Count) { COPLT_THROW_FMT( "Incompatible binding slot {{ Id = {}, Scope = {}, Stage = {} }} at Group {} [{}]; The group provides fewer bindings than the shader requires", @@ -241,16 +303,21 @@ D3d12BindingLayout::D3d12BindingLayout(Rc&& device, const FBindi } std::vector root_parameters{}; - std::vector static_samplers{}; + std::vector static_samplers{}; std::vector> tables{}; tables.reserve(m_groups.size()); for (u32 i = 0; i < m_groups.size(); i++) tables.push_back({}); + m_group_bind_item_infos.reserve(m_groups.size()); + for (u32 i = 0; i < m_groups.size(); i++) m_group_bind_item_infos.push_back({}); for (auto& info : m_slot_infos) { const auto& item = defines[info.Index]; + const auto& group = m_groups[info.Group]; + const auto& group_item = group->Items()[info.IndexInGroup]; + const auto view = group_item.View; - if (item.View == FShaderLayoutItemView::Constants) + if (view == FShaderLayoutItemView::Constants) { if (item.Type != FShaderLayoutItemType::ConstantBuffer) COPLT_THROW("Push Const / Root Const view must be ConstantBuffer"); @@ -259,19 +326,20 @@ D3d12BindingLayout::D3d12BindingLayout(Rc&& device, const FBindi param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; param.Constants.ShaderRegister = item.Slot; param.Constants.RegisterSpace = item.Space; - param.Constants.Num32BitValues = std::max(1u, item.Count); + param.Constants.Num32BitValues = std::max(1u, group_item.Count); param.ShaderVisibility = ToDxVisibility(item.Stage); info.SigIndex = static_cast(root_parameters.size()); info.SigPlace = SigPlace::Const; root_parameters.push_back(param); - BindItemInfo item_info{}; + RootItemInfo item_info{}; item_info.Group = info.Group; item_info.IndexInGroup = info.Index; item_info.RootIndex = info.SigIndex; - item_info.Place = BindItemPlace::Const; - item_info.Type = BindItemType::Resource; + item_info.Place = RootItemPlace::Const; + item_info.Type = RootItemType::Resource; m_bind_item_infos.push_back(item_info); + m_group_bind_item_infos[info.Group].push_back(item_info); continue; } @@ -280,37 +348,37 @@ D3d12BindingLayout::D3d12BindingLayout(Rc&& device, const FBindi COPLT_THROW("TODO"); D3D12_ROOT_PARAMETER_TYPE type; - const auto& group = m_groups[info.Group]; - if (item.View == FShaderLayoutItemView::StaticSampler) + if (view == FShaderLayoutItemView::StaticSampler) goto DefineStaticSampler; - if (item.View == FShaderLayoutItemView::Sampler) + if (view == FShaderLayoutItemView::Sampler) goto DefineDescriptorTable; const auto usage = group->Data().Usage; if (usage == FBindGroupUsage::Dynamic) { - if (item.Count > 1 || !IsBuffer(item.Type)) - { - goto DefineDescriptorTable; - } - switch (item.View) - { - case FShaderLayoutItemView::Cbv: - type = D3D12_ROOT_PARAMETER_TYPE_CBV; - goto DefineDescriptor; - case FShaderLayoutItemView::Srv: - type = D3D12_ROOT_PARAMETER_TYPE_SRV; - goto DefineDescriptor; - case FShaderLayoutItemView::Uav: - type = D3D12_ROOT_PARAMETER_TYPE_UAV; - goto DefineDescriptor; - case FShaderLayoutItemView::StaticSampler: - case FShaderLayoutItemView::Sampler: - case FShaderLayoutItemView::Constants: - default: - COPLT_THROW_FMT("Unknown shader layout item type {}", static_cast(item.Type)); - } + goto DefineDescriptorTable; // 暂时不支持直接资源 + // if (item.Count > 1 || !IsBuffer(item.Type)) + // { + // goto DefineDescriptorTable; + // } + // switch (view) + // { + // case FShaderLayoutItemView::Cbv: + // type = D3D12_ROOT_PARAMETER_TYPE_CBV; + // goto DefineDescriptor; + // case FShaderLayoutItemView::Srv: + // type = D3D12_ROOT_PARAMETER_TYPE_SRV; + // goto DefineDescriptor; + // case FShaderLayoutItemView::Uav: + // type = D3D12_ROOT_PARAMETER_TYPE_UAV; + // goto DefineDescriptor; + // case FShaderLayoutItemView::StaticSampler: + // case FShaderLayoutItemView::Sampler: + // case FShaderLayoutItemView::Constants: + // default: + // COPLT_THROW_FMT("Unknown shader layout item type {}", static_cast(item.Type)); + // } } DefineDescriptorTable: @@ -319,7 +387,7 @@ D3d12BindingLayout::D3d12BindingLayout(Rc&& device, const FBindi const auto& slot = slots[info.IndexInGroup]; COPLT_DEBUG_ASSERT(slot.Place != BindSlotPlace::NonTable); D3D12_DESCRIPTOR_RANGE1 range{}; - const auto range_type = ToTableType(item.View); + const auto range_type = ToTableType(view); range.RangeType = range_type; auto& table = tables[info.Group].GetOrAdd(TableKey(item.Stage, range_type), [&](auto& p) { @@ -342,32 +410,31 @@ D3d12BindingLayout::D3d12BindingLayout(Rc&& device, const FBindi } DefineDescriptor: { - D3D12_ROOT_PARAMETER1 param{}; - param.ParameterType = type; - param.Descriptor.ShaderRegister = item.Slot; - param.Descriptor.RegisterSpace = item.Space; - param.Descriptor.Flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE; - param.ShaderVisibility = ToDxVisibility(item.Stage); - info.SigIndex = static_cast(root_parameters.size()); - info.SigPlace = SigPlace::Direct; - root_parameters.push_back(param); - - BindItemInfo item_info{}; - item_info.Group = info.Group; - item_info.IndexInGroup = info.Index; - item_info.RootIndex = info.SigIndex; - item_info.Place = BindItemPlace::Direct; - item_info.Type = BindItemType::Resource; - m_bind_item_infos.push_back(item_info); - continue; + // D3D12_ROOT_PARAMETER1 param{}; + // param.ParameterType = type; + // param.Descriptor.ShaderRegister = item.Slot; + // param.Descriptor.RegisterSpace = item.Space; + // param.Descriptor.Flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE; + // param.ShaderVisibility = ToDxVisibility(item.Stage); + // info.SigIndex = static_cast(root_parameters.size()); + // info.SigPlace = SigPlace::Direct; + // root_parameters.push_back(param); + // + // RootItemInfo item_info{}; + // item_info.Group = info.Group; + // item_info.IndexInGroup = info.Index; + // item_info.RootIndex = info.SigIndex; + // item_info.Place = RootItemPlace::Direct; + // item_info.Type = RootItemType::Resource; + // m_bind_item_infos.push_back(item_info); + // m_group_bind_item_infos[info.Group].push_back(item_info); + // continue; } DefineStaticSampler: { - const auto group_items = group->GetItems(); - const auto samplers = group->GetStaticSamplers(); - const auto& group_item = group_items[info.IndexInGroup]; + const auto samplers = group->StaticSamplerInfos(); const auto& sampler = samplers[group_item.StaticSamplerIndex]; - D3D12_STATIC_SAMPLER_DESC desc{}; + D3D12_STATIC_SAMPLER_DESC1 desc{}; desc.ShaderRegister = item.Slot; desc.RegisterSpace = item.Space; desc.ShaderVisibility = ToDxVisibility(item.Stage); @@ -396,32 +463,33 @@ D3d12BindingLayout::D3d12BindingLayout(Rc&& device, const FBindi root_parameters.push_back(param); infos.push_back(Info); - BindItemInfo item_info{}; + RootItemInfo item_info{}; item_info.Group = Info.Group; item_info.IndexInGroup = 0; item_info.RootIndex = Info.RootIndex; - item_info.Place = BindItemPlace::Table; - item_info.Type = Info.Type == D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER ? BindItemType::Sampler : BindItemType::Resource; + item_info.Place = RootItemPlace::Table; + item_info.Type = Info.Type == D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER ? RootItemType::Sampler : RootItemType::Resource; m_bind_item_infos.push_back(item_info); + m_group_bind_item_infos[Info.Group].push_back(item_info); } m_tables.push_back(std::move(infos)); } D3D12_VERSIONED_ROOT_SIGNATURE_DESC desc{}; desc.Version = D3D_ROOT_SIGNATURE_VERSION_1_2; - desc.Desc_1_1.NumParameters = static_cast(root_parameters.size()); - desc.Desc_1_1.pParameters = root_parameters.data(); - desc.Desc_1_1.NumStaticSamplers = 0; - desc.Desc_1_1.pStaticSamplers = nullptr; - desc.Desc_1_1.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE; + desc.Desc_1_2.NumParameters = static_cast(root_parameters.size()); + desc.Desc_1_2.pParameters = root_parameters.data(); + desc.Desc_1_2.NumStaticSamplers = static_cast(static_samplers.size()); + desc.Desc_1_2.pStaticSamplers = static_samplers.data(); + desc.Desc_1_2.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE; const auto layout = *NonNull(m_shader_layout->ShaderLayoutData()); if (HasFlags(layout.Flags, FShaderLayoutFlags::InputAssembler)) - desc.Desc_1_1.Flags |= D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; + desc.Desc_1_2.Flags |= D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; if (HasFlags(layout.Flags, FShaderLayoutFlags::StreamOutput)) - desc.Desc_1_1.Flags |= D3D12_ROOT_SIGNATURE_FLAG_ALLOW_STREAM_OUTPUT; + desc.Desc_1_2.Flags |= D3D12_ROOT_SIGNATURE_FLAG_ALLOW_STREAM_OUTPUT; if (HasFlags(layout.Flags, FShaderLayoutFlags::DynBindLess)) - desc.Desc_1_1.Flags |= + desc.Desc_1_2.Flags |= D3D12_ROOT_SIGNATURE_FLAG_CBV_SRV_UAV_HEAP_DIRECTLY_INDEXED | D3D12_ROOT_SIGNATURE_FLAG_SAMPLER_HEAP_DIRECTLY_INDEXED; @@ -480,11 +548,16 @@ std::span D3d12BindingLayout::SlotInfos() co return m_slot_infos; } -std::span D3d12BindingLayout::BindItemInfos() const noexcept +std::span D3d12BindingLayout::RootItemInfos() const noexcept { return m_bind_item_infos; } +std::span> D3d12BindingLayout::GroupRootItemInfos() const noexcept +{ + return m_group_bind_item_infos; +} + std::span> D3d12BindingLayout::TableInfos() const noexcept { return m_tables; diff --git a/Coplt.Graphics.Native/D3d12/Src/Layout.h b/Coplt.Graphics.Native/D3d12/Src/Layout.h index abf58e1..7e42fd6 100644 --- a/Coplt.Graphics.Native/D3d12/Src/Layout.h +++ b/Coplt.Graphics.Native/D3d12/Src/Layout.h @@ -131,27 +131,27 @@ namespace Coplt FResourceAccess UavAccess{}; }; - enum class BindItemPlace : u8 + enum class RootItemPlace : u8 { Table, Direct, Const, }; - enum class BindItemType : u8 + enum class RootItemType : u8 { Resource, Sampler, }; - struct BindItemInfo + struct RootItemInfo { u32 Group{}; // Place 为 Table 时永远为 0 u32 IndexInGroup{}; u32 RootIndex{}; - BindItemPlace Place{}; - BindItemType Type{}; + RootItemPlace Place{}; + RootItemType Type{}; }; } @@ -175,6 +175,8 @@ namespace Coplt COPLT_INTERFACE_DEFINE(ID3d12BindGroupLayout, "2e440ba8-f47a-4f98-a434-31125406e55c", FBindGroupLayout) { using BindSlotInfo = Layout::BindSlotInfo; + virtual std::span Items() const noexcept = 0; + virtual std::span StaticSamplerInfos() const noexcept = 0; virtual const D3d12BindGroupLayoutData& Data() const noexcept = 0; virtual std::span Slots() const noexcept = 0; }; @@ -190,6 +192,8 @@ namespace Coplt FResult SetName(const FStr8or16& name) noexcept override; FBindGroupLayoutData* BindGroupLayoutData() noexcept override; + std::span Items() const noexcept override; + std::span StaticSamplerInfos() const noexcept override; const D3d12BindGroupLayoutData& Data() const noexcept override; std::span Slots() const noexcept override; }; @@ -205,14 +209,16 @@ namespace Coplt using SlotInfo = Layout::SlotInfo; using GroupItemInfo = Layout::GroupItemInfo; using TableInfo = Layout::TableInfo; - using BindItemInfo = Layout::BindItemInfo; + using RootItemInfo = Layout::RootItemInfo; virtual const D3d12BindingLayoutData& Data() const noexcept = 0; virtual const Rc& ShaderLayout() const noexcept = 0; virtual std::span> Groups() const noexcept = 0; virtual const ComPtr& RootSignature() const noexcept = 0; virtual std::span SlotInfos() const noexcept = 0; - virtual std::span BindItemInfos() const noexcept = 0; + virtual std::span RootItemInfos() const noexcept = 0; + // 第一层索引是 Group index + virtual std::span> GroupRootItemInfos() const noexcept = 0; // 第一层索引是 Group index virtual std::span> TableInfos() const noexcept = 0; // 第一层索引是 Group index @@ -228,9 +234,11 @@ namespace Coplt Rc m_shader_layout{}; std::vector> m_groups{}; std::vector m_slot_infos{}; - std::vector m_bind_item_infos{}; + std::vector m_bind_item_infos{}; HashMap m_slot_to_info{}; // 第一层索引是 Group index + std::vector> m_group_bind_item_infos{}; + // 第一层索引是 Group index std::vector> m_tables{}; // 第一层索引是 Group index std::vector> m_group_item_infos{}; @@ -244,7 +252,8 @@ namespace Coplt std::span> Groups() const noexcept override; const ComPtr& RootSignature() const noexcept override; std::span SlotInfos() const noexcept override; - std::span BindItemInfos() const noexcept override; + std::span RootItemInfos() const noexcept override; + std::span> GroupRootItemInfos() const noexcept override; std::span> TableInfos() const noexcept override; std::span> GroupItemInfos() const noexcept override; }; @@ -278,4 +287,11 @@ namespace Coplt const FMeshBufferElement* TryGetElement(u32 SlotId, u32 SlotIndex) const noexcept override; }; + + inline bool IsCompatible(const FShaderLayoutItemView Shader, const FShaderLayoutItemView Bind) + { + if (Shader == FShaderLayoutItemView::Sampler && Bind == FShaderLayoutItemView::StaticSampler) return true; + if (Shader == FShaderLayoutItemView::Cbv && Bind == FShaderLayoutItemView::Constants) return true; + return Shader == Bind; + } } diff --git a/Coplt.Graphics.Native/D3d12/Src/Pipeline.h b/Coplt.Graphics.Native/D3d12/Src/Pipeline.h index b2b2c70..ab1c3ee 100644 --- a/Coplt.Graphics.Native/D3d12/Src/Pipeline.h +++ b/Coplt.Graphics.Native/D3d12/Src/Pipeline.h @@ -13,7 +13,11 @@ namespace Coplt virtual const ComPtr& GetPipelineState() const noexcept = 0; }; - COPLT_INTERFACE_DEFINE(ID3d12GraphicsShaderPipeline, "345aeb23-3dc9-4365-babc-dfc7badf4ff8", FGraphicsShaderPipeline) + COPLT_INTERFACE_DEFINE(ID3d12ComputeShaderPipeline, "85989313-9f53-4d01-8ea9-5a2d3decd508", FComputeShaderPipeline) + { + }; + + COPLT_INTERFACE_DEFINE(ID3d12GraphicsShaderPipeline, "b27e1754-365f-4f44-ac9d-50a44b877226", FGraphicsShaderPipeline) { }; } diff --git a/Coplt.Graphics.Native/D3d12/Src/Record.cc b/Coplt.Graphics.Native/D3d12/Src/Record.cc index 3e194b0..bc4f29f 100644 --- a/Coplt.Graphics.Native/D3d12/Src/Record.cc +++ b/Coplt.Graphics.Native/D3d12/Src/Record.cc @@ -49,7 +49,7 @@ ResourceInfo::ResourceInfo(Rc&& resource, const FCmdRes& res, const ResourceInfo::ResourceInfo(Rc&& resource, const View& view, u32 index): Resource(std::move(resource)), Index(index) { - switch (view.m_type) + switch (view.m_data.Type) { case FViewType::None: COPLT_THROW("Null view"); @@ -57,7 +57,18 @@ ResourceInfo::ResourceInfo(Rc&& resource, const View& view, u32 inde Type = FCmdResType::Buffer; Buffer = NonNull(view.TryGetBuffer()); return; - case FViewType::Image: + case FViewType::UploadBuffer: + COPLT_THROW("Upload buffer does not require barriers"); + return; + case FViewType::Image1D: + case FViewType::Image1DArray: + case FViewType::Image2D: + case FViewType::Image2DArray: + case FViewType::Image2DMs: + case FViewType::Image2DMsArray: + case FViewType::Image3D: + case FViewType::ImageCube: + case FViewType::ImageCubeArray: Type = FCmdResType::Image; Image = NonNull(view.TryGetImage()); return; @@ -229,6 +240,15 @@ void D3d12GpuRecord::PipelineContext::SetPipeline(NonNull pipel } GPipeline = g_pipeline; } + else if (HasFlags(stages, FShaderStageFlags::Compute)) + { + const Ptr c_pipeline = pipeline->QueryInterface(); + if (!c_pipeline) + { + COPLT_THROW_FMT("[{}] Invalid pipeline: pipeline is not a compute pipeline, but there is a compute shader in the stages.", i); + } + CPipeline = c_pipeline; + } PipelineChanged = true; if (Binding && Binding->Layout()->ObjectId() != Layout->ObjectId()) { @@ -252,12 +272,69 @@ void D3d12GpuRecord::PipelineContext::SetBinding(NonNull binding COPLT_THROW_FMT("[{}] The binding layout is not compatible with the currently set pipeline", i); } Binding = dx_binding; - SetBindingIndex = cmd.Index; + BindingIndex = cmd.Binding; +} + +D3d12GpuRecord::TmpDescHeap::TmpDescHeap(const ComPtr& device, const D3D12_DESCRIPTOR_HEAP_TYPE type, const u32 size) + : m_size(size) +{ + D3D12_DESCRIPTOR_HEAP_DESC desc{}; + desc.Type = type; + desc.NumDescriptors = size; + chr | device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&m_heap)); +} + +D3d12GpuRecord::TmpDescHeaps::TmpDescHeaps(NonNull isolate, const D3D12_DESCRIPTOR_HEAP_TYPE type) + : m_device(isolate->m_device->m_device), m_type(type) +{ +} + +void D3d12GpuRecord::TmpDescHeaps::Recycle() +{ + m_sum_size = 0; + if (!m_heaps.empty()) + { + TmpDescHeap last = std::move(m_heaps.back()); + last.m_offset = 0; + m_heaps.clear(); + m_heaps.push_back(std::move(last)); + } +} + +D3d12GpuRecord::TmpDescAllocation D3d12GpuRecord::TmpDescHeaps::Alloc(const u32 Size) +{ + if (m_heaps.empty()) goto Grow; + for (u32 i = 0; i < m_heaps.size(); ++i) + { + auto& heap = m_heaps[i]; + if (heap.m_size - heap.m_offset < Size) continue; + const auto offset = heap.m_offset; + heap.m_offset += Size; + m_sum_size += Size; + return TmpDescAllocation{i, offset, Size}; + } +Grow: + Grow(Size); + { + auto& heap = m_heaps.back(); + const auto offset = heap.m_offset; + heap.m_offset += Size; + m_sum_size += Size; + return TmpDescAllocation{m_heaps.size() - 1, offset, Size}; + } +} + +void D3d12GpuRecord::TmpDescHeaps::Grow(const u32 MinSize) +{ + const auto size = std::max(m_heaps.empty() ? InitSize : m_heaps.back().m_size * 2, std::bit_ceil(MinSize)); + m_heaps.push_back(TmpDescHeap(m_device, m_type, size)); } D3d12GpuRecord::D3d12GpuRecord(const NonNull isolate) : FGpuRecordData(isolate->m_device->m_instance->m_allocator.get()), - m_isolate_config(isolate->m_config), m_device(isolate->m_device) + m_isolate_config(isolate->m_config), m_device(isolate->m_device), + m_tmp_res_heaps(isolate, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV), + m_tmp_smp_heaps(isolate, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER) { Id = m_isolate_id = isolate->m_object_id; m_record_id = isolate->m_record_inc++; @@ -325,13 +402,23 @@ void D3d12GpuRecord::Recycle() m_queue_wait_points.clear(); m_barrier_analyzer->Clear(); m_context->Recycle(); + m_object_handles.clear(); + m_persistent_allocation_infos.clear(); m_resource_map.Clear(); + m_dynamic_bind_group_map.Clear(); m_resource_infos.clear(); m_binding_infos.clear(); - m_set_binding_infos.clear(); + m_dynamic_bind_group_infos.clear(); + m_dynamic_bind_group_info_inds.clear(); + m_dynamic_bind_group_info_sync_inds.clear(); + m_set_constants_chunks.clear(); + m_set_bind_item_chunks.clear(); + m_sync_binding_infos.clear(); m_allocations.clear(); - m_bind_items.clear(); + m_table_bind_items.clear(); m_pipeline_context.Reset(); + m_tmp_res_heaps.Recycle(); + m_tmp_smp_heaps.Recycle(); ClearData(); Ended = false; } @@ -358,10 +445,9 @@ void D3d12GpuRecord::DoEnd() COPLT_THROW("Too many resources"); m_barrier_analyzer->StartAnalyze(this); ReadyResource(); - u32 MinResHeapSize{}, MinSmpHeapSize{}; - ReadyBindings(MinResHeapSize, MinSmpHeapSize); - m_context->m_descriptor_manager.ReadyFrame(MinResHeapSize, MinSmpHeapSize); + ReadyBindings(); Analyze(); + UploadDescriptors(); m_barrier_analyzer->EndAnalyze(); if (m_isolate_config->MultiThreadRecord) { @@ -413,6 +499,8 @@ FResIndex D3d12GpuRecord::AddResource(const FCmdRes& res) FResIndex D3d12GpuRecord::AddResource(const View& view) { + if (view.IsUploadBuffer()) + COPLT_THROW("Upload buffer does not require barriers"); const auto obj = view.GetViewable(); const auto index = m_resource_map.GetOrAdd(obj->ObjectId(), [this, obj, &view](auto& p) { @@ -436,34 +524,120 @@ void D3d12GpuRecord::ReadyResource() } } -void D3d12GpuRecord::ReadyBindings(u32& MinResHeapSize, u32& MinSmpHeapSize) +void D3d12GpuRecord::ReadyBindings() { - MinResHeapSize = 0; - MinSmpHeapSize = 0; m_binding_infos.clear(); m_binding_infos.reserve(Bindings.size()); for (u32 i = 0; i < Bindings.size(); ++i) { m_binding_infos.push_back({}); } - m_set_binding_infos.clear(); - m_set_binding_infos.reserve(NumSetBindings); - for (u32 i = 0; i < NumSetBindings; ++i) + m_sync_binding_infos.clear(); + m_sync_binding_infos.reserve(NumSyncBindings); + for (u32 i = 0; i < NumSyncBindings; ++i) { - m_set_binding_infos.push_back(SetBindingInfo()); + m_sync_binding_infos.push_back(SyncBindingInfo()); } - for (u32 i = 0; i < BindingChange.size(); ++i) +} + +void D3d12GpuRecord::UploadDescriptors() +{ + m_context->m_descriptor_manager.ReadyFrame(m_tmp_res_heaps.m_sum_size, m_tmp_smp_heaps.m_sum_size); + for (auto& allocation : m_allocations) { - const auto& item = BindingChange[i]; - const auto binding = NonNull(Bindings[item.Binding].Binding)->QueryInterface(); - if (binding == nullptr) - COPLT_THROW_FMT("Binding [{}] comes from different backends", item.Binding); - const auto& data = binding->Layout()->Data(); - MinResHeapSize += data.SumTableResourceSlots; - MinSmpHeapSize += data.SumTableSamplerSlots; + if (allocation.PersistentInfoIndex == COPLT_U32_MAX) + { + const auto f = [this]( + DescriptorAllocation& allocation, const TmpDescAllocation& tmp, const Rc& heap, const TmpDescHeaps& tmp_heap + ) + { + if (!tmp) return; + allocation = heap->AllocateTmp(tmp.Size); + m_device->m_device->CopyDescriptorsSimple( + tmp.Size, + allocation.GetCpuHandle(), + CD3DX12_CPU_DESCRIPTOR_HANDLE( + tmp_heap.m_heaps[tmp.HeapIndex].m_heap->GetCPUDescriptorHandleForHeapStart(), + tmp.OffsetInHeap, + heap->m_inc + ), + heap->m_type + ); + }; + f(allocation.Resource, allocation.TmpResource, m_context->m_descriptor_manager.m_res, m_tmp_res_heaps); + f(allocation.Sampler, allocation.TmpSampler, m_context->m_descriptor_manager.m_smp, m_tmp_smp_heaps); + } + else + { + const auto& info = m_persistent_allocation_infos[allocation.PersistentInfoIndex]; + const auto f = [this, &info]( + DescriptorAllocation& allocation, const Rc& heap, const u64 ver, const ComPtr& cpu_heap + ) + { + if (cpu_heap == nullptr) return; + const auto size = cpu_heap->GetDesc().NumDescriptors; + if (info.Last) + { + bool need_upload = false; + allocation = heap->Allocate(info.ObjectId, ver, size, need_upload); + if (!need_upload) return; + } + else + { + allocation = heap->AllocateTmp(size); + } + m_device->m_device->CopyDescriptorsSimple( + size, + allocation.GetCpuHandle(), + cpu_heap->GetCPUDescriptorHandleForHeapStart(), + heap->m_type + ); + }; + f(allocation.Resource, m_context->m_descriptor_manager.m_res, info.Heap.ResourceVersion, info.Heap.ResourceHeap); + f(allocation.Sampler, m_context->m_descriptor_manager.m_smp, info.Heap.SamplerVersion, info.Heap.SamplerHeap); + } } } +D3d12GpuRecord::BindGroupInfo& D3d12GpuRecord::QueryBindGroupInfo(ID3d12ShaderBindGroup& group, bool mut) +{ + const auto id = group.ObjectId(); + bool exists = false; + const auto new_index = m_dynamic_bind_group_infos.size(); + auto& index = m_dynamic_bind_group_map.GetOrAdd(id, new_index, exists); + if (!exists) goto Create; + auto& info = m_dynamic_bind_group_infos[index]; + if (!mut || !info.Frozen) return info; + index = new_index; +Create: + m_dynamic_bind_group_infos.push_back(BindGroupInfo{ + .BindGroup = Rc::UnsafeClone(std::addressof(group)), + .Index = new_index, + }); + return m_dynamic_bind_group_infos.back(); +} + +D3d12GpuRecord::BindGroupInfo& D3d12GpuRecord::QueryBindGroupInfo(NonNull group, u32 i, bool mut) +{ + const auto id = group->ObjectId(); + bool exists = false; + const auto new_index = m_dynamic_bind_group_infos.size(); + auto& index = m_dynamic_bind_group_map.GetOrAdd(id, new_index, exists); + if (!exists) goto Create; + auto& info = m_dynamic_bind_group_infos[index]; + if (!mut || !info.Frozen) return info; + index = new_index; +Create: + const auto Group = group->QueryInterface(); + if (Group == nullptr) + COPLT_THROW_FMT("[{}] BindGroup From different backends", i); + m_dynamic_bind_group_infos.push_back(BindGroupInfo{ + .BindGroup = Rc::UnsafeClone(Group), + .Index = new_index, + }); + return m_dynamic_bind_group_infos.back(); +} + void D3d12GpuRecord::Analyze() { const auto commands = Commands.AsSpan(); @@ -480,6 +654,8 @@ void D3d12GpuRecord::Analyze() case FCmdType::End: if (m_state == RecordState::Render) Analyze_RenderEnd(i, m_cur_render.Cmd); + else if (m_state == RecordState::Compute) + Analyze_ComputeEnd(i, m_cur_compute.Cmd); else COPLT_THROW("Cannot use End in main scope"); break; @@ -502,13 +678,23 @@ void D3d12GpuRecord::Analyze() Analyze_Render(i, command.Render); break; case FCmdType::Compute: - COPLT_THROW("TODO"); + Analyze_Compute(i, command.Compute); + break; case FCmdType::SetPipeline: Analyze_SetPipeline(i, command.SetPipeline); break; case FCmdType::SetBinding: Analyze_SetBinding(i, command.SetBinding); break; + case FCmdType::SetConstants: + Analyze_SetConstants(i, command.SetConstants); + break; + case FCmdType::SetDynArraySize: + Analyze_SetDynArraySize(i, command.SetDynArraySize); + break; + case FCmdType::SetBindItem: + Analyze_SetBindItem(i, command.SetBindItem); + break; case FCmdType::SetMeshBuffers: Analyze_SetMeshBuffers(i, command.SetMeshBuffers); break; @@ -517,8 +703,7 @@ void D3d12GpuRecord::Analyze() COPLT_THROW_FMT("[{}] Can only SetViewportScissor on the direct mode", i); break; case FCmdType::Draw: - if (Mode != FGpuRecordMode::Direct) - COPLT_THROW_FMT("[{}] Can only Draw on the direct mode", i); + Analyze_Draw(i, command.Draw); break; case FCmdType::Dispatch: Analyze_Dispatch(i, command.Dispatch); @@ -544,7 +729,7 @@ void D3d12GpuRecord::Analyze_ClearColor(u32 i, const FCmdClearColor& cmd) const m_barrier_analyzer->OnCmd(); } -void D3d12GpuRecord::Analyze_ClearDepthStencil(u32 i, const FCmdClearDepthStencil& cmd) const +void D3d12GpuRecord::Analyze_ClearDepthStencil(const u32 i, const FCmdClearDepthStencil& cmd) const { if (m_state != RecordState::Main) COPLT_THROW_FMT("[{}] Cannot use ClearDepthStencil in sub scope", i); @@ -552,7 +737,7 @@ void D3d12GpuRecord::Analyze_ClearDepthStencil(u32 i, const FCmdClearDepthStenci m_barrier_analyzer->OnCmd(); } -void D3d12GpuRecord::Analyze_BufferCopy(u32 i, const FCmdBufferCopy& cmd) const +void D3d12GpuRecord::Analyze_BufferCopy(const u32 i, const FCmdBufferCopy& cmd) const { if (m_state != RecordState::Main) COPLT_THROW_FMT("[{}] Cannot use BufferCopy in sub scope", i); @@ -576,7 +761,7 @@ void D3d12GpuRecord::Analyze_BufferCopy(u32 i, const FCmdBufferCopy& cmd) const } } -void D3d12GpuRecord::Analyze_BufferImageCopy(u32 i, const FCmdBufferImageCopy& cmd) const +void D3d12GpuRecord::Analyze_BufferImageCopy(const u32 i, const FCmdBufferImageCopy& cmd) const { if (m_state != RecordState::Main) COPLT_THROW_FMT("[{}] Cannot use BufferImageCopy in sub scope", i); @@ -599,7 +784,7 @@ void D3d12GpuRecord::Analyze_BufferImageCopy(u32 i, const FCmdBufferImageCopy& c m_barrier_analyzer->OnCmd(); } -void D3d12GpuRecord::Analyze_Render(u32 i, const FCmdRender& cmd) +void D3d12GpuRecord::Analyze_Render(const u32 i, const FCmdRender& cmd) { if (m_state != RecordState::Main) COPLT_THROW_FMT("[{}] Cannot use Render in sub scope", i); @@ -620,7 +805,7 @@ void D3d12GpuRecord::Analyze_Render(u32 i, const FCmdRender& cmd) m_barrier_analyzer->OnCmd(); } -void D3d12GpuRecord::Analyze_RenderEnd(u32 i, const FCmdRender& cmd) +void D3d12GpuRecord::Analyze_RenderEnd(const u32 i, const FCmdRender& cmd) { if (m_state != RecordState::Render) COPLT_THROW_FMT("[{}] Cannot use End in main scope or some end is missing", i); @@ -633,7 +818,7 @@ void D3d12GpuRecord::Analyze_RenderEnd(u32 i, const FCmdRender& cmd) m_pipeline_context.Reset(); } -void D3d12GpuRecord::Analyze_Compute(u32 i, const FCmdCompute& cmd) +void D3d12GpuRecord::Analyze_Compute(const u32 i, const FCmdCompute& cmd) { if (m_state != RecordState::Main) COPLT_THROW_FMT("[{}] Cannot use Compute in sub scope", i); @@ -643,7 +828,7 @@ void D3d12GpuRecord::Analyze_Compute(u32 i, const FCmdCompute& cmd) m_cur_compute = ComputeState{.StartIndex = i, .Cmd = cmd}; } -void D3d12GpuRecord::Analyze_ComputeEnd(u32 i, const FCmdCompute& cmd) +void D3d12GpuRecord::Analyze_ComputeEnd(const u32 i, const FCmdCompute& cmd) { if (m_state != RecordState::Compute) COPLT_THROW_FMT("[{}] Cannot use End in main scope or some end is missing", i); @@ -651,18 +836,18 @@ void D3d12GpuRecord::Analyze_ComputeEnd(u32 i, const FCmdCompute& cmd) m_pipeline_context.Reset(); } -void D3d12GpuRecord::Analyze_SetPipeline(u32 i, const FCmdSetPipeline& cmd) +void D3d12GpuRecord::Analyze_SetPipeline(const u32 i, const FCmdSetPipeline& cmd) { if (m_state != RecordState::Render && m_state != RecordState::Compute) COPLT_THROW_FMT("[{}] Cannot use SetPipeline in main scope", i); SetPipeline(cmd.Pipeline, i); } -void D3d12GpuRecord::Analyze_SetBinding(u32 i, const FCmdSetBinding& cmd) +void D3d12GpuRecord::Analyze_SetBinding(const u32 i, const FCmdSetBinding& cmd) { if (m_state != RecordState::Render && m_state != RecordState::Compute) COPLT_THROW_FMT("[{}] Cannot use SetBinding in main scope", i); - if (!SetBinding(Bindings[cmd.Binding].Binding, i, cmd)) return; + if (!SetBinding(i, cmd)) return; const NonNull binding = m_pipeline_context.Binding; ReadGuard binding_guard(binding->SelfLock()); Finally clear_lock([&] { m_tmp_locks.clear(); }); @@ -675,80 +860,299 @@ void D3d12GpuRecord::Analyze_SetBinding(u32 i, const FCmdSetBinding& cmd) } const auto& binding_layout = binding->Layout(); const auto group_item_infos = binding_layout->GroupItemInfos(); - const auto bind_item_infos = binding_layout->BindItemInfos(); + const auto group_bind_item_infos = binding_layout->GroupRootItemInfos(); bool any_changed = false; + m_tmp_gdhs.clear(); for (u32 g = 0; g < groups.size(); ++g) { const auto& group = groups[g]; if (!group) continue; + const auto& data = group->Layout()->Data(); + if (data.Usage == FBindGroupUsage::Dynamic) continue; + const auto define_item_indexes = group->DefineIndexes(); + const auto& group_item_info = group_item_infos[g]; const auto views = group->Views(); for (u32 v = 0; v < views.size(); ++v) { const auto& view = views[v]; - if (!view || view.IsSampler()) continue; + if (!view || !view.NeedBarrier()) continue; const auto& res_index = AddResource(view); - const auto& info = group_item_infos[g][v]; + const auto& info = group_item_info[define_item_indexes[v]]; m_barrier_analyzer->OnUse(res_index, view, info); } - if (group->EnsureAvailable()) any_changed = true; + TmpGroupDescriptorHeap group_descriptor_heap{}; + group_descriptor_heap.GroupIndex = g; + if (group->EnsureAvailable(group_descriptor_heap.Heap)) any_changed = group_descriptor_heap.Changed = true; + m_tmp_gdhs.push_back(std::move(group_descriptor_heap)); } - auto& binding_info = m_binding_infos[cmd.Binding]; - auto& set_binding_info = m_set_binding_infos[cmd.Index]; + auto& binding_info = m_binding_infos[m_pipeline_context.BindingIndex]; + binding_info.Layout = binding_layout.get(); const auto binding_version = binding->Version(); - if (binding_info.AllocationIndex == COPLT_U32_MAX || binding_info.BindingVersion != binding_version || any_changed) + const auto first = binding_info.PersistentRootItemIndex == COPLT_U32_MAX; + if (first || binding_info.BindingVersion != binding_version || any_changed) { binding_info.BindingVersion = binding_version; - binding_info.AllocationIndex = m_allocations.size(); - binding_info.BindItemIndex = m_bind_items.size(); + binding_info.PersistentRootItemIndex = m_table_bind_items.size(); + binding_info.DynamicBindGroupInfoIndIndex = m_dynamic_bind_group_info_inds.size(); + for (u32 g = 0; g < groups.size(); ++g) { const auto& group = groups[g]; - if (!group) + if (!group) continue; + const auto& data = group->Layout()->Data(); + if (data.Usage != FBindGroupUsage::Dynamic) continue; + // 对于动态资源,只记录动态组信息索引, SyncBinding 时再处理 + m_dynamic_bind_group_info_inds.push_back(BindGroupInd{ + .GroupIndex = g, + .InfoIndex = QueryBindGroupInfo(*group, false).Index, + }); + } + + for (auto& tmp_gdh : m_tmp_gdhs) + { + const auto& group = groups[tmp_gdh.GroupIndex]; + const auto& data = group->Layout()->Data(); + const auto& bind_item_infos = group_bind_item_infos[tmp_gdh.GroupIndex]; + if (first || tmp_gdh.Changed) { - m_allocations.push_back({}); - continue; + m_tmp_res_heaps.m_sum_size += data.ResourceTableSize; + m_tmp_smp_heaps.m_sum_size += data.SamplerTableSize; } - const auto object_id = group->ObjectId(); - const auto& data = group->Layout()->Data(); - DescriptorAllocation resource_allocation{}; - DescriptorAllocation sampler_allocation{}; - // todo 确定是最后一次修改 - const auto f = [&]( - const bool last, const u32 size, const Rc& heap, - const ComPtr& dh, DescriptorAllocation& allocation - ) + const auto AllocationIndex = m_allocations.size(); + const auto PersistentInfoIndex = m_persistent_allocation_infos.size(); + m_persistent_allocation_infos.push_back(PersistentAllocationPointInfo{ + .ObjectId = group->ObjectId(), + .Heap = std::move(tmp_gdh.Heap), + .Changed = tmp_gdh.Changed, + .Last = true, // todo 确定是最后一次修改 + }); + m_allocations.push_back(AllocationPoint{ + .GroupIndex = tmp_gdh.GroupIndex, + .PersistentInfoIndex = PersistentInfoIndex, + }); + for (const auto& bind_item_info : bind_item_infos) { - if (size == 0) return; - if (data.Usage == FBindGroupUsage::Dynamic && !last) - { - allocation = heap->AllocateTmp(size); - } - else - { - bool exists = false; - allocation = heap->Allocate(object_id, size, exists); - if (exists) return; - } - m_device->m_device->CopyDescriptorsSimple(size, allocation.GetCpuHandle(), dh->GetCPUDescriptorHandleForHeapStart(), heap->m_type); - }; - f(true, data.ResourceTableSize, m_context->m_descriptor_manager.m_res, group->ResourceHeap(), resource_allocation); - f(true, data.SamplerTableSize, m_context->m_descriptor_manager.m_smp, group->SamplerHeap(), sampler_allocation); - m_allocations.push_back({resource_allocation, sampler_allocation}); + // 持久绑定只有描述符表 + COPLT_DEBUG_ASSERT(bind_item_info.Place == Layout::RootItemPlace::Table); + TableRootItem item{ + .Info = &bind_item_info, + .AllocationIndex = AllocationIndex, + }; + m_table_bind_items.push_back(item); + } + } + m_tmp_gdhs.clear(); + + binding_info.PersistentRootItemCount = m_table_bind_items.size() - binding_info.PersistentRootItemIndex; + binding_info.DynamicBindGroupInfoIndCount = m_dynamic_bind_group_info_inds.size() - binding_info.DynamicBindGroupInfoIndIndex; + binding_info.Changed = true; + } +} + +void D3d12GpuRecord::Analyze_SetConstants(const u32 i, const FCmdSetConstants& cmd) +{ + auto& info = QueryBindGroupInfo(NonNull(cmd.Group), i, true); + if (info.SetConstantsHead != COPLT_U32_MAX) + { + auto& chunk = m_set_constants_chunks[info.SetConstantsTail]; + for (u8 n = 0; n < 7; ++n) + { + if (chunk.CmdIndex[n] != COPLT_U32_MAX) continue; + chunk.CmdIndex[n] = i; + return; } - for (const auto& bind_item_info : bind_item_infos) + info.SetConstantsTail = chunk.Next = m_set_constants_chunks.size(); + } + else + { + info.SetConstantsTail = info.SetConstantsHead = m_set_constants_chunks.size(); + } + SetConstantsChunk chunk{}; + chunk.CmdIndex[0] = i; + m_set_constants_chunks.push_back(chunk); +} + +void D3d12GpuRecord::Analyze_SetDynArraySize(const u32 i, const FCmdSetDynArraySize& cmd) +{ + auto& info = QueryBindGroupInfo(NonNull(cmd.Group), i, true); + info.DynArraySize = cmd.Size; +} + +void D3d12GpuRecord::Analyze_SetBindItem(const u32 i, const FCmdSetBindItem& cmd) +{ + auto& info = QueryBindGroupInfo(NonNull(cmd.Group), i, true); + if (info.SetBindItemHead != COPLT_U32_MAX) + { + auto& chunk = m_set_bind_item_chunks[info.SetBindItemTail]; + for (u8 n = 0; n < 7; ++n) { - BindItem item{}; - // todo 直接资源和常量 - m_bind_items.push_back(item); + if (chunk.CmdIndex[n] != COPLT_U32_MAX) continue; + chunk.CmdIndex[n] = i; + return; } + info.SetBindItemTail = chunk.Next = m_set_bind_item_chunks.size(); } - set_binding_info.Layout = binding_layout.get(); - set_binding_info.AllocationIndex = binding_info.AllocationIndex; - set_binding_info.BindItemIndex = binding_info.BindItemIndex; - m_barrier_analyzer->OnCmd(); + else + { + info.SetBindItemTail = info.SetBindItemHead = m_set_bind_item_chunks.size(); + } + SetBindItemChunk chunk{}; + chunk.CmdIndex[0] = i; + m_set_bind_item_chunks.push_back(chunk); } -void D3d12GpuRecord::Analyze_SetMeshBuffers(u32 i, const FCmdSetMeshBuffers& cmd) +void D3d12GpuRecord::Analyze_SyncBinding(const u32 i, const FCmdSyncBinding& cmd) +{ + if (m_pipeline_context.Binding == nullptr) return; + auto& binding_info = m_binding_infos[m_pipeline_context.BindingIndex]; + auto& sync_binding_info = m_sync_binding_infos[cmd.SyncBindingIndex]; + sync_binding_info.Layout = binding_info.Layout; + auto changed = binding_info.Changed; + binding_info.Changed = false; + const NonNull binding_layout = sync_binding_info.Layout; + const auto group_root_item_infos = binding_layout->GroupRootItemInfos(); + const auto group_item_infos = binding_layout->GroupItemInfos(); + sync_binding_info.PersistentRootItemIndex = binding_info.PersistentRootItemIndex; + sync_binding_info.PersistentRootItemCount = binding_info.PersistentRootItemCount; + sync_binding_info.DynamicBindGroupInfoSyncIndIndex = m_dynamic_bind_group_info_sync_inds.size(); + const auto bi_ind = std::span(m_dynamic_bind_group_info_inds) + .subspan(binding_info.DynamicBindGroupInfoIndIndex, binding_info.DynamicBindGroupInfoIndCount); + for (auto& ind : bi_ind) + { + const auto& old_bind_group_info = m_dynamic_bind_group_infos[ind.InfoIndex]; + auto& info = QueryBindGroupInfo(*old_bind_group_info.BindGroup, false); + m_dynamic_bind_group_info_sync_inds.push_back({.GroupIndex = ind.GroupIndex, .InfoIndex = info.Index}); + if (!info.Frozen) + { + info.Frozen = true; + changed = true; + info.DynamicRootItemIndex = m_table_bind_items.size(); + const auto& group = info.BindGroup; + const auto& group_root_item_info = group_root_item_infos[ind.GroupIndex]; + const auto& group_item_info = group_item_infos[ind.GroupIndex]; + const auto& group_layout = group->Layout(); + const auto& data = group_layout->Data(); + const auto slots = group_layout->Slots(); + const auto defs = group_layout->GetItems(); + + const auto dyn_size = data.DynamicArrayIndex == COPLT_U32_MAX ? 0 : info.DynArraySize; + const auto res_table_size = data.ResourceTableSize + dyn_size; + const auto smp_table_size = data.SamplerTableSize; + TmpDescAllocation TmpResource{}; + TmpDescAllocation TmpSampler{}; + if (res_table_size > 0) TmpResource = m_tmp_res_heaps.Alloc(res_table_size); + if (smp_table_size > 0) TmpSampler = m_tmp_smp_heaps.Alloc(smp_table_size); + const auto AllocationIndex = m_allocations.size(); + m_allocations.push_back(AllocationPoint{ + .GroupIndex = ind.GroupIndex, + .TmpResource = TmpResource, + .TmpSampler = TmpSampler, + }); + for (const auto& root_item_info : group_root_item_info) + { + // 常量和直接不在这里处理 + if (root_item_info.Place != Layout::RootItemPlace::Table) continue; + TableRootItem item{ + .Info = &root_item_info, + .AllocationIndex = AllocationIndex, + }; + m_table_bind_items.push_back(item); + } + info.DynamicRootItemCount = m_table_bind_items.size() - info.DynamicRootItemIndex; + + #pragma region 设置绑定项 + + const auto res_inc = m_context->m_descriptor_manager.m_res->m_inc; + const auto smp_inc = m_context->m_descriptor_manager.m_smp->m_inc; + const Ptr tmp_res_heap = TmpResource ? m_tmp_res_heaps.m_heaps[TmpResource.HeapIndex].m_heap.Get() : nullptr; + const Ptr tmp_smp_heap = TmpSampler ? m_tmp_smp_heaps.m_heaps[TmpSampler.HeapIndex].m_heap.Get() : nullptr; + + auto set_bind_item_chunk_index = info.SetBindItemHead; + while (set_bind_item_chunk_index != COPLT_U32_MAX) + { + const auto& set_bind_item_chunk = m_set_bind_item_chunks[set_bind_item_chunk_index]; + for (auto& set_bind_item_cmd_index : set_bind_item_chunk.CmdIndex) + { + if (set_bind_item_cmd_index == COPLT_U32_MAX) break; + const auto& set_bind_item_cmd = Commands[set_bind_item_cmd_index].SetBindItem; + if (set_bind_item_cmd.Count == 0) continue; + const auto set_bind_items = std::span(PayloadSetBindItem.data() + set_bind_item_cmd.ItemIndex, set_bind_item_cmd.Count); + for (const auto& set_bind_item : set_bind_items) + { + if (set_bind_item.Slot > slots.size()) + COPLT_THROW_FMT("[{}] Invalid set bind item slot index, out of range", i); + const auto& slot = slots[set_bind_item.Slot]; + const auto& def = defs[set_bind_item.Slot]; + const auto array_index = def.Count == 0 ? 0 : set_bind_item.Index; + if (def.View == FShaderLayoutItemView::Constants || def.View == FShaderLayoutItemView::Sampler) + COPLT_THROW_FMT("[{}] Invalid set bind item view, dose not support Constants and Sampler", i); + if (def.Count == COPLT_U32_MAX) + { + if (array_index >= dyn_size) + { + COPLT_THROW_FMT( + "[{}] Invalid set bind item array index, out of range, dyn array size is {} but index is {}", + i, dyn_size, array_index + ); + } + } + else + { + if (array_index >= def.Count) + { + COPLT_THROW_FMT( + "[{}] Invalid set bind item array index, out of range, array size is {} but index is {}", + i, def.Count, array_index + ); + } + } + View view(set_bind_item.View); + if (view && view.NeedBarrier()) + { + const auto& res_index = AddResource(view); + const auto& item_info = group_item_info[set_bind_item.Slot]; + m_barrier_analyzer->OnUse(res_index, view, item_info); + } + switch (slot.Place) + { + case Layout::BindSlotPlace::NonTable: + COPLT_THROW("TODO"); + break; + case Layout::BindSlotPlace::ResourceTable: + { + CD3DX12_CPU_DESCRIPTOR_HANDLE handle( + tmp_res_heap->GetCPUDescriptorHandleForHeapStart(), + TmpResource.OffsetInHeap + slot.OffsetInTable + array_index, + res_inc + ); + view.CreateDescriptor(m_device->m_device.Get(), m_context, def, handle); + break; + } + case Layout::BindSlotPlace::SamplerTable: + { + CD3DX12_CPU_DESCRIPTOR_HANDLE handle( + tmp_smp_heap->GetCPUDescriptorHandleForHeapStart(), + TmpSampler.OffsetInHeap + slot.OffsetInTable + array_index, + smp_inc + ); + view.CreateDescriptor(m_device->m_device.Get(), nullptr, def, handle); + break; + } + } + } + } + set_bind_item_chunk_index = set_bind_item_chunk.Next; + } + + #pragma endregion + } + } + sync_binding_info.DynamicBindGroupInfoSyncIndCount = + m_dynamic_bind_group_info_sync_inds.size() - sync_binding_info.DynamicBindGroupInfoSyncIndIndex; + if (!changed) sync_binding_info.Skip = true; +} + +void D3d12GpuRecord::Analyze_SetMeshBuffers(const u32 i, const FCmdSetMeshBuffers& cmd) { if (m_state != RecordState::Render) COPLT_THROW_FMT("[{}] Can only use SetMeshBuffers in render scope", i); @@ -765,7 +1169,17 @@ void D3d12GpuRecord::Analyze_SetMeshBuffers(u32 i, const FCmdSetMeshBuffers& cmd m_barrier_analyzer->OnCmd(); } -void D3d12GpuRecord::Analyze_Dispatch(u32 i, const FCmdDispatch& cmd) const +void D3d12GpuRecord::Analyze_Draw(const u32 i, const FCmdDraw& cmd) +{ + if (Mode != FGpuRecordMode::Direct) + COPLT_THROW_FMT("[{}] Can only Draw on the direct mode", i); + if (m_state != RecordState::Render) + COPLT_THROW_FMT("[{}] Can only use Draw in render scope", i); + Analyze_SyncBinding(i, cmd); + m_barrier_analyzer->OnCmd(); +} + +void D3d12GpuRecord::Analyze_Dispatch(const u32 i, const FCmdDispatch& cmd) { switch (cmd.Type) { @@ -778,6 +1192,8 @@ void D3d12GpuRecord::Analyze_Dispatch(u32 i, const FCmdDispatch& cmd) const COPLT_THROW_FMT("[{}] Can only use Dispatch (Mesh) in render scope", i); break; } + Analyze_SyncBinding(i, cmd); + m_barrier_analyzer->OnCmd(); } void D3d12GpuRecord::BeforeInterpret(const D3d12RentedCommandList& list) @@ -804,10 +1220,16 @@ void D3d12GpuRecord::Interpret(const D3d12RentedCommandList& list, const u32 off { case FCmdType::None: case FCmdType::PreparePresent: + case FCmdType::SetBinding: + case FCmdType::SetDynArraySize: + case FCmdType::SetBindItem: + case FCmdType::SetConstants: break; case FCmdType::End: if (m_state == RecordState::Render) Interpret_RenderEnd(list, i, m_cur_render.Cmd); + else if (m_state == RecordState::Compute) + Interpret_ComputeEnd(list, i, m_cur_compute.Cmd); else COPLT_THROW("Cannot use End in main scope"); break; @@ -836,13 +1258,11 @@ void D3d12GpuRecord::Interpret(const D3d12RentedCommandList& list, const u32 off Interpret_Render(list, i, command.Render); break; case FCmdType::Compute: - COPLT_THROW("TODO"); + Interpret_Compute(list, i, command.Compute); + break; case FCmdType::SetPipeline: Interpret_SetPipeline(list, i, command.SetPipeline); break; - case FCmdType::SetBinding: - Interpret_SetBinding(list, i, command.SetBinding); - break; case FCmdType::SetViewportScissor: Interpret_SetViewportScissor(list, i, command.SetViewportScissor); break; @@ -1210,42 +1630,106 @@ void D3d12GpuRecord::Interpret_SetPipeline(const CmdList& list, const u32 i, con SetPipeline(list, cmd.Pipeline, i); } -void D3d12GpuRecord::Interpret_SetBinding(const CmdList& list, u32 i, const FCmdSetBinding& cmd) +void D3d12GpuRecord::Interpret_SyncBinding(const CmdList& list, u32 i, const FCmdSyncBinding& cmd) { - if (m_state != RecordState::Render && m_state != RecordState::Compute) - COPLT_THROW_FMT("[{}] Cannot use SetBinding in main scope", i); - if (!SetBinding(Bindings[cmd.Binding].Binding, i, cmd)) return; - const auto& set_binding_info = m_set_binding_infos[m_pipeline_context.SetBindingIndex]; - const auto bind_item_infos = set_binding_info.Layout->BindItemInfos(); - for (usize n = 0; n < bind_item_infos.size(); ++n) - { - const auto& bind_item_info = bind_item_infos[n]; - const auto& bind_item = m_bind_items[set_binding_info.BindItemIndex + n]; - // todo 直接资源和常量 - switch (bind_item_info.Place) + const auto& sync_binding_info = m_sync_binding_infos[cmd.SyncBindingIndex]; + if (!sync_binding_info || sync_binding_info.Skip) return; + const NonNull layout = sync_binding_info.Layout; + + #pragma region 持久分配的描述符表 + + const auto persistent_bind_items = std::span(m_table_bind_items) + .subspan(sync_binding_info.PersistentRootItemIndex, sync_binding_info.PersistentRootItemCount); + for (const auto& bind_item : persistent_bind_items) + { + const auto& bind_item_info = *bind_item.Info; + COPLT_DEBUG_ASSERT(bind_item_info.Place == Layout::RootItemPlace::Table); + const auto& allocation = m_allocations[bind_item.AllocationIndex]; + const auto& al = bind_item_info.Type == Layout::RootItemType::Resource ? allocation.Resource : allocation.Sampler; + COPLT_DEBUG_ASSERT(al.m_heap != nullptr); + if (m_state == RecordState::Render) + { + list->g0->SetGraphicsRootDescriptorTable(bind_item_info.RootIndex, al.GetGpuHandle()); + } + else { - case Layout::BindItemPlace::Table: + list->g0->SetComputeRootDescriptorTable(bind_item_info.RootIndex, al.GetGpuHandle()); + } + } + + #pragma endregion + + #pragma region 动态组 + + const auto dyn_bind_group_info_inds = std::span(m_dynamic_bind_group_info_sync_inds) + .subspan(sync_binding_info.DynamicBindGroupInfoSyncIndIndex, sync_binding_info.DynamicBindGroupInfoSyncIndCount); + for (const auto& dyn_bind_group_info_ind : dyn_bind_group_info_inds) + { + const auto& info = m_dynamic_bind_group_infos[dyn_bind_group_info_ind.InfoIndex]; + if (!info.BindGroup) continue; + const auto& group_root_item_info = layout->GroupRootItemInfos()[dyn_bind_group_info_ind.GroupIndex]; + + #pragma region 常量 + + auto set_constants_chunk_index = info.SetConstantsHead; + while (set_constants_chunk_index != COPLT_U32_MAX) + { + const auto& set_constants_chunk = m_set_constants_chunks[set_constants_chunk_index]; + for (auto& set_constants_cmd_index : set_constants_chunk.CmdIndex) { - const auto& allocation = m_allocations[set_binding_info.AllocationIndex + bind_item_info.Group]; - const auto& al = bind_item_info.Type == Layout::BindItemType::Resource ? allocation.Resource : allocation.Sampler; + if (set_constants_cmd_index == COPLT_U32_MAX) break; + const auto& set_constants_cmd = Commands[set_constants_cmd_index].SetConstants; + if (set_constants_cmd.Slot > group_root_item_info.size()) + COPLT_THROW_FMT("[{}] Invalid set constant slot index, out of range", i); + const auto& root_item_info = group_root_item_info[set_constants_cmd.Slot]; + COPLT_DEBUG_ASSERT(root_item_info.Place == Layout::RootItemPlace::Const); + const auto root_index = root_item_info.RootIndex; + if (set_constants_cmd.Count == 0) continue; if (m_state == RecordState::Render) { - list->g0->SetGraphicsRootDescriptorTable(bind_item_info.RootIndex, al.GetGpuHandle()); + list->g0->SetGraphicsRoot32BitConstants( + root_index, set_constants_cmd.Count, + &Payload32Bits[set_constants_cmd.ValueIndex], set_constants_cmd.Offset + ); } else { - list->g0->SetComputeRootDescriptorTable(bind_item_info.RootIndex, al.GetGpuHandle()); + list->g0->SetComputeRoot32BitConstants( + root_index, set_constants_cmd.Count, + &Payload32Bits[set_constants_cmd.ValueIndex], set_constants_cmd.Offset + ); } - break; } - case Layout::BindItemPlace::Direct: - // todo - break; - case Layout::BindItemPlace::Const: - // todo - break; + set_constants_chunk_index = set_constants_chunk.Next; + } + + #pragma endregion + + #pragma region 描述符表 + + const auto dynamic_bind_items = std::span(m_table_bind_items) + .subspan(info.DynamicRootItemIndex, info.DynamicRootItemCount); + for (const auto& bind_item : dynamic_bind_items) + { + const auto& bind_item_info = *bind_item.Info; + COPLT_DEBUG_ASSERT(bind_item_info.Place == Layout::RootItemPlace::Table); + const auto& allocation = m_allocations[bind_item.AllocationIndex]; + const auto& al = bind_item_info.Type == Layout::RootItemType::Resource ? allocation.Resource : allocation.Sampler; + COPLT_DEBUG_ASSERT(al.m_heap != nullptr); + if (m_state == RecordState::Render) + { + list->g0->SetGraphicsRootDescriptorTable(bind_item_info.RootIndex, al.GetGpuHandle()); + } + else + { + list->g0->SetComputeRootDescriptorTable(bind_item_info.RootIndex, al.GetGpuHandle()); + } } + + #pragma endregion } + + #pragma endregion } void D3d12GpuRecord::Interpret_SetViewportScissor(const CmdList& list, u32 i, const FCmdSetViewportScissor& cmd) const @@ -1287,10 +1771,11 @@ void D3d12GpuRecord::Interpret_SetMeshBuffers(const CmdList& list, u32 i, const list->g0->IASetVertexBuffers(cmd.VertexStartSlot, buffers.VertexBufferCount, views); } -void D3d12GpuRecord::Interpret_Draw(const CmdList& list, u32 i, const FCmdDraw& cmd) const +void D3d12GpuRecord::Interpret_Draw(const CmdList& list, u32 i, const FCmdDraw& cmd) { if (m_state != RecordState::Render) COPLT_THROW_FMT("[{}] Can only use Draw in render scope", i); + Interpret_SyncBinding(list, i, cmd); if (cmd.Indexed) { list->g0->DrawIndexedInstanced(cmd.VertexOrIndexCount, cmd.InstanceCount, cmd.FirstVertexOrIndex, cmd.VertexOffset, cmd.FirstInstance); @@ -1301,13 +1786,14 @@ void D3d12GpuRecord::Interpret_Draw(const CmdList& list, u32 i, const FCmdDraw& } } -void D3d12GpuRecord::Interpret_Dispatch(const CmdList& list, u32 i, const FCmdDispatch& cmd) const +void D3d12GpuRecord::Interpret_Dispatch(const CmdList& list, u32 i, const FCmdDispatch& cmd) { switch (cmd.Type) { case FDispatchType::Compute: if (m_state != RecordState::Compute) COPLT_THROW_FMT("[{}] Can only use Dispatch (Compute) in compute scope", i); + Interpret_SyncBinding(list, i, cmd); list->g0->Dispatch(cmd.GroupCountX, cmd.GroupCountY, cmd.GroupCountZ); break; case FDispatchType::Mesh: @@ -1315,6 +1801,7 @@ void D3d12GpuRecord::Interpret_Dispatch(const CmdList& list, u32 i, const FCmdDi COPLT_THROW_FMT("[{}] Can only use Dispatch (Mesh) in render scope", i); if (!list->g6) COPLT_THROW_FMT("[{}] The device does not support mesh shaders", i); + Interpret_SyncBinding(list, i, cmd); list->g6->DispatchMesh(cmd.GroupCountX, cmd.GroupCountY, cmd.GroupCountZ); break; } @@ -1326,8 +1813,9 @@ void D3d12GpuRecord::SetPipeline(NonNull pipeline, u32 i) m_pipeline_context.SetPipeline(pipeline, i); } -bool D3d12GpuRecord::SetBinding(NonNull binding, u32 i, const FCmdSetBinding& cmd) +bool D3d12GpuRecord::SetBinding(u32 i, const FCmdSetBinding& cmd) { + const NonNull binding = Bindings[cmd.Binding].Binding; if (m_pipeline_context.Binding && binding->ObjectId() == m_pipeline_context.Binding->ObjectId()) return false; m_pipeline_context.SetBinding(binding, i, cmd); return true; diff --git a/Coplt.Graphics.Native/D3d12/Src/Record.h b/Coplt.Graphics.Native/D3d12/Src/Record.h index ca8ae87..9b948d6 100644 --- a/Coplt.Graphics.Native/D3d12/Src/Record.h +++ b/Coplt.Graphics.Native/D3d12/Src/Record.h @@ -13,6 +13,7 @@ #include "Isolate.h" #include "Layout.h" #include "Output.h" +#include "Pipeline.h" namespace Coplt { @@ -103,9 +104,11 @@ namespace Coplt Ptr Layout{}; // 如果不是图形管线将不会设置 Ptr GPipeline{}; + // 如果不是计算管线将不会设置 + Ptr CPipeline{}; Ptr Binding{}; - u32 SetBindingIndex{}; + u32 BindingIndex{}; bool PipelineChanged{}; bool BindingChanged{}; @@ -117,30 +120,145 @@ namespace Coplt struct BindingInfo { + Ptr Layout{}; u64 BindingVersion{}; - u32 AllocationIndex{COPLT_U32_MAX}; - u32 BindItemIndex{COPLT_U32_MAX}; + u32 PersistentRootItemIndex{COPLT_U32_MAX}; + u32 PersistentRootItemCount{0}; + u32 DynamicBindGroupInfoIndIndex{COPLT_U32_MAX}; + u32 DynamicBindGroupInfoIndCount{0}; + bool Changed{}; + + explicit operator bool() const noexcept + { + return Layout; + } + }; + + struct BindGroupInfo + { + Rc BindGroup{}; + u32 Index{}; + u32 DynArraySize{}; + // 类型为 SetConstantsChunk + u32 SetConstantsHead{COPLT_U32_MAX}; + // 类型为 SetConstantsChunk + u32 SetConstantsTail{COPLT_U32_MAX}; + // 类型为 SetBindItemChunk + u32 SetBindItemHead{COPLT_U32_MAX}; + // 类型为 SetBindItemChunk + u32 SetBindItemTail{COPLT_U32_MAX}; + // 只有再 Frozen 变为 true 后才会设置 + u32 DynamicRootItemIndex{COPLT_U32_MAX}; + u32 DynamicRootItemCount{0}; + // 冻结后不可再修改 + bool Frozen{false}; }; - struct SetBindingInfo + struct SetConstantsChunk { - ID3d12BindingLayout* Layout{}; - u32 AllocationIndex{COPLT_U32_MAX}; - u32 BindItemIndex{COPLT_U32_MAX}; + u32 Next{COPLT_U32_MAX}; + u32 CmdIndex[7]{COPLT_U32_MAX}; }; - struct BindItem + struct SetBindItemChunk { - union + u32 Next{COPLT_U32_MAX}; + u32 CmdIndex[7]{COPLT_U32_MAX}; + }; + + struct SyncBindingInfo + { + Ptr Layout{}; + u32 PersistentRootItemIndex{COPLT_U32_MAX}; + u32 PersistentRootItemCount{0}; + // 类型为 BindGroupInd + u32 DynamicBindGroupInfoSyncIndIndex{COPLT_U32_MAX}; + u32 DynamicBindGroupInfoSyncIndCount{0}; + bool Skip{}; + + explicit operator bool() const noexcept { - // todo 直接资源和常量 - }; + return Layout; + } + }; + + struct TableRootItem + { + Ptr Info{}; + u32 AllocationIndex{}; + }; + + struct BindGroupInd + { + u32 GroupIndex{}; + // 类型是 BindGroupInfo + u32 InfoIndex{}; + }; + + struct TmpDescHeap + { + ComPtr m_heap{}; + u32 m_size{}; + u32 m_offset{}; + + TmpDescHeap() = default; + + explicit TmpDescHeap(const ComPtr& device, D3D12_DESCRIPTOR_HEAP_TYPE type, u32 size); + }; + + struct TmpDescAllocation + { + u32 HeapIndex{COPLT_U32_MAX}; + u32 OffsetInHeap{}; + u32 Size{}; + + explicit operator bool() const noexcept { return HeapIndex != COPLT_U32_MAX; } + }; + + struct TmpDescHeaps + { + constexpr static u32 InitSize = 1024; + + ComPtr m_device{}; + std::vector m_heaps{}; + D3D12_DESCRIPTOR_HEAP_TYPE m_type{}; + // 只记录,实际值可能超过分配量,可以记录但是不分配 + u32 m_sum_size{}; + + TmpDescHeaps() = default; + + explicit TmpDescHeaps(NonNull isolate, D3D12_DESCRIPTOR_HEAP_TYPE type); + + void Recycle(); + + TmpDescAllocation Alloc(u32 Size); + void Grow(u32 MinSize); + }; + + struct TmpGroupDescriptorHeap + { + u32 GroupIndex{}; + DescriptorHeapPair Heap{}; + bool Changed{}; }; struct AllocationPoint { + u32 GroupIndex{}; + // 类型为 PersistentAllocationPointInfo;u32::Max 表示是临时动态分配,其他表示是持久分配 + u32 PersistentInfoIndex{COPLT_U32_MAX}; DescriptorAllocation Resource{}; DescriptorAllocation Sampler{}; + TmpDescAllocation TmpResource{}; + TmpDescAllocation TmpSampler{}; + }; + + struct PersistentAllocationPointInfo + { + u64 ObjectId{}; + DescriptorHeapPair Heap{}; + bool Changed{}; + bool Last{}; }; u64 m_isolate_id{}; @@ -150,13 +268,24 @@ namespace Coplt Rc m_context{}; Rc m_barrier_analyzer{}; HashMap m_resource_map{}; // id -> index + std::vector> m_object_handles{}; std::vector m_resource_infos{}; std::vector m_binding_infos{}; - std::vector m_set_binding_infos{}; + std::vector m_dynamic_bind_group_infos{}; + HashMap m_dynamic_bind_group_map{}; // id -> index + std::vector m_sync_binding_infos{}; std::vector m_allocations{}; - std::vector m_bind_items{}; + std::vector m_persistent_allocation_infos{}; + std::vector m_dynamic_bind_group_info_inds{}; + std::vector m_dynamic_bind_group_info_sync_inds{}; + std::vector m_set_constants_chunks{}; + std::vector m_set_bind_item_chunks{}; + std::vector m_table_bind_items{}; + TmpDescHeaps m_tmp_res_heaps{}; + TmpDescHeaps m_tmp_smp_heaps{}; std::vector m_queue_wait_points{}; std::vector m_tmp_locks{}; + std::vector m_tmp_gdhs{}; RenderState m_cur_render{}; ComputeState m_cur_compute{}; PipelineContext m_pipeline_context{}; @@ -193,8 +322,11 @@ namespace Coplt FResIndex AddResource(const FCmdRes& res); FResIndex AddResource(const View& view); void ReadyResource(); - // 返回帧所需的最小描述符堆大小 - void ReadyBindings(u32& MinResHeapSize, u32& MinSmpHeapSize); + void ReadyBindings(); + void UploadDescriptors(); + + BindGroupInfo& QueryBindGroupInfo(ID3d12ShaderBindGroup& group, bool mut); + BindGroupInfo& QueryBindGroupInfo(NonNull group, u32 i, bool mut); void Analyze(); void Analyze_PreparePresent(u32 i, const FCmdPreparePresent& cmd) const; @@ -208,8 +340,13 @@ namespace Coplt void Analyze_ComputeEnd(u32 i, const FCmdCompute& cmd); void Analyze_SetPipeline(u32 i, const FCmdSetPipeline& cmd); void Analyze_SetBinding(u32 i, const FCmdSetBinding& cmd); + void Analyze_SetConstants(u32 i, const FCmdSetConstants& cmd); + void Analyze_SetDynArraySize(u32 i, const FCmdSetDynArraySize& cmd); + void Analyze_SetBindItem(u32 i, const FCmdSetBindItem& cmd); + void Analyze_SyncBinding(u32 i, const FCmdSyncBinding& cmd); void Analyze_SetMeshBuffers(u32 i, const FCmdSetMeshBuffers& cmd); - void Analyze_Dispatch(u32 i, const FCmdDispatch& cmd) const; + void Analyze_Draw(u32 i, const FCmdDraw& cmd); + void Analyze_Dispatch(u32 i, const FCmdDispatch& cmd); void BeforeInterpret(const D3d12RentedCommandList& list) override; void AfterInterpret(const D3d12RentedCommandList& list) override; @@ -226,15 +363,15 @@ namespace Coplt void Interpret_Compute(const CmdList& list, u32 i, const FCmdCompute& cmd); void Interpret_ComputeEnd(const CmdList& list, u32 i, const FCmdCompute& cmd); void Interpret_SetPipeline(const CmdList& list, u32 i, const FCmdSetPipeline& cmd); - void Interpret_SetBinding(const CmdList& list, u32 i, const FCmdSetBinding& cmd); + void Interpret_SyncBinding(const CmdList& list, u32 i, const FCmdSyncBinding& cmd); void Interpret_SetViewportScissor(const CmdList& list, u32 i, const FCmdSetViewportScissor& cmd) const; void Interpret_SetMeshBuffers(const CmdList& list, u32 i, const FCmdSetMeshBuffers& cmd) const; - void Interpret_Draw(const CmdList& list, u32 i, const FCmdDraw& cmd) const; - void Interpret_Dispatch(const CmdList& list, u32 i, const FCmdDispatch& cmd) const; + void Interpret_Draw(const CmdList& list, u32 i, const FCmdDraw& cmd); + void Interpret_Dispatch(const CmdList& list, u32 i, const FCmdDispatch& cmd); void SetPipeline(NonNull pipeline, u32 i); // 返回是否改变 - bool SetBinding(NonNull binding, u32 i, const FCmdSetBinding& cmd); + bool SetBinding(u32 i, const FCmdSetBinding& cmd); void SetPipeline(const CmdList& list, NonNull pipeline, u32 i); }; diff --git a/Coplt.Graphics.Native/D3d12/Src/ResState.cc b/Coplt.Graphics.Native/D3d12/Src/ResState.cc index 03e7634..01064ee 100644 --- a/Coplt.Graphics.Native/D3d12/Src/ResState.cc +++ b/Coplt.Graphics.Native/D3d12/Src/ResState.cc @@ -99,18 +99,26 @@ bool Coplt::IsReadOnly(const ResAccess access) ); } +bool Coplt::IsNonUavReadOnly(const ResAccess access) +{ + return HasFlagsOnly( + access, + ResAccess::VertexBufferRead | ResAccess::ConstantBufferRead | ResAccess::IndexBufferRead | + ResAccess::DepthStencilRead | ResAccess::ShaderResourceRead | ResAccess::IndirectArgumentRead | ResAccess::CopySourceRead | + ResAccess::ResolveSourceRead | ResAccess::ShadingRateSourceRead | + ResAccess::VideoEncodeRead | ResAccess::VideoDecodeRead | ResAccess::VideoProcessRead + ); +} + bool Coplt::IsCompatible(const ResAccess Old, const ResAccess New) { switch (Old) { case ResAccess::None: return true; - case ResAccess::UnorderedAccessRead: - case ResAccess::RayTracingAccelerationStructureRead: - return false; default: - if (IsReadOnly(Old) && IsReadOnly(New)) return true; - return false; + if (IsNonUavReadOnly(Old) && IsNonUavReadOnly(New)) return true; + return Old == New; } } diff --git a/Coplt.Graphics.Native/D3d12/Src/View.cc b/Coplt.Graphics.Native/D3d12/Src/View.cc index 72892fe..9e0bce4 100644 --- a/Coplt.Graphics.Native/D3d12/Src/View.cc +++ b/Coplt.Graphics.Native/D3d12/Src/View.cc @@ -9,59 +9,193 @@ using namespace Coplt; View& View::swap(View& other) noexcept { std::swap(m_viewable, other.m_viewable); - std::swap(m_type, other.m_type); + std::swap(m_data, other.m_data); return *this; } View::View(const FView& view) { - if (view.Type != FViewType::None && view.Viewable) + if (view.Data.Type != FViewType::None) { - const auto viewable = view.Viewable->QueryInterface(); - if (!viewable) - COPLT_THROW("Viewable from different backends"); - m_viewable = Rc::UnsafeClone(viewable); - m_type = view.Type; + m_data = view.Data; + if (view.Viewable) + { + const auto viewable = view.Viewable->QueryInterface(); + if (!viewable) + COPLT_THROW("Viewable from different backends"); + m_viewable = Rc::UnsafeClone(viewable); + } } } View& View::operator=(const FView& view) { - return View(view).swap(*this); + View(view).swap(*this); + return *this; } View::operator bool() const { - return m_viewable && m_type != FViewType::None; + switch (m_data.Type) + { + case FViewType::None: + case FViewType::UploadBuffer: + return true; + case FViewType::Sampler: + case FViewType::Buffer: + case FViewType::Image1D: + case FViewType::Image1DArray: + case FViewType::Image2D: + case FViewType::Image2DArray: + case FViewType::Image2DMs: + case FViewType::Image2DMsArray: + case FViewType::Image3D: + case FViewType::ImageCube: + case FViewType::ImageCubeArray: + return static_cast(m_viewable); + } + return false; } bool View::IsCompatible(const FBindGroupItem& def) const { - switch (m_type) + if (!*this) return true; + switch (m_data.Type) { case FViewType::None: - break; - case FViewType::Buffer: - case FViewType::Image: + return true; case FViewType::Sampler: - return m_viewable->IsCompatible(def); + return def.Type == FShaderLayoutItemType::Sampler; + case FViewType::Buffer: + switch (def.Type) + { + case FShaderLayoutItemType::ConstantBuffer: + case FShaderLayoutItemType::Buffer: + case FShaderLayoutItemType::RawBuffer: + case FShaderLayoutItemType::StructureBuffer: + case FShaderLayoutItemType::StructureBufferWithCounter: + case FShaderLayoutItemType::RayTracingAccelerationStructure: + return m_viewable->IsCompatible(def); + default: + return false; + } + case FViewType::UploadBuffer: + switch (def.Type) + { + case FShaderLayoutItemType::ConstantBuffer: + case FShaderLayoutItemType::Buffer: + case FShaderLayoutItemType::RawBuffer: + case FShaderLayoutItemType::StructureBuffer: + return true; + default: + return false; + } + case FViewType::Image2DMs: + case FViewType::Image2DMsArray: + if (def.Type == FShaderLayoutItemType::Texture2DMultisample) break; + if (def.Type == FShaderLayoutItemType::Texture2DArrayMultisample) break; + return false; + case FViewType::Image1D: + case FViewType::Image1DArray: + case FViewType::Image2D: + case FViewType::Image2DArray: + case FViewType::Image3D: + case FViewType::ImageCube: + case FViewType::ImageCubeArray: + switch (def.Type) + { + case FShaderLayoutItemType::Texture1D: + case FShaderLayoutItemType::Texture1DArray: + case FShaderLayoutItemType::Texture2D: + case FShaderLayoutItemType::Texture2DArray: + case FShaderLayoutItemType::Texture3D: + case FShaderLayoutItemType::TextureCube: + case FShaderLayoutItemType::TextureCubeArray: + return m_viewable->IsCompatible(def); + default: + return false; + } } - return true; + return m_viewable->IsCompatible(def); } bool View::IsBuffer() const { - return m_type == FViewType::Buffer; + switch (m_data.Type) + { + case FViewType::None: + case FViewType::Buffer: + case FViewType::UploadBuffer: + return true; + case FViewType::Sampler: + case FViewType::Image1D: + case FViewType::Image1DArray: + case FViewType::Image2D: + case FViewType::Image2DArray: + case FViewType::Image2DMs: + case FViewType::Image2DMsArray: + case FViewType::Image3D: + case FViewType::ImageCube: + case FViewType::ImageCubeArray: + break; + } + return false; } bool View::IsImage() const { - return m_type == FViewType::Image; + switch (m_data.Type) + { + case FViewType::None: + case FViewType::Sampler: + case FViewType::Buffer: + case FViewType::UploadBuffer: + break; + case FViewType::Image1D: + case FViewType::Image1DArray: + case FViewType::Image2D: + case FViewType::Image2DArray: + case FViewType::Image2DMs: + case FViewType::Image2DMsArray: + case FViewType::Image3D: + case FViewType::ImageCube: + case FViewType::ImageCubeArray: + return true; + } + return false; } bool View::IsSampler() const { - return m_type == FViewType::Sampler; + return m_data.Type == FViewType::Sampler; +} + +bool View::IsUploadBuffer() const +{ + return m_data.Type == FViewType::UploadBuffer; +} + +bool View::NeedBarrier() const +{ + switch (m_data.Type) + { + case FViewType::None: + case FViewType::Sampler: + case FViewType::UploadBuffer: + return false; + case FViewType::Buffer: + case FViewType::Image1D: + case FViewType::Image1DArray: + case FViewType::Image2D: + case FViewType::Image2DArray: + case FViewType::Image2DMs: + case FViewType::Image2DMsArray: + case FViewType::Image3D: + case FViewType::ImageCube: + case FViewType::ImageCubeArray: + return true; + } + return false; } NonNull View::GetViewable() const @@ -84,9 +218,11 @@ Ptr View::TryGetSampler() const return m_viewable->QueryInterface(); } -void View::CreateDescriptor(NonNull device, const FBindGroupItem& def, CD3DX12_CPU_DESCRIPTOR_HANDLE handle) const +void View::CreateDescriptor( + NonNull device, Ptr context, const FBindGroupItem& def, CD3DX12_CPU_DESCRIPTOR_HANDLE handle +) const { - switch (m_type) + switch (m_data.Type) { case FViewType::None: CreateNullDescriptor(device, def, handle); @@ -94,12 +230,23 @@ void View::CreateDescriptor(NonNull device, const FBindGroupItem& case FViewType::Buffer: CreateBufferDescriptor(device, def, handle); break; - case FViewType::Image: + case FViewType::Image1D: + case FViewType::Image1DArray: + case FViewType::Image2D: + case FViewType::Image2DArray: + case FViewType::Image2DMs: + case FViewType::Image2DMsArray: + case FViewType::Image3D: + case FViewType::ImageCube: + case FViewType::ImageCubeArray: CreateImageDescriptor(device, def, handle); break; case FViewType::Sampler: CreateSamplerDescriptor(device, def, handle); break; + case FViewType::UploadBuffer: + CreateUploadBufferDescriptor(device, context, def, handle); + break; } } @@ -116,6 +263,7 @@ void View::CreateNullDescriptor(NonNull device, const FBindGroupI { D3D12_SHADER_RESOURCE_VIEW_DESC desc{}; desc.Format = ToDx(def.Format); + if (desc.Format == DXGI_FORMAT_UNKNOWN) desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; switch (def.Type) { @@ -207,6 +355,7 @@ void View::CreateNullDescriptor(NonNull device, const FBindGroupI { D3D12_UNORDERED_ACCESS_VIEW_DESC desc{}; desc.Format = ToDx(def.Format); + if (desc.Format == DXGI_FORMAT_UNKNOWN) desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; switch (def.Type) { case FShaderLayoutItemType::ConstantBuffer: @@ -302,15 +451,15 @@ void View::CreateBufferDescriptor(NonNull device, const FBindGrou case FShaderLayoutItemView::Cbv: { D3D12_CONSTANT_BUFFER_VIEW_DESC desc{}; - desc.BufferLocation = resource->GetGPUVirtualAddress(); - desc.SizeInBytes = data->m_size; + desc.BufferLocation = resource->GetGPUVirtualAddress() + m_data.Buffer.Offset; + desc.SizeInBytes = m_data.Buffer.Size; device->CreateConstantBufferView(&desc, handle); break; } case FShaderLayoutItemView::Srv: { D3D12_SHADER_RESOURCE_VIEW_DESC desc{}; - desc.Format = ToDx(def.Format); + desc.Format = ToDx(m_data.Buffer.Format == FGraphicsFormat::Unknown ? def.Format : m_data.Buffer.Format); desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; switch (def.Type) { @@ -318,23 +467,54 @@ void View::CreateBufferDescriptor(NonNull device, const FBindGrou COPLT_THROW("Srv does not support ConstantBuffer"); case FShaderLayoutItemType::Buffer: desc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; + desc.Buffer.StructureByteStride = 0; + if (m_data.Buffer.Stride != 0) + { + desc.Buffer.FirstElement = 0; + desc.Buffer.NumElements = data->m_size; + } + else + { + desc.Buffer.FirstElement = m_data.Buffer.Offset; + desc.Buffer.NumElements = m_data.Buffer.Size; + } break; case FShaderLayoutItemType::RawBuffer: - if (data->m_usage != FBufferUsage::Raw) - COPLT_THROW("Buffer not a Raw Buffer"); desc.Format = DXGI_FORMAT_R32_TYPELESS; desc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; + desc.Buffer.StructureByteStride = 0; + if (m_data.Buffer.Stride != 0) + { + if (data->m_usage != FBufferUsage::Raw) + COPLT_THROW("Buffer not a Raw Buffer"); + desc.Buffer.FirstElement = 0; + desc.Buffer.NumElements = data->m_size; + } + else + { + desc.Buffer.FirstElement = m_data.Buffer.Offset; + desc.Buffer.NumElements = m_data.Buffer.Size; + } desc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_RAW; break; case FShaderLayoutItemType::StructureBuffer: case FShaderLayoutItemType::StructureBufferWithCounter: - if (data->m_usage != FBufferUsage::Structured) - COPLT_THROW("Buffer not a Structured Buffer"); desc.Format = DXGI_FORMAT_UNKNOWN; desc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; - desc.Buffer.FirstElement = 0; - desc.Buffer.NumElements = data->m_count; - desc.Buffer.StructureByteStride = data->m_stride; + if (m_data.Buffer.Stride == 0) + { + if (data->m_usage != FBufferUsage::Structured) + COPLT_THROW("Buffer not a Structured Buffer"); + desc.Buffer.FirstElement = 0; + desc.Buffer.NumElements = data->m_count; + desc.Buffer.StructureByteStride = data->m_stride; + } + else + { + desc.Buffer.FirstElement = m_data.Buffer.Offset; + desc.Buffer.NumElements = m_data.Buffer.Size; + desc.Buffer.StructureByteStride = m_data.Buffer.Stride; + } break; case FShaderLayoutItemType::Texture1D: case FShaderLayoutItemType::Texture1DArray: @@ -350,7 +530,7 @@ void View::CreateBufferDescriptor(NonNull device, const FBindGrou COPLT_THROW("Buffer Srv does not support Sampler"); case FShaderLayoutItemType::RayTracingAccelerationStructure: desc.ViewDimension = D3D12_SRV_DIMENSION_RAYTRACING_ACCELERATION_STRUCTURE; - desc.RaytracingAccelerationStructure.Location = resource->GetGPUVirtualAddress(); + desc.RaytracingAccelerationStructure.Location = resource->GetGPUVirtualAddress() + m_data.Buffer.Offset; break; } device->CreateShaderResourceView(resource, &desc, handle); @@ -359,30 +539,61 @@ void View::CreateBufferDescriptor(NonNull device, const FBindGrou case FShaderLayoutItemView::Uav: { D3D12_UNORDERED_ACCESS_VIEW_DESC desc{}; - desc.Format = ToDx(def.Format); + desc.Format = ToDx(m_data.Buffer.Format == FGraphicsFormat::Unknown ? def.Format : m_data.Buffer.Format); switch (def.Type) { case FShaderLayoutItemType::ConstantBuffer: COPLT_THROW("Uav does not support ConstantBuffer"); case FShaderLayoutItemType::Buffer: desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER; + desc.Buffer.StructureByteStride = 0; + if (m_data.Buffer.Stride != 0) + { + desc.Buffer.FirstElement = 0; + desc.Buffer.NumElements = data->m_size; + } + else + { + desc.Buffer.FirstElement = m_data.Buffer.Offset; + desc.Buffer.NumElements = m_data.Buffer.Size; + } break; case FShaderLayoutItemType::RawBuffer: - if (data->m_usage != FBufferUsage::Raw) - COPLT_THROW("Buffer not a Raw Buffer"); desc.Format = DXGI_FORMAT_R32_TYPELESS; desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER; + desc.Buffer.StructureByteStride = 0; + if (m_data.Buffer.Stride != 0) + { + if (data->m_usage != FBufferUsage::Raw) + COPLT_THROW("Buffer not a Raw Buffer"); + desc.Buffer.FirstElement = 0; + desc.Buffer.NumElements = data->m_size; + } + else + { + desc.Buffer.FirstElement = m_data.Buffer.Offset; + desc.Buffer.NumElements = m_data.Buffer.Size; + } desc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_RAW; break; case FShaderLayoutItemType::StructureBuffer: case FShaderLayoutItemType::StructureBufferWithCounter: - if (data->m_usage != FBufferUsage::Structured) - COPLT_THROW("Buffer not a Structured Buffer"); desc.Format = DXGI_FORMAT_UNKNOWN; desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER; - desc.Buffer.FirstElement = 0; - desc.Buffer.NumElements = data->m_count; - desc.Buffer.StructureByteStride = data->m_stride; + if (m_data.Buffer.Stride == 0) + { + if (data->m_usage != FBufferUsage::Structured) + COPLT_THROW("Buffer not a Structured Buffer"); + desc.Buffer.FirstElement = 0; + desc.Buffer.NumElements = data->m_count; + desc.Buffer.StructureByteStride = data->m_stride; + } + else + { + desc.Buffer.FirstElement = m_data.Buffer.Offset; + desc.Buffer.NumElements = m_data.Buffer.Size; + desc.Buffer.StructureByteStride = m_data.Buffer.Stride; + } desc.Buffer.CounterOffsetInBytes = 0; break; case FShaderLayoutItemType::Texture1D: @@ -410,6 +621,86 @@ void View::CreateBufferDescriptor(NonNull device, const FBindGrou } } +void View::CreateUploadBufferDescriptor( + NonNull device, NonNull context, const FBindGroupItem& def, CD3DX12_CPU_DESCRIPTOR_HANDLE handle +) const +{ + const auto& buffers = context->m_upload_buffers; + if (m_data.UploadBuffer.Index >= buffers.size()) + COPLT_THROW("Index out of range"); + const auto& buffer = buffers[m_data.UploadBuffer.Index]; + const auto& resource = buffer.m_resource.m_resource; + switch (def.View) + { + case FShaderLayoutItemView::Cbv: + { + D3D12_CONSTANT_BUFFER_VIEW_DESC desc{}; + desc.BufferLocation = resource->GetGPUVirtualAddress() + m_data.UploadBuffer.Offset; + desc.SizeInBytes = m_data.UploadBuffer.Size; + device->CreateConstantBufferView(&desc, handle); + break; + } + case FShaderLayoutItemView::Srv: + { + D3D12_SHADER_RESOURCE_VIEW_DESC desc{}; + desc.Format = DXGI_FORMAT_UNKNOWN; + desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + switch (def.Type) + { + case FShaderLayoutItemType::ConstantBuffer: + COPLT_THROW("Srv does not support ConstantBuffer"); + case FShaderLayoutItemType::Buffer: + COPLT_THROW("Upload Buffer does not support Srv Buffer"); + // desc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; + // desc.Buffer.StructureByteStride = 0; + // desc.Buffer.FirstElement = m_data.UploadBuffer.Offset; + // desc.Buffer.NumElements = m_data.UploadBuffer.Size; + break; + case FShaderLayoutItemType::RawBuffer: + desc.Format = DXGI_FORMAT_R32_TYPELESS; + desc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; + desc.Buffer.StructureByteStride = 0; + desc.Buffer.FirstElement = m_data.UploadBuffer.Offset; + desc.Buffer.NumElements = m_data.UploadBuffer.Size; + desc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_RAW; + break; + case FShaderLayoutItemType::StructureBuffer: + case FShaderLayoutItemType::StructureBufferWithCounter: + COPLT_THROW("Upload Buffer does not support StructureBuffer"); + // desc.Format = DXGI_FORMAT_UNKNOWN; + // desc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; + // desc.Buffer.FirstElement = m_data.UploadBuffer.Offset; + // desc.Buffer.NumElements = m_data.UploadBuffer.Size; + // desc.Buffer.StructureByteStride = m_data.UploadBuffer.Stride; + break; + case FShaderLayoutItemType::Texture1D: + case FShaderLayoutItemType::Texture1DArray: + case FShaderLayoutItemType::Texture2D: + case FShaderLayoutItemType::Texture2DArray: + case FShaderLayoutItemType::Texture2DMultisample: + case FShaderLayoutItemType::Texture2DArrayMultisample: + case FShaderLayoutItemType::Texture3D: + case FShaderLayoutItemType::TextureCube: + case FShaderLayoutItemType::TextureCubeArray: + COPLT_THROW("Buffer Srv does not support Texture"); + case FShaderLayoutItemType::Sampler: + COPLT_THROW("Buffer Srv does not support Sampler"); + case FShaderLayoutItemType::RayTracingAccelerationStructure: + COPLT_THROW("Upload Buffer does not support RayTracingAccelerationStructure"); + break; + } + device->CreateShaderResourceView(resource.Get(), &desc, handle); + break; + } + case FShaderLayoutItemView::Uav: + COPLT_THROW("The upload buffer cannot be used for unordered access"); + case FShaderLayoutItemView::Sampler: + COPLT_THROW("Buffer cannot be used as a sampler"); + default: + std::unreachable(); + } +} + void View::CreateImageDescriptor(NonNull device, const FBindGroupItem& def, CD3DX12_CPU_DESCRIPTOR_HANDLE handle) const { const NonNull image = TryGetImage(); @@ -422,7 +713,7 @@ void View::CreateImageDescriptor(NonNull device, const FBindGroup case FShaderLayoutItemView::Srv: { D3D12_SHADER_RESOURCE_VIEW_DESC desc{}; - desc.Format = ToDx(data->m_format); + desc.Format = ToDx(m_data.Image.Format == FGraphicsFormat::Unknown ? data->m_format : m_data.Image.Format); desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; switch (def.Type) { @@ -438,8 +729,8 @@ void View::CreateImageDescriptor(NonNull device, const FBindGroup if (data->m_dimension != FImageDimension::One) COPLT_THROW("Bind required Texture1D, but the actual resources provided are not"); desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1D; - desc.Texture1D.MostDetailedMip = 0; - desc.Texture1D.MipLevels = data->m_mip_levels; + desc.Texture1D.MostDetailedMip = m_data.Image.Mip; + desc.Texture1D.MipLevels = m_data.Image.NumMips; desc.Texture1D.ResourceMinLODClamp = 0; break; } @@ -448,10 +739,10 @@ void View::CreateImageDescriptor(NonNull device, const FBindGroup if (data->m_dimension != FImageDimension::One) COPLT_THROW("Bind required Texture1DArray, but the actual resources provided are not"); desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1DARRAY; - desc.Texture1DArray.MostDetailedMip = 0; - desc.Texture1DArray.MipLevels = data->m_mip_levels; - desc.Texture1DArray.FirstArraySlice = 0; - desc.Texture1DArray.ArraySize = data->m_depth_or_length; + desc.Texture1DArray.MostDetailedMip = m_data.Image.Mip; + desc.Texture1DArray.MipLevels = m_data.Image.NumMips; + desc.Texture1DArray.FirstArraySlice = m_data.Image.Index; + desc.Texture1DArray.ArraySize = m_data.Image.Size; desc.Texture1DArray.ResourceMinLODClamp = 0; break; } @@ -460,9 +751,9 @@ void View::CreateImageDescriptor(NonNull device, const FBindGroup if (data->m_dimension != FImageDimension::Two) COPLT_THROW("Bind required Texture2D, but the actual resources provided are not"); desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; - desc.Texture2D.MostDetailedMip = 0; - desc.Texture2D.MipLevels = data->m_mip_levels; - desc.Texture2D.PlaneSlice = 0; + desc.Texture2D.MostDetailedMip = m_data.Image.Mip; + desc.Texture2D.MipLevels = m_data.Image.NumMips; + desc.Texture2D.PlaneSlice = m_data.Image.Plane; desc.Texture2D.ResourceMinLODClamp = 0; break; } @@ -471,11 +762,11 @@ void View::CreateImageDescriptor(NonNull device, const FBindGroup if (data->m_dimension != FImageDimension::Two) COPLT_THROW("Bind required Texture2DArray, but the actual resources provided are not"); desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY; - desc.Texture2DArray.MostDetailedMip = 0; - desc.Texture2DArray.MipLevels = data->m_mip_levels; - desc.Texture2DArray.FirstArraySlice = 0; - desc.Texture2DArray.ArraySize = data->m_depth_or_length; - desc.Texture2DArray.PlaneSlice = 0; + desc.Texture2DArray.MostDetailedMip = m_data.Image.Mip; + desc.Texture2DArray.MipLevels = m_data.Image.NumMips; + desc.Texture2DArray.FirstArraySlice = m_data.Image.Index; + desc.Texture2DArray.ArraySize = m_data.Image.Size; + desc.Texture2DArray.PlaneSlice = m_data.Image.Plane; desc.Texture2DArray.ResourceMinLODClamp = 0; break; } @@ -491,8 +782,8 @@ void View::CreateImageDescriptor(NonNull device, const FBindGroup if (data->m_dimension != FImageDimension::Two) COPLT_THROW("Bind required Texture2DArrayMultisample, but the actual resources provided are not"); desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY; - desc.Texture2DMSArray.FirstArraySlice = 0; - desc.Texture2DMSArray.ArraySize = data->m_depth_or_length; + desc.Texture2DMSArray.FirstArraySlice = m_data.Image.Index; + desc.Texture2DMSArray.ArraySize = m_data.Image.Size; break; } case FShaderLayoutItemType::Texture3D: @@ -500,8 +791,8 @@ void View::CreateImageDescriptor(NonNull device, const FBindGroup if (data->m_dimension != FImageDimension::Three) COPLT_THROW("Bind required Texture3D, but the actual resources provided are not"); desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D; - desc.Texture3D.MostDetailedMip = 0; - desc.Texture3D.MipLevels = data->m_mip_levels; + desc.Texture3D.MostDetailedMip = m_data.Image.Mip; + desc.Texture3D.MipLevels = m_data.Image.NumMips; desc.Texture3D.ResourceMinLODClamp = 0; break; } @@ -510,8 +801,8 @@ void View::CreateImageDescriptor(NonNull device, const FBindGroup if (data->m_dimension != FImageDimension::Cube) COPLT_THROW("Bind required TextureCube, but the actual resources provided are not"); desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE; - desc.TextureCube.MostDetailedMip = 0; - desc.TextureCube.MipLevels = data->m_mip_levels; + desc.TextureCube.MostDetailedMip = m_data.Image.Mip; + desc.TextureCube.MipLevels = m_data.Image.NumMips; desc.TextureCube.ResourceMinLODClamp = 0; break; } @@ -520,10 +811,10 @@ void View::CreateImageDescriptor(NonNull device, const FBindGroup if (data->m_dimension != FImageDimension::Cube) COPLT_THROW("Bind required TextureCubeArray, but the actual resources provided are not"); desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY; - desc.TextureCubeArray.MostDetailedMip = 0; - desc.TextureCubeArray.MipLevels = data->m_mip_levels; - desc.TextureCubeArray.First2DArrayFace = 0; - desc.TextureCubeArray.NumCubes = (data->m_depth_or_length + 5) / 6; + desc.TextureCubeArray.MostDetailedMip = m_data.Image.Mip; + desc.TextureCubeArray.MipLevels = m_data.Image.NumMips; + desc.TextureCubeArray.First2DArrayFace = m_data.Image.Index; + desc.TextureCubeArray.NumCubes = m_data.Image.Size; desc.TextureCubeArray.ResourceMinLODClamp = 0; break; } @@ -538,7 +829,7 @@ void View::CreateImageDescriptor(NonNull device, const FBindGroup case FShaderLayoutItemView::Uav: { D3D12_UNORDERED_ACCESS_VIEW_DESC desc{}; - desc.Format = ToDx(data->m_format); + desc.Format = ToDx(m_data.Image.Format == FGraphicsFormat::Unknown ? data->m_format : m_data.Image.Format); switch (def.Type) { case FShaderLayoutItemType::ConstantBuffer: @@ -554,7 +845,7 @@ void View::CreateImageDescriptor(NonNull device, const FBindGroup if (data->m_dimension != FImageDimension::One) COPLT_THROW("Bind required Texture1D, but the actual resources provided are not"); desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE1D; - desc.Texture1D.MipSlice = 0; + desc.Texture1D.MipSlice = m_data.Image.Mip; break; } case FShaderLayoutItemType::Texture1DArray: @@ -562,9 +853,9 @@ void View::CreateImageDescriptor(NonNull device, const FBindGroup if (data->m_dimension != FImageDimension::One) COPLT_THROW("Bind required Texture1DArray, but the actual resources provided are not"); desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE1DARRAY; - desc.Texture1DArray.MipSlice = 0; - desc.Texture1DArray.FirstArraySlice = 0; - desc.Texture1DArray.ArraySize = data->m_depth_or_length; + desc.Texture1DArray.MipSlice = m_data.Image.Mip; + desc.Texture1DArray.FirstArraySlice = m_data.Image.Index; + desc.Texture1DArray.ArraySize = m_data.Image.Size; break; } case FShaderLayoutItemType::Texture2D: @@ -572,8 +863,8 @@ void View::CreateImageDescriptor(NonNull device, const FBindGroup if (data->m_dimension != FImageDimension::Two) COPLT_THROW("Bind required Texture2D, but the actual resources provided are not"); desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D; - desc.Texture2D.MipSlice = 0; - desc.Texture2D.PlaneSlice = 0; + desc.Texture2D.MipSlice = m_data.Image.Mip; + desc.Texture2D.PlaneSlice = m_data.Image.Plane; break; } case FShaderLayoutItemType::Texture2DArray: @@ -581,10 +872,10 @@ void View::CreateImageDescriptor(NonNull device, const FBindGroup if (data->m_dimension != FImageDimension::Two) COPLT_THROW("Bind required Texture2DArray, but the actual resources provided are not"); desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY; - desc.Texture2DArray.MipSlice = 0; - desc.Texture2DArray.FirstArraySlice = 0; - desc.Texture2DArray.ArraySize = data->m_depth_or_length; - desc.Texture2DArray.PlaneSlice = 0; + desc.Texture2DArray.MipSlice = m_data.Image.Mip; + desc.Texture2DArray.FirstArraySlice = m_data.Image.Index; + desc.Texture2DArray.ArraySize = m_data.Image.Size; + desc.Texture2DArray.PlaneSlice = m_data.Image.Plane; break; } case FShaderLayoutItemType::Texture2DMultisample: @@ -599,17 +890,17 @@ void View::CreateImageDescriptor(NonNull device, const FBindGroup if (data->m_dimension != FImageDimension::Two) COPLT_THROW("Bind required Texture2DArrayMultisample, but the actual resources provided are not"); desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DMSARRAY; - desc.Texture2DMSArray.FirstArraySlice = 0; - desc.Texture2DMSArray.ArraySize = data->m_depth_or_length; + desc.Texture2DMSArray.FirstArraySlice = m_data.Image.Index; + desc.Texture2DMSArray.ArraySize = m_data.Image.Size; break; } case FShaderLayoutItemType::Texture3D: { if (data->m_dimension != FImageDimension::Three) COPLT_THROW("Bind required Texture3D, but the actual resources provided are not"); - desc.Texture3D.MipSlice = 0; - desc.Texture3D.FirstWSlice = 0; - desc.Texture3D.WSize = data->m_depth_or_length; + desc.Texture3D.MipSlice = m_data.Image.Mip; + desc.Texture3D.FirstWSlice = m_data.Image.Z; + desc.Texture3D.WSize = m_data.Image.Depth; break; } case FShaderLayoutItemType::TextureCube: diff --git a/Coplt.Graphics.sln b/Coplt.Graphics.sln index fa52178..913aeee 100644 --- a/Coplt.Graphics.sln +++ b/Coplt.Graphics.sln @@ -18,6 +18,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Image", "Examples\Image\Ima EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwapChain", "Examples\SwapChain\SwapChain.csproj", "{B2648CB7-DF8C-43CE-A814-46FFBEF9F12A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mipmap", "Examples\Mipmap\Mipmap.csproj", "{BD5E37C4-DB40-430A-A136-46850F83F1AE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -56,6 +58,10 @@ Global {B2648CB7-DF8C-43CE-A814-46FFBEF9F12A}.Debug|Any CPU.Build.0 = Debug|Any CPU {B2648CB7-DF8C-43CE-A814-46FFBEF9F12A}.Release|Any CPU.ActiveCfg = Release|Any CPU {B2648CB7-DF8C-43CE-A814-46FFBEF9F12A}.Release|Any CPU.Build.0 = Release|Any CPU + {BD5E37C4-DB40-430A-A136-46850F83F1AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BD5E37C4-DB40-430A-A136-46850F83F1AE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BD5E37C4-DB40-430A-A136-46850F83F1AE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BD5E37C4-DB40-430A-A136-46850F83F1AE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {2ABF3CFF-8E44-4992-A633-491B2AAA5BFB} = {DBD91642-35A7-4287-9856-CA8E09605928} @@ -64,5 +70,6 @@ Global {C4C82737-D197-447B-B663-2DEFE3CE6679} = {DBD91642-35A7-4287-9856-CA8E09605928} {DB8B374C-102E-4825-B51E-F2A9805847C5} = {DBD91642-35A7-4287-9856-CA8E09605928} {B2648CB7-DF8C-43CE-A814-46FFBEF9F12A} = {DBD91642-35A7-4287-9856-CA8E09605928} + {BD5E37C4-DB40-430A-A136-46850F83F1AE} = {DBD91642-35A7-4287-9856-CA8E09605928} EndGlobalSection EndGlobal diff --git a/Examples/ExampleBase/ExampleBase.cs b/Examples/ExampleBase/ExampleBase.cs index 6b704b1..afca727 100644 --- a/Examples/ExampleBase/ExampleBase.cs +++ b/Examples/ExampleBase/ExampleBase.cs @@ -10,6 +10,9 @@ namespace Examples; public abstract class ExampleBase(IntPtr Handle, uint Width, uint Height) { + public uint Width = Width; + public uint Height = Height; + public float AspectRatio = (float)((double)Width / Height); public bool IsClosed = false; public GraphicsInstance Graphics = null!; public GpuDevice Device = null!; diff --git a/Examples/Mipmap/App.xaml b/Examples/Mipmap/App.xaml new file mode 100644 index 0000000..76b7b05 --- /dev/null +++ b/Examples/Mipmap/App.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/Examples/Mipmap/App.xaml.cs b/Examples/Mipmap/App.xaml.cs new file mode 100644 index 0000000..2b631ed --- /dev/null +++ b/Examples/Mipmap/App.xaml.cs @@ -0,0 +1,62 @@ +using System.Configuration; +using System.Data; +using System.IO; +using System.Text; +using System.Windows; +using Serilog; +using Serilog.Exceptions; + +namespace Examples; + +/// +/// Interaction logic for App.xaml +/// +public partial class App : Application +{ + protected override void OnStartup(StartupEventArgs e) + { + Console.OutputEncoding = Encoding.UTF8; + + base.OnStartup(e); + + InitLogger(); + } + + private static void InitLogger() + { + if (File.Exists("./logs/latest.log")) + { + try + { + var time = File.GetCreationTime("./logs/latest.log"); + var time_name = $"{time:yyyy-MM-dd}"; + var max_count = Directory.GetFiles("./logs/") + .Where(static n => Path.GetExtension(n) == ".log") + .Select(static n => Path.GetFileName(n)) + .Where(n => n.StartsWith(time_name)) + .Select(n => n.Substring(time_name.Length)) + .Select(static n => (n, i: n.IndexOf('.'))) + .Where(static a => a.i > 1) + .Select(static a => (s: uint.TryParse(a.n.Substring(1, a.i - 1), out var n), n)) + .Where(static a => a.s) + .OrderByDescending(static a => a.n) + .Select(static a => a.n) + .FirstOrDefault(); + var count = max_count + 1; + File.Move("./logs/latest.log", $"./logs/{time_name}_{count}.log"); + } + catch (Exception e) + { + Log.Error(e, ""); + } + } + Log.Logger = new LoggerConfiguration() + .Enrich.WithThreadId() + .Enrich.WithThreadName() + .Enrich.WithExceptionDetails() + .WriteTo.Console() + .WriteTo.Debug() + .WriteTo.Async(c => c.File("./logs/latest.log")) + .CreateLogger(); + } +} diff --git a/Examples/Mipmap/AssemblyInfo.cs b/Examples/Mipmap/AssemblyInfo.cs new file mode 100644 index 0000000..b9d746b --- /dev/null +++ b/Examples/Mipmap/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/Examples/Mipmap/Example.cs b/Examples/Mipmap/Example.cs new file mode 100644 index 0000000..da3d792 --- /dev/null +++ b/Examples/Mipmap/Example.cs @@ -0,0 +1,265 @@ +using System.Runtime.InteropServices; +using Coplt.Graphics; +using Coplt.Graphics.Core; +using Coplt.Graphics.States; +using Coplt.Mathematics; +using static Coplt.Mathematics.math; +using static Coplt.Mathematics.ctor; + +namespace Examples; + +public class Example(IntPtr Handle, uint Width, uint Height) : ExampleBase(Handle, Width, Height) +{ + private MipmapPass Mipmap = null!; + private DisplayPass Display = null!; + private GpuImage Image = null!; + + public override string Name => "Mipmap"; + protected override async Task LoadResources(GpuRecord cmd) + { + Mipmap = new(this); + Display = new(this); + await Mipmap.LoadResources(cmd); + await Display.LoadResources(cmd); + } + + protected override void Render(GpuRecord cmd, Time time) + { + Display.Render(cmd, time); + } + + private class MipmapPass(Example Example) + { + private ShaderLayout ShaderLayout = null!; + private ShaderBindGroupLayout BindGroupLayout = null!; + private ShaderBindingLayout BindingLayout = null!; + private Shader Shader = null!; + private ComputeShaderPipeline Pipeline = null!; + private ShaderBindGroup BindGroup = null!; + private ShaderBinding Binding = null!; + + public async Task LoadResources(GpuRecord cmd) + { + var modules = await Example.LoadShaderModules("Mipmap", [ShaderStage.Compute]); + ShaderLayout = Example.Device.CreateShaderLayout( + [ + new() + { + Id = 0, + Slot = 0, + Stage = ShaderStage.Compute, + View = ShaderLayoutItemView.Constants, + Count = 4, + Type = ShaderLayoutItemType.ConstantBuffer, + }, + new() + { + Id = 1, + Slot = 0, + Count = uint.MaxValue, + Stage = ShaderStage.Compute, + View = ShaderLayoutItemView.Uav, + Type = ShaderLayoutItemType.Texture2D, + UavAccess = ResourceAccess.ReadWrite, + } + ] + ); + BindingLayout = Example.Device.CreateBindingLayout( + ShaderLayout, [ + BindGroupLayout = Example.Device.CreateBindGroupLayout( + [ + new() + { + Id = 0, + Stages = ShaderStageFlags.Compute, + View = ShaderLayoutItemView.Constants, + Count = 4, + Type = ShaderLayoutItemType.ConstantBuffer, + }, + new() + { + Id = 1, + Count = uint.MaxValue, + Stages = ShaderStageFlags.Compute, + View = ShaderLayoutItemView.Uav, + Type = ShaderLayoutItemType.Texture2D, + } + ], + Usage: BindGroupUsage.Dynamic + ) + ], Name: "Gen Mipmap" + ); + Shader = Example.Device.CreateShader(modules, ShaderLayout); + Pipeline = Example.Device.CreateComputeShaderPipeline( + Shader, BindingLayout, Name: "Gen Mipmap" + ); + BindGroup = Example.Isolate.CreateBindGroup(BindGroupLayout, []); + Binding = Example.Isolate.CreateBinding(BindingLayout, [new(0, BindGroup)]); + + using var image_data = await LoadImage("./pattern.png"); + var upload_memory = cmd.AllocImageUploadMemory2D(4, (uint)image_data.Width, (uint)image_data.Height); + for (var row = 0u; row < upload_memory.RowCount; row++) + { + var row_span = image_data.Frames[0].PixelBuffer.DangerousGetRowSpan((int)row); + MemoryMarshal.AsBytes(row_span).CopyTo(upload_memory[row]); + } + var mip_levels = 1 + (uint)math.log2((double)math.max(image_data.Width, image_data.Height)); + Example.Image = Example.Isolate.CreateImage( + new() + { + Purpose = ResourcePurpose.ShaderResource | ResourcePurpose.UnorderedAccess, + Format = GraphicsFormat.R8G8B8A8_UNorm, + Width = (uint)image_data.Width, + Height = (uint)image_data.Height, + MipLevels = mip_levels, + }, + Name: "Test Image" + ); + cmd.Upload(Example.Image, upload_memory); + + GenMipmaps(cmd, Example.Image); + } + + private void GenMipmaps(GpuRecord cmd, GpuImage image) + { + // Not handling non-power-of-2 cases + using var compute = cmd.Compute(Name: "GenMipmaps"); + compute.SetBinding(Binding); + var size = new uint2(image.Width, image.Height); + var i = 0u; + var l = (image.MipLevels - 1u) % 4; + if (l == 0) l = 4; + for (; i < image.MipLevels; i += l, size >>= (int)l, l = 4) + { + var mip_levels = math.min(image.MipLevels - i - 1, 4); + var arr = math.min(l, image.MipLevels - i - 1); + if (arr == 0) break; + var half_size = size >> 1; + var groups = (half_size + 7) >> 3; + compute.SetDynArraySize(BindGroup, arr + 1); + compute.SetConstants(BindGroup, 0, [size.x, size.y, mip_levels, arr]); + Span set_items = + [ + new(1, 0, image.View2D((byte)(i + 0))), + new(1, 1, image.View2D((byte)(i + 1))), + new(1, 2, image.View2D((byte)(i + 2))), + new(1, 3, image.View2D((byte)(i + 3))), + new(1, 4, image.View2D((byte)(i + 4))), + ]; + compute.SetBindItem(BindGroup, set_items[..((int)arr + 1)]); + compute.Dispatch(Pipeline, groups.x, groups.y); + } + } + } + + private class DisplayPass(Example Example) + { + private ShaderLayout ShaderLayout = null!; + private ShaderBindGroupLayout BindGroupLayout = null!; + private ShaderBindingLayout BindingLayout = null!; + private Shader Shader = null!; + private GraphicsShaderPipeline Pipeline = null!; + private ShaderBindGroup BindGroup = null!; + private ShaderBinding Binding = null!; + + public async Task LoadResources(GpuRecord cmd) + { + var modules = await Example.LoadShaderModules("Display", [ShaderStage.Vertex, ShaderStage.Pixel]); + ShaderLayout = Example.Device.CreateShaderLayout( + [ + new() + { + Id = 0, + Slot = 0, + Stage = ShaderStage.Vertex, + View = ShaderLayoutItemView.Cbv, + Type = ShaderLayoutItemType.ConstantBuffer, + }, + new() + { + Id = 1, + Slot = 0, + Stage = ShaderStage.Pixel, + View = ShaderLayoutItemView.Srv, + Type = ShaderLayoutItemType.Texture2D, + }, + new() + { + Id = 2, + Slot = 0, + Stage = ShaderStage.Pixel, + View = ShaderLayoutItemView.Sampler, + Type = ShaderLayoutItemType.Sampler, + } + ] + ); + BindingLayout = Example.Device.CreateBindingLayout( + ShaderLayout, [ + BindGroupLayout = Example.Device.CreateBindGroupLayout( + [ + new() + { + Id = 0, + Stages = ShaderStageFlags.Vertex, + View = ShaderLayoutItemView.Cbv, + Type = ShaderLayoutItemType.ConstantBuffer, + }, + new() + { + Id = 1, + Stages = ShaderStageFlags.Pixel, + View = ShaderLayoutItemView.Srv, + Type = ShaderLayoutItemType.Texture2D, + }, + new() + { + Id = 2, + StaticSamplerIndex = 0, + Stages = ShaderStageFlags.Pixel, + View = ShaderLayoutItemView.StaticSampler, + Type = ShaderLayoutItemType.Sampler, + } + ], + StaticSamplers: [StaticSamplerInfo.LinearClamp], + Usage: BindGroupUsage.Dynamic + ) + ], Name: "Display" + ); + Shader = Example.Device.CreateShader(modules, ShaderLayout); + Pipeline = Example.Device.CreateGraphicsShaderPipeline( + Shader, new() + { + DsvFormat = GraphicsFormat.Unknown, + Topology = PrimitiveTopologyType.TriangleStrip, + }, BindingLayout, Name: "Display" + ); + BindGroup = Example.Isolate.CreateBindGroup(BindGroupLayout, []); + Binding = Example.Isolate.CreateBinding(BindingLayout, [new(0, BindGroup)]); + } + + private float Fov = 60; + private float Near = 0.01f; + private float Far = 1000; + + private struct Args + { + public float4x4 ViewProj; + } + + public void Render(GpuRecord cmd, Time time) + { + var t = (float)time.Total.TotalSeconds; + var (sin_t, cos_t) = sincos(float2(t, t * 0.1f)); + var pos = mad(float3(0, -cos_t.y, sin_t.x), float3(1, 0.1f, 1), float3(0, -0.25f, -2)); + var rot = quaternion.Euler(float3(radians(-45f + cos_t.y * 15f), 0, 0)); + var view = float4x4.TR(-pos, inverse(rot)); + var proj = float4x4.PerspectiveFov(radians(Fov), Example.AspectRatio, Far, Near); // invert depth (near <> far) + var view_proj = mul(proj, view); + var args = cmd.UploadConstants(new Args { ViewProj = view_proj }); + + using var render = cmd.Render([new(Example.Output, new Color(0.83f, 0.8f, 0.97f))], Name: "Display"); + render.SetBindItem(BindGroup, [new(0, args), new(1, Example.Image)]); + render.Draw(Pipeline, 4, Binding: Binding); + } + } +} diff --git a/Examples/Mipmap/MainWindow.xaml b/Examples/Mipmap/MainWindow.xaml new file mode 100644 index 0000000..8c3fd8b --- /dev/null +++ b/Examples/Mipmap/MainWindow.xaml @@ -0,0 +1,11 @@ + + \ No newline at end of file diff --git a/Examples/Mipmap/MainWindow.xaml.cs b/Examples/Mipmap/MainWindow.xaml.cs new file mode 100644 index 0000000..e2a3b0c --- /dev/null +++ b/Examples/Mipmap/MainWindow.xaml.cs @@ -0,0 +1,43 @@ +using System.Windows; +using System.Windows.Interop; +using Serilog; + +namespace Examples; + +/// +/// Interaction logic for MainWindow.xaml +/// +public partial class MainWindow : Window +{ + public readonly Example Example; + + #region Ctor + + public MainWindow() + { + InitializeComponent(); + Example = new(new WindowInteropHelper(this).EnsureHandle(), (uint)Width, (uint)Height); + Title = Example.Name; + Example.DoInitGraphics(); + } + + #endregion + + #region Misc + + protected override void OnClosed(EventArgs e) + { + Example.IsClosed = true; + } + + protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) + { + Log.Information("OnRenderSizeChanged {NewSize}", sizeInfo.NewSize); + Example.Width = (uint)sizeInfo.NewSize.Width; + Example.Height = (uint)sizeInfo.NewSize.Height; + Example.AspectRatio = (float)((double)Example.Width / Example.Height); + Example.Output.Resize(Example.Width, Example.Height); + } + + #endregion +} diff --git a/Examples/Mipmap/Mipmap.csproj b/Examples/Mipmap/Mipmap.csproj new file mode 100644 index 0000000..1ce1449 --- /dev/null +++ b/Examples/Mipmap/Mipmap.csproj @@ -0,0 +1,84 @@ + + + + WinExe + net9.0-windows + enable + enable + true + Examples + + + + 715 + D3D12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/Mipmap/Mipmap.md b/Examples/Mipmap/Mipmap.md new file mode 100644 index 0000000..1e8f2ed --- /dev/null +++ b/Examples/Mipmap/Mipmap.md @@ -0,0 +1,498 @@ +# Mipmap + +Demonstrate the use of compute shaders and dynamic groups + - Generate mipmaps using compute shaders, use wave shuffling and group sharing to process multiple mipmap levels in a single dispatch + +![](./Mipmap.png) + +## The Mipmap Shader + +
+ The Mipmap Shader + +```hlsl +cbuffer Args : register(b0) +{ + uint2 size; + uint mip_levels; // 0 .. 4 + uint arr; +} + +RWTexture2D images[] : register(u0); + +groupshared float tmp_r[8][8]; +groupshared float tmp_g[8][8]; +groupshared float tmp_b[8][8]; +groupshared float tmp_a[8][8]; + +void StoreColor(uint2 pos, float4 color) +{ + tmp_r[pos.y][pos.x] = color.r; + tmp_g[pos.y][pos.x] = color.g; + tmp_b[pos.y][pos.x] = color.b; + tmp_a[pos.y][pos.x] = color.a; +} + +float4 LoadColor(uint2 pos) +{ + return float4( + tmp_r[pos.y][pos.x], + tmp_g[pos.y][pos.x], + tmp_b[pos.y][pos.x], + tmp_a[pos.y][pos.x] + ); +} + +void X8_8(uint2 gtid, uint2 gid, inout uint index, inout float4 color); +void X4_4(uint2 gtid, uint2 gid, inout uint index, inout float4 color); +void X2_2(uint2 gid, inout uint index, inout float4 color); + +[shader("compute")] +[numthreads(8, 8, 1)] +void Compute(uint2 tid : SV_DispatchThreadID, uint2 gtid : SV_GroupThreadID, uint2 gid: SV_GroupID) +{ + uint index = 1; + + float4 color; // cur pixel color + switch (mip_levels) + { + case 4: + { + uint2 pos = 2 * tid; + RWTexture2D src = images[0]; // 16x16 + RWTexture2D dst = images[1]; // 8x8 + color = ( + src.Load(pos + uint2(0, 0)) + + src.Load(pos + uint2(0, 1)) + + src.Load(pos + uint2(1, 0)) + + src.Load(pos + uint2(1, 1)) + ) * 0.25; + dst[tid] = color; + index = 2; + X8_8(gtid, gid, index, color); + return; + } + case 3: + { + uint2 base = gid * 8; + uint2 pos = base + gtid; + RWTexture2D src = images[0]; // 8x8 + color = src.Load(pos); + X8_8(gtid, gid, index, color); + return; + } + case 2: + { + if (any(gtid >= 4)) return; + uint2 base = gid * 4; + uint2 pos = base + gtid; + RWTexture2D src = images[0]; // 4x4 + color = src.Load(pos); + X4_4(gtid, gid, index, color); + return; + } + case 1: + { + if (any(gtid >= 2)) return; + uint2 base = gid * 2; + uint2 pos = base + gtid; + RWTexture2D src = images[0]; // 2x2 + color = src.Load(pos); + X2_2(gid, index, color); + } + default: + return; + } +} + +bool QuadIsFirst() +{ + return WaveGetLaneIndex() % 4 == 0; +} + +void X8_8(uint2 gtid, uint2 gid, inout uint index, inout float4 color) +{ + if (index > arr) return; + RWTexture2D dst = images[index]; // 4x4 + // color from 8x8 + float4 c1 = QuadReadAcrossX(color); + float4 c2 = QuadReadAcrossY(color); + float4 c3 = QuadReadAcrossDiagonal(color); + float4 dst_color = (color + c1 + c2 + c3) * 0.25; + bool can_wave = WaveGetLaneCount() >= 64; + if (QuadIsFirst()) + { + uint2 base = gid * 4; + uint2 pos = gtid / 2; + dst[base + pos] = dst_color; + if (!can_wave) StoreColor(pos, dst_color); + } + if (!can_wave) + { + GroupMemoryBarrierWithGroupSync(); + if (any(gtid >= 4)) return; + color = LoadColor(gtid); + } + else + { + if (all(gtid < 4)) + { + color = WaveReadLaneAt(dst_color, WaveGetLaneIndex() * 4); + } + if (any(gtid >= 4)) return; + } + index++; + X4_4(gtid, gid, index, color); +} + +void X4_4(uint2 gtid, uint2 gid, inout uint index, inout float4 color) +{ + if (index > arr) return; + RWTexture2D dst = images[index]; // 2x2 + // color from 4x4 + float4 c1 = QuadReadAcrossX(color); + float4 c2 = QuadReadAcrossY(color); + float4 c3 = QuadReadAcrossDiagonal(color); + float4 dst_color = (color + c1 + c2 + c3) * 0.25; + bool can_wave = WaveGetLaneCount() >= 32; + if (QuadIsFirst()) + { + uint2 base = gid * 2; + uint2 pos = gtid / 2; + dst[base + pos] = dst_color; + if (!can_wave) StoreColor(pos, dst_color); + } + if (!can_wave) + { + GroupMemoryBarrierWithGroupSync(); + if (any(gtid >= 2)) return; + color = LoadColor(gtid); + } + else + { + if (all(gtid < 2)) + { + color = WaveReadLaneAt(dst_color, WaveGetLaneIndex() * 2); + } + if (any(gtid >= 2)) return; + } + index++; + X2_2(gid, index, color); +} + +void X2_2(uint2 gid, inout uint index, inout float4 color) +{ + if (index > arr) return; + RWTexture2D dst = images[index]; // 1x1 + // color from 2x2 + float4 c1 = QuadReadAcrossX(color); + float4 c2 = QuadReadAcrossY(color); + float4 c3 = QuadReadAcrossDiagonal(color); + float4 dst_color = (color + c1 + c2 + c3) * 0.25; + if (QuadIsFirst()) + { + dst[gid] = dst_color; + } +} + +``` + +
+ +## The Display Shader + +
+ The Mipmap Shader + +```hlsl +struct Attribute +{ + uint VertexID : SV_VertexID; +}; + +struct Varying +{ + float4 PositionCS : SV_Position; + float2 Uv : UV; +}; + +float4 GetQuadVertexPosition(out float2 uv, uint vid, float z = 1) +{ + float2 uv_ = float2((vid << 1) & 2, vid & 2); + uv = uv_ * 0.5f; + return float4(uv_ * float2(1, -1) + float2(-1, 1), z, 1); +} + +cbuffer Args : register(b0) +{ + float4x4 ViewProj; +} + +float4 ApplyVP(float3 positionWS) +{ + return mul(ViewProj, float4(positionWS, 1.0)); +} + +[shader("vertex")] +Varying Vertex(Attribute input) +{ + Varying output; + float3 pos = GetQuadVertexPosition(output.Uv, input.VertexID, 0).xyz; + output.PositionCS = ApplyVP(pos); + return output; +} + +Texture2D Texture : register(t0); +SamplerState Sampler : register(s0); + +[shader("pixel")] +float4 Pixel(Varying input) : SV_Target +{ + float3 color = Texture.Sample(Sampler, input.Uv); + return float4(color, 1); +} + +``` + +
+ +## 2 Ready For Gen Mipmap +```cs +var modules = await Example.LoadShaderModules("Mipmap", [ShaderStage.Compute]); +ShaderLayout = Example.Device.CreateShaderLayout( + [ + new() + { + Id = 0, + Slot = 0, + Stage = ShaderStage.Compute, + View = ShaderLayoutItemView.Constants, + // 4 x 32bits values + Count = 4, + Type = ShaderLayoutItemType.ConstantBuffer, + }, + new() + { + Id = 1, + Slot = 0, + // Dynamic length arrays + Count = uint.MaxValue, + Stage = ShaderStage.Compute, + View = ShaderLayoutItemView.Uav, + Type = ShaderLayoutItemType.Texture2D, + UavAccess = ResourceAccess.ReadWrite, + } + ] +); +BindingLayout = Example.Device.CreateBindingLayout( + ShaderLayout, [ + BindGroupLayout = Example.Device.CreateBindGroupLayout( + [ + new() + { + Id = 0, + Stages = ShaderStageFlags.Compute, + View = ShaderLayoutItemView.Constants, + Count = 4, + Type = ShaderLayoutItemType.ConstantBuffer, + }, + new() + { + Id = 1, + // Only dynamic groups support dynamic length arrays within the group + // The actual length will be specified each time it is drawn. + Count = uint.MaxValue, + Stages = ShaderStageFlags.Compute, + View = ShaderLayoutItemView.Uav, + Type = ShaderLayoutItemType.Texture2D, + } + ], + Usage: BindGroupUsage.Dynamic + ) + ], Name: "Gen Mipmap" +); +Shader = Example.Device.CreateShader(modules, ShaderLayout); +// Create the compute pipeline +Pipeline = Example.Device.CreateComputeShaderPipeline( + Shader, BindingLayout, Name: "Gen Mipmap" +); +// Dynamic groups cannot be bound when created +BindGroup = Example.Isolate.CreateBindGroup(BindGroupLayout, []); +Binding = Example.Isolate.CreateBinding(BindingLayout, [new(0, BindGroup)]); + +// Upload the image +using var image_data = await LoadImage("./pattern.png"); +var upload_memory = cmd.AllocImageUploadMemory2D(4, (uint)image_data.Width, (uint)image_data.Height); +for (var row = 0u; row < upload_memory.RowCount; row++) +{ + var row_span = image_data.Frames[0].PixelBuffer.DangerousGetRowSpan((int)row); + MemoryMarshal.AsBytes(row_span).CopyTo(upload_memory[row]); +} +var mip_levels = 1 + (uint)math.log2((double)math.max(image_data.Width, image_data.Height)); +Example.Image = Example.Isolate.CreateImage( + new() + { + // Need uav to gen mipmap + Purpose = ResourcePurpose.ShaderResource | ResourcePurpose.UnorderedAccess, + Format = GraphicsFormat.R8G8B8A8_UNorm, + Width = (uint)image_data.Width, + Height = (uint)image_data.Height, + MipLevels = mip_levels, + }, + Name: "Test Image" +); +cmd.Upload(Example.Image, upload_memory); +``` + +## 3 Gen Mipmap + +```cs +using var compute = cmd.Compute(Name: "GenMipmaps"); +compute.SetBinding(Binding); +var size = new uint2(image.Width, image.Height); +var i = 0u; +var l = (image.MipLevels - 1u) % 4; +if (l == 0) l = 4; +for (; i < image.MipLevels; i += l, size >>= (int)l, l = 4) +{ + var mip_levels = math.min(image.MipLevels - i - 1, 4); + var arr = math.min(l, image.MipLevels - i - 1); + if (arr == 0) break; + var half_size = size >> 1; + var groups = (half_size + 7) >> 3; + // Setting dynamic array size + compute.SetDynArraySize(BindGroup, arr + 1); + // Set the root constant value + compute.SetConstants(BindGroup, 0, [size.x, size.y, mip_levels, arr]); + Span set_items = + [ + new(1, 0, image.View2D(Mip: (byte)(i + 0))), + new(1, 1, image.View2D(Mip: (byte)(i + 1))), + new(1, 2, image.View2D(Mip: (byte)(i + 2))), + new(1, 3, image.View2D(Mip: (byte)(i + 3))), + new(1, 4, image.View2D(Mip: (byte)(i + 4))), + ]; + compute.SetBindItem(BindGroup, set_items[..((int)arr + 1)]); + compute.Dispatch(Pipeline, groups.x, groups.y); +} +``` + +## 4 Ready For Gen Display + +```cs +var modules = await Example.LoadShaderModules("Display", [ShaderStage.Vertex, ShaderStage.Pixel]); +ShaderLayout = Example.Device.CreateShaderLayout( + [ + new() + { + Id = 0, + Slot = 0, + Stage = ShaderStage.Vertex, + View = ShaderLayoutItemView.Cbv, + Type = ShaderLayoutItemType.ConstantBuffer, + }, + new() + { + Id = 1, + Slot = 0, + Stage = ShaderStage.Pixel, + View = ShaderLayoutItemView.Srv, + Type = ShaderLayoutItemType.Texture2D, + }, + new() + { + Id = 2, + Slot = 0, + Stage = ShaderStage.Pixel, + View = ShaderLayoutItemView.Sampler, + Type = ShaderLayoutItemType.Sampler, + } + ] +); +BindingLayout = Example.Device.CreateBindingLayout( + ShaderLayout, [ + BindGroupLayout = Example.Device.CreateBindGroupLayout( + [ + new() + { + Id = 0, + Stages = ShaderStageFlags.Vertex, + View = ShaderLayoutItemView.Cbv, + Type = ShaderLayoutItemType.ConstantBuffer, + }, + new() + { + Id = 1, + Stages = ShaderStageFlags.Pixel, + View = ShaderLayoutItemView.Srv, + Type = ShaderLayoutItemType.Texture2D, + }, + new() + { + Id = 2, + StaticSamplerIndex = 0, + Stages = ShaderStageFlags.Pixel, + // In dx backend, group layout can use static samplers even if shader layout is not static sampler + View = ShaderLayoutItemView.StaticSampler, + Type = ShaderLayoutItemType.Sampler, + } + ], + StaticSamplers: [StaticSamplerInfo.LinearClamp], + Usage: BindGroupUsage.Dynamic + ) + ], Name: "Display" +); +Shader = Example.Device.CreateShader(modules, ShaderLayout); +Pipeline = Example.Device.CreateGraphicsShaderPipeline( + Shader, new() + { + DsvFormat = GraphicsFormat.Unknown, + Topology = PrimitiveTopologyType.TriangleStrip, + }, BindingLayout, Name: "Display" +); +BindGroup = Example.Isolate.CreateBindGroup(BindGroupLayout, []); +Binding = Example.Isolate.CreateBinding(BindingLayout, [new(0, BindGroup)]); +``` + +## 5 Display + +```cs +// Coplt.Mathematics is used here, but it is not necessary, you can use other math libraries +using Coplt.Mathematics; +using static Coplt.Mathematics.math; +using static Coplt.Mathematics.ctor; + +float Fov = 60; +float Near = 0.01f; +float Far = 1000; + +struct Args +{ + public float4x4 ViewProj; +} + +void Render(GpuRecord cmd, Time time) +{ + // Using quaternions to create a view matrix is better than look at + var t = (float)time.Total.TotalSeconds; + var (sin_t, cos_t) = sincos(float2(t, t * 0.1f)); + + var pos = mad(float3(0, -cos_t.y, sin_t.x), float3(1, 0.1f, 1), float3(0, -0.25f, -2)); + var rot = quaternion.Euler(float3(radians(-45f + cos_t.y * 15f), 0, 0)); + var view = float4x4.TR(-pos, inverse(rot)); + + var proj = float4x4.PerspectiveFov(radians(Fov), Example.AspectRatio, near: Far, far: Near); // invert depth (near <> far) + // I don't know why, the arcball in the render doc is reversed, + // inverting depth helps with debugging in render doc, + // and can also improve depth accuracy, although depth is not used here + + var view_proj = mul(proj, view); + + // Allocate a block of memory in the upload buffer for dynamically passed constant parameters + var args = cmd.UploadConstants(new Args { ViewProj = view_proj }); + + using var render = cmd.Render([new(Example.Output, new Color(0.83f, 0.8f, 0.97f))], Name: "Display"); + render.SetBindItem(BindGroup, [new(0, args), new(1, Example.Image)]); + render.Draw(Pipeline, 4, Binding: Binding); +} +``` diff --git a/Examples/Mipmap/Mipmap.png b/Examples/Mipmap/Mipmap.png new file mode 100644 index 0000000..e609502 Binary files /dev/null and b/Examples/Mipmap/Mipmap.png differ diff --git a/Examples/Mipmap/Properties/launchSettings.json b/Examples/Mipmap/Properties/launchSettings.json new file mode 100644 index 0000000..673c307 --- /dev/null +++ b/Examples/Mipmap/Properties/launchSettings.json @@ -0,0 +1,9 @@ +{ + "profiles": { + "Launch": { + "commandName": "Project", + "nativeDebugging": true, + "commandLineArgs": "-D" + } + } +} \ No newline at end of file diff --git a/Examples/Mipmap/Shaders/Display.hlsl b/Examples/Mipmap/Shaders/Display.hlsl new file mode 100644 index 0000000..a0d8b4d --- /dev/null +++ b/Examples/Mipmap/Shaders/Display.hlsl @@ -0,0 +1,46 @@ +struct Attribute +{ + uint VertexID : SV_VertexID; +}; + +struct Varying +{ + float4 PositionCS : SV_Position; + float2 Uv : UV; +}; + +float4 GetQuadVertexPosition(out float2 uv, uint vid, float z = 1) +{ + float2 uv_ = float2((vid << 1) & 2, vid & 2); + uv = uv_ * 0.5f; + return float4(uv_ * float2(1, -1) + float2(-1, 1), z, 1); +} + +cbuffer Args : register(b0) +{ + float4x4 ViewProj; +} + +float4 ApplyVP(float3 positionWS) +{ + return mul(ViewProj, float4(positionWS, 1.0)); +} + +[shader("vertex")] +Varying Vertex(Attribute input) +{ + Varying output; + float3 pos = GetQuadVertexPosition(output.Uv, input.VertexID, 0).xyz; + output.PositionCS = ApplyVP(pos); + return output; +} + +Texture2D Texture : register(t0); +SamplerState Sampler : register(s0); + +[shader("pixel")] +float4 Pixel(Varying input) : SV_Target +{ + float3 color = Texture.Sample(Sampler, input.Uv); + return float4(color, 1); +} diff --git a/Examples/Mipmap/Shaders/Mipmap.hlsl b/Examples/Mipmap/Shaders/Mipmap.hlsl new file mode 100644 index 0000000..e763179 --- /dev/null +++ b/Examples/Mipmap/Shaders/Mipmap.hlsl @@ -0,0 +1,183 @@ +cbuffer Args : register(b0) +{ + uint2 size; + uint mip_levels; // 0 .. 4 + uint arr; +} + +RWTexture2D images[] : register(u0); + +groupshared float tmp_r[8][8]; +groupshared float tmp_g[8][8]; +groupshared float tmp_b[8][8]; +groupshared float tmp_a[8][8]; + +void StoreColor(uint2 pos, float4 color) +{ + tmp_r[pos.y][pos.x] = color.r; + tmp_g[pos.y][pos.x] = color.g; + tmp_b[pos.y][pos.x] = color.b; + tmp_a[pos.y][pos.x] = color.a; +} + +float4 LoadColor(uint2 pos) +{ + return float4( + tmp_r[pos.y][pos.x], + tmp_g[pos.y][pos.x], + tmp_b[pos.y][pos.x], + tmp_a[pos.y][pos.x] + ); +} + +void X8_8(uint2 gtid, uint2 gid, inout uint index, inout float4 color); +void X4_4(uint2 gtid, uint2 gid, inout uint index, inout float4 color); +void X2_2(uint2 gid, inout uint index, inout float4 color); + +[shader("compute")] +[numthreads(8, 8, 1)] +void Compute(uint2 tid : SV_DispatchThreadID, uint2 gtid : SV_GroupThreadID, uint2 gid: SV_GroupID) +{ + uint index = 1; + + float4 color; // cur pixel color + switch (mip_levels) + { + case 4: + { + uint2 pos = 2 * tid; + RWTexture2D src = images[0]; // 16x16 + RWTexture2D dst = images[1]; // 8x8 + color = ( + src.Load(pos + uint2(0, 0)) + + src.Load(pos + uint2(0, 1)) + + src.Load(pos + uint2(1, 0)) + + src.Load(pos + uint2(1, 1)) + ) * 0.25; + dst[tid] = color; + index = 2; + X8_8(gtid, gid, index, color); + return; + } + case 3: + { + uint2 base = gid * 8; + uint2 pos = base + gtid; + RWTexture2D src = images[0]; // 8x8 + color = src.Load(pos); + X8_8(gtid, gid, index, color); + return; + } + case 2: + { + if (any(gtid >= 4)) return; + uint2 base = gid * 4; + uint2 pos = base + gtid; + RWTexture2D src = images[0]; // 4x4 + color = src.Load(pos); + X4_4(gtid, gid, index, color); + return; + } + case 1: + { + if (any(gtid >= 2)) return; + uint2 base = gid * 2; + uint2 pos = base + gtid; + RWTexture2D src = images[0]; // 2x2 + color = src.Load(pos); + X2_2(gid, index, color); + } + default: + return; + } +} + +bool QuadIsFirst() +{ + return WaveGetLaneIndex() % 4 == 0; +} + +void X8_8(uint2 gtid, uint2 gid, inout uint index, inout float4 color) +{ + if (index > arr) return; + RWTexture2D dst = images[index]; // 4x4 + // color from 8x8 + float4 c1 = QuadReadAcrossX(color); + float4 c2 = QuadReadAcrossY(color); + float4 c3 = QuadReadAcrossDiagonal(color); + float4 dst_color = (color + c1 + c2 + c3) * 0.25; + bool can_wave = WaveGetLaneCount() >= 64; + if (QuadIsFirst()) + { + uint2 base = gid * 4; + uint2 pos = gtid / 2; + dst[base + pos] = dst_color; + if (!can_wave) StoreColor(pos, dst_color); + } + if (!can_wave) + { + GroupMemoryBarrierWithGroupSync(); + if (any(gtid >= 4)) return; + color = LoadColor(gtid); + } + else + { + if (all(gtid < 4)) + { + color = WaveReadLaneAt(dst_color, WaveGetLaneIndex() * 4); + } + if (any(gtid >= 4)) return; + } + index++; + X4_4(gtid, gid, index, color); +} + +void X4_4(uint2 gtid, uint2 gid, inout uint index, inout float4 color) +{ + if (index > arr) return; + RWTexture2D dst = images[index]; // 2x2 + // color from 4x4 + float4 c1 = QuadReadAcrossX(color); + float4 c2 = QuadReadAcrossY(color); + float4 c3 = QuadReadAcrossDiagonal(color); + float4 dst_color = (color + c1 + c2 + c3) * 0.25; + bool can_wave = WaveGetLaneCount() >= 32; + if (QuadIsFirst()) + { + uint2 base = gid * 2; + uint2 pos = gtid / 2; + dst[base + pos] = dst_color; + if (!can_wave) StoreColor(pos, dst_color); + } + if (!can_wave) + { + GroupMemoryBarrierWithGroupSync(); + if (any(gtid >= 2)) return; + color = LoadColor(gtid); + } + else + { + if (all(gtid < 2)) + { + color = WaveReadLaneAt(dst_color, WaveGetLaneIndex() * 2); + } + if (any(gtid >= 2)) return; + } + index++; + X2_2(gid, index, color); +} + +void X2_2(uint2 gid, inout uint index, inout float4 color) +{ + if (index > arr) return; + RWTexture2D dst = images[index]; // 1x1 + // color from 2x2 + float4 c1 = QuadReadAcrossX(color); + float4 c2 = QuadReadAcrossY(color); + float4 c3 = QuadReadAcrossDiagonal(color); + float4 dst_color = (color + c1 + c2 + c3) * 0.25; + if (QuadIsFirst()) + { + dst[gid] = dst_color; + } +} diff --git a/README.md b/README.md index b67c046..b9b435c 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ Render Hardware Interface designed for c# |[](./Examples/HelloTriangleVertex/HelloTriangleVertex.md)|[Hello Triangle Vertex](./Examples/HelloTriangleVertex/HelloTriangleVertex.md)|Demonstrates the use of vertex buffers.| |[](./Examples/Colorful/Colorful.md)|[Colorful](./Examples/Colorful/Colorful.md)|Demonstrates the use of shader binding.| |[](./Examples/Image/Image.md)|[Image](./Examples/Image/Image.md)|Demonstrates the use of image load, upload and sampler.| +|[](./Examples/Mipmap/Mipmap.md)|[Mipmap](./Examples/Mipmap/Mipmap.md)|Demonstrate the use of compute shaders and dynamic groups.| # Build