diff --git a/src/SharpEmu.ShaderCompiler/Resources/BindingLayout.cs b/src/SharpEmu.ShaderCompiler/Resources/BindingLayout.cs index 9829b63..f482710 100644 --- a/src/SharpEmu.ShaderCompiler/Resources/BindingLayout.cs +++ b/src/SharpEmu.ShaderCompiler/Resources/BindingLayout.cs @@ -336,11 +336,14 @@ public sealed class BindingLayout : IEquatable public uint ShaderBaseDword { get; init; } = NoShaderBase; public uint MemoryOffsetDword { get; init; } public uint MemoryOffsetCount { get; init; } + public uint BufferWordCount { get; init; } public bool UsesDispatchThreadLimits { get; init; } public IReadOnlyList UserDataRegisters { get; init; } = []; public IReadOnlyList Descriptors { get; init; } = []; - public uint DispatchThreadLimitsDword => MemoryOffsetDword + (MemoryOffsetCount + 3) / 4; + public uint BufferWordDword => MemoryOffsetDword + (MemoryOffsetCount + 3) / 4; + + public uint DispatchThreadLimitsDword => BufferWordDword + BufferWordCount; public uint ShaderDataDwordCount => DispatchThreadLimitsDword + (UsesDispatchThreadLimits ? 3u : 0u); @@ -561,12 +564,14 @@ public sealed class BindingLayout : IEquatable bool usesFlattenedTable, bool usesShaderBase, uint pushDataStartDword = 0, - bool usesDispatchThreadLimits = false) + bool usesDispatchThreadLimits = false, + bool portableBuffers = false) { var shaderBaseDword = usesShaderBase ? (uint)userDataRegisters.Count : NoShaderBase; var memoryOffsetDword = (uint)userDataRegisters.Count + (usesShaderBase ? ShaderBaseDwordCount : 0); var memoryOffsetCount = (uint)info.Buffers.Count; - var shaderDataDwords = memoryOffsetDword + (memoryOffsetCount + 3) / 4 + (usesDispatchThreadLimits ? 3u : 0u); + var bufferWordCount = portableBuffers ? memoryOffsetCount * PortableBufferWord.DwordCount : 0; + var shaderDataDwords = memoryOffsetDword + (memoryOffsetCount + 3) / 4 + bufferWordCount + (usesDispatchThreadLimits ? 3u : 0u); var pushStart = PushData.StartFor(pushDataStartDword, shaderDataDwords); var descriptors = new List(); if (info.Buffers.Count != 0) @@ -643,6 +648,7 @@ public sealed class BindingLayout : IEquatable ShaderBaseDword = shaderBaseDword, MemoryOffsetDword = memoryOffsetDword, MemoryOffsetCount = memoryOffsetCount, + BufferWordCount = bufferWordCount, UsesDispatchThreadLimits = usesDispatchThreadLimits, UserDataRegisters = userDataRegisters, Descriptors = descriptors, @@ -656,6 +662,7 @@ public sealed class BindingLayout : IEquatable ShaderBaseDword == other.ShaderBaseDword && MemoryOffsetDword == other.MemoryOffsetDword && MemoryOffsetCount == other.MemoryOffsetCount && + BufferWordCount == other.BufferWordCount && UsesDispatchThreadLimits == other.UsesDispatchThreadLimits && UserDataRegisters.SequenceEqual(other.UserDataRegisters) && Descriptors.Count == other.Descriptors.Count && @@ -663,7 +670,7 @@ public sealed class BindingLayout : IEquatable public override bool Equals(object? obj) => Equals(obj as BindingLayout); - public override int GetHashCode() => HashCode.Combine(PushDataStartDword, MemoryOffsetDword, MemoryOffsetCount, Descriptors.Count, UsesDispatchThreadLimits); + public override int GetHashCode() => HashCode.Combine(PushDataStartDword, MemoryOffsetDword, MemoryOffsetCount, BufferWordCount, Descriptors.Count, UsesDispatchThreadLimits); } // Recomputes the layout an emitter was given from the same inputs and the same push @@ -685,7 +692,8 @@ public static class BindingLayoutValidator throw new ResourcePlanException("Only a compute shader can use dispatch thread limits."); } - var expected = BindingLayout.Allocate(info, userDataRegisters, usesGlobalDataShare, usesFlattenedTable, usesShaderBase, layout.AllocationCursor, layout.UsesDispatchThreadLimits); + var expected = BindingLayout.Allocate(info, userDataRegisters, usesGlobalDataShare, usesFlattenedTable, usesShaderBase, + layout.AllocationCursor, layout.UsesDispatchThreadLimits, portableBuffers: layout.BufferWordCount != 0); if (!expected.Equals(layout)) { throw new ResourcePlanException( @@ -696,6 +704,7 @@ public static class BindingLayoutValidator private static string Describe(BindingLayout layout) => $"push={layout.PushDataStartDword} base={layout.ShaderBaseDword} offsets={layout.MemoryOffsetDword}+{layout.MemoryOffsetCount} " + + $"words={layout.BufferWordCount} " + $"user=[{string.Join(",", layout.UserDataRegisters)}] " + string.Join(" ", layout.Descriptors.Select(binding => $"{binding.Kind}:{string.Join(",", binding.Resources)}")); } diff --git a/src/SharpEmu.ShaderCompiler/Resources/PortableBufferWord.cs b/src/SharpEmu.ShaderCompiler/Resources/PortableBufferWord.cs new file mode 100644 index 0000000..4cc7a9a --- /dev/null +++ b/src/SharpEmu.ShaderCompiler/Resources/PortableBufferWord.cs @@ -0,0 +1,22 @@ +// Copyright (C) 2026 SharpEmu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +namespace SharpEmu.ShaderCompiler.Resources; + +public static class PortableBufferWord +{ + public const int DwordCount = 2; + public const uint StrideMask = 0x3FFF; + + public static bool ReadsFormatAtRuntime(BufferResource buffer) => buffer.Formatted && !buffer.Written; + + public static (uint Stride, uint DescriptorWord3) Pack(ReadOnlySpan descriptor) + { + if (descriptor.Length != 4 || (descriptor[3] >> 30) != 0) + { + return default; + } + + return ((descriptor[1] >> 16) & StrideMask, descriptor[3]); + } +}