Allow texture arrays to use separate descriptor sets on Vulkan (#6870)

* Report base and extra sets from the backend

* Pass texture set index everywhere

* Key textures using set and binding (rather than just binding)

* Start using extra sets for array textures

* Shader cache version bump

* Separate new commands, some PR feedback

* Introduce new manual descriptor set reservation method that prevents it from being used by something else while owned by an array

* Move bind extra sets logic to new method

* Should only use separate array is MaximumExtraSets is not zero

* Format whitespace
This commit is contained in:
gdkchan 2024-05-26 13:30:19 -03:00 committed by GitHub
parent 4cc00bb4b1
commit 53d096e392
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
47 changed files with 996 additions and 262 deletions

View file

@ -51,6 +51,13 @@ namespace Ryujinx.Graphics.GAL
public readonly bool SupportsIndirectParameters; public readonly bool SupportsIndirectParameters;
public readonly bool SupportsDepthClipControl; public readonly bool SupportsDepthClipControl;
public readonly int UniformBufferSetIndex;
public readonly int StorageBufferSetIndex;
public readonly int TextureSetIndex;
public readonly int ImageSetIndex;
public readonly int ExtraSetBaseIndex;
public readonly int MaximumExtraSets;
public readonly uint MaximumUniformBuffersPerStage; public readonly uint MaximumUniformBuffersPerStage;
public readonly uint MaximumStorageBuffersPerStage; public readonly uint MaximumStorageBuffersPerStage;
public readonly uint MaximumTexturesPerStage; public readonly uint MaximumTexturesPerStage;
@ -109,6 +116,12 @@ namespace Ryujinx.Graphics.GAL
bool supportsViewportSwizzle, bool supportsViewportSwizzle,
bool supportsIndirectParameters, bool supportsIndirectParameters,
bool supportsDepthClipControl, bool supportsDepthClipControl,
int uniformBufferSetIndex,
int storageBufferSetIndex,
int textureSetIndex,
int imageSetIndex,
int extraSetBaseIndex,
int maximumExtraSets,
uint maximumUniformBuffersPerStage, uint maximumUniformBuffersPerStage,
uint maximumStorageBuffersPerStage, uint maximumStorageBuffersPerStage,
uint maximumTexturesPerStage, uint maximumTexturesPerStage,
@ -164,6 +177,12 @@ namespace Ryujinx.Graphics.GAL
SupportsViewportSwizzle = supportsViewportSwizzle; SupportsViewportSwizzle = supportsViewportSwizzle;
SupportsIndirectParameters = supportsIndirectParameters; SupportsIndirectParameters = supportsIndirectParameters;
SupportsDepthClipControl = supportsDepthClipControl; SupportsDepthClipControl = supportsDepthClipControl;
UniformBufferSetIndex = uniformBufferSetIndex;
StorageBufferSetIndex = storageBufferSetIndex;
TextureSetIndex = textureSetIndex;
ImageSetIndex = imageSetIndex;
ExtraSetBaseIndex = extraSetBaseIndex;
MaximumExtraSets = maximumExtraSets;
MaximumUniformBuffersPerStage = maximumUniformBuffersPerStage; MaximumUniformBuffersPerStage = maximumUniformBuffersPerStage;
MaximumStorageBuffersPerStage = maximumStorageBuffersPerStage; MaximumStorageBuffersPerStage = maximumStorageBuffersPerStage;
MaximumTexturesPerStage = maximumTexturesPerStage; MaximumTexturesPerStage = maximumTexturesPerStage;

View file

@ -60,6 +60,7 @@ namespace Ryujinx.Graphics.GAL
void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat); void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat);
void SetImageArray(ShaderStage stage, int binding, IImageArray array); void SetImageArray(ShaderStage stage, int binding, IImageArray array);
void SetImageArraySeparate(ShaderStage stage, int setIndex, IImageArray array);
void SetLineParameters(float width, bool smooth); void SetLineParameters(float width, bool smooth);
@ -91,6 +92,7 @@ namespace Ryujinx.Graphics.GAL
void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler); void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler);
void SetTextureArray(ShaderStage stage, int binding, ITextureArray array); void SetTextureArray(ShaderStage stage, int binding, ITextureArray array);
void SetTextureArraySeparate(ShaderStage stage, int setIndex, ITextureArray array);
void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers); void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers);
void SetUniformBuffers(ReadOnlySpan<BufferAssignment> buffers); void SetUniformBuffers(ReadOnlySpan<BufferAssignment> buffers);

View file

@ -124,6 +124,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
Register<SetUniformBuffersCommand>(CommandType.SetUniformBuffers); Register<SetUniformBuffersCommand>(CommandType.SetUniformBuffers);
Register<SetImageCommand>(CommandType.SetImage); Register<SetImageCommand>(CommandType.SetImage);
Register<SetImageArrayCommand>(CommandType.SetImageArray); Register<SetImageArrayCommand>(CommandType.SetImageArray);
Register<SetImageArraySeparateCommand>(CommandType.SetImageArraySeparate);
Register<SetIndexBufferCommand>(CommandType.SetIndexBuffer); Register<SetIndexBufferCommand>(CommandType.SetIndexBuffer);
Register<SetLineParametersCommand>(CommandType.SetLineParameters); Register<SetLineParametersCommand>(CommandType.SetLineParameters);
Register<SetLogicOpStateCommand>(CommandType.SetLogicOpState); Register<SetLogicOpStateCommand>(CommandType.SetLogicOpState);
@ -141,6 +142,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
Register<SetStencilTestCommand>(CommandType.SetStencilTest); Register<SetStencilTestCommand>(CommandType.SetStencilTest);
Register<SetTextureAndSamplerCommand>(CommandType.SetTextureAndSampler); Register<SetTextureAndSamplerCommand>(CommandType.SetTextureAndSampler);
Register<SetTextureArrayCommand>(CommandType.SetTextureArray); Register<SetTextureArrayCommand>(CommandType.SetTextureArray);
Register<SetTextureArraySeparateCommand>(CommandType.SetTextureArraySeparate);
Register<SetUserClipDistanceCommand>(CommandType.SetUserClipDistance); Register<SetUserClipDistanceCommand>(CommandType.SetUserClipDistance);
Register<SetVertexAttribsCommand>(CommandType.SetVertexAttribs); Register<SetVertexAttribsCommand>(CommandType.SetVertexAttribs);
Register<SetVertexBuffersCommand>(CommandType.SetVertexBuffers); Register<SetVertexBuffersCommand>(CommandType.SetVertexBuffers);

View file

@ -84,6 +84,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
SetUniformBuffers, SetUniformBuffers,
SetImage, SetImage,
SetImageArray, SetImageArray,
SetImageArraySeparate,
SetIndexBuffer, SetIndexBuffer,
SetLineParameters, SetLineParameters,
SetLogicOpState, SetLogicOpState,
@ -101,6 +102,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
SetStencilTest, SetStencilTest,
SetTextureAndSampler, SetTextureAndSampler,
SetTextureArray, SetTextureArray,
SetTextureArraySeparate,
SetUserClipDistance, SetUserClipDistance,
SetVertexAttribs, SetVertexAttribs,
SetVertexBuffers, SetVertexBuffers,

View file

@ -0,0 +1,26 @@
using Ryujinx.Graphics.GAL.Multithreading.Model;
using Ryujinx.Graphics.GAL.Multithreading.Resources;
using Ryujinx.Graphics.Shader;
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
{
struct SetImageArraySeparateCommand : IGALCommand, IGALCommand<SetImageArraySeparateCommand>
{
public readonly CommandType CommandType => CommandType.SetImageArraySeparate;
private ShaderStage _stage;
private int _setIndex;
private TableRef<IImageArray> _array;
public void Set(ShaderStage stage, int setIndex, TableRef<IImageArray> array)
{
_stage = stage;
_setIndex = setIndex;
_array = array;
}
public static void Run(ref SetImageArraySeparateCommand command, ThreadedRenderer threaded, IRenderer renderer)
{
renderer.Pipeline.SetImageArraySeparate(command._stage, command._setIndex, command._array.GetAs<ThreadedImageArray>(threaded)?.Base);
}
}
}

View file

@ -0,0 +1,26 @@
using Ryujinx.Graphics.GAL.Multithreading.Model;
using Ryujinx.Graphics.GAL.Multithreading.Resources;
using Ryujinx.Graphics.Shader;
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
{
struct SetTextureArraySeparateCommand : IGALCommand, IGALCommand<SetTextureArraySeparateCommand>
{
public readonly CommandType CommandType => CommandType.SetTextureArraySeparate;
private ShaderStage _stage;
private int _setIndex;
private TableRef<ITextureArray> _array;
public void Set(ShaderStage stage, int setIndex, TableRef<ITextureArray> array)
{
_stage = stage;
_setIndex = setIndex;
_array = array;
}
public static void Run(ref SetTextureArraySeparateCommand command, ThreadedRenderer threaded, IRenderer renderer)
{
renderer.Pipeline.SetTextureArraySeparate(command._stage, command._setIndex, command._array.GetAs<ThreadedTextureArray>(threaded)?.Base);
}
}
}

View file

