mirror of
https://git.ryujinx.app/projects/Kenji-NX.git
synced 2026-09-20 09:41:14 +02:00
Implement buffer texture alignment
This commit is contained in:
@@ -32,6 +32,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
public readonly bool SupportsGeometryShader;
|
||||
public readonly bool SupportsGeometryShaderPassthrough;
|
||||
public readonly bool SupportsTransformFeedback;
|
||||
public readonly bool SupportsImageBufferPixelAlignment;
|
||||
public readonly bool SupportsImageLoadFormatted;
|
||||
public readonly bool SupportsLayerVertexTessellation;
|
||||
public readonly bool SupportsMismatchingViewFormat;
|
||||
@@ -43,6 +44,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
public readonly bool SupportsShaderBarrierDivergence;
|
||||
public readonly bool SupportsShaderFloat64;
|
||||
public readonly bool SupportsShaderNonUniformIndexing;
|
||||
public readonly bool SupportsTextureBufferPixelAlignment;
|
||||
public readonly bool SupportsTextureGatherOffsets;
|
||||
public readonly bool SupportsTextureShadowLod;
|
||||
public readonly bool SupportsVertexStoreAndAtomics;
|
||||
@@ -101,6 +103,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
bool supportsGeometryShader,
|
||||
bool supportsGeometryShaderPassthrough,
|
||||
bool supportsTransformFeedback,
|
||||
bool supportsImageBufferPixelAlignment,
|
||||
bool supportsImageLoadFormatted,
|
||||
bool supportsLayerVertexTessellation,
|
||||
bool supportsMismatchingViewFormat,
|
||||
@@ -112,6 +115,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
bool supportsShaderBarrierDivergence,
|
||||
bool supportsShaderFloat64,
|
||||
bool supportsShaderNonUniformIndexing,
|
||||
bool supportsTextureBufferPixelAlignment,
|
||||
bool supportsTextureGatherOffsets,
|
||||
bool supportsTextureShadowLod,
|
||||
bool supportsVertexStoreAndAtomics,
|
||||
@@ -164,6 +168,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
SupportsGeometryShader = supportsGeometryShader;
|
||||
SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough;
|
||||
SupportsTransformFeedback = supportsTransformFeedback;
|
||||
SupportsImageBufferPixelAlignment = supportsImageBufferPixelAlignment;
|
||||
SupportsImageLoadFormatted = supportsImageLoadFormatted;
|
||||
SupportsLayerVertexTessellation = supportsLayerVertexTessellation;
|
||||
SupportsMismatchingViewFormat = supportsMismatchingViewFormat;
|
||||
@@ -175,6 +180,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence;
|
||||
SupportsShaderFloat64 = supportsShaderFloat64;
|
||||
SupportsShaderNonUniformIndexing = supportsShaderNonUniformIndexing;
|
||||
SupportsTextureBufferPixelAlignment = supportsTextureBufferPixelAlignment;
|
||||
SupportsTextureGatherOffsets = supportsTextureGatherOffsets;
|
||||
SupportsTextureShadowLod = supportsTextureShadowLod;
|
||||
SupportsVertexStoreAndAtomics = supportsVertexStoreAndAtomics;
|
||||
|
||||
@@ -4,6 +4,7 @@ using Ryujinx.Graphics.Gpu.Engine.Types;
|
||||
using Ryujinx.Graphics.Gpu.Memory;
|
||||
using Ryujinx.Graphics.Gpu.Shader;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using Ryujinx.Memory.Range;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
@@ -67,6 +68,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
private int _lastFragmentTotal;
|
||||
|
||||
private readonly int _bufferTextureAlignment;
|
||||
private readonly int _bufferImageAlignment;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of the texture bindings manager.
|
||||
/// </summary>
|
||||
@@ -108,6 +112,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
}
|
||||
|
||||
_textureCounts = [];
|
||||
|
||||
int alignment = context.Capabilities.TextureBufferOffsetAlignment;
|
||||
_bufferTextureAlignment = context.Capabilities.SupportsTextureBufferPixelAlignment ? 0 : alignment;
|
||||
_bufferImageAlignment = context.Capabilities.SupportsImageBufferPixelAlignment ? 0 : alignment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -521,10 +529,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
if (hostTexture != null && texture.Target == Target.TextureBuffer)
|
||||
{
|
||||
MultiRange range = GetAlignedBufferTextureRange(texture, index, stageIndex, false);
|
||||
|
||||
// Ensure that the buffer texture is using the correct buffer as storage.
|
||||
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
|
||||
// to ensure we're not using a old buffer that was already deleted.
|
||||
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, false);
|
||||
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, range, bindingInfo, false);
|
||||
|
||||
// Cache is not used for buffer texture, it must always rebind.
|
||||
state.CachedTexture = null;
|
||||
@@ -659,7 +669,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
|
||||
// to ensure we're not using a old buffer that was already deleted.
|
||||
|
||||
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, true);
|
||||
MultiRange range = GetAlignedBufferTextureRange(texture, index, stageIndex, true);
|
||||
|
||||
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, range, bindingInfo, true);
|
||||
|
||||
// Cache is not used for buffer texture, it must always rebind.
|
||||
state.CachedTexture = null;
|
||||
@@ -692,6 +704,61 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
return specStateMatches;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the aligned address of the texture according to the host requirements.
|
||||
/// </summary>
|
||||
/// <param name="texture">Texture to have its address aligned</param>
|
||||
/// <param name="index">Index of the texture in the shader</param>
|
||||
/// <param name="stageIndex">Index of the shader stage</param>
|
||||
/// <param name="isImage">True if the texture is bound as image, false for sampled textures</param>
|
||||
/// <returns>Aligned address and size of the buffer texture</returns>
|
||||
private MultiRange GetAlignedBufferTextureRange(Texture texture, int index, int stageIndex, bool isImage)
|
||||
{
|
||||
MultiRange range = texture.Range;
|
||||
int alignment = isImage ? _bufferImageAlignment : _bufferTextureAlignment;
|
||||
|
||||
if (alignment != 0)
|
||||
{
|
||||
MemoryRange firstRange = range.GetSubRange(0);
|
||||
ulong misalign = firstRange.Address & ((ulong)alignment - 1);
|
||||
int offset = misalign != 0 ? (int)misalign / texture.Info.FormatInfo.BytesPerPixel : 0;
|
||||
|
||||
if (misalign != 0)
|
||||
{
|
||||
firstRange = new MemoryRange(firstRange.Address - misalign, firstRange.Size + misalign);
|
||||
|
||||
if (range.Count > 1)
|
||||
{
|
||||
MemoryRange[] ranges = new MemoryRange[range.Count];
|
||||
|
||||
ranges[0] = firstRange;
|
||||
|
||||
for (int i = 1; i < range.Count; i++)
|
||||
{
|
||||
ranges[i] = range.GetSubRange(i);
|
||||
}
|
||||
|
||||
range = new MultiRange(ranges);
|
||||
}
|
||||
else
|
||||
{
|
||||
range = new MultiRange(firstRange.Address, firstRange.Size);
|
||||
}
|
||||
}
|
||||
|
||||
if (isImage)
|
||||
{
|
||||
_context.SupportBufferUpdater.UpdateBufferImageOffset(stageIndex, index, offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
_context.SupportBufferUpdater.UpdateBufferTextureOffset(stageIndex, index, offset);
|
||||
}
|
||||
}
|
||||
|
||||
return range;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the texture descriptor for a given texture handle.
|
||||
/// </summary>
|
||||
|
||||
@@ -129,6 +129,38 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the offset used for accessing buffer textures that are not aligned to the host requirements.
|
||||
/// </summary>
|
||||
/// <param name="stageIndex">Index of the shader stage where the texture is used</param>
|
||||
/// <param name="bindingIndex">Index of the texture binding in the shader</param>
|
||||
/// <param name="offset">Offset for the misaligned part of the address</param>
|
||||
public void UpdateBufferTextureOffset(int stageIndex, int bindingIndex, int offset)
|
||||
{
|
||||
if (_data.BufferTextureOffset[stageIndex][bindingIndex].X != offset)
|
||||
{
|
||||
_data.BufferTextureOffset[stageIndex][bindingIndex].X = offset;
|
||||
int index = stageIndex * SupportBuffer.TextureCount + bindingIndex;
|
||||
MarkDirty(SupportBuffer.BufferTextureOffsetOffset + index * sizeof(int) * 4, sizeof(int));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the offset used for accessing buffer images that are not aligned to the host requirements.
|
||||
/// </summary>
|
||||
/// <param name="stageIndex">Index of the shader stage where the image is used</param>
|
||||
/// <param name="bindingIndex">Index of the image binding in the shader</param>
|
||||
/// <param name="offset">Offset for the misaligned part of the address</param>
|
||||
public void UpdateBufferImageOffset(int stageIndex, int bindingIndex, int offset)
|
||||
{
|
||||
if (_data.BufferTextureOffset[stageIndex][bindingIndex].Y != offset)
|
||||
{
|
||||
_data.BufferTextureOffset[stageIndex][bindingIndex].Y = offset;
|
||||
int index = stageIndex * SupportBuffer.TextureCount + bindingIndex;
|
||||
MarkDirty(SupportBuffer.BufferTextureOffsetOffset + index * sizeof(int) * 4 + sizeof(int), sizeof(int));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets whether the format of a given render target is a BGRA format.
|
||||
/// </summary>
|
||||
|
||||
@@ -207,6 +207,10 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
|
||||
public bool QueryHostSupportsBgraFormat() => _context.Capabilities.SupportsBgraFormat;
|
||||
|
||||
public bool QueryHostSupportsBufferImagePixelAlignment() => _context.Capabilities.SupportsImageBufferPixelAlignment;
|
||||
|
||||
public bool QueryHostSupportsBufferTexturePixelAlignment() => _context.Capabilities.SupportsTextureBufferPixelAlignment;
|
||||
|
||||
public bool QueryHostSupportsFragmentShaderInterlock() => _context.Capabilities.SupportsFragmentShaderInterlock;
|
||||
|
||||
public bool QueryHostSupportsFragmentShaderOrderingIntel() => _context.Capabilities.SupportsFragmentShaderOrderingIntel;
|
||||
|
||||
@@ -174,6 +174,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
supportsGeometryShader: true,
|
||||
supportsGeometryShaderPassthrough: HwCapabilities.SupportsGeometryShaderPassthrough,
|
||||
supportsTransformFeedback: true,
|
||||
supportsImageBufferPixelAlignment: false,
|
||||
supportsImageLoadFormatted: HwCapabilities.SupportsImageLoadFormatted,
|
||||
supportsLayerVertexTessellation: HwCapabilities.SupportsShaderViewportLayerArray,
|
||||
supportsMismatchingViewFormat: HwCapabilities.SupportsMismatchingViewFormat,
|
||||
@@ -185,6 +186,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
supportsShaderBarrierDivergence: !(intelWindows || intelUnix),
|
||||
supportsShaderFloat64: true,
|
||||
supportsShaderNonUniformIndexing: false,
|
||||
supportsTextureBufferPixelAlignment: false,
|
||||
supportsTextureGatherOffsets: true,
|
||||
supportsTextureShadowLod: HwCapabilities.SupportsTextureShadowLod,
|
||||
supportsVertexStoreAndAtomics: true,
|
||||
|
||||
@@ -234,6 +234,24 @@ namespace Ryujinx.Graphics.Shader
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries host support for buffer image access with the buffer offset aligned to a single pixel rather than a fixed alignment.
|
||||
/// </summary>
|
||||
/// <returns>True if the host supports buffer image access with pixel alignment, false otherwise</returns>
|
||||
bool QueryHostSupportsBufferImagePixelAlignment()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries host support for buffer texture access with the buffer offset aligned to a single pixel rather than a fixed alignment.
|
||||
/// </summary>
|
||||
/// <returns>True if the host supports buffer texture access with pixel alignment, false otherwise</returns>
|
||||
bool QueryHostSupportsBufferTexturePixelAlignment()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries host support for fragment shader ordering critical sections on the shader code.
|
||||
/// </summary>
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace Ryujinx.Graphics.Shader
|
||||
RenderScale,
|
||||
TfeOffset,
|
||||
TfeVertexCount,
|
||||
BufferTextureOffset,
|
||||
}
|
||||
|
||||
public struct SupportBuffer
|
||||
@@ -42,10 +43,13 @@ namespace Ryujinx.Graphics.Shader
|
||||
public static readonly int ComputeRenderScaleOffset;
|
||||
public static readonly int TfeOffsetOffset;
|
||||
public static readonly int TfeVertexCountOffset;
|
||||
public static readonly int BufferTextureOffsetOffset;
|
||||
|
||||
public const int FragmentIsBgraCount = 8;
|
||||
public const int TextureCount = 64;
|
||||
|
||||
// One for the render target, 64 for the textures, and 8 for the images.
|
||||
public const int RenderScaleMaxCount = 1 + 64 + 8;
|
||||
public const int RenderScaleMaxCount = 1 + TextureCount + 8;
|
||||
|
||||
private static int OffsetOf<T>(ref SupportBuffer storage, ref T target)
|
||||
{
|
||||
@@ -68,6 +72,7 @@ namespace Ryujinx.Graphics.Shader
|
||||
ComputeRenderScaleOffset = GraphicsRenderScaleOffset + FieldSize;
|
||||
TfeOffsetOffset = OffsetOf(ref instance, ref instance.TfeOffset);
|
||||
TfeVertexCountOffset = OffsetOf(ref instance, ref instance.TfeVertexCount);
|
||||
BufferTextureOffsetOffset = OffsetOf(ref instance, ref instance.BufferTextureOffset);
|
||||
}
|
||||
|
||||
internal static StructureType GetStructureType()
|
||||
@@ -80,7 +85,8 @@ namespace Ryujinx.Graphics.Shader
|
||||
new StructureField(AggregateType.S32, "frag_scale_count"),
|
||||
new StructureField(AggregateType.Array | AggregateType.FP32, "render_scale", RenderScaleMaxCount),
|
||||
new StructureField(AggregateType.Vector4 | AggregateType.S32, "tfe_offset"),
|
||||
new StructureField(AggregateType.S32, "tfe_vertex_count")
|
||||
new StructureField(AggregateType.S32, "tfe_vertex_count"),
|
||||
new StructureField(AggregateType.Array | AggregateType.Vector2 | AggregateType.S32, "buffer_texture_offset")
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -95,5 +101,7 @@ namespace Ryujinx.Graphics.Shader
|
||||
|
||||
public Vector4<int> TfeOffset;
|
||||
public Vector4<int> TfeVertexCount;
|
||||
|
||||
public Array5<Array64<Vector4<int>>> BufferTextureOffset;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using Ryujinx.Graphics.Shader.Translation.Optimizations;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||
@@ -27,7 +28,19 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
||||
|
||||
if (texOp.Type == SamplerType.TextureBuffer && !context.GpuAccessor.QueryHostSupportsSnormBufferTextureFormat())
|
||||
{
|
||||
node = InsertSnormNormalization(node, context.ResourceManager, context.GpuAccessor);
|
||||
if (!context.GpuAccessor.QueryHostSupportsSnormBufferTextureFormat())
|
||||
{
|
||||
node = InsertSnormNormalization(node, context.ResourceManager, context.GpuAccessor);
|
||||
}
|
||||
|
||||
if (!context.GpuAccessor.QueryHostSupportsBufferTexturePixelAlignment())
|
||||
{
|
||||
node = InsertCoordOffset(node, context.ResourceManager, context.Stage, isImage: false);
|
||||
}
|
||||
}
|
||||
else if (!context.GpuAccessor.QueryHostSupportsBufferTexturePixelAlignment() && texOp.Inst.IsImage())
|
||||
{
|
||||
node = InsertCoordOffset(node, context.ResourceManager, context.Stage, isImage: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -278,6 +291,87 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
||||
return node;
|
||||
}
|
||||
|
||||
private static LinkedListNode<INode> InsertCoordOffset(LinkedListNode<INode> node, ResourceManager resourceManager, ShaderStage stage, bool isImage)
|
||||
{
|
||||
// Some GPUs have fixed alignment requirements for buffer textures.
|
||||
// For those cases, we bind the aligned buffer offset, and apply the remaining offset on the shader.
|
||||
|
||||
TextureOperation texOp = (TextureOperation)node.Value;
|
||||
|
||||
if ((texOp.Type & SamplerType.Mask) != SamplerType.TextureBuffer)
|
||||
{
|
||||
return node;
|
||||
}
|
||||
|
||||
Operand[] sources = new Operand[texOp.SourcesCount];
|
||||
|
||||
for (int i = 0; i < texOp.SourcesCount; i++)
|
||||
{
|
||||
sources[i] = texOp.GetSource(i);
|
||||
}
|
||||
|
||||
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
||||
bool isIndexed = resourceManager.IsArrayOfTexturesOrImages(texOp.Binding, isImage);
|
||||
|
||||
int coordsIndex = isBindless || isIndexed ? 1 : 0;
|
||||
|
||||
Operand[] dests = new Operand[texOp.DestsCount];
|
||||
|
||||
for (int i = 0; i < texOp.DestsCount; i++)
|
||||
{
|
||||
dests[i] = texOp.GetDest(i);
|
||||
}
|
||||
|
||||
LinkedListNode<INode> oldNode = node;
|
||||
|
||||
Operand source = sources[coordsIndex];
|
||||
Operand offset = Local();
|
||||
Operand coordPlusOffset = Local();
|
||||
|
||||
int stageIndex = stage switch
|
||||
{
|
||||
ShaderStage.TessellationControl => 1,
|
||||
ShaderStage.TessellationEvaluation => 2,
|
||||
ShaderStage.Geometry => 3,
|
||||
ShaderStage.Fragment => 4,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
int bindingIndex = isImage
|
||||
? resourceManager.FindImageDescriptorIndex(texOp.Binding)
|
||||
: resourceManager.FindTextureDescriptorIndex(texOp.Binding);
|
||||
|
||||
node.List.AddBefore(node, new Operation(
|
||||
Instruction.Load,
|
||||
StorageKind.ConstantBuffer,
|
||||
offset,
|
||||
Const(SupportBuffer.Binding),
|
||||
Const((int)SupportBufferField.BufferTextureOffset),
|
||||
Const(stageIndex * SupportBuffer.TextureCount + bindingIndex),
|
||||
Const(isImage ? 1 : 0)));
|
||||
|
||||
node.List.AddBefore(node, new Operation(Instruction.Add, coordPlusOffset, source, offset));
|
||||
|
||||
sources[coordsIndex] = coordPlusOffset;
|
||||
|
||||
TextureOperation newTexOp = new(
|
||||
texOp.Inst,
|
||||
texOp.Type,
|
||||
texOp.Format,
|
||||
texOp.Flags,
|
||||
texOp.Set,
|
||||
texOp.Binding,
|
||||
texOp.Index,
|
||||
dests,
|
||||
sources);
|
||||
|
||||
node = node.List.AddBefore(node, newTexOp);
|
||||
|
||||
Utils.DeleteNode(oldNode, texOp);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private static LinkedListNode<INode> InsertConstOffsets(LinkedListNode<INode> node, ResourceManager resourceManager, IGpuAccessor gpuAccessor, ShaderStage stage)
|
||||
{
|
||||
// Non-constant texture offsets are not allowed (according to the spec),
|
||||
|
||||
@@ -784,6 +784,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
supportsGeometryShader: Capabilities.SupportsGeometryShader,
|
||||
supportsGeometryShaderPassthrough: Capabilities.SupportsGeometryShaderPassthrough,
|
||||
supportsTransformFeedback: Capabilities.SupportsTransformFeedback,
|
||||
supportsImageBufferPixelAlignment: false,
|
||||
supportsImageLoadFormatted: features2.Features.ShaderStorageImageReadWithoutFormat,
|
||||
supportsLayerVertexTessellation: featuresVk12.ShaderOutputLayer,
|
||||
supportsMismatchingViewFormat: true,
|
||||
@@ -797,6 +798,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
supportsShaderNonUniformIndexing:
|
||||
featuresVk12.ShaderSampledImageArrayNonUniformIndexing &&
|
||||
featuresVk12.ShaderStorageImageArrayNonUniformIndexing,
|
||||
supportsTextureBufferPixelAlignment: false,
|
||||
supportsTextureGatherOffsets: features2.Features.ShaderImageGatherExtended,
|
||||
supportsTextureShadowLod: false,
|
||||
supportsVertexStoreAndAtomics: features2.Features.VertexPipelineStoresAndAtomics,
|
||||
|
||||
Reference in New Issue
Block a user