Files
Kenji-NX/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs
T
LotP c6deb3800f Memory Changes 3.2
Fixes a few crashes:
- fixes a crash related to waking threads (priorities were wrong).
- fixes a crash from reusing the SetRenderTargets texture array (left-over data causing issues).
- fixes a mistake and an oversight in the buffer system.
  - buffers were getting updated wrong causing bad data to be stored or some times cut.
  - modified ranges would extend past their old buffers, crashing on syncs. Old buffers are now skipped as the new buffers already sync instead.

Introduces pooling in a few more places to increase memory efficiency.

simplified RangeList item logic.
- removed RangeItem by making all the range objects use the I(NonOverlapping)Range interface.
- BufferCache class no longer locks its RangeList, as the list is only ever accessed synchronously.

Small change to how keyboard snapshots are stored.

Increase ThreadedRenderer SpanPool size to fit slightly more data (4MB -> 8MB).
2025-12-08 20:26:36 -06:00

514 lines
17 KiB
C#

using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan;
using System;
using Format = Ryujinx.Graphics.GAL.Format;
using VkFormat = Silk.NET.Vulkan.Format;
namespace Ryujinx.Graphics.Vulkan
{
class FramebufferParams
{
private readonly Device _device;
private Auto<DisposableImageView>[] _attachments;
private TextureView[] _colors;
private TextureView _depthStencil;
private TextureView[] _colorsCanonical;
private TextureView _baseAttachment;
private uint _validColorAttachments;
private int _totalCount;
public uint Width { get; private set; }
public uint Height { get; private set; }
public uint Layers { get; private set; }
public uint[] AttachmentSamples { get; private set; }
public VkFormat[] AttachmentFormats { get; private set; }
public int[] AttachmentIndices { get; private set; }
public uint AttachmentIntegerFormatMask { get; private set; }
public bool LogicOpsAllowed { get; private set; }
public int AttachmentsCount { get; private set; }
public int MaxColorAttachmentIndex => ColorAttachmentsCount > 0 ? AttachmentIndices[ColorAttachmentsCount - 1] : -1;
public bool HasDepthStencil { get; private set; }
public int ColorAttachmentsCount => AttachmentsCount - (HasDepthStencil ? 1 : 0);
public FramebufferParams(Device device, TextureView view, uint width, uint height)
{
var format = view.Info.Format;
bool isDepthStencil = format.IsDepthOrStencil();
_device = device;
_attachments = [view.GetImageViewForAttachment()];
_validColorAttachments = isDepthStencil ? 0u : 1u;
_baseAttachment = view;
if (isDepthStencil)
{
_depthStencil = view;
}
else
{
_colors = [view];
_colorsCanonical = [view];
}
Width = width;
Height = height;
Layers = 1;
AttachmentSamples = [(uint)view.Info.Samples];
AttachmentFormats = [view.VkFormat];
AttachmentIndices = isDepthStencil ? [] : [0];
AttachmentIntegerFormatMask = format.IsInteger() ? 1u : 0u;
LogicOpsAllowed = !format.IsFloatOrSrgb();
AttachmentsCount = 1;
_totalCount = 1;
HasDepthStencil = isDepthStencil;
}
public FramebufferParams(Device device, ReadOnlySpan<ITexture> colors, ITexture depthStencil)
{
_device = device;
int colorsCount = 0;
_colorsCanonical = new TextureView[colors.Length];
for (int i = 0; i < colors.Length; i++)
{
ITexture color = colors[i];
if (color is TextureView { Valid: true } view)
{
colorsCount++;
_colorsCanonical[i] = view;
}
else
{
_colorsCanonical[i] = null;
}
}
int count = colorsCount + (IsValidTextureView(depthStencil) ? 1 : 0);
_attachments = new Auto<DisposableImageView>[count];
_colors = new TextureView[colorsCount];
AttachmentSamples = new uint[count];
AttachmentFormats = new VkFormat[count];
AttachmentIndices = new int[colorsCount];
uint width = uint.MaxValue;
uint height = uint.MaxValue;
uint layers = uint.MaxValue;
int index = 0;
int bindIndex = 0;
uint attachmentIntegerFormatMask = 0;
bool allFormatsFloatOrSrgb = colorsCount != 0;
foreach (ITexture color in colors)
{
if (IsValidTextureView(color))
{
var texture = (TextureView)color;
_attachments[index] = texture.GetImageViewForAttachment();
_colors[index] = texture;
_validColorAttachments |= 1u << bindIndex;
_baseAttachment = texture;
AttachmentSamples[index] = (uint)texture.Info.Samples;
AttachmentFormats[index] = texture.VkFormat;
AttachmentIndices[index] = bindIndex;
var format = texture.Info.Format;
if (format.IsInteger())
{
attachmentIntegerFormatMask |= 1u << bindIndex;
}
allFormatsFloatOrSrgb &= format.IsFloatOrSrgb();
width = Math.Min(width, (uint)texture.Width);
height = Math.Min(height, (uint)texture.Height);
layers = Math.Min(layers, (uint)texture.Layers);
if (++index >= colorsCount)
{
break;
}
}
bindIndex++;
}
AttachmentIntegerFormatMask = attachmentIntegerFormatMask;
LogicOpsAllowed = !allFormatsFloatOrSrgb;
if (depthStencil is TextureView { Valid: true } dsTexture)
{
_attachments[count - 1] = dsTexture.GetImageViewForAttachment();
_depthStencil = dsTexture;
_baseAttachment ??= dsTexture;
AttachmentSamples[count - 1] = (uint)dsTexture.Info.Samples;
AttachmentFormats[count - 1] = dsTexture.VkFormat;
width = Math.Min(width, (uint)dsTexture.Width);
height = Math.Min(height, (uint)dsTexture.Height);
layers = Math.Min(layers, (uint)dsTexture.Layers);
HasDepthStencil = true;
}
if (count == 0)
{
width = height = layers = 1;
}
Width = width;
Height = height;
Layers = layers;
AttachmentsCount = count;
_totalCount = colors.Length;
}
public FramebufferParams Update(ReadOnlySpan<ITexture> colors, ITexture depthStencil)
{
int colorsCount = 0;
foreach (ITexture color in colors)
{
if (IsValidTextureView(color))
{
colorsCount++;
}
}
int count = colorsCount + (IsValidTextureView(depthStencil) ? 1 : 0);
Array.Clear(_attachments);
Array.Clear(_colors);
if (_attachments.Length < count)
{
Array.Resize(ref _attachments, count);
}
if (_colors.Length < colorsCount)
{
Array.Resize(ref _colors, colorsCount);
}
if (_colorsCanonical.Length < colors.Length)
{
Array.Resize(ref _colorsCanonical, colors.Length);
}
for (int i = 0; i < colors.Length; i++)
{
ITexture color = colors[i];
if (color is TextureView { Valid: true } view)
{
_colorsCanonical[i] = view;
}
else
{
_colorsCanonical[i] = null;
}
}
Array.Clear(AttachmentSamples);
Array.Clear(AttachmentFormats);
Array.Clear(AttachmentIndices);
if (AttachmentSamples.Length < count)
{
uint[] attachmentSamples = AttachmentSamples;
Array.Resize(ref attachmentSamples, count);
AttachmentSamples = attachmentSamples;
}
if (AttachmentFormats.Length < count)
{
VkFormat[] attachmentFormats = AttachmentFormats;
Array.Resize(ref attachmentFormats, count);
AttachmentFormats = attachmentFormats;
}
if (AttachmentIndices.Length < colorsCount)
{
int[] attachmentIndices = AttachmentIndices;
Array.Resize(ref attachmentIndices, colorsCount);
AttachmentIndices = attachmentIndices;
}
uint width = uint.MaxValue;
uint height = uint.MaxValue;
uint layers = uint.MaxValue;
int index = 0;
uint attachmentIntegerFormatMask = 0;
bool allFormatsFloatOrSrgb = colorsCount != 0;
_validColorAttachments = 0;
_baseAttachment = null;
for (int bindIndex = 0; bindIndex < colors.Length; bindIndex++)
{
TextureView texture = _colorsCanonical[bindIndex];
if (texture is not null)
{
_attachments[index] = texture.GetImageViewForAttachment();
_colors[index] = texture;
_validColorAttachments |= 1u << bindIndex;
_baseAttachment = texture;
AttachmentSamples[index] = (uint)texture.Info.Samples;
AttachmentFormats[index] = texture.VkFormat;
AttachmentIndices[index] = bindIndex;
Format format = texture.Info.Format;
if (format.IsInteger())
{
attachmentIntegerFormatMask |= 1u << bindIndex;
}
allFormatsFloatOrSrgb &= format.IsFloatOrSrgb();
width = Math.Min(width, (uint)texture.Width);
height = Math.Min(height, (uint)texture.Height);
layers = Math.Min(layers, (uint)texture.Layers);
if (++index >= colorsCount)
{
break;
}
}
}
AttachmentIntegerFormatMask = attachmentIntegerFormatMask;
LogicOpsAllowed = !allFormatsFloatOrSrgb;
_depthStencil = null;
HasDepthStencil = false;
if (depthStencil is TextureView { Valid: true } dsTexture)
{
_attachments[count - 1] = dsTexture.GetImageViewForAttachment();
_depthStencil = dsTexture;
_baseAttachment ??= dsTexture;
AttachmentSamples[count - 1] = (uint)dsTexture.Info.Samples;
AttachmentFormats[count - 1] = dsTexture.VkFormat;
width = Math.Min(width, (uint)dsTexture.Width);
height = Math.Min(height, (uint)dsTexture.Height);
layers = Math.Min(layers, (uint)dsTexture.Layers);
HasDepthStencil = true;
}
if (count == 0)
{
width = height = layers = 1;
}
Width = width;
Height = height;
Layers = layers;
AttachmentsCount = count;
_totalCount = colors.Length;
return this;
}
public Auto<DisposableImageView> GetAttachment(int index)
{
if ((uint)index >= AttachmentsCount)
{
return null;
}
return _attachments[index];
}
public Auto<DisposableImageView> GetDepthStencilAttachment()
{
if (!HasDepthStencil)
{
return null;
}
return _attachments[AttachmentsCount - 1];
}
public ComponentType GetAttachmentComponentType(int index)
{
if (_colors != null && (uint)index < ColorAttachmentsCount)
{
var format = _colors[index].Info.Format;
if (format.IsSint())
{
return ComponentType.SignedInteger;
}
if (format.IsUint())
{
return ComponentType.UnsignedInteger;
}
}
return ComponentType.Float;
}
public ImageAspectFlags GetDepthStencilAspectFlags()
{
if (_depthStencil == null)
{
return ImageAspectFlags.None;
}
return _depthStencil.Info.Format.ConvertAspectFlags();
}
public bool IsValidColorAttachment(int bindIndex)
{
return (uint)bindIndex < Constants.MaxRenderTargets && (_validColorAttachments & (1u << bindIndex)) != 0;
}
private static bool IsValidTextureView(ITexture texture)
{
return texture is TextureView { Valid: true };
}
public ClearRect GetClearRect(Rectangle<int> scissor, int layer, int layerCount)
{
int x = scissor.X;
int y = scissor.Y;
int width = Math.Min((int)Width - scissor.X, scissor.Width);
int height = Math.Min((int)Height - scissor.Y, scissor.Height);
return new ClearRect(new Rect2D(new Offset2D(x, y), new Extent2D((uint)width, (uint)height)), (uint)layer, (uint)layerCount);
}
public unsafe Auto<DisposableFramebuffer> Create(Vk api, CommandBufferScoped cbs, Auto<DisposableRenderPass> renderPass)
{
ImageView* attachments = stackalloc ImageView[AttachmentsCount];
for (int i = 0; i < AttachmentsCount; i++)
{
attachments[i] = _attachments[i].Get(cbs).Value;
}
var framebufferCreateInfo = new FramebufferCreateInfo
{
SType = StructureType.FramebufferCreateInfo,
RenderPass = renderPass.Get(cbs).Value,
AttachmentCount = (uint)AttachmentsCount,
PAttachments = attachments,
Width = Width,
Height = Height,
Layers = Layers,
};
api.CreateFramebuffer(_device, in framebufferCreateInfo, null, out var framebuffer).ThrowOnError();
return new Auto<DisposableFramebuffer>(new DisposableFramebuffer(api, _device, framebuffer), null, _attachments[..AttachmentsCount]);
}
public TextureView[] GetAttachmentViews()
{
var result = new TextureView[AttachmentsCount];
_colors?.AsSpan(..ColorAttachmentsCount).CopyTo(result.AsSpan());
if (_depthStencil != null)
{
result[^1] = _depthStencil;
}
return result;
}
public RenderPassCacheKey GetRenderPassCacheKey()
{
return new RenderPassCacheKey(_depthStencil, _colorsCanonical);
}
public void InsertLoadOpBarriers(VulkanRenderer gd, CommandBufferScoped cbs)
{
if (_colors != null)
{
int count = ColorAttachmentsCount;
for (int i = 0; i < count; i++)
{
TextureView color = _colors[i];
// If Clear or DontCare were used, this would need to be write bit.
color.Storage?.QueueLoadOpBarrier(cbs, false);
}
}
_depthStencil?.Storage?.QueueLoadOpBarrier(cbs, true);
gd.Barriers.Flush(cbs, false, null, null);
}
public void AddStoreOpUsage()
{
if (_colors != null)
{
int count = ColorAttachmentsCount;
for (int i = 0; i < count; i++)
{
TextureView color = _colors[i];
color.Storage?.AddStoreOpUsage(false);
}
}
_depthStencil?.Storage?.AddStoreOpUsage(true);
}
public void ClearBindings()
{
_depthStencil?.Storage.ClearBindings();
for (int i = 0; i < _totalCount; i++)
{
_colorsCanonical[i]?.Storage.ClearBindings();
}
}
public void AddBindings()
{
_depthStencil?.Storage.AddBinding(_depthStencil);
for (int i = 0; i < _totalCount; i++)
{
TextureView color = _colorsCanonical[i];
color?.Storage.AddBinding(color);
}
}
public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
VulkanRenderer gd,
Device device,
CommandBufferScoped cbs)
{
return _baseAttachment.GetPassAndFramebuffer(gd, device, cbs, this);
}
public TextureView GetColorView(int index)
{
return _colorsCanonical[index];
}
public TextureView GetDepthStencilView()
{
return _depthStencil;
}
}
}