@ -189,6 +189,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand(); _renderer.QueueCommand();
} }
public void SetImageArraySeparate(ShaderStage stage, int setIndex, IImageArray array)
{
_renderer.New<SetImageArraySeparateCommand>().Set(stage, setIndex, Ref(array));
_renderer.QueueCommand();
}
public void SetIndexBuffer(BufferRange buffer, IndexType type) public void SetIndexBuffer(BufferRange buffer, IndexType type)
{ {
_renderer.New<SetIndexBufferCommand>().Set(buffer, type); _renderer.New<SetIndexBufferCommand>().Set(buffer, type);
@ -297,6 +303,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand(); _renderer.QueueCommand();
} }
public void SetTextureArraySeparate(ShaderStage stage, int setIndex, ITextureArray array)
{
_renderer.New<SetTextureArraySeparateCommand>().Set(stage, setIndex, Ref(array));
_renderer.QueueCommand();
}
public void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers) public void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers)
{ {
_renderer.New<SetTransformFeedbackBuffersCommand>().Set(_renderer.CopySpan(buffers)); _renderer.New<SetTransformFeedbackBuffersCommand>().Set(_renderer.CopySpan(buffers));

View file

@ -19,6 +19,11 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
public Format Format { get; } public Format Format { get; }
/// <summary>
/// Shader texture host set index.
/// </summary>
public int Set { get; }
/// <summary> /// <summary>
/// Shader texture host binding point. /// Shader texture host binding point.
/// </summary> /// </summary>
@ -54,15 +59,17 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
/// <param name="target">The shader sampler target type</param> /// <param name="target">The shader sampler target type</param>
/// <param name="format">Format of the image as declared on the shader</param> /// <param name="format">Format of the image as declared on the shader</param>
/// <param name="set">Shader texture host set index</param>
/// <param name="binding">The shader texture binding point</param> /// <param name="binding">The shader texture binding point</param>
/// <param name="arrayLength">For array of textures, this indicates the length of the array. A value of one indicates it is not an array</param> /// <param name="arrayLength">For array of textures, this indicates the length of the array. A value of one indicates it is not an array</param>
/// <param name="cbufSlot">Constant buffer slot where the texture handle is located</param> /// <param name="cbufSlot">Constant buffer slot where the texture handle is located</param>
/// <param name="handle">The shader texture handle (read index into the texture constant buffer)</param> /// <param name="handle">The shader texture handle (read index into the texture constant buffer)</param>
/// <param name="flags">The texture's usage flags, indicating how it is used in the shader</param> /// <param name="flags">The texture's usage flags, indicating how it is used in the shader</param>
public TextureBindingInfo(Target target, Format format, int binding, int arrayLength, int cbufSlot, int handle, TextureUsageFlags flags) public TextureBindingInfo(Target target, Format format, int set, int binding, int arrayLength, int cbufSlot, int handle, TextureUsageFlags flags)
{ {
Target = target; Target = target;
Format = format; Format = format;
Set = set;
Binding = binding; Binding = binding;
ArrayLength = arrayLength; ArrayLength = arrayLength;
CbufSlot = cbufSlot; CbufSlot = cbufSlot;
@ -74,6 +81,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Constructs the texture binding information structure. /// Constructs the texture binding information structure.
/// </summary> /// </summary>
/// <param name="target">The shader sampler target type</param> /// <param name="target">The shader sampler target type</param>
/// <param name="set">Shader texture host set index</param>
/// <param name="binding">The shader texture binding point</param> /// <param name="binding">The shader texture binding point</param>
/// <param name="arrayLength">For array of textures, this indicates the length of the array. A value of one indicates it is not an array</param> /// <param name="arrayLength">For array of textures, this indicates the length of the array. A value of one indicates it is not an array</param>
/// <param name="cbufSlot">Constant buffer slot where the texture handle is located</param> /// <param name="cbufSlot">Constant buffer slot where the texture handle is located</param>
@ -82,12 +90,13 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="isSamplerOnly">Indicates that the binding is for a sampler</param> /// <param name="isSamplerOnly">Indicates that the binding is for a sampler</param>
public TextureBindingInfo( public TextureBindingInfo(
Target target, Target target,
int set,
int binding, int binding,
int arrayLength, int arrayLength,
int cbufSlot, int cbufSlot,
int handle, int handle,
TextureUsageFlags flags, TextureUsageFlags flags,
bool isSamplerOnly) : this(target, 0, binding, arrayLength, cbufSlot, handle, flags) bool isSamplerOnly) : this(target, 0, set, binding, arrayLength, cbufSlot, handle, flags)
{ {
IsSamplerOnly = isSamplerOnly; IsSamplerOnly = isSamplerOnly;
} }

View file

@ -566,7 +566,7 @@ namespace Ryujinx.Graphics.Gpu.Image
int stageIndex, int stageIndex,
int textureBufferIndex, int textureBufferIndex,
SamplerIndex samplerIndex, SamplerIndex samplerIndex,
TextureBindingInfo bindingInfo) in TextureBindingInfo bindingInfo)
{ {
Update(texturePool, samplerPool, stage, stageIndex, textureBufferIndex, isImage: false, samplerIndex, bindingInfo); Update(texturePool, samplerPool, stage, stageIndex, textureBufferIndex, isImage: false, samplerIndex, bindingInfo);
} }
@ -579,7 +579,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="stageIndex">Shader stage index where the array is used</param> /// <param name="stageIndex">Shader stage index where the array is used</param>
/// <param name="textureBufferIndex">Texture constant buffer index</param> /// <param name="textureBufferIndex">Texture constant buffer index</param>
/// <param name="bindingInfo">Array binding information</param> /// <param name="bindingInfo">Array binding information</param>
public void UpdateImageArray(TexturePool texturePool, ShaderStage stage, int stageIndex, int textureBufferIndex, TextureBindingInfo bindingInfo) public void UpdateImageArray(TexturePool texturePool, ShaderStage stage, int stageIndex, int textureBufferIndex, in TextureBindingInfo bindingInfo)
{ {
Update(texturePool, null, stage, stageIndex, textureBufferIndex, isImage: true, SamplerIndex.ViaHeaderIndex, bindingInfo); Update(texturePool, null, stage, stageIndex, textureBufferIndex, isImage: true, SamplerIndex.ViaHeaderIndex, bindingInfo);
} }
@ -603,7 +603,7 @@ namespace Ryujinx.Graphics.Gpu.Image
int textureBufferIndex, int textureBufferIndex,
bool isImage, bool isImage,
SamplerIndex samplerIndex, SamplerIndex samplerIndex,
TextureBindingInfo bindingInfo) in TextureBindingInfo bindingInfo)
{ {
if (IsDirectHandleType(bindingInfo.Handle)) if (IsDirectHandleType(bindingInfo.Handle))
{ {
@ -623,7 +623,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="stage">Shader stage where the array is used</param> /// <param name="stage">Shader stage where the array is used</param>
/// <param name="isImage">Whether the array is a image or texture array</param> /// <param name="isImage">Whether the array is a image or texture array</param>
/// <param name="bindingInfo">Array binding information</param> /// <param name="bindingInfo">Array binding information</param>
private void UpdateFromPool(TexturePool texturePool, SamplerPool samplerPool, ShaderStage stage, bool isImage, TextureBindingInfo bindingInfo) private void UpdateFromPool(TexturePool texturePool, SamplerPool samplerPool, ShaderStage stage, bool isImage, in TextureBindingInfo bindingInfo)
{ {
CacheEntry entry = GetOrAddEntry(texturePool, samplerPool, bindingInfo, isImage, out bool isNewEntry); CacheEntry entry = GetOrAddEntry(texturePool, samplerPool, bindingInfo, isImage, out bool isNewEntry);
@ -638,11 +638,11 @@ namespace Ryujinx.Graphics.Gpu.Image
if (isImage) if (isImage)
{ {
_context.Renderer.Pipeline.SetImageArray(stage, bindingInfo.Binding, entry.ImageArray); SetImageArray(stage, bindingInfo, entry.ImageArray);
} }
else else
{ {
_context.Renderer.Pipeline.SetTextureArray(stage, bindingInfo.Binding, entry.TextureArray); SetTextureArray(stage, bindingInfo, entry.TextureArray);
} }
return; return;
@ -737,14 +737,14 @@ namespace Ryujinx.Graphics.Gpu.Image
entry.ImageArray.SetFormats(0, formats); entry.ImageArray.SetFormats(0, formats);
entry.ImageArray.SetImages(0, textures); entry.ImageArray.SetImages(0, textures);
_context.Renderer.Pipeline.SetImageArray(stage, bindingInfo.Binding, entry.ImageArray); SetImageArray(stage, bindingInfo, entry.ImageArray);
} }
else else
{ {
entry.TextureArray.SetSamplers(0, samplers); entry.TextureArray.SetSamplers(0, samplers);
entry.TextureArray.SetTextures(0, textures); entry.TextureArray.SetTextures(0, textures);
_context.Renderer.Pipeline.SetTextureArray(stage, bindingInfo.Binding, entry.TextureArray); SetTextureArray(stage, bindingInfo, entry.TextureArray);
} }
} }
@ -767,7 +767,7 @@ namespace Ryujinx.Graphics.Gpu.Image
int textureBufferIndex, int textureBufferIndex,
bool isImage, bool isImage,
SamplerIndex samplerIndex, SamplerIndex samplerIndex,
TextureBindingInfo bindingInfo) in TextureBindingInfo bindingInfo)
{ {
(textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(bindingInfo.CbufSlot, textureBufferIndex); (textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(bindingInfo.CbufSlot, textureBufferIndex);
@ -800,11 +800,11 @@ namespace Ryujinx.Graphics.Gpu.Image
if (isImage) if (isImage)
{ {
_context.Renderer.Pipeline.SetImageArray(stage, bindingInfo.Binding, entry.ImageArray); SetImageArray(stage, bindingInfo, entry.ImageArray);
} }
else else
{ {
_context.Renderer.Pipeline.SetTextureArray(stage, bindingInfo.Binding, entry.TextureArray); SetTextureArray(stage, bindingInfo, entry.TextureArray);
} }
return; return;
@ -829,11 +829,11 @@ namespace Ryujinx.Graphics.Gpu.Image
if (isImage) if (isImage)
{ {
_context.Renderer.Pipeline.SetImageArray(stage, bindingInfo.Binding, entry.ImageArray); SetImageArray(stage, bindingInfo, entry.ImageArray);
} }
else else
{ {
_context.Renderer.Pipeline.SetTextureArray(stage, bindingInfo.Binding, entry.TextureArray); SetTextureArray(stage, bindingInfo, entry.TextureArray);
} }
return; return;
@ -950,14 +950,50 @@ namespace Ryujinx.Graphics.Gpu.Image
entry.ImageArray.SetFormats(0, formats); entry.ImageArray.SetFormats(0, formats);
entry.ImageArray.SetImages(0, textures); entry.ImageArray.SetImages(0, textures);
_context.Renderer.Pipeline.SetImageArray(stage, bindingInfo.Binding, entry.ImageArray); SetImageArray(stage, bindingInfo, entry.ImageArray);
} }
else else
{ {
entry.TextureArray.SetSamplers(0, samplers); entry.TextureArray.SetSamplers(0, samplers);
entry.TextureArray.SetTextures(0, textures); entry.TextureArray.SetTextures(0, textures);
_context.Renderer.Pipeline.SetTextureArray(stage, bindingInfo.Binding, entry.TextureArray); SetTextureArray(stage, bindingInfo, entry.TextureArray);
}
}
/// <summary>
/// Updates a texture array binding on the host.
/// </summary>
/// <param name="stage">Shader stage where the array is used</param>
/// <param name="bindingInfo">Array binding information</param>
/// <param name="array">Texture array</param>
private void SetTextureArray(ShaderStage stage, in TextureBindingInfo bindingInfo, ITextureArray array)
{
if (bindingInfo.Set >= _context.Capabilities.ExtraSetBaseIndex && _context.Capabilities.MaximumExtraSets != 0)
{
_context.Renderer.Pipeline.SetTextureArraySeparate(stage, bindingInfo.Set, array);
}
else
{
_context.Renderer.Pipeline.SetTextureArray(stage, bindingInfo.Binding, array);
}
}
/// <summary>
/// Updates a image array binding on the host.
/// </summary>
/// <param name="stage">Shader stage where the array is used</param>
/// <param name="bindingInfo">Array binding information</param>
/// <param name="array">Image array</param>
private void SetImageArray(ShaderStage stage, in TextureBindingInfo bindingInfo, IImageArray array)
{
if (bindingInfo.Set >= _context.Capabilities.ExtraSetBaseIndex && _context.Capabilities.MaximumExtraSets != 0)
{
_context.Renderer.Pipeline.SetImageArraySeparate(stage, bindingInfo.Set, array);
}
else
{
_context.Renderer.Pipeline.SetImageArray(stage, bindingInfo.Binding, array);
} }
} }
@ -973,7 +1009,7 @@ namespace Ryujinx.Graphics.Gpu.Image
private CacheEntry GetOrAddEntry( private CacheEntry GetOrAddEntry(
TexturePool texturePool, TexturePool texturePool,
SamplerPool samplerPool, SamplerPool samplerPool,
TextureBindingInfo bindingInfo, in TextureBindingInfo bindingInfo,
bool isImage, bool isImage,
out bool isNew) out bool isNew)
{ {
@ -1015,7 +1051,7 @@ namespace Ryujinx.Graphics.Gpu.Image
private CacheEntryFromBuffer GetOrAddEntry( private CacheEntryFromBuffer GetOrAddEntry(
TexturePool texturePool, TexturePool texturePool,
SamplerPool samplerPool, SamplerPool samplerPool,
TextureBindingInfo bindingInfo, in TextureBindingInfo bindingInfo,
bool isImage, bool isImage,
ref BufferBounds textureBufferBounds, ref BufferBounds textureBufferBounds,
out bool isNew) out bool isNew)

View file

@ -62,6 +62,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
var result = new TextureBindingInfo( var result = new TextureBindingInfo(
target, target,
descriptor.Set,
descriptor.Binding, descriptor.Binding,
descriptor.ArrayLength, descriptor.ArrayLength,
descriptor.CbufSlot, descriptor.CbufSlot,
@ -90,6 +91,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
var result = new TextureBindingInfo( var result = new TextureBindingInfo(
target, target,
format, format,
descriptor.Set,
descriptor.Binding, descriptor.Binding,
descriptor.ArrayLength, descriptor.ArrayLength,
descriptor.CbufSlot, descriptor.CbufSlot,

View file

@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2; private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 5936; private const uint CodeGenVersion = 6870;
private const string SharedTocFileName = "shared.toc"; private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data"; private const string SharedDataFileName = "shared.data";

View file

@ -51,7 +51,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
_reservedImages = rrc.ReservedImages; _reservedImages = rrc.ReservedImages;
} }
public int CreateConstantBufferBinding(int index) public SetBindingPair CreateConstantBufferBinding(int index)
{ {
int binding; int binding;
@ -64,10 +64,10 @@ namespace Ryujinx.Graphics.Gpu.Shader
binding = _resourceCounts.UniformBuffersCount++; binding = _resourceCounts.UniformBuffersCount++;
} }
return binding + _reservedConstantBuffers; return new SetBindingPair(_context.Capabilities.UniformBufferSetIndex, binding + _reservedConstantBuffers);
} }
public int CreateImageBinding(int count, bool isBuffer) public SetBindingPair CreateImageBinding(int count, bool isBuffer)
{ {
int binding; int binding;
@ -96,10 +96,10 @@ namespace Ryujinx.Graphics.Gpu.Shader
_resourceCounts.ImagesCount += count; _resourceCounts.ImagesCount += count;
} }
return binding + _reservedImages; return new SetBindingPair(_context.Capabilities.ImageSetIndex, binding + _reservedImages);
} }
public int CreateStorageBufferBinding(int index) public SetBindingPair CreateStorageBufferBinding(int index)
{ {
int binding; int binding;
@ -112,10 +112,10 @@ namespace Ryujinx.Graphics.Gpu.Shader
binding = _resourceCounts.StorageBuffersCount++; binding = _resourceCounts.StorageBuffersCount++;
} }
return binding + _reservedStorageBuffers; return new SetBindingPair(_context.Capabilities.StorageBufferSetIndex, binding + _reservedStorageBuffers);
} }
public int CreateTextureBinding(int count, bool isBuffer) public SetBindingPair CreateTextureBinding(int count, bool isBuffer)
{ {
int binding; int binding;
@ -144,7 +144,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
_resourceCounts.TexturesCount += count; _resourceCounts.TexturesCount += count;
} }
return binding + _reservedTextures; return new SetBindingPair(_context.Capabilities.TextureSetIndex, binding + _reservedTextures);
} }
private int GetBindingFromIndex(int index, uint maxPerStage, string resourceName) private int GetBindingFromIndex(int index, uint maxPerStage, string resourceName)
@ -183,6 +183,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
return maxPerStage * Constants.ShaderStages; return maxPerStage * Constants.ShaderStages;
} }
public int CreateExtraSet()
{
if (_resourceCounts.SetsCount >= _context.Capabilities.MaximumExtraSets)
{
return -1;
}
return _context.Capabilities.ExtraSetBaseIndex + _resourceCounts.SetsCount++;
}
public int QueryHostGatherBiasPrecision() => _context.Capabilities.GatherBiasPrecision; public int QueryHostGatherBiasPrecision() => _context.Capabilities.GatherBiasPrecision;
public bool QueryHostReducedPrecision() => _context.Capabilities.ReduceShaderPrecision; public bool QueryHostReducedPrecision() => _context.Capabilities.ReduceShaderPrecision;

View file

@ -24,5 +24,10 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// Total of images used by the shaders. /// Total of images used by the shaders.
/// </summary> /// </summary>
public int ImagesCount; public int ImagesCount;
/// <summary>
/// Total of extra sets used by the shaders.
/// </summary>
public int SetsCount;
} }
} }

View file

@ -1,5 +1,6 @@
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader;
using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu.Shader namespace Ryujinx.Graphics.Gpu.Shader
@ -9,13 +10,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// </summary> /// </summary>
class ShaderInfoBuilder class ShaderInfoBuilder
{ {
private const int TotalSets = 4;
private const int UniformSetIndex = 0;
private const int StorageSetIndex = 1;
private const int TextureSetIndex = 2;
private const int ImageSetIndex = 3;
private const ResourceStages SupportBufferStages = private const ResourceStages SupportBufferStages =
ResourceStages.Compute | ResourceStages.Compute |
ResourceStages.Vertex | ResourceStages.Vertex |
@ -36,8 +30,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
private readonly int _reservedTextures; private readonly int _reservedTextures;
private readonly int _reservedImages; private readonly int _reservedImages;
private readonly List<ResourceDescriptor>[] _resourceDescriptors; private List<ResourceDescriptor>[] _resourceDescriptors;
private readonly List<ResourceUsage>[] _resourceUsages; private List<ResourceUsage>[] _resourceUsages;
/// <summary> /// <summary>
/// Creates a new shader info builder. /// Creates a new shader info builder.
@ -51,17 +45,27 @@ namespace Ryujinx.Graphics.Gpu.Shader
_fragmentOutputMap = -1; _fragmentOutputMap = -1;
_resourceDescriptors = new List<ResourceDescriptor>[TotalSets]; int uniformSetIndex = context.Capabilities.UniformBufferSetIndex;
_resourceUsages = new List<ResourceUsage>[TotalSets]; int storageSetIndex = context.Capabilities.StorageBufferSetIndex;
int textureSetIndex = context.Capabilities.TextureSetIndex;
int imageSetIndex = context.Capabilities.ImageSetIndex;
for (int index = 0; index < TotalSets; index++) int totalSets = Math.Max(uniformSetIndex, storageSetIndex);
totalSets = Math.Max(totalSets, textureSetIndex);
totalSets = Math.Max(totalSets, imageSetIndex);
totalSets++;
_resourceDescriptors = new List<ResourceDescriptor>[totalSets];
_resourceUsages = new List<ResourceUsage>[totalSets];
for (int index = 0; index < totalSets; index++)
{ {
_resourceDescriptors[index] = new(); _resourceDescriptors[index] = new();
_resourceUsages[index] = new(); _resourceUsages[index] = new();
} }
AddDescriptor(SupportBufferStages, ResourceType.UniformBuffer, UniformSetIndex, 0, 1); AddDescriptor(SupportBufferStages, ResourceType.UniformBuffer, uniformSetIndex, 0, 1);
AddUsage(SupportBufferStages, ResourceType.UniformBuffer, UniformSetIndex, 0, 1); AddUsage(SupportBufferStages, ResourceType.UniformBuffer, uniformSetIndex, 0, 1);
ResourceReservationCounts rrc = new(!context.Capabilities.SupportsTransformFeedback && tfEnabled, vertexAsCompute); ResourceReservationCounts rrc = new(!context.Capabilities.SupportsTransformFeedback && tfEnabled, vertexAsCompute);
@ -73,12 +77,20 @@ namespace Ryujinx.Graphics.Gpu.Shader
// TODO: Handle that better? Maybe we should only set the binding that are really needed on each shader. // TODO: Handle that better? Maybe we should only set the binding that are really needed on each shader.
ResourceStages stages = vertexAsCompute ? ResourceStages.Compute | ResourceStages.Vertex : VtgStages; ResourceStages stages = vertexAsCompute ? ResourceStages.Compute | ResourceStages.Vertex : VtgStages;
PopulateDescriptorAndUsages(stages, ResourceType.UniformBuffer, UniformSetIndex, 1, rrc.ReservedConstantBuffers - 1); PopulateDescriptorAndUsages(stages, ResourceType.UniformBuffer, uniformSetIndex, 1, rrc.ReservedConstantBuffers - 1);
PopulateDescriptorAndUsages(stages, ResourceType.StorageBuffer, StorageSetIndex, 0, rrc.ReservedStorageBuffers); PopulateDescriptorAndUsages(stages, ResourceType.StorageBuffer, storageSetIndex, 0, rrc.ReservedStorageBuffers);
PopulateDescriptorAndUsages(stages, ResourceType.BufferTexture, TextureSetIndex, 0, rrc.ReservedTextures); PopulateDescriptorAndUsages(stages, ResourceType.BufferTexture, textureSetIndex, 0, rrc.ReservedTextures);
PopulateDescriptorAndUsages(stages, ResourceType.BufferImage, ImageSetIndex, 0, rrc.ReservedImages); PopulateDescriptorAndUsages(stages, ResourceType.BufferImage, imageSetIndex, 0, rrc.ReservedImages);
} }
/// <summary>
/// Populates descriptors and usages for vertex as compute and transform feedback emulation reserved resources.
/// </summary>
/// <param name="stages">Shader stages where the resources are used</param>
/// <param name="type">Resource type</param>
/// <param name="setIndex">Resource set index where the resources are used</param>
/// <param name="start">First binding number</param>
/// <param name="count">Amount of bindings</param>
private void PopulateDescriptorAndUsages(ResourceStages stages, ResourceType type, int setIndex, int start, int count) private void PopulateDescriptorAndUsages(ResourceStages stages, ResourceType type, int setIndex, int start, int count)
{ {
AddDescriptor(stages, type, setIndex, start, count); AddDescriptor(stages, type, setIndex, start, count);
@ -127,18 +139,23 @@ namespace Ryujinx.Graphics.Gpu.Shader
int textureBinding = _reservedTextures + stageIndex * texturesPerStage * 2; int textureBinding = _reservedTextures + stageIndex * texturesPerStage * 2;
int imageBinding = _reservedImages + stageIndex * imagesPerStage * 2; int imageBinding = _reservedImages + stageIndex * imagesPerStage * 2;
AddDescriptor(stages, ResourceType.UniformBuffer, UniformSetIndex, uniformBinding, uniformsPerStage); int uniformSetIndex = _context.Capabilities.UniformBufferSetIndex;
AddDescriptor(stages, ResourceType.StorageBuffer, StorageSetIndex, storageBinding, storagesPerStage); int storageSetIndex = _context.Capabilities.StorageBufferSetIndex;
AddDualDescriptor(stages, ResourceType.TextureAndSampler, ResourceType.BufferTexture, TextureSetIndex, textureBinding, texturesPerStage); int textureSetIndex = _context.Capabilities.TextureSetIndex;
AddDualDescriptor(stages, ResourceType.Image, ResourceType.BufferImage, ImageSetIndex, imageBinding, imagesPerStage); int imageSetIndex = _context.Capabilities.ImageSetIndex;
AddArrayDescriptors(info.Textures, stages, TextureSetIndex, isImage: false); AddDescriptor(stages, ResourceType.UniformBuffer, uniformSetIndex, uniformBinding, uniformsPerStage);
AddArrayDescriptors(info.Images, stages, TextureSetIndex, isImage: true); AddDescriptor(stages, ResourceType.StorageBuffer, storageSetIndex, storageBinding, storagesPerStage);
AddDualDescriptor(stages, ResourceType.TextureAndSampler, ResourceType.BufferTexture, textureSetIndex, textureBinding, texturesPerStage);
AddDualDescriptor(stages, ResourceType.Image, ResourceType.BufferImage, imageSetIndex, imageBinding, imagesPerStage);
AddUsage(info.CBuffers, stages, UniformSetIndex, isStorage: false); AddArrayDescriptors(info.Textures, stages, isImage: false);
AddUsage(info.SBuffers, stages, StorageSetIndex, isStorage: true); AddArrayDescriptors(info.Images, stages, isImage: true);
AddUsage(info.Textures, stages, TextureSetIndex, isImage: false);
AddUsage(info.Images, stages, ImageSetIndex, isImage: true); AddUsage(info.CBuffers, stages, isStorage: false);
AddUsage(info.SBuffers, stages, isStorage: true);
AddUsage(info.Textures, stages, isImage: false);
AddUsage(info.Images, stages, isImage: true);
} }
/// <summary> /// <summary>
@ -177,9 +194,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// </summary> /// </summary>
/// <param name="textures">Textures to be added</param> /// <param name="textures">Textures to be added</param>
/// <param name="stages">Stages where the textures are used</param> /// <param name="stages">Stages where the textures are used</param>
/// <param name="setIndex">Descriptor set index where the textures will be bound</param>
/// <param name="isImage">True for images, false for textures</param> /// <param name="isImage">True for images, false for textures</param>
private void AddArrayDescriptors(IEnumerable<TextureDescriptor> textures, ResourceStages stages, int setIndex, bool isImage) private void AddArrayDescriptors(IEnumerable<TextureDescriptor> textures, ResourceStages stages, bool isImage)
{ {
foreach (TextureDescriptor texture in textures) foreach (TextureDescriptor texture in textures)
{ {
@ -187,7 +203,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{ {
ResourceType type = GetTextureResourceType(texture, isImage); ResourceType type = GetTextureResourceType(texture, isImage);
_resourceDescriptors[setIndex].Add(new ResourceDescriptor(texture.Binding, texture.ArrayLength, type, stages)); GetDescriptors(texture.Set).Add(new ResourceDescriptor(texture.Binding, texture.ArrayLength, type, stages));
} }
} }
} }
@ -213,13 +229,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// </summary> /// </summary>
/// <param name="buffers">Buffers to be added</param> /// <param name="buffers">Buffers to be added</param>
/// <param name="stages">Stages where the buffers are used</param> /// <param name="stages">Stages where the buffers are used</param>
/// <param name="setIndex">Descriptor set index where the buffers will be bound</param>
/// <param name="isStorage">True for storage buffers, false for uniform buffers</param> /// <param name="isStorage">True for storage buffers, false for uniform buffers</param>
private void AddUsage(IEnumerable<BufferDescriptor> buffers, ResourceStages stages, int setIndex, bool isStorage) private void AddUsage(IEnumerable<BufferDescriptor> buffers, ResourceStages stages, bool isStorage)
{ {
foreach (BufferDescriptor buffer in buffers) foreach (BufferDescriptor buffer in buffers)
{ {
_resourceUsages[setIndex].Add(new ResourceUsage( GetUsages(buffer.Set).Add(new ResourceUsage(
buffer.Binding, buffer.Binding,
1, 1,
isStorage ? ResourceType.StorageBuffer : ResourceType.UniformBuffer, isStorage ? ResourceType.StorageBuffer : ResourceType.UniformBuffer,
@ -232,18 +247,65 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// </summary> /// </summary>
/// <param name="textures">Textures to be added</param> /// <param name="textures">Textures to be added</param>
/// <param name="stages">Stages where the textures are used</param> /// <param name="stages">Stages where the textures are used</param>
/// <param name="setIndex">Descriptor set index where the textures will be bound</param>
/// <param name="isImage">True for images, false for textures</param> /// <param name="isImage">True for images, false for textures</param>
private void AddUsage(IEnumerable<TextureDescriptor> textures, ResourceStages stages, int setIndex, bool isImage) private void AddUsage(IEnumerable<TextureDescriptor> textures, ResourceStages stages, bool isImage)
{ {
foreach (TextureDescriptor texture in textures) foreach (TextureDescriptor texture in textures)
{ {
ResourceType type = GetTextureResourceType(texture, isImage); ResourceType type = GetTextureResourceType(texture, isImage);
_resourceUsages[setIndex].Add(new ResourceUsage(texture.Binding, texture.ArrayLength, type, stages)); GetUsages(texture.Set).Add(new ResourceUsage(texture.Binding, texture.ArrayLength, type, stages));
} }
} }
/// <summary>
/// Gets the list of resource descriptors for a given set index. A new list will be created if needed.
/// </summary>
/// <param name="setIndex">Resource set index</param>
/// <returns>List of resource descriptors</returns>
private List<ResourceDescriptor> GetDescriptors(int setIndex)
{
if (_resourceDescriptors.Length <= setIndex)
{
int oldLength = _resourceDescriptors.Length;
Array.Resize(ref _resourceDescriptors, setIndex + 1);
for (int index = oldLength; index <= setIndex; index++)
{
_resourceDescriptors[index] = new();
}
}
return _resourceDescriptors[setIndex];
}
/// <summary>
/// Gets the list of resource usages for a given set index. A new list will be created if needed.
/// </summary>
/// <param name="setIndex">Resource set index</param>
/// <returns>List of resource usages</returns>
private List<ResourceUsage> GetUsages(int setIndex)
{
if (_resourceUsages.Length <= setIndex)
{
int oldLength = _resourceUsages.Length;
Array.Resize(ref _resourceUsages, setIndex + 1);
for (int index = oldLength; index <= setIndex; index++)
{
_resourceUsages[index] = new();
}
}
return _resourceUsages[setIndex];
}
/// <summary>
/// Gets a resource type from a texture descriptor.
/// </summary>
/// <param name="texture">Texture descriptor</param>
/// <param name="isImage">Whether the texture is a image texture (writable) or not (sampled)</param>
/// <returns>Resource type</returns>
private static ResourceType GetTextureResourceType(TextureDescriptor texture, bool isImage) private static ResourceType GetTextureResourceType(TextureDescriptor texture, bool isImage)
{ {
bool isBuffer = (texture.Type & SamplerType.Mask) == SamplerType.TextureBuffer; bool isBuffer = (texture.Type & SamplerType.Mask) == SamplerType.TextureBuffer;
@ -278,10 +340,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <returns>Shader information</returns> /// <returns>Shader information</returns>
public ShaderInfo Build(ProgramPipelineState? pipeline, bool fromCache = false) public ShaderInfo Build(ProgramPipelineState? pipeline, bool fromCache = false)
{ {
var descriptors = new ResourceDescriptorCollection[TotalSets]; int totalSets = _resourceDescriptors.Length;
var usages = new ResourceUsageCollection[TotalSets];
for (int index = 0; index < TotalSets; index++) var descriptors = new ResourceDescriptorCollection[totalSets];
var usages = new ResourceUsageCollection[totalSets];
for (int index = 0; index < totalSets; index++)
{ {
descriptors[index] = new ResourceDescriptorCollection(_resourceDescriptors[index].ToArray().AsReadOnly()); descriptors[index] = new ResourceDescriptorCollection(_resourceDescriptors[index].ToArray().AsReadOnly());
usages[index] = new ResourceUsageCollection(_resourceUsages[index].ToArray().AsReadOnly()); usages[index] = new ResourceUsageCollection(_resourceUsages[index].ToArray().AsReadOnly());

View file

@ -187,6 +187,12 @@ namespace Ryujinx.Graphics.OpenGL
supportsViewportSwizzle: HwCapabilities.SupportsViewportSwizzle, supportsViewportSwizzle: HwCapabilities.SupportsViewportSwizzle,
supportsIndirectParameters: HwCapabilities.SupportsIndirectParameters, supportsIndirectParameters: HwCapabilities.SupportsIndirectParameters,
supportsDepthClipControl: true, supportsDepthClipControl: true,
uniformBufferSetIndex: 0,
storageBufferSetIndex: 1,
textureSetIndex: 2,
imageSetIndex: 3,
extraSetBaseIndex: 0,
maximumExtraSets: 0,
maximumUniformBuffersPerStage: 13, // TODO: Avoid hardcoding those limits here and get from driver? maximumUniformBuffersPerStage: 13, // TODO: Avoid hardcoding those limits here and get from driver?
maximumStorageBuffersPerStage: 16, maximumStorageBuffersPerStage: 16,
maximumTexturesPerStage: 32, maximumTexturesPerStage: 32,

View file

@ -963,6 +963,11 @@ namespace Ryujinx.Graphics.OpenGL
(array as ImageArray).Bind(binding); (array as ImageArray).Bind(binding);
} }
public void SetImageArraySeparate(ShaderStage stage, int setIndex, IImageArray array)
{
throw new NotSupportedException("OpenGL does not support descriptor sets.");
}
public void SetIndexBuffer(BufferRange buffer, IndexType type) public void SetIndexBuffer(BufferRange buffer, IndexType type)
{ {
_elementsType = type.Convert(); _elementsType = type.Convert();
@ -1312,6 +1317,11 @@ namespace Ryujinx.Graphics.OpenGL
(array as TextureArray).Bind(binding); (array as TextureArray).Bind(binding);
} }
public void SetTextureArraySeparate(ShaderStage stage, int setIndex, ITextureArray array)
{
throw new NotSupportedException("OpenGL does not support descriptor sets.");
}
public void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers) public void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers)
{ {
if (_tfEnabled) if (_tfEnabled)

View file

@ -4,14 +4,16 @@ namespace Ryujinx.Graphics.Shader
{ {
// New fields should be added to the end of the struct to keep disk shader cache compatibility. // New fields should be added to the end of the struct to keep disk shader cache compatibility.
public readonly int Set;
public readonly int Binding; public readonly int Binding;
public readonly byte Slot; public readonly byte Slot;
public readonly byte SbCbSlot; public readonly byte SbCbSlot;
public readonly ushort SbCbOffset; public readonly ushort SbCbOffset;
public readonly BufferUsageFlags Flags; public readonly BufferUsageFlags Flags;
public BufferDescriptor(int binding, int slot) public BufferDescriptor(int set, int binding, int slot)
{ {
Set = set;
Binding = binding; Binding = binding;
Slot = (byte)slot; Slot = (byte)slot;
SbCbSlot = 0; SbCbSlot = 0;
@ -19,8 +21,9 @@ namespace Ryujinx.Graphics.Shader
Flags = BufferUsageFlags.None; Flags = BufferUsageFlags.None;
} }
public BufferDescriptor(int binding, int slot, int sbCbSlot, int sbCbOffset, BufferUsageFlags flags) public BufferDescriptor(int set, int binding, int slot, int sbCbSlot, int sbCbOffset, BufferUsageFlags flags)
{ {
Set = set;
Binding = binding; Binding = binding;
Slot = (byte)slot; Slot = (byte)slot;
SbCbSlot = (byte)sbCbSlot; SbCbSlot = (byte)sbCbSlot;

View file

@ -462,7 +462,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
} }
else else
{ {
context.Properties.Textures.TryGetValue(texOp.Binding, out TextureDefinition definition); context.Properties.Textures.TryGetValue(texOp.GetTextureSetAndBinding(), out TextureDefinition definition);
bool hasLod = !definition.Type.HasFlag(SamplerType.Multisample) && (definition.Type & SamplerType.Mask) != SamplerType.TextureBuffer; bool hasLod = !definition.Type.HasFlag(SamplerType.Multisample) && (definition.Type & SamplerType.Mask) != SamplerType.TextureBuffer;
string texCall; string texCall;
@ -639,7 +639,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
private static string GetSamplerName(CodeGenContext context, AstTextureOperation texOp, ref int srcIndex) private static string GetSamplerName(CodeGenContext context, AstTextureOperation texOp, ref int srcIndex)
{ {
TextureDefinition textureDefinition = context.Properties.Textures[texOp.Binding]; TextureDefinition textureDefinition = context.Properties.Textures[texOp.GetTextureSetAndBinding()];
string name = textureDefinition.Name; string name = textureDefinition.Name;
if (textureDefinition.ArrayLength != 1) if (textureDefinition.ArrayLength != 1)
@ -649,7 +649,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
if (texOp.IsSeparate) if (texOp.IsSeparate)
{ {
TextureDefinition samplerDefinition = context.Properties.Textures[texOp.SamplerBinding]; TextureDefinition samplerDefinition = context.Properties.Textures[texOp.GetSamplerSetAndBinding()];
string samplerName = samplerDefinition.Name; string samplerName = samplerDefinition.Name;
if (samplerDefinition.ArrayLength != 1) if (samplerDefinition.ArrayLength != 1)
@ -665,7 +665,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
private static string GetImageName(CodeGenContext context, AstTextureOperation texOp, ref int srcIndex) private static string GetImageName(CodeGenContext context, AstTextureOperation texOp, ref int srcIndex)
{ {
TextureDefinition definition = context.Properties.Images[texOp.Binding]; TextureDefinition definition = context.Properties.Images[texOp.GetTextureSetAndBinding()];
string name = definition.Name; string name = definition.Name;
if (definition.ArrayLength != 1) if (definition.ArrayLength != 1)

View file

@ -33,9 +33,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
public Dictionary<int, Instruction> LocalMemories { get; } = new(); public Dictionary<int, Instruction> LocalMemories { get; } = new();
public Dictionary<int, Instruction> SharedMemories { get; } = new(); public Dictionary<int, Instruction> SharedMemories { get; } = new();
public Dictionary<int, SamplerType> SamplersTypes { get; } = new(); public Dictionary<SetBindingPair, SamplerType> SamplersTypes { get; } = new();
public Dictionary<int, SamplerDeclaration> Samplers { get; } = new(); public Dictionary<SetBindingPair, SamplerDeclaration> Samplers { get; } = new();
public Dictionary<int, ImageDeclaration> Images { get; } = new(); public Dictionary<SetBindingPair, ImageDeclaration> Images { get; } = new();
public Dictionary<IoDefinition, Instruction> Inputs { get; } = new(); public Dictionary<IoDefinition, Instruction> Inputs { get; } = new();
public Dictionary<IoDefinition, Instruction> Outputs { get; } = new(); public Dictionary<IoDefinition, Instruction> Outputs { get; } = new();

View file

@ -208,13 +208,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var sampledImageVariable = context.Variable(sampledImageArrayPointerType, StorageClass.UniformConstant); var sampledImageVariable = context.Variable(sampledImageArrayPointerType, StorageClass.UniformConstant);
context.Samplers.Add(sampler.Binding, new SamplerDeclaration( context.Samplers.Add(new(sampler.Set, sampler.Binding), new SamplerDeclaration(
imageType, imageType,
sampledImageType, sampledImageType,
sampledImagePointerType, sampledImagePointerType,
sampledImageVariable, sampledImageVariable,
sampler.ArrayLength != 1)); sampler.ArrayLength != 1));
context.SamplersTypes.Add(sampler.Binding, sampler.Type); context.SamplersTypes.Add(new(sampler.Set, sampler.Binding), sampler.Type);
context.Name(sampledImageVariable, sampler.Name); context.Name(sampledImageVariable, sampler.Name);
context.Decorate(sampledImageVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex); context.Decorate(sampledImageVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex);
@ -256,7 +256,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var imageVariable = context.Variable(imageArrayPointerType, StorageClass.UniformConstant); var imageVariable = context.Variable(imageArrayPointerType, StorageClass.UniformConstant);
context.Images.Add(image.Binding, new ImageDeclaration(imageType, imagePointerType, imageVariable, image.ArrayLength != 1)); context.Images.Add(new(image.Set, image.Binding), new ImageDeclaration(imageType, imagePointerType, imageVariable, image.ArrayLength != 1));
context.Name(imageVariable, image.Name); context.Name(imageVariable, image.Name);
context.Decorate(imageVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex); context.Decorate(imageVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex);

View file

@ -602,7 +602,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return context.Get(type, texOp.GetSource(srcIndex++)); return context.Get(type, texOp.GetSource(srcIndex++));
} }
ImageDeclaration declaration = context.Images[texOp.Binding]; ImageDeclaration declaration = context.Images[texOp.GetTextureSetAndBinding()];
SpvInstruction image = declaration.Image; SpvInstruction image = declaration.Image;
SpvInstruction resultType = context.GetType(componentType); SpvInstruction resultType = context.GetType(componentType);
@ -681,7 +681,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return context.Get(type, texOp.GetSource(srcIndex++)); return context.Get(type, texOp.GetSource(srcIndex++));
} }
ImageDeclaration declaration = context.Images[texOp.Binding]; ImageDeclaration declaration = context.Images[texOp.GetTextureSetAndBinding()];
SpvInstruction image = declaration.Image; SpvInstruction image = declaration.Image;
if (declaration.IsIndexed) if (declaration.IsIndexed)
@ -738,7 +738,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return context.Get(type, texOp.GetSource(srcIndex++)); return context.Get(type, texOp.GetSource(srcIndex++));
} }
ImageDeclaration declaration = context.Images[texOp.Binding]; ImageDeclaration declaration = context.Images[texOp.GetTextureSetAndBinding()];
SpvInstruction image = declaration.Image; SpvInstruction image = declaration.Image;
if (declaration.IsIndexed) if (declaration.IsIndexed)
@ -837,7 +837,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return context.Get(type, texOp.GetSource(srcIndex++)); return context.Get(type, texOp.GetSource(srcIndex++));
} }
SamplerDeclaration declaration = context.Samplers[texOp.Binding]; SamplerDeclaration declaration = context.Samplers[texOp.GetTextureSetAndBinding()];
SpvInstruction image = GenerateSampledImageLoad(context, texOp, declaration, ref srcIndex); SpvInstruction image = GenerateSampledImageLoad(context, texOp, declaration, ref srcIndex);
int pCount = texOp.Type.GetDimensions(); int pCount = texOp.Type.GetDimensions();
@ -1161,7 +1161,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return context.Get(type, texOp.GetSource(srcIndex++)); return context.Get(type, texOp.GetSource(srcIndex++));
} }
SamplerDeclaration declaration = context.Samplers[texOp.Binding]; SamplerDeclaration declaration = context.Samplers[texOp.GetTextureSetAndBinding()];
SpvInstruction image = GenerateSampledImageLoad(context, texOp, declaration, ref srcIndex); SpvInstruction image = GenerateSampledImageLoad(context, texOp, declaration, ref srcIndex);
int coordsCount = texOp.Type.GetDimensions(); int coordsCount = texOp.Type.GetDimensions();
@ -1433,7 +1433,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
int srcIndex = 0; int srcIndex = 0;
SamplerDeclaration declaration = context.Samplers[texOp.Binding]; SamplerDeclaration declaration = context.Samplers[texOp.GetTextureSetAndBinding()];
SpvInstruction image = GenerateSampledImageLoad(context, texOp, declaration, ref srcIndex); SpvInstruction image = GenerateSampledImageLoad(context, texOp, declaration, ref srcIndex);
image = context.Image(declaration.ImageType, image); image = context.Image(declaration.ImageType, image);
@ -1449,7 +1449,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
int srcIndex = 0; int srcIndex = 0;
SamplerDeclaration declaration = context.Samplers[texOp.Binding]; SamplerDeclaration declaration = context.Samplers[texOp.GetTextureSetAndBinding()];
SpvInstruction image = GenerateSampledImageLoad(context, texOp, declaration, ref srcIndex); SpvInstruction image = GenerateSampledImageLoad(context, texOp, declaration, ref srcIndex);
image = context.Image(declaration.ImageType, image); image = context.Image(declaration.ImageType, image);
@ -1460,7 +1460,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
} }
else else
{ {
var type = context.SamplersTypes[texOp.Binding]; var type = context.SamplersTypes[texOp.GetTextureSetAndBinding()];
bool hasLod = !type.HasFlag(SamplerType.Multisample) && type != SamplerType.TextureBuffer; bool hasLod = !type.HasFlag(SamplerType.Multisample) && type != SamplerType.TextureBuffer;
int dimensions = (type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : type.GetDimensions(); int dimensions = (type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : type.GetDimensions();
@ -1889,7 +1889,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{ {
image = context.Load(declaration.ImageType, image); image = context.Load(declaration.ImageType, image);
SamplerDeclaration samplerDeclaration = context.Samplers[texOp.SamplerBinding]; SamplerDeclaration samplerDeclaration = context.Samplers[texOp.GetSamplerSetAndBinding()];
SpvInstruction sampler = samplerDeclaration.Image; SpvInstruction sampler = samplerDeclaration.Image;

View file

@ -27,34 +27,43 @@ namespace Ryujinx.Graphics.Shader
ReadOnlySpan<ulong> GetCode(ulong address, int minimumSize); ReadOnlySpan<ulong> GetCode(ulong address, int minimumSize);
/// <summary> /// <summary>
/// Queries the binding number of a constant buffer. /// Gets the binding number of a constant buffer.
/// </summary> /// </summary>
/// <param name="index">Constant buffer index</param> /// <param name="index">Constant buffer index</param>
/// <returns>Binding number</returns> /// <returns>Binding number</returns>
int CreateConstantBufferBinding(int index); SetBindingPair CreateConstantBufferBinding(int index);
/// <summary> /// <summary>
/// Queries the binding number of an image. /// Gets the binding number of an image.
/// </summary> /// </summary>
/// <param name="count">For array of images, the number of elements of the array, otherwise it should be 1</param> /// <param name="count">For array of images, the number of elements of the array, otherwise it should be 1</param>
/// <param name="isBuffer">Indicates if the image is a buffer image</param> /// <param name="isBuffer">Indicates if the image is a buffer image</param>
/// <returns>Binding number</returns> /// <returns>Binding number</returns>
int CreateImageBinding(int count, bool isBuffer); SetBindingPair CreateImageBinding(int count, bool isBuffer);
/// <summary> /// <summary>
/// Queries the binding number of a storage buffer. /// Gets the binding number of a storage buffer.
/// </summary> /// </summary>
/// <param name="index">Storage buffer index</param> /// <param name="index">Storage buffer index</param>
/// <returns>Binding number</returns> /// <returns>Binding number</returns>
int CreateStorageBufferBinding(int index); SetBindingPair CreateStorageBufferBinding(int index);
/// <summary> /// <summary>
/// Queries the binding number of a texture. /// Gets the binding number of a texture.
/// </summary> /// </summary>
/// <param name="count">For array of textures, the number of elements of the array, otherwise it should be 1</param> /// <param name="count">For array of textures, the number of elements of the array, otherwise it should be 1</param>
/// <param name="isBuffer">Indicates if the texture is a buffer texture</param> /// <param name="isBuffer">Indicates if the texture is a buffer texture</param>
/// <returns>Binding number</returns> /// <returns>Binding number</returns>
int CreateTextureBinding(int count, bool isBuffer); SetBindingPair CreateTextureBinding(int count, bool isBuffer);
/// <summary>
/// Gets the set index for an additional set, or -1 if there's no extra set available.
/// </summary>
/// <returns>Extra set index, or -1 if not available</returns>
int CreateExtraSet()
{
return -1;
}
/// <summary> /// <summary>
/// Queries Local Size X for compute shaders. /// Queries Local Size X for compute shaders.

View file

@ -278,7 +278,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
flags |= TextureFlags.Bindless; flags |= TextureFlags.Bindless;
} }
int binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding( SetBindingPair setAndBinding = isBindless ? default : context.ResourceManager.GetTextureOrImageBinding(
Instruction.ImageAtomic, Instruction.ImageAtomic,
type, type,
format, format,
@ -286,7 +286,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
TextureOperation.DefaultCbufSlot, TextureOperation.DefaultCbufSlot,
imm); imm);
Operand res = context.ImageAtomic(type, format, flags, binding, sources); Operand res = context.ImageAtomic(type, format, flags, setAndBinding, sources);
context.Copy(d, res); context.Copy(d, res);
} }
@ -389,7 +389,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
TextureFormat format = isBindless ? TextureFormat.Unknown : ShaderProperties.GetTextureFormat(context.TranslatorContext.GpuAccessor, handle); TextureFormat format = isBindless ? TextureFormat.Unknown : ShaderProperties.GetTextureFormat(context.TranslatorContext.GpuAccessor, handle);
int binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding( SetBindingPair setAndBinding = isBindless ? default : context.ResourceManager.GetTextureOrImageBinding(
Instruction.ImageLoad, Instruction.ImageLoad,
type, type,
format, format,
@ -397,7 +397,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
TextureOperation.DefaultCbufSlot, TextureOperation.DefaultCbufSlot,
handle); handle);
context.ImageLoad(type, format, flags, binding, (int)componentMask, dests, sources); context.ImageLoad(type, format, flags, setAndBinding, (int)componentMask, dests, sources);
} }
else else
{ {
@ -432,7 +432,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
TextureFormat format = GetTextureFormat(size); TextureFormat format = GetTextureFormat(size);
int binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding( SetBindingPair setAndBinding = isBindless ? default : context.ResourceManager.GetTextureOrImageBinding(
Instruction.ImageLoad, Instruction.ImageLoad,
type, type,
format, format,
@ -440,7 +440,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
TextureOperation.DefaultCbufSlot, TextureOperation.DefaultCbufSlot,
handle); handle);
context.ImageLoad(type, format, flags, binding, compMask, dests, sources); context.ImageLoad(type, format, flags, setAndBinding, compMask, dests, sources);
switch (size) switch (size)
{ {
@ -552,7 +552,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
flags |= TextureFlags.Bindless; flags |= TextureFlags.Bindless;
} }
int binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding( SetBindingPair setAndBinding = isBindless ? default : context.ResourceManager.GetTextureOrImageBinding(
Instruction.ImageAtomic, Instruction.ImageAtomic,
type, type,
format, format,
@ -560,7 +560,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
TextureOperation.DefaultCbufSlot, TextureOperation.DefaultCbufSlot,
imm); imm);
context.ImageAtomic(type, format, flags, binding, sources); context.ImageAtomic(type, format, flags, setAndBinding, sources);
} }
private static void EmitSust( private static void EmitSust(
@ -679,7 +679,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
flags |= TextureFlags.Coherent; flags |= TextureFlags.Coherent;
} }
int binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding( SetBindingPair setAndBinding = isBindless ? default : context.ResourceManager.GetTextureOrImageBinding(
Instruction.ImageStore, Instruction.ImageStore,
type, type,
format, format,
@ -687,7 +687,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
TextureOperation.DefaultCbufSlot, TextureOperation.DefaultCbufSlot,
handle); handle);
context.ImageStore(type, format, flags, binding, sources); context.ImageStore(type, format, flags, setAndBinding, sources);
} }
private static int GetComponentSizeInBytesLog2(SuatomSize size) private static int GetComponentSizeInBytesLog2(SuatomSize size)

View file

@ -885,7 +885,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
return Register(dest++, RegisterType.Gpr); return Register(dest++, RegisterType.Gpr);
} }
int binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding( SetBindingPair setAndBinding = isBindless ? default : context.ResourceManager.GetTextureOrImageBinding(
Instruction.Lod, Instruction.Lod,
type, type,
TextureFormat.Unknown, TextureFormat.Unknown,
@ -913,7 +913,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
else else
{ {
// The instruction component order is the inverse of GLSL's. // The instruction component order is the inverse of GLSL's.
Operand res = context.Lod(type, flags, binding, compIndex ^ 1, sources); Operand res = context.Lod(type, flags, setAndBinding, compIndex ^ 1, sources);
res = context.FPMultiply(res, ConstF(256.0f)); res = context.FPMultiply(res, ConstF(256.0f));
@ -1116,12 +1116,12 @@ namespace Ryujinx.Graphics.Shader.Instructions
} }
TextureFlags flags = isBindless ? TextureFlags.Bindless : TextureFlags.None; TextureFlags flags = isBindless ? TextureFlags.Bindless : TextureFlags.None;
int binding; SetBindingPair setAndBinding;
switch (query) switch (query)
{ {
case TexQuery.TexHeaderDimension: case TexQuery.TexHeaderDimension:
binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding( setAndBinding = isBindless ? default : context.ResourceManager.GetTextureOrImageBinding(
Instruction.TextureQuerySize, Instruction.TextureQuerySize,
type, type,
TextureFormat.Unknown, TextureFormat.Unknown,
@ -1140,13 +1140,13 @@ namespace Ryujinx.Graphics.Shader.Instructions
break; break;
} }
context.Copy(d, context.TextureQuerySize(type, flags, binding, compIndex, sources)); context.Copy(d, context.TextureQuerySize(type, flags, setAndBinding, compIndex, sources));
} }
} }
break; break;
case TexQuery.TexHeaderTextureType: case TexQuery.TexHeaderTextureType:
binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding( setAndBinding = isBindless ? default : context.ResourceManager.GetTextureOrImageBinding(
Instruction.TextureQuerySamples, Instruction.TextureQuerySamples,
type, type,
TextureFormat.Unknown, TextureFormat.Unknown,
@ -1171,7 +1171,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
if (d != null) if (d != null)
{ {
context.Copy(d, context.TextureQuerySamples(type, flags, binding, sources)); context.Copy(d, context.TextureQuerySamples(type, flags, setAndBinding, sources));
} }
} }
break; break;
@ -1191,7 +1191,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
Operand[] dests, Operand[] dests,
Operand[] sources) Operand[] sources)
{ {
int binding = flags.HasFlag(TextureFlags.Bindless) ? 0 : context.ResourceManager.GetTextureOrImageBinding( SetBindingPair setAndBinding = flags.HasFlag(TextureFlags.Bindless) ? default : context.ResourceManager.GetTextureOrImageBinding(
Instruction.TextureSample, Instruction.TextureSample,
type, type,
TextureFormat.Unknown, TextureFormat.Unknown,
@ -1199,7 +1199,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
TextureOperation.DefaultCbufSlot, TextureOperation.DefaultCbufSlot,
handle); handle);
context.TextureSample(type, flags, binding, componentMask, dests, sources); context.TextureSample(type, flags, setAndBinding, componentMask, dests, sources);
} }
private static SamplerType ConvertSamplerType(TexDim dimensions) private static SamplerType ConvertSamplerType(TexDim dimensions)

View file

@ -8,7 +8,9 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
public TextureFormat Format { get; set; } public TextureFormat Format { get; set; }
public TextureFlags Flags { get; private set; } public TextureFlags Flags { get; private set; }
public int Set { get; private set; }
public int Binding { get; private set; } public int Binding { get; private set; }
public int SamplerSet { get; private set; }
public int SamplerBinding { get; private set; } public int SamplerBinding { get; private set; }
public TextureOperation( public TextureOperation(
@ -16,6 +18,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
SamplerType type, SamplerType type,
TextureFormat format, TextureFormat format,
TextureFlags flags, TextureFlags flags,
int set,
int binding, int binding,
int compIndex, int compIndex,
Operand[] dests, Operand[] dests,
@ -24,24 +27,28 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
Type = type; Type = type;
Format = format; Format = format;
Flags = flags; Flags = flags;
Set = set;
Binding = binding; Binding = binding;
SamplerSet = -1;
SamplerBinding = -1; SamplerBinding = -1;
} }
public void TurnIntoArray(int binding) public void TurnIntoArray(SetBindingPair setAndBinding)
{ {
Flags &= ~TextureFlags.Bindless; Flags &= ~TextureFlags.Bindless;
Binding = binding; Set = setAndBinding.SetIndex;
Binding = setAndBinding.Binding;
} }
public void TurnIntoArray(int textureBinding, int samplerBinding) public void TurnIntoArray(SetBindingPair textureSetAndBinding, SetBindingPair samplerSetAndBinding)
{ {
TurnIntoArray(textureBinding); TurnIntoArray(textureSetAndBinding);
SamplerBinding = samplerBinding; SamplerSet = samplerSetAndBinding.SetIndex;
SamplerBinding = samplerSetAndBinding.Binding;
} }
public void SetBinding(int binding) public void SetBinding(SetBindingPair setAndBinding)
{ {
if ((Flags & TextureFlags.Bindless) != 0) if ((Flags & TextureFlags.Bindless) != 0)
{ {
@ -50,7 +57,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
RemoveSource(0); RemoveSource(0);
} }
Binding = binding; Set = setAndBinding.SetIndex;
Binding = setAndBinding.Binding;
} }
public void SetLodLevelFlag() public void SetLodLevelFlag()

View file

@ -0,0 +1,4 @@
namespace Ryujinx.Graphics.Shader
{
public readonly record struct SetBindingPair(int SetIndex, int Binding);
}

View file

@ -8,7 +8,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
public TextureFormat Format { get; } public TextureFormat Format { get; }
public TextureFlags Flags { get; } public TextureFlags Flags { get; }
public int Set { get; }
public int Binding { get; } public int Binding { get; }
public int SamplerSet { get; }
public int SamplerBinding { get; } public int SamplerBinding { get; }
public bool IsSeparate => SamplerBinding >= 0; public bool IsSeparate => SamplerBinding >= 0;
@ -18,7 +20,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
SamplerType type, SamplerType type,
TextureFormat format, TextureFormat format,
TextureFlags flags, TextureFlags flags,
int set,
int binding, int binding,
int samplerSet,
int samplerBinding, int samplerBinding,
int index, int index,
params IAstNode[] sources) : base(inst, StorageKind.None, false, index, sources, sources.Length) params IAstNode[] sources) : base(inst, StorageKind.None, false, index, sources, sources.Length)
@ -26,8 +30,20 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
Type = type; Type = type;
Format = format; Format = format;
Flags = flags; Flags = flags;
Set = set;
Binding = binding; Binding = binding;
SamplerSet = samplerSet;
SamplerBinding = samplerBinding; SamplerBinding = samplerBinding;
} }
public SetBindingPair GetTextureSetAndBinding()
{
return new SetBindingPair(Set, Binding);
}
public SetBindingPair GetSamplerSetAndBinding()
{
return new SetBindingPair(SamplerSet, SamplerBinding);
}
} }
} }

View file

@ -6,15 +6,15 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{ {
private readonly Dictionary<int, BufferDefinition> _constantBuffers; private readonly Dictionary<int, BufferDefinition> _constantBuffers;
private readonly Dictionary<int, BufferDefinition> _storageBuffers; private readonly Dictionary<int, BufferDefinition> _storageBuffers;
private readonly Dictionary<int, TextureDefinition> _textures; private readonly Dictionary<SetBindingPair, TextureDefinition> _textures;
private readonly Dictionary<int, TextureDefinition> _images; private readonly Dictionary<SetBindingPair, TextureDefinition> _images;
private readonly Dictionary<int, MemoryDefinition> _localMemories; private readonly Dictionary<int, MemoryDefinition> _localMemories;
private readonly Dictionary<int, MemoryDefinition> _sharedMemories; private readonly Dictionary<int, MemoryDefinition> _sharedMemories;
public IReadOnlyDictionary<int, BufferDefinition> ConstantBuffers => _constantBuffers; public IReadOnlyDictionary<int, BufferDefinition> ConstantBuffers => _constantBuffers;
public IReadOnlyDictionary<int, BufferDefinition> StorageBuffers => _storageBuffers; public IReadOnlyDictionary<int, BufferDefinition> StorageBuffers => _storageBuffers;
public IReadOnlyDictionary<int, TextureDefinition> Textures => _textures; public IReadOnlyDictionary<SetBindingPair, TextureDefinition> Textures => _textures;
public IReadOnlyDictionary<int, TextureDefinition> Images => _images; public IReadOnlyDictionary<SetBindingPair, TextureDefinition> Images => _images;
public IReadOnlyDictionary<int, MemoryDefinition> LocalMemories => _localMemories; public IReadOnlyDictionary<int, MemoryDefinition> LocalMemories => _localMemories;
public IReadOnlyDictionary<int, MemoryDefinition> SharedMemories => _sharedMemories; public IReadOnlyDictionary<int, MemoryDefinition> SharedMemories => _sharedMemories;
@ -22,8 +22,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{ {
_constantBuffers = new Dictionary<int, BufferDefinition>(); _constantBuffers = new Dictionary<int, BufferDefinition>();
_storageBuffers = new Dictionary<int, BufferDefinition>(); _storageBuffers = new Dictionary<int, BufferDefinition>();
_textures = new Dictionary<int, TextureDefinition>(); _textures = new Dictionary<SetBindingPair, TextureDefinition>();
_images = new Dictionary<int, TextureDefinition>(); _images = new Dictionary<SetBindingPair, TextureDefinition>();
_localMemories = new Dictionary<int, MemoryDefinition>(); _localMemories = new Dictionary<int, MemoryDefinition>();
_sharedMemories = new Dictionary<int, MemoryDefinition>(); _sharedMemories = new Dictionary<int, MemoryDefinition>();
} }
@ -40,12 +40,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
public void AddOrUpdateTexture(TextureDefinition definition) public void AddOrUpdateTexture(TextureDefinition definition)
{ {
_textures[definition.Binding] = definition; _textures[new(definition.Set, definition.Binding)] = definition;
} }
public void AddOrUpdateImage(TextureDefinition definition) public void AddOrUpdateImage(TextureDefinition definition)
{ {
_images[definition.Binding] = definition; _images[new(definition.Set, definition.Binding)] = definition;
} }
public int AddLocalMemory(MemoryDefinition definition) public int AddLocalMemory(MemoryDefinition definition)

View file

@ -169,7 +169,17 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
AstTextureOperation GetAstTextureOperation(TextureOperation texOp) AstTextureOperation GetAstTextureOperation(TextureOperation texOp)
{ {
return new AstTextureOperation(inst, texOp.Type, texOp.Format, texOp.Flags, texOp.Binding, texOp.SamplerBinding, texOp.Index, sources); return new AstTextureOperation(
inst,
texOp.Type,
texOp.Format,
texOp.Flags,
texOp.Set,
texOp.Binding,
texOp.SamplerSet,
texOp.SamplerBinding,
texOp.Index,
sources);
} }
int componentsCount = BitOperations.PopCount((uint)operation.Index); int componentsCount = BitOperations.PopCount((uint)operation.Index);

View file

@ -4,6 +4,7 @@ namespace Ryujinx.Graphics.Shader
{ {
// New fields should be added to the end of the struct to keep disk shader cache compatibility. // New fields should be added to the end of the struct to keep disk shader cache compatibility.
public readonly int Set;
public readonly int Binding; public readonly int Binding;
public readonly SamplerType Type; public readonly SamplerType Type;
@ -18,6 +19,7 @@ namespace Ryujinx.Graphics.Shader
public readonly TextureUsageFlags Flags; public readonly TextureUsageFlags Flags;
public TextureDescriptor( public TextureDescriptor(
int set,
int binding, int binding,
SamplerType type, SamplerType type,
TextureFormat format, TextureFormat format,
@ -27,6 +29,7 @@ namespace Ryujinx.Graphics.Shader
bool separate, bool separate,
TextureUsageFlags flags) TextureUsageFlags flags)
{ {
Set = set;
Binding = binding; Binding = binding;
Type = type; Type = type;
Format = format; Format = format;

View file

@ -124,7 +124,7 @@ namespace Ryujinx.Graphics.Shader.Translation
this.TextureSample( this.TextureSample(
SamplerType.TextureBuffer, SamplerType.TextureBuffer,
TextureFlags.IntCoords, TextureFlags.IntCoords,
ResourceManager.Reservations.IndexBufferTextureBinding, ResourceManager.Reservations.GetIndexBufferTextureSetAndBinding(),
1, 1,
new[] { vertexIndexVr }, new[] { vertexIndexVr },
new[] { this.IAdd(ibBaseOffset, outputVertexOffset) }); new[] { this.IAdd(ibBaseOffset, outputVertexOffset) });
@ -145,7 +145,7 @@ namespace Ryujinx.Graphics.Shader.Translation
this.TextureSample( this.TextureSample(
SamplerType.TextureBuffer, SamplerType.TextureBuffer,
TextureFlags.IntCoords, TextureFlags.IntCoords,
ResourceManager.Reservations.TopologyRemapBufferTextureBinding, ResourceManager.Reservations.GetTopologyRemapBufferTextureSetAndBinding(),
1, 1,
new[] { vertexIndex }, new[] { vertexIndex },
new[] { this.IAdd(baseVertex, Const(index)) }); new[] { this.IAdd(baseVertex, Const(index)) });

View file

@ -618,12 +618,21 @@ namespace Ryujinx.Graphics.Shader.Translation
SamplerType type, SamplerType type,
TextureFormat format, TextureFormat format,
TextureFlags flags, TextureFlags flags,
int binding, SetBindingPair setAndBinding,
Operand[] sources) Operand[] sources)
{ {
Operand dest = Local(); Operand dest = Local();
context.Add(new TextureOperation(Instruction.ImageAtomic, type, format, flags, binding, 0, new[] { dest }, sources)); context.Add(new TextureOperation(
Instruction.ImageAtomic,
type,
format,
flags,
setAndBinding.SetIndex,
setAndBinding.Binding,
0,
new[] { dest },
sources));
return dest; return dest;
} }
@ -633,12 +642,21 @@ namespace Ryujinx.Graphics.Shader.Translation
SamplerType type, SamplerType type,
TextureFormat format, TextureFormat format,
TextureFlags flags, TextureFlags flags,
int binding, SetBindingPair setAndBinding,
int compMask, int compMask,
Operand[] dests, Operand[] dests,
Operand[] sources) Operand[] sources)
{ {
context.Add(new TextureOperation(Instruction.ImageLoad, type, format, flags, binding, compMask, dests, sources)); context.Add(new TextureOperation(
Instruction.ImageLoad,
type,
format,
flags,
setAndBinding.SetIndex,
setAndBinding.Binding,
compMask,
dests,
sources));
} }
public static void ImageStore( public static void ImageStore(
@ -646,10 +664,19 @@ namespace Ryujinx.Graphics.Shader.Translation
SamplerType type, SamplerType type,
TextureFormat format, TextureFormat format,
TextureFlags flags, TextureFlags flags,
int binding, SetBindingPair setAndBinding,
Operand[] sources) Operand[] sources)
{ {
context.Add(new TextureOperation(Instruction.ImageStore, type, format, flags, binding, 0, null, sources)); context.Add(new TextureOperation(
Instruction.ImageStore,
type,
format,
flags,
setAndBinding.SetIndex,
setAndBinding.Binding,
0,
null,
sources));
} }
public static Operand IsNan(this EmitterContext context, Operand a, Instruction fpType = Instruction.FP32) public static Operand IsNan(this EmitterContext context, Operand a, Instruction fpType = Instruction.FP32)
@ -718,13 +745,22 @@ namespace Ryujinx.Graphics.Shader.Translation
this EmitterContext context, this EmitterContext context,
SamplerType type, SamplerType type,
TextureFlags flags, TextureFlags flags,
int binding, SetBindingPair setAndBinding,
int compIndex, int compIndex,
Operand[] sources) Operand[] sources)
{ {
Operand dest = Local(); Operand dest = Local();
context.Add(new TextureOperation(Instruction.Lod, type, TextureFormat.Unknown, flags, binding, compIndex, new[] { dest }, sources)); context.Add(new TextureOperation(
Instruction.Lod,
type,
TextureFormat.Unknown,
flags,
setAndBinding.SetIndex,
setAndBinding.Binding,
compIndex,
new[] { dest },
sources));
return dest; return dest;
} }
@ -889,24 +925,42 @@ namespace Ryujinx.Graphics.Shader.Translation
this EmitterContext context, this EmitterContext context,
SamplerType type, SamplerType type,
TextureFlags flags, TextureFlags flags,
int binding, SetBindingPair setAndBinding,
int compMask, int compMask,
Operand[] dests, Operand[] dests,
Operand[] sources) Operand[] sources)
{ {
context.Add(new TextureOperation(Instruction.TextureSample, type, TextureFormat.Unknown, flags, binding, compMask, dests, sources)); context.Add(new TextureOperation(
Instruction.TextureSample,
type,
TextureFormat.Unknown,
flags,
setAndBinding.SetIndex,
setAndBinding.Binding,
compMask,
dests,
sources));
} }
public static Operand TextureQuerySamples( public static Operand TextureQuerySamples(
this EmitterContext context, this EmitterContext context,
SamplerType type, SamplerType type,
TextureFlags flags, TextureFlags flags,
int binding, SetBindingPair setAndBinding,
Operand[] sources) Operand[] sources)
{ {
Operand dest = Local(); Operand dest = Local();
context.Add(new TextureOperation(Instruction.TextureQuerySamples, type, TextureFormat.Unknown, flags, binding, 0, new[] { dest }, sources)); context.Add(new TextureOperation(
Instruction.TextureQuerySamples,
type,
TextureFormat.Unknown,
flags,
setAndBinding.SetIndex,
setAndBinding.Binding,
0,
new[] { dest },
sources));
return dest; return dest;
} }
@ -915,13 +969,22 @@ namespace Ryujinx.Graphics.Shader.Translation
this EmitterContext context, this EmitterContext context,
SamplerType type, SamplerType type,
TextureFlags flags, TextureFlags flags,
int binding, SetBindingPair setAndBinding,
int compIndex, int compIndex,
Operand[] sources) Operand[] sources)
{ {
Operand dest = Local(); Operand dest = Local();
context.Add(new TextureOperation(Instruction.TextureQuerySize, type, TextureFormat.Unknown, flags, binding, compIndex, new[] { dest }, sources)); context.Add(new TextureOperation(
Instruction.TextureQuerySize,
type,
TextureFormat.Unknown,
flags,
setAndBinding.SetIndex,
setAndBinding.Binding,
compIndex,
new[] { dest },
sources));
return dest; return dest;
} }

View file

@ -91,7 +91,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
bool hasSampler = !texOp.Inst.IsImage(); bool hasSampler = !texOp.Inst.IsImage();
int textureBinding = resourceManager.GetTextureOrImageBinding( SetBindingPair textureSetAndBinding = resourceManager.GetTextureOrImageBinding(
texOp.Inst, texOp.Inst,
texOp.Type, texOp.Type,
texOp.Format, texOp.Format,
@ -111,7 +111,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
texOp.InsertSource(1, samplerIndex); texOp.InsertSource(1, samplerIndex);
int samplerBinding = resourceManager.GetTextureOrImageBinding( SetBindingPair samplerSetAndBinding = resourceManager.GetTextureOrImageBinding(
texOp.Inst, texOp.Inst,
SamplerType.None, SamplerType.None,
texOp.Format, texOp.Format,
@ -120,11 +120,11 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
TextureHandle.PackOffsets(0, 0, TextureHandleType.Direct), TextureHandle.PackOffsets(0, 0, TextureHandleType.Direct),
samplerPoolLength); samplerPoolLength);
texOp.TurnIntoArray(textureBinding, samplerBinding); texOp.TurnIntoArray(textureSetAndBinding, samplerSetAndBinding);
} }
else else
{ {
texOp.TurnIntoArray(textureBinding); texOp.TurnIntoArray(textureSetAndBinding);
} }
return true; return true;
@ -445,7 +445,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
} }
} }
int binding = resourceManager.GetTextureOrImageBinding( SetBindingPair setAndBinding = resourceManager.GetTextureOrImageBinding(
texOp.Inst, texOp.Inst,
texOp.Type, texOp.Type,
texOp.Format, texOp.Format,
@ -453,7 +453,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
cbufSlot, cbufSlot,
cbufOffset); cbufOffset);
texOp.SetBinding(binding); texOp.SetBinding(setAndBinding);
} }
} }
} }

View file

@ -221,7 +221,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
private static void TurnIntoArray(ResourceManager resourceManager, TextureOperation texOp, int cbufSlot, int handleIndex, int length) private static void TurnIntoArray(ResourceManager resourceManager, TextureOperation texOp, int cbufSlot, int handleIndex, int length)
{ {
int binding = resourceManager.GetTextureOrImageBinding( SetBindingPair setAndBinding = resourceManager.GetTextureOrImageBinding(
texOp.Inst, texOp.Inst,
texOp.Type, texOp.Type,
texOp.Format, texOp.Format,
@ -230,7 +230,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
handleIndex, handleIndex,
length); length);
texOp.TurnIntoArray(binding); texOp.TurnIntoArray(setAndBinding);
} }
} }
} }

View file

@ -20,8 +20,8 @@ namespace Ryujinx.Graphics.Shader.Translation
private readonly ShaderStage _stage; private readonly ShaderStage _stage;
private readonly string _stagePrefix; private readonly string _stagePrefix;
private readonly int[] _cbSlotToBindingMap; private readonly SetBindingPair[] _cbSlotToBindingMap;
private readonly int[] _sbSlotToBindingMap; private readonly SetBindingPair[] _sbSlotToBindingMap;
private uint _sbSlotWritten; private uint _sbSlotWritten;
private readonly Dictionary<int, int> _sbSlots; private readonly Dictionary<int, int> _sbSlots;
@ -33,6 +33,7 @@ namespace Ryujinx.Graphics.Shader.Translation
private struct TextureMeta private struct TextureMeta
{ {
public int Set;
public int Binding; public int Binding;
public bool AccurateType; public bool AccurateType;
public SamplerType Type; public SamplerType Type;
@ -64,10 +65,10 @@ namespace Ryujinx.Graphics.Shader.Translation
_stage = stage; _stage = stage;
_stagePrefix = GetShaderStagePrefix(stage); _stagePrefix = GetShaderStagePrefix(stage);
_cbSlotToBindingMap = new int[18]; _cbSlotToBindingMap = new SetBindingPair[18];
_sbSlotToBindingMap = new int[16]; _sbSlotToBindingMap = new SetBindingPair[16];
_cbSlotToBindingMap.AsSpan().Fill(-1); _cbSlotToBindingMap.AsSpan().Fill(new(-1, -1));
_sbSlotToBindingMap.AsSpan().Fill(-1); _sbSlotToBindingMap.AsSpan().Fill(new(-1, -1));
_sbSlots = new(); _sbSlots = new();
_sbSlotsReverse = new(); _sbSlotsReverse = new();
@ -146,16 +147,16 @@ namespace Ryujinx.Graphics.Shader.Translation
public int GetConstantBufferBinding(int slot) public int GetConstantBufferBinding(int slot)
{ {
int binding = _cbSlotToBindingMap[slot]; SetBindingPair setAndBinding = _cbSlotToBindingMap[slot];
if (binding < 0) if (setAndBinding.Binding < 0)
{ {
binding = _gpuAccessor.CreateConstantBufferBinding(slot); setAndBinding = _gpuAccessor.CreateConstantBufferBinding(slot);
_cbSlotToBindingMap[slot] = binding; _cbSlotToBindingMap[slot] = setAndBinding;
string slotNumber = slot.ToString(CultureInfo.InvariantCulture); string slotNumber = slot.ToString(CultureInfo.InvariantCulture);
AddNewConstantBuffer(binding, $"{_stagePrefix}_c{slotNumber}"); AddNewConstantBuffer(setAndBinding.SetIndex, setAndBinding.Binding, $"{_stagePrefix}_c{slotNumber}");
} }
return binding; return setAndBinding.Binding;
} }
public bool TryGetStorageBufferBinding(int sbCbSlot, int sbCbOffset, bool write, out int binding) public bool TryGetStorageBufferBinding(int sbCbSlot, int sbCbOffset, bool write, out int binding)
@ -166,14 +167,14 @@ namespace Ryujinx.Graphics.Shader.Translation
return false; return false;
} }
binding = _sbSlotToBindingMap[slot]; SetBindingPair setAndBinding = _sbSlotToBindingMap[slot];
if (binding < 0) if (setAndBinding.Binding < 0)
{ {
binding = _gpuAccessor.CreateStorageBufferBinding(slot); setAndBinding = _gpuAccessor.CreateStorageBufferBinding(slot);
_sbSlotToBindingMap[slot] = binding; _sbSlotToBindingMap[slot] = setAndBinding;
string slotNumber = slot.ToString(CultureInfo.InvariantCulture); string slotNumber = slot.ToString(CultureInfo.InvariantCulture);
AddNewStorageBuffer(binding, $"{_stagePrefix}_s{slotNumber}"); AddNewStorageBuffer(setAndBinding.SetIndex, setAndBinding.Binding, $"{_stagePrefix}_s{slotNumber}");
} }
if (write) if (write)
@ -181,6 +182,7 @@ namespace Ryujinx.Graphics.Shader.Translation
_sbSlotWritten |= 1u << slot; _sbSlotWritten |= 1u << slot;
} }
binding = setAndBinding.Binding;
return true; return true;
} }
@ -208,7 +210,7 @@ namespace Ryujinx.Graphics.Shader.Translation
{ {
for (slot = 0; slot < _cbSlotToBindingMap.Length; slot++) for (slot = 0; slot < _cbSlotToBindingMap.Length; slot++)
{ {
if (_cbSlotToBindingMap[slot] == binding) if (_cbSlotToBindingMap[slot].Binding == binding)
{ {
return true; return true;
} }
@ -218,7 +220,7 @@ namespace Ryujinx.Graphics.Shader.Translation
return false; return false;
} }
public int GetTextureOrImageBinding( public SetBindingPair GetTextureOrImageBinding(
Instruction inst, Instruction inst,
SamplerType type, SamplerType type,
TextureFormat format, TextureFormat format,
@ -240,7 +242,7 @@ namespace Ryujinx.Graphics.Shader.Translation
format = TextureFormat.Unknown; format = TextureFormat.Unknown;
} }
int binding = GetTextureOrImageBinding( SetBindingPair setAndBinding = GetTextureOrImageBinding(
cbufSlot, cbufSlot,
handle, handle,
arrayLength, arrayLength,
@ -255,10 +257,10 @@ namespace Ryujinx.Graphics.Shader.Translation
_gpuAccessor.RegisterTexture(handle, cbufSlot); _gpuAccessor.RegisterTexture(handle, cbufSlot);
return binding; return setAndBinding;
} }
private int GetTextureOrImageBinding( private SetBindingPair GetTextureOrImageBinding(
int cbufSlot, int cbufSlot,
int handle, int handle,
int arrayLength, int arrayLength,
@ -311,21 +313,38 @@ namespace Ryujinx.Graphics.Shader.Translation
UsageFlags = usageFlags, UsageFlags = usageFlags,
}; };
int setIndex;
int binding; int binding;
if (dict.TryGetValue(info, out var existingMeta)) if (dict.TryGetValue(info, out var existingMeta))
{ {
dict[info] = MergeTextureMeta(meta, existingMeta); dict[info] = MergeTextureMeta(meta, existingMeta);
setIndex = existingMeta.Set;
binding = existingMeta.Binding; binding = existingMeta.Binding;
} }
else else
{ {
bool isBuffer = (type & SamplerType.Mask) == SamplerType.TextureBuffer; if (arrayLength > 1 && (setIndex = _gpuAccessor.CreateExtraSet()) >= 0)
{
// We reserved an "extra set" for the array.
// In this case the binding is always the first one (0).
// Using separate sets for array is better as we need to do less descriptor set updates.
binding = isImage binding = 0;
? _gpuAccessor.CreateImageBinding(arrayLength, isBuffer) }
: _gpuAccessor.CreateTextureBinding(arrayLength, isBuffer); else
{
bool isBuffer = (type & SamplerType.Mask) == SamplerType.TextureBuffer;
SetBindingPair setAndBinding = isImage
? _gpuAccessor.CreateImageBinding(arrayLength, isBuffer)
: _gpuAccessor.CreateTextureBinding(arrayLength, isBuffer);
setIndex = setAndBinding.SetIndex;
binding = setAndBinding.Binding;
}
meta.Set = setIndex;
meta.Binding = binding; meta.Binding = binding;
dict.Add(info, meta); dict.Add(info, meta);
@ -355,7 +374,7 @@ namespace Ryujinx.Graphics.Shader.Translation
} }
var definition = new TextureDefinition( var definition = new TextureDefinition(
isImage ? 3 : 2, setIndex,
binding, binding,
arrayLength, arrayLength,
separate, separate,
@ -373,11 +392,12 @@ namespace Ryujinx.Graphics.Shader.Translation
Properties.AddOrUpdateTexture(definition); Properties.AddOrUpdateTexture(definition);
} }
return binding; return new SetBindingPair(setIndex, binding);
} }
private static TextureMeta MergeTextureMeta(TextureMeta meta, TextureMeta existingMeta) private static TextureMeta MergeTextureMeta(TextureMeta meta, TextureMeta existingMeta)
{ {
meta.Set = existingMeta.Set;
meta.Binding = existingMeta.Binding; meta.Binding = existingMeta.Binding;
meta.UsageFlags |= existingMeta.UsageFlags; meta.UsageFlags |= existingMeta.UsageFlags;
@ -440,11 +460,11 @@ namespace Ryujinx.Graphics.Shader.Translation
for (int slot = 0; slot < _cbSlotToBindingMap.Length; slot++) for (int slot = 0; slot < _cbSlotToBindingMap.Length; slot++)
{ {
int binding = _cbSlotToBindingMap[slot]; SetBindingPair setAndBinding = _cbSlotToBindingMap[slot];
if (binding >= 0 && _usedConstantBufferBindings.Contains(binding)) if (setAndBinding.Binding >= 0 && _usedConstantBufferBindings.Contains(setAndBinding.Binding))
{ {
descriptors[descriptorIndex++] = new BufferDescriptor(binding, slot); descriptors[descriptorIndex++] = new BufferDescriptor(setAndBinding.SetIndex, setAndBinding.Binding, slot);
} }
} }
@ -464,13 +484,13 @@ namespace Ryujinx.Graphics.Shader.Translation
foreach ((int key, int slot) in _sbSlots) foreach ((int key, int slot) in _sbSlots)
{ {
int binding = _sbSlotToBindingMap[slot]; SetBindingPair setAndBinding = _sbSlotToBindingMap[slot];
if (binding >= 0) if (setAndBinding.Binding >= 0)
{ {
(int sbCbSlot, int sbCbOffset) = UnpackSbCbInfo(key); (int sbCbSlot, int sbCbOffset) = UnpackSbCbInfo(key);
BufferUsageFlags flags = (_sbSlotWritten & (1u << slot)) != 0 ? BufferUsageFlags.Write : BufferUsageFlags.None; BufferUsageFlags flags = (_sbSlotWritten & (1u << slot)) != 0 ? BufferUsageFlags.Write : BufferUsageFlags.None;
descriptors[descriptorIndex++] = new BufferDescriptor(binding, slot, sbCbSlot, sbCbOffset, flags); descriptors[descriptorIndex++] = new BufferDescriptor(setAndBinding.SetIndex, setAndBinding.Binding, slot, sbCbSlot, sbCbOffset, flags);
} }
} }
@ -507,6 +527,7 @@ namespace Ryujinx.Graphics.Shader.Translation
} }
descriptors.Add(new TextureDescriptor( descriptors.Add(new TextureDescriptor(
meta.Set,
meta.Binding, meta.Binding,
meta.Type, meta.Type,
info.Format, info.Format,
@ -527,6 +548,7 @@ namespace Ryujinx.Graphics.Shader.Translation
} }
descriptors.Add(new TextureDescriptor( descriptors.Add(new TextureDescriptor(
meta.Set,
meta.Binding, meta.Binding,
meta.Type, meta.Type,
info.Format, info.Format,
@ -587,24 +609,24 @@ namespace Ryujinx.Graphics.Shader.Translation
return false; return false;
} }
private void AddNewConstantBuffer(int binding, string name) private void AddNewConstantBuffer(int setIndex, int binding, string name)
{ {
StructureType type = new(new[] StructureType type = new(new[]
{ {
new StructureField(AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32, "data", Constants.ConstantBufferSize / 16), new StructureField(AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32, "data", Constants.ConstantBufferSize / 16),
}); });
Properties.AddOrUpdateConstantBuffer(new(BufferLayout.Std140, 0, binding, name, type)); Properties.AddOrUpdateConstantBuffer(new(BufferLayout.Std140, setIndex, binding, name, type));
} }
private void AddNewStorageBuffer(int binding, string name) private void AddNewStorageBuffer(int setIndex, int binding, string name)
{ {
StructureType type = new(new[] StructureType type = new(new[]
{ {
new StructureField(AggregateType.Array | AggregateType.U32, "data", 0), new StructureField(AggregateType.Array | AggregateType.U32, "data", 0),
}); });
Properties.AddOrUpdateStorageBuffer(new(BufferLayout.Std430, 1, binding, name, type)); Properties.AddOrUpdateStorageBuffer(new(BufferLayout.Std430, setIndex, binding, name, type));
} }
public static string GetShaderStagePrefix(ShaderStage stage) public static string GetShaderStagePrefix(ShaderStage stage)

View file

@ -11,6 +11,8 @@ namespace Ryujinx.Graphics.Shader.Translation
public const int MaxVertexBufferTextures = 32; public const int MaxVertexBufferTextures = 32;
private const int TextureSetIndex = 2; // TODO: Get from GPU accessor.
public int VertexInfoConstantBufferBinding { get; } public int VertexInfoConstantBufferBinding { get; }
public int VertexOutputStorageBufferBinding { get; } public int VertexOutputStorageBufferBinding { get; }
public int GeometryVertexOutputStorageBufferBinding { get; } public int GeometryVertexOutputStorageBufferBinding { get; }
@ -163,6 +165,21 @@ namespace Ryujinx.Graphics.Shader.Translation
return _vertexBufferTextureBaseBinding + vaLocation; return _vertexBufferTextureBaseBinding + vaLocation;
} }
public SetBindingPair GetVertexBufferTextureSetAndBinding(int vaLocation)
{
return new SetBindingPair(TextureSetIndex, GetVertexBufferTextureBinding(vaLocation));
}
public SetBindingPair GetIndexBufferTextureSetAndBinding()
{
return new SetBindingPair(TextureSetIndex, IndexBufferTextureBinding);
}
public SetBindingPair GetTopologyRemapBufferTextureSetAndBinding()
{
return new SetBindingPair(TextureSetIndex, TopologyRemapBufferTextureBinding);
}
internal bool TryGetOffset(StorageKind storageKind, int location, int component, out int offset) internal bool TryGetOffset(StorageKind storageKind, int location, int component, out int offset)
{ {
return _offsets.TryGetValue(new IoDefinition(storageKind, IoVariable.UserDefined, location, component), out offset); return _offsets.TryGetValue(new IoDefinition(storageKind, IoVariable.UserDefined, location, component), out offset);

View file

@ -182,6 +182,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
texOp.Type, texOp.Type,
texOp.Format, texOp.Format,
texOp.Flags, texOp.Flags,
texOp.Set,
texOp.Binding, texOp.Binding,
index, index,
new[] { coordSize }, new[] { coordSize },
@ -251,6 +252,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
texOp.Type, texOp.Type,
texOp.Format, texOp.Format,
texOp.Flags, texOp.Flags,
texOp.Set,
texOp.Binding, texOp.Binding,
index, index,
new[] { coordSize }, new[] { coordSize },
@ -471,6 +473,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
texOp.Type, texOp.Type,
texOp.Format, texOp.Format,
texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets), texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets),
texOp.Set,
texOp.Binding, texOp.Binding,
1 << 3, // W component: i=0, j=0 1 << 3, // W component: i=0, j=0
new[] { dests[destIndex++] }, new[] { dests[destIndex++] },
@ -527,6 +530,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
texOp.Type, texOp.Type,
texOp.Format, texOp.Format,
texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets), texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets),
texOp.Set,
texOp.Binding, texOp.Binding,
componentIndex, componentIndex,
dests, dests,
@ -573,6 +577,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
texOp.Type, texOp.Type,
texOp.Format, texOp.Format,
texOp.Flags, texOp.Flags,
texOp.Set,
texOp.Binding, texOp.Binding,
index, index,
new[] { texSizes[index] }, new[] { texSizes[index] },
@ -603,6 +608,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
texOp.Type, texOp.Type,
texOp.Format, texOp.Format,
texOp.Flags, texOp.Flags,
texOp.Set,
texOp.Binding, texOp.Binding,
0, 0,
new[] { lod }, new[] { lod },
@ -633,6 +639,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
texOp.Type, texOp.Type,
texOp.Format, texOp.Format,
texOp.Flags, texOp.Flags,
texOp.Set,
texOp.Binding, texOp.Binding,
index, index,
new[] { texSizes[index] }, new[] { texSizes[index] },

View file

@ -54,6 +54,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
{ {
bool needsSextNorm = context.Definitions.IsAttributePackedRgb10A2Signed(location); bool needsSextNorm = context.Definitions.IsAttributePackedRgb10A2Signed(location);
SetBindingPair setAndBinding = context.ResourceManager.Reservations.GetVertexBufferTextureSetAndBinding(location);
Operand temp = needsSextNorm ? Local() : dest; Operand temp = needsSextNorm ? Local() : dest;
Operand vertexElemOffset = GenerateVertexOffset(context.ResourceManager, node, location, 0); Operand vertexElemOffset = GenerateVertexOffset(context.ResourceManager, node, location, 0);
@ -62,7 +63,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
SamplerType.TextureBuffer, SamplerType.TextureBuffer,
TextureFormat.Unknown, TextureFormat.Unknown,
TextureFlags.IntCoords, TextureFlags.IntCoords,
context.ResourceManager.Reservations.GetVertexBufferTextureBinding(location), setAndBinding.SetIndex,
setAndBinding.Binding,
1 << component, 1 << component,
new[] { temp }, new[] { temp },
new[] { vertexElemOffset })); new[] { vertexElemOffset }));
@ -75,6 +77,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
} }
else else
{ {
SetBindingPair setAndBinding = context.ResourceManager.Reservations.GetVertexBufferTextureSetAndBinding(location);
Operand temp = component > 0 ? Local() : dest; Operand temp = component > 0 ? Local() : dest;
Operand vertexElemOffset = GenerateVertexOffset(context.ResourceManager, node, location, component); Operand vertexElemOffset = GenerateVertexOffset(context.ResourceManager, node, location, component);
@ -83,7 +86,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
SamplerType.TextureBuffer, SamplerType.TextureBuffer,
TextureFormat.Unknown, TextureFormat.Unknown,
TextureFlags.IntCoords, TextureFlags.IntCoords,
context.ResourceManager.Reservations.GetVertexBufferTextureBinding(location), setAndBinding.SetIndex,
setAndBinding.Binding,
1, 1,
new[] { temp }, new[] { temp },
new[] { vertexElemOffset })); new[] { vertexElemOffset }));

View file

@ -412,8 +412,8 @@ namespace Ryujinx.Graphics.Shader.Translation
if (Stage == ShaderStage.Vertex) if (Stage == ShaderStage.Vertex)
{ {
int ibBinding = resourceManager.Reservations.IndexBufferTextureBinding; SetBindingPair ibSetAndBinding = resourceManager.Reservations.GetIndexBufferTextureSetAndBinding();
TextureDefinition indexBuffer = new(2, ibBinding, "ib_data", SamplerType.TextureBuffer); TextureDefinition indexBuffer = new(ibSetAndBinding.SetIndex, ibSetAndBinding.Binding, "ib_data", SamplerType.TextureBuffer);
resourceManager.Properties.AddOrUpdateTexture(indexBuffer); resourceManager.Properties.AddOrUpdateTexture(indexBuffer);
int inputMap = _program.AttributeUsage.UsedInputAttributes; int inputMap = _program.AttributeUsage.UsedInputAttributes;
@ -421,8 +421,8 @@ namespace Ryujinx.Graphics.Shader.Translation
while (inputMap != 0) while (inputMap != 0)
{ {
int location = BitOperations.TrailingZeroCount(inputMap); int location = BitOperations.TrailingZeroCount(inputMap);
int binding = resourceManager.Reservations.GetVertexBufferTextureBinding(location); SetBindingPair setAndBinding = resourceManager.Reservations.GetVertexBufferTextureSetAndBinding(location);
TextureDefinition vaBuffer = new(2, binding, $"vb_data{location}", SamplerType.TextureBuffer); TextureDefinition vaBuffer = new(setAndBinding.SetIndex, setAndBinding.Binding, $"vb_data{location}", SamplerType.TextureBuffer);
resourceManager.Properties.AddOrUpdateTexture(vaBuffer); resourceManager.Properties.AddOrUpdateTexture(vaBuffer);
inputMap &= ~(1 << location); inputMap &= ~(1 << location);
@ -430,8 +430,8 @@ namespace Ryujinx.Graphics.Shader.Translation
} }
else if (Stage == ShaderStage.Geometry) else if (Stage == ShaderStage.Geometry)
{ {
int trbBinding = resourceManager.Reservations.TopologyRemapBufferTextureBinding; SetBindingPair trbSetAndBinding = resourceManager.Reservations.GetTopologyRemapBufferTextureSetAndBinding();
TextureDefinition remapBuffer = new(2, trbBinding, "trb_data", SamplerType.TextureBuffer); TextureDefinition remapBuffer = new(trbSetAndBinding.SetIndex, trbSetAndBinding.Binding, "trb_data", SamplerType.TextureBuffer);
resourceManager.Properties.AddOrUpdateTexture(remapBuffer); resourceManager.Properties.AddOrUpdateTexture(remapBuffer);
int geometryVbOutputSbBinding = resourceManager.Reservations.GeometryVertexOutputStorageBufferBinding; int geometryVbOutputSbBinding = resourceManager.Reservations.GeometryVertexOutputStorageBufferBinding;

View file

@ -69,17 +69,7 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
private record struct ArrayRef<T> private readonly record struct ArrayRef<T>(ShaderStage Stage, T Array);
{
public ShaderStage Stage;
public T Array;
public ArrayRef(ShaderStage stage, T array)
{
Stage = stage;
Array = array;
}
}
private readonly VulkanRenderer _gd; private readonly VulkanRenderer _gd;
private readonly Device _device; private readonly Device _device;
@ -97,6 +87,9 @@ namespace Ryujinx.Graphics.Vulkan
private ArrayRef<TextureArray>[] _textureArrayRefs; private ArrayRef<TextureArray>[] _textureArrayRefs;
private ArrayRef<ImageArray>[] _imageArrayRefs; private ArrayRef<ImageArray>[] _imageArrayRefs;
private ArrayRef<TextureArray>[] _textureArrayExtraRefs;
private ArrayRef<ImageArray>[] _imageArrayExtraRefs;
private readonly DescriptorBufferInfo[] _uniformBuffers; private readonly DescriptorBufferInfo[] _uniformBuffers;
private readonly DescriptorBufferInfo[] _storageBuffers; private readonly DescriptorBufferInfo[] _storageBuffers;
private readonly DescriptorImageInfo[] _textures; private readonly DescriptorImageInfo[] _textures;
@ -152,6 +145,9 @@ namespace Ryujinx.Graphics.Vulkan
_textureArrayRefs = Array.Empty<ArrayRef<TextureArray>>(); _textureArrayRefs = Array.Empty<ArrayRef<TextureArray>>();
_imageArrayRefs = Array.Empty<ArrayRef<ImageArray>>(); _imageArrayRefs = Array.Empty<ArrayRef<ImageArray>>();
_textureArrayExtraRefs = Array.Empty<ArrayRef<TextureArray>>();
_imageArrayExtraRefs = Array.Empty<ArrayRef<ImageArray>>();
_uniformBuffers = new DescriptorBufferInfo[Constants.MaxUniformBufferBindings]; _uniformBuffers = new DescriptorBufferInfo[Constants.MaxUniformBufferBindings];
_storageBuffers = new DescriptorBufferInfo[Constants.MaxStorageBufferBindings]; _storageBuffers = new DescriptorBufferInfo[Constants.MaxStorageBufferBindings];
_textures = new DescriptorImageInfo[Constants.MaxTexturesPerStage]; _textures = new DescriptorImageInfo[Constants.MaxTexturesPerStage];
@ -495,25 +491,39 @@ namespace Ryujinx.Graphics.Vulkan
public void SetTextureArray(CommandBufferScoped cbs, ShaderStage stage, int binding, ITextureArray array) public void SetTextureArray(CommandBufferScoped cbs, ShaderStage stage, int binding, ITextureArray array)
{ {
if (_textureArrayRefs.Length <= binding) ref ArrayRef<TextureArray> arrayRef = ref GetArrayRef(ref _textureArrayRefs, binding, ArrayGrowthSize);
{
Array.Resize(ref _textureArrayRefs, binding + ArrayGrowthSize);
}
if (_textureArrayRefs[binding].Stage != stage || _textureArrayRefs[binding].Array != array) if (arrayRef.Stage != stage || arrayRef.Array != array)
{ {
if (_textureArrayRefs[binding].Array != null) arrayRef.Array?.DecrementBindCount();
{
_textureArrayRefs[binding].Array.Bound = false;
}
if (array is TextureArray textureArray) if (array is TextureArray textureArray)
{ {
textureArray.Bound = true; textureArray.IncrementBindCount();
textureArray.QueueWriteToReadBarriers(cbs, stage.ConvertToPipelineStageFlags()); textureArray.QueueWriteToReadBarriers(cbs, stage.ConvertToPipelineStageFlags());
} }
_textureArrayRefs[binding] = new ArrayRef<TextureArray>(stage, array as TextureArray); arrayRef = new ArrayRef<TextureArray>(stage, array as TextureArray);
SignalDirty(DirtyFlags.Texture);
}
}
public void SetTextureArraySeparate(CommandBufferScoped cbs, ShaderStage stage, int setIndex, ITextureArray array)
{
ref ArrayRef<TextureArray> arrayRef = ref GetArrayRef(ref _textureArrayExtraRefs, setIndex - PipelineBase.DescriptorSetLayouts);
if (arrayRef.Stage != stage || arrayRef.Array != array)
{
arrayRef.Array?.DecrementBindCount();
if (array is TextureArray textureArray)
{
textureArray.IncrementBindCount();
textureArray.QueueWriteToReadBarriers(cbs, stage.ConvertToPipelineStageFlags());
}
arrayRef = new ArrayRef<TextureArray>(stage, array as TextureArray);
SignalDirty(DirtyFlags.Texture); SignalDirty(DirtyFlags.Texture);
} }
@ -521,30 +531,56 @@ namespace Ryujinx.Graphics.Vulkan
public void SetImageArray(CommandBufferScoped cbs, ShaderStage stage, int binding, IImageArray array) public void SetImageArray(CommandBufferScoped cbs, ShaderStage stage, int binding, IImageArray array)
{ {
if (_imageArrayRefs.Length <= binding) ref ArrayRef<ImageArray> arrayRef = ref GetArrayRef(ref _imageArrayRefs, binding, ArrayGrowthSize);
{
Array.Resize(ref _imageArrayRefs, binding + ArrayGrowthSize);
}
if (_imageArrayRefs[binding].Stage != stage || _imageArrayRefs[binding].Array != array) if (arrayRef.Stage != stage || arrayRef.Array != array)
{ {
if (_imageArrayRefs[binding].Array != null) arrayRef.Array?.DecrementBindCount();
{
_imageArrayRefs[binding].Array.Bound = false;
}
if (array is ImageArray imageArray) if (array is ImageArray imageArray)
{ {
imageArray.Bound = true; imageArray.IncrementBindCount();
imageArray.QueueWriteToReadBarriers(cbs, stage.ConvertToPipelineStageFlags()); imageArray.QueueWriteToReadBarriers(cbs, stage.ConvertToPipelineStageFlags());
} }
_imageArrayRefs[binding] = new ArrayRef<ImageArray>(stage, array as ImageArray); arrayRef = new ArrayRef<ImageArray>(stage, array as ImageArray);
SignalDirty(DirtyFlags.Image); SignalDirty(DirtyFlags.Image);
} }
} }
public void SetImageArraySeparate(CommandBufferScoped cbs, ShaderStage stage, int setIndex, IImageArray array)
{
ref ArrayRef<ImageArray> arrayRef = ref GetArrayRef(ref _imageArrayExtraRefs, setIndex - PipelineBase.DescriptorSetLayouts);
if (arrayRef.Stage != stage || arrayRef.Array != array)
{
arrayRef.Array?.DecrementBindCount();
if (array is ImageArray imageArray)
{
imageArray.IncrementBindCount();
imageArray.QueueWriteToReadBarriers(cbs, stage.ConvertToPipelineStageFlags());
}
arrayRef = new ArrayRef<ImageArray>(stage, array as ImageArray);
SignalDirty(DirtyFlags.Image);
}
}
private static ref ArrayRef<T> GetArrayRef<T>(ref ArrayRef<T>[] array, int index, int growthSize = 1)
{
ArgumentOutOfRangeException.ThrowIfNegative(index);
if (array.Length <= index)
{
Array.Resize(ref array, index + growthSize);
}
return ref array[index];
}
public void SetUniformBuffers(CommandBuffer commandBuffer, ReadOnlySpan<BufferAssignment> buffers) public void SetUniformBuffers(CommandBuffer commandBuffer, ReadOnlySpan<BufferAssignment> buffers)
{ {
for (int i = 0; i < buffers.Length; i++) for (int i = 0; i < buffers.Length; i++)
@ -594,31 +630,40 @@ namespace Ryujinx.Graphics.Vulkan
return; return;
} }
var program = _program;
if (_dirty.HasFlag(DirtyFlags.Uniform)) if (_dirty.HasFlag(DirtyFlags.Uniform))
{ {
if (_program.UsePushDescriptors) if (program.UsePushDescriptors)
{ {
UpdateAndBindUniformBufferPd(cbs, pbp); UpdateAndBindUniformBufferPd(cbs);
} }
else else
{ {
UpdateAndBind(cbs, PipelineBase.UniformSetIndex, pbp); UpdateAndBind(cbs, program, PipelineBase.UniformSetIndex, pbp);
} }
} }
if (_dirty.HasFlag(DirtyFlags.Storage)) if (_dirty.HasFlag(DirtyFlags.Storage))
{ {
UpdateAndBind(cbs, PipelineBase.StorageSetIndex, pbp); UpdateAndBind(cbs, program, PipelineBase.StorageSetIndex, pbp);
} }
if (_dirty.HasFlag(DirtyFlags.Texture)) if (_dirty.HasFlag(DirtyFlags.Texture))
{ {
UpdateAndBind(cbs, PipelineBase.TextureSetIndex, pbp); UpdateAndBind(cbs, program, PipelineBase.TextureSetIndex, pbp);
} }
if (_dirty.HasFlag(DirtyFlags.Image)) if (_dirty.HasFlag(DirtyFlags.Image))
{ {
UpdateAndBind(cbs, PipelineBase.ImageSetIndex, pbp); UpdateAndBind(cbs, program, PipelineBase.ImageSetIndex, pbp);
}
if (program.BindingSegments.Length > PipelineBase.DescriptorSetLayouts)
{
// Program is using extra sets, we need to bind those too.
BindExtraSets(cbs, program, pbp);
} }
_dirty = DirtyFlags.None; _dirty = DirtyFlags.None;
@ -658,9 +703,8 @@ namespace Ryujinx.Graphics.Vulkan
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void UpdateAndBind(CommandBufferScoped cbs, int setIndex, PipelineBindPoint pbp) private void UpdateAndBind(CommandBufferScoped cbs, ShaderCollection program, int setIndex, PipelineBindPoint pbp)
{ {
var program = _program;
var bindingSegments = program.BindingSegments[setIndex]; var bindingSegments = program.BindingSegments[setIndex];
if (bindingSegments.Length == 0) if (bindingSegments.Length == 0)
@ -869,7 +913,7 @@ namespace Ryujinx.Graphics.Vulkan
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void UpdateAndBindUniformBufferPd(CommandBufferScoped cbs, PipelineBindPoint pbp) private void UpdateAndBindUniformBufferPd(CommandBufferScoped cbs)
{ {
int sequence = _pdSequence; int sequence = _pdSequence;
var bindingSegments = _program.BindingSegments[PipelineBase.UniformSetIndex]; var bindingSegments = _program.BindingSegments[PipelineBase.UniformSetIndex];
@ -933,6 +977,56 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
private void BindExtraSets(CommandBufferScoped cbs, ShaderCollection program, PipelineBindPoint pbp)
{
for (int setIndex = PipelineBase.DescriptorSetLayouts; setIndex < program.BindingSegments.Length; setIndex++)
{
var bindingSegments = program.BindingSegments[setIndex];
if (bindingSegments.Length == 0)
{
continue;
}
ResourceBindingSegment segment = bindingSegments[0];
if (segment.IsArray)
{
DescriptorSet[] sets = null;
if (segment.Type == ResourceType.Texture ||
segment.Type == ResourceType.Sampler ||
segment.Type == ResourceType.TextureAndSampler ||
segment.Type == ResourceType.BufferTexture)
{
sets = _textureArrayExtraRefs[setIndex - PipelineBase.DescriptorSetLayouts].Array.GetDescriptorSets(
_device,
cbs,
_templateUpdater,
program,
setIndex,
_dummyTexture,
_dummySampler);
}
else if (segment.Type == ResourceType.Image || segment.Type == ResourceType.BufferImage)
{
sets = _imageArrayExtraRefs[setIndex - PipelineBase.DescriptorSetLayouts].Array.GetDescriptorSets(
_device,
cbs,
_templateUpdater,
program,
setIndex,
_dummyTexture);
}
if (sets != null)
{
_gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan<uint>.Empty);
}
}
}
}
public void SignalCommandBufferChange() public void SignalCommandBufferChange()
{ {
_updateDescriptorCacheCbIndex = true; _updateDescriptorCacheCbIndex = true;

View file

@ -2,6 +2,7 @@ using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
{ {
@ -24,12 +25,18 @@ namespace Ryujinx.Graphics.Vulkan
private HashSet<TextureStorage> _storages; private HashSet<TextureStorage> _storages;
private DescriptorSet[] _cachedDescriptorSets;
private int _cachedCommandBufferIndex; private int _cachedCommandBufferIndex;
private int _cachedSubmissionCount; private int _cachedSubmissionCount;
private ShaderCollection _cachedDscProgram;
private int _cachedDscSetIndex;
private int _cachedDscIndex;
private readonly bool _isBuffer; private readonly bool _isBuffer;
public bool Bound; private int _bindCount;
public ImageArray(VulkanRenderer gd, int size, bool isBuffer) public ImageArray(VulkanRenderer gd, int size, bool isBuffer)
{ {
@ -97,8 +104,12 @@ namespace Ryujinx.Graphics.Vulkan
{ {
_cachedCommandBufferIndex = -1; _cachedCommandBufferIndex = -1;
_storages = null; _storages = null;
_cachedDescriptorSets = null;
_gd.PipelineInternal.ForceImageDirty(); if (_bindCount != 0)
{
_gd.PipelineInternal.ForceImageDirty();
}
} }
public void QueueWriteToReadBarriers(CommandBufferScoped cbs, PipelineStageFlags stageFlags) public void QueueWriteToReadBarriers(CommandBufferScoped cbs, PipelineStageFlags stageFlags)
@ -175,5 +186,65 @@ namespace Ryujinx.Graphics.Vulkan
return bufferTextures; return bufferTextures;
} }
public DescriptorSet[] GetDescriptorSets(
Device device,
CommandBufferScoped cbs,
DescriptorSetTemplateUpdater templateUpdater,
ShaderCollection program,
int setIndex,
TextureView dummyTexture)
{
if (_cachedDescriptorSets != null)
{
// We still need to ensure the current command buffer holds a reference to all used textures.
if (!_isBuffer)
{
GetImageInfos(_gd, cbs, dummyTexture);
}
else
{
GetBufferViews(cbs);
}
return _cachedDescriptorSets;
}
_cachedDscProgram?.ReleaseManualDescriptorSetCollection(_cachedDscSetIndex, _cachedDscIndex);
var dsc = program.GetNewManualDescriptorSetCollection(cbs.CommandBufferIndex, setIndex, out _cachedDscIndex).Get(cbs);
DescriptorSetTemplate template = program.Templates[setIndex];
DescriptorSetTemplateWriter tu = templateUpdater.Begin(template);
if (!_isBuffer)
{
tu.Push(GetImageInfos(_gd, cbs, dummyTexture));
}
else
{
tu.Push(GetBufferViews(cbs));
}
var sets = dsc.GetSets();
templateUpdater.Commit(_gd, device, sets[0]);
_cachedDescriptorSets = sets;
_cachedDscProgram = program;
_cachedDscSetIndex = setIndex;
return sets;
}
public void IncrementBindCount()
{
_bindCount++;
}
public void DecrementBindCount()
{
int newBindCount = --_bindCount;
Debug.Assert(newBindCount >= 0);
}
} }
} }

View file

@ -751,14 +751,12 @@ namespace Ryujinx.Graphics.Vulkan
_vertexBufferUpdater.Commit(Cbs); _vertexBufferUpdater.Commit(Cbs);
} }
#pragma warning disable CA1822 // Mark member as static
public void SetAlphaTest(bool enable, float reference, CompareOp op) public void SetAlphaTest(bool enable, float reference, CompareOp op)
{ {
// This is currently handled using shader specialization, as Vulkan does not support alpha test. // This is currently handled using shader specialization, as Vulkan does not support alpha test.
// In the future, we may want to use this to write the reference value into the support buffer, // In the future, we may want to use this to write the reference value into the support buffer,
// to avoid creating one version of the shader per reference value used. // to avoid creating one version of the shader per reference value used.
} }
#pragma warning restore CA1822
public void SetBlendState(AdvancedBlendDescriptor blend) public void SetBlendState(AdvancedBlendDescriptor blend)
{ {
@ -903,6 +901,11 @@ namespace Ryujinx.Graphics.Vulkan
_descriptorSetUpdater.SetImageArray(Cbs, stage, binding, array); _descriptorSetUpdater.SetImageArray(Cbs, stage, binding, array);
} }
public void SetImageArraySeparate(ShaderStage stage, int setIndex, IImageArray array)
{
_descriptorSetUpdater.SetImageArraySeparate(Cbs, stage, setIndex, array);
}
public void SetIndexBuffer(BufferRange buffer, IndexType type) public void SetIndexBuffer(BufferRange buffer, IndexType type)
{ {
if (buffer.Handle != BufferHandle.Null) if (buffer.Handle != BufferHandle.Null)
@ -945,7 +948,6 @@ namespace Ryujinx.Graphics.Vulkan
// TODO: Default levels (likely needs emulation on shaders?) // TODO: Default levels (likely needs emulation on shaders?)
} }
#pragma warning disable CA1822 // Mark member as static
public void SetPointParameters(float size, bool isProgramPointSize, bool enablePointSprite, Origin origin) public void SetPointParameters(float size, bool isProgramPointSize, bool enablePointSprite, Origin origin)
{ {
// TODO. // TODO.
@ -955,7 +957,6 @@ namespace Ryujinx.Graphics.Vulkan
{ {
// TODO. // TODO.
} }
#pragma warning restore CA1822
public void SetPrimitiveRestart(bool enable, int index) public void SetPrimitiveRestart(bool enable, int index)
{ {
@ -1156,6 +1157,11 @@ namespace Ryujinx.Graphics.Vulkan
_descriptorSetUpdater.SetTextureArray(Cbs, stage, binding, array); _descriptorSetUpdater.SetTextureArray(Cbs, stage, binding, array);
} }
public void SetTextureArraySeparate(ShaderStage stage, int setIndex, ITextureArray array)
{
_descriptorSetUpdater.SetTextureArraySeparate(Cbs, stage, setIndex, array);
}
public void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers) public void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers)
{ {
PauseTransformFeedbackInternal(); PauseTransformFeedbackInternal();
@ -1186,12 +1192,10 @@ namespace Ryujinx.Graphics.Vulkan
_descriptorSetUpdater.SetUniformBuffers(CommandBuffer, buffers); _descriptorSetUpdater.SetUniformBuffers(CommandBuffer, buffers);
} }
#pragma warning disable CA1822 // Mark member as static
public void SetUserClipDistance(int index, bool enableClip) public void SetUserClipDistance(int index, bool enableClip)
{ {
// TODO. // TODO.
} }
#pragma warning restore CA1822
public void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs) public void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs)
{ {

View file

@ -3,6 +3,7 @@ using Silk.NET.Vulkan;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
{ {
@ -27,6 +28,24 @@ namespace Ryujinx.Graphics.Vulkan
private int _dsLastCbIndex; private int _dsLastCbIndex;
private int _dsLastSubmissionCount; private int _dsLastSubmissionCount;
private struct ManualDescriptorSetEntry
{
public Auto<DescriptorSetCollection> DescriptorSet;
public int CbIndex;
public int CbSubmissionCount;
public bool InUse;
public ManualDescriptorSetEntry(Auto<DescriptorSetCollection> descriptorSet, int cbIndex, int cbSubmissionCount, bool inUse)
{
DescriptorSet = descriptorSet;
CbIndex = cbIndex;
CbSubmissionCount = cbSubmissionCount;
InUse = inUse;
}
}
private readonly List<ManualDescriptorSetEntry>[] _manualDsCache;
private readonly Dictionary<long, DescriptorSetTemplate> _pdTemplates; private readonly Dictionary<long, DescriptorSetTemplate> _pdTemplates;
private readonly ResourceDescriptorCollection _pdDescriptors; private readonly ResourceDescriptorCollection _pdDescriptors;
private long _lastPdUsage; private long _lastPdUsage;
@ -50,6 +69,7 @@ namespace Ryujinx.Graphics.Vulkan
} }
_dsCacheCursor = new int[setsCount]; _dsCacheCursor = new int[setsCount];
_manualDsCache = new List<ManualDescriptorSetEntry>[setsCount];
} }
public PipelineLayoutCacheEntry( public PipelineLayoutCacheEntry(
@ -124,6 +144,51 @@ namespace Ryujinx.Graphics.Vulkan
return list[index]; return list[index];
} }
public Auto<DescriptorSetCollection> GetNewManualDescriptorSetCollection(int commandBufferIndex, int setIndex, out int cacheIndex)
{
int submissionCount = _gd.CommandBufferPool.GetSubmissionCount(commandBufferIndex);
var list = _manualDsCache[setIndex] ??= new();
var span = CollectionsMarshal.AsSpan(list);
for (int index = 0; index < span.Length; index++)
{
ref ManualDescriptorSetEntry entry = ref span[index];
if (!entry.InUse && (entry.CbIndex != commandBufferIndex || entry.CbSubmissionCount != submissionCount))
{
entry.InUse = true;
entry.CbIndex = commandBufferIndex;
entry.CbSubmissionCount = submissionCount;
cacheIndex = index;
return entry.DescriptorSet;
}
}
var dsc = _descriptorSetManager.AllocateDescriptorSet(
_gd.Api,
DescriptorSetLayouts[setIndex],
_poolSizes[setIndex],
setIndex,
_consumedDescriptorsPerSet[setIndex],
false);
cacheIndex = list.Count;
list.Add(new ManualDescriptorSetEntry(dsc, commandBufferIndex, submissionCount, inUse: true));
return dsc;
}
public void ReleaseManualDescriptorSetCollection(int setIndex, int cacheIndex)
{
var list = _manualDsCache[setIndex];
var span = CollectionsMarshal.AsSpan(list);
span[cacheIndex].InUse = false;
}
private static Span<DescriptorPoolSize> GetDescriptorPoolSizes(Span<DescriptorPoolSize> output, ResourceDescriptorCollection setDescriptor, uint multiplier) private static Span<DescriptorPoolSize> GetDescriptorPoolSizes(Span<DescriptorPoolSize> output, ResourceDescriptorCollection setDescriptor, uint multiplier)
{ {
int count = 0; int count = 0;
@ -204,6 +269,21 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
for (int i = 0; i < _manualDsCache.Length; i++)
{
if (_manualDsCache[i] == null)
{
continue;
}
for (int j = 0; j < _manualDsCache[i].Count; j++)
{
_manualDsCache[i][j].DescriptorSet.Dispose();
}
_manualDsCache[i].Clear();
}
_gd.Api.DestroyPipelineLayout(_device, PipelineLayout, null); _gd.Api.DestroyPipelineLayout(_device, PipelineLayout, null);
for (int i = 0; i < DescriptorSetLayouts.Length; i++) for (int i = 0; i < DescriptorSetLayouts.Length; i++)

View file

@ -604,6 +604,16 @@ namespace Ryujinx.Graphics.Vulkan
return _plce.GetNewDescriptorSetCollection(setIndex, out isNew); return _plce.GetNewDescriptorSetCollection(setIndex, out isNew);
} }
public Auto<DescriptorSetCollection> GetNewManualDescriptorSetCollection(int commandBufferIndex, int setIndex, out int cacheIndex)
{
return _plce.GetNewManualDescriptorSetCollection(commandBufferIndex, setIndex, out cacheIndex);
}
public void ReleaseManualDescriptorSetCollection(int setIndex, int cacheIndex)
{
_plce.ReleaseManualDescriptorSetCollection(setIndex, cacheIndex);
}
public bool HasSameLayout(ShaderCollection other) public bool HasSameLayout(ShaderCollection other)
{ {
return other != null && _plce == other._plce; return other != null && _plce == other._plce;

View file

@ -2,6 +2,7 @@ using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
{ {
@ -24,12 +25,18 @@ namespace Ryujinx.Graphics.Vulkan
private HashSet<TextureStorage> _storages; private HashSet<TextureStorage> _storages;
private DescriptorSet[] _cachedDescriptorSets;
private int _cachedCommandBufferIndex; private int _cachedCommandBufferIndex;
private int _cachedSubmissionCount; private int _cachedSubmissionCount;
private ShaderCollection _cachedDscProgram;
private int _cachedDscSetIndex;
private int _cachedDscIndex;
private readonly bool _isBuffer; private readonly bool _isBuffer;
public bool Bound; private int _bindCount;
public TextureArray(VulkanRenderer gd, int size, bool isBuffer) public TextureArray(VulkanRenderer gd, int size, bool isBuffer)
{ {
@ -106,8 +113,12 @@ namespace Ryujinx.Graphics.Vulkan
{ {
_cachedCommandBufferIndex = -1; _cachedCommandBufferIndex = -1;
_storages = null; _storages = null;
_cachedDescriptorSets = null;
_gd.PipelineInternal.ForceTextureDirty(); if (_bindCount != 0)
{
_gd.PipelineInternal.ForceTextureDirty();
}
} }
public void QueueWriteToReadBarriers(CommandBufferScoped cbs, PipelineStageFlags stageFlags) public void QueueWriteToReadBarriers(CommandBufferScoped cbs, PipelineStageFlags stageFlags)
@ -190,5 +201,66 @@ namespace Ryujinx.Graphics.Vulkan
return bufferTextures; return bufferTextures;
} }
public DescriptorSet[] GetDescriptorSets(
Device device,
CommandBufferScoped cbs,
DescriptorSetTemplateUpdater templateUpdater,
ShaderCollection program,
int setIndex,
TextureView dummyTexture,
SamplerHolder dummySampler)
{
if (_cachedDescriptorSets != null)
{
// We still need to ensure the current command buffer holds a reference to all used textures.
if (!_isBuffer)
{
GetImageInfos(_gd, cbs, dummyTexture, dummySampler);
}
else
{
GetBufferViews(cbs);
}
return _cachedDescriptorSets;
}
_cachedDscProgram?.ReleaseManualDescriptorSetCollection(_cachedDscSetIndex, _cachedDscIndex);
var dsc = program.GetNewManualDescriptorSetCollection(cbs.CommandBufferIndex, setIndex, out _cachedDscIndex).Get(cbs);
DescriptorSetTemplate template = program.Templates[setIndex];
DescriptorSetTemplateWriter tu = templateUpdater.Begin(template);
if (!_isBuffer)
{
tu.Push(GetImageInfos(_gd, cbs, dummyTexture, dummySampler));
}
else
{
tu.Push(GetBufferViews(cbs));
}
var sets = dsc.GetSets();
templateUpdater.Commit(_gd, device, sets[0]);
_cachedDescriptorSets = sets;
_cachedDscProgram = program;
_cachedDscSetIndex = setIndex;
return sets;
}
public void IncrementBindCount()
{
_bindCount++;
}
public void DecrementBindCount()
{
int newBindCount = --_bindCount;
Debug.Assert(newBindCount >= 0);
}
} }
} }

View file

@ -728,6 +728,12 @@ namespace Ryujinx.Graphics.Vulkan
supportsViewportSwizzle: false, supportsViewportSwizzle: false,
supportsIndirectParameters: true, supportsIndirectParameters: true,
supportsDepthClipControl: Capabilities.SupportsDepthClipControl, supportsDepthClipControl: Capabilities.SupportsDepthClipControl,
uniformBufferSetIndex: PipelineBase.UniformSetIndex,
storageBufferSetIndex: PipelineBase.StorageSetIndex,
textureSetIndex: PipelineBase.TextureSetIndex,
imageSetIndex: PipelineBase.ImageSetIndex,
extraSetBaseIndex: PipelineBase.DescriptorSetLayouts,
maximumExtraSets: Math.Max(0, (int)limits.MaxBoundDescriptorSets - PipelineBase.DescriptorSetLayouts),
maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage, maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage,
maximumStorageBuffersPerStage: Constants.MaxStorageBuffersPerStage, maximumStorageBuffersPerStage: Constants.MaxStorageBuffersPerStage,
maximumTexturesPerStage: Constants.MaxTexturesPerStage, maximumTexturesPerStage: Constants.MaxTexturesPerStage,

View file

@ -25,32 +25,32 @@ namespace Ryujinx.ShaderTools
_imagesCount = 0; _imagesCount = 0;
} }
public int CreateConstantBufferBinding(int index) public SetBindingPair CreateConstantBufferBinding(int index)
{ {
return index + 1; return new SetBindingPair(0, index + 1);
} }
public int CreateImageBinding(int count, bool isBuffer) public SetBindingPair CreateImageBinding(int count, bool isBuffer)
{ {
int binding = _imagesCount; int binding = _imagesCount;
_imagesCount += count; _imagesCount += count;
return binding; return new SetBindingPair(3, binding);
} }
public int CreateStorageBufferBinding(int index) public SetBindingPair CreateStorageBufferBinding(int index)
{ {
return index; return new SetBindingPair(1, index);
} }
public int CreateTextureBinding(int count, bool isBuffer) public SetBindingPair CreateTextureBinding(int count, bool isBuffer)
{ {
int binding = _texturesCount; int binding = _texturesCount;
_texturesCount += count; _texturesCount += count;
return binding; return new SetBindingPair(2, binding);
} }
public ReadOnlySpan<ulong> GetCode(ulong address, int minimumSize) public ReadOnlySpan<ulong> GetCode(ulong address, int minimumSize)