aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorriperiperi <rhy3756547@hotmail.com>2024-01-31 22:49:50 +0000
committerGitHub <noreply@github.com>2024-01-31 23:49:50 +0100
commitc94f0fbb8307873f68df982c100d3fb01aa6ccf5 (patch)
tree327a039f016b3e0ae45713e0f5dd413a04d673ae /src
parentd1b30fbe08d79ad81167358779d77cf4e7167386 (diff)
Vulkan: Add Render Pass / Framebuffer Cache (#6182)1.1.1154
* Vulkan: Add Render Pass / Framebuffer Cache Cache is owned by each texture view. - Window's way of getting framebuffer cache for swapchain images is really messy - it creates a TextureView out of just a vk image view, with invalid info and no storage. * Clear up limited use of alternate TextureView constructor * Formatting and messages * More formatting and messages I apologize for `_colorsCanonical[index]?.Storage?.InsertReadToWriteBarrier`, the compiler made me do it * Self review, change GetFramebuffer to GetPassAndFramebuffer * Avoid allocations on Remove for HashTableSlim * Member can be readonly * Generate texture create info for swapchain images * Improve hashcode * Remove format, samples, size and isDepthStencil when possible Tested in a number of games, seems fine. * Removed load op barriers These can be introduced later. * Reintroduce UpdateModifications Technically meant to be replaced by load op stuff.
Diffstat (limited to 'src')
-rw-r--r--src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs2
-rw-r--r--src/Ryujinx.Graphics.Vulkan/FormatTable.cs14
-rw-r--r--src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs107
-rw-r--r--src/Ryujinx.Graphics.Vulkan/HashTableSlim.cs87
-rw-r--r--src/Ryujinx.Graphics.Vulkan/HelperShader.cs92
-rw-r--r--src/Ryujinx.Graphics.Vulkan/PipelineBase.cs98
-rw-r--r--src/Ryujinx.Graphics.Vulkan/PipelineFull.cs5
-rw-r--r--src/Ryujinx.Graphics.Vulkan/PipelineHelperShader.cs13
-rw-r--r--src/Ryujinx.Graphics.Vulkan/RenderPassCacheKey.cs43
-rw-r--r--src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs180
-rw-r--r--src/Ryujinx.Graphics.Vulkan/TextureView.cs67
-rw-r--r--src/Ryujinx.Graphics.Vulkan/Window.cs35
12 files changed, 512 insertions, 231 deletions
diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs
index 802b73b8..be392fe0 100644
--- a/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs
+++ b/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs
@@ -257,7 +257,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
scissors[0] = new Rectangle<int>(0, 0, texture.Width, texture.Height);
- _pipeline.SetRenderTarget(texture.GetImageViewForAttachment(), (uint)texture.Width, (uint)texture.Height, false, texture.VkFormat);
+ _pipeline.SetRenderTarget(texture, (uint)texture.Width, (uint)texture.Height);
_pipeline.SetRenderTargetColorMasks(colorMasks);
_pipeline.SetScissors(scissors);
_pipeline.ClearRenderTargetColor(0, 0, 1, new ColorF(0f, 0f, 0f, 1f));
diff --git a/src/Ryujinx.Graphics.Vulkan/FormatTable.cs b/src/Ryujinx.Graphics.Vulkan/FormatTable.cs
index 5f767df1..a12e3efd 100644
--- a/src/Ryujinx.Graphics.Vulkan/FormatTable.cs
+++ b/src/Ryujinx.Graphics.Vulkan/FormatTable.cs
@@ -1,5 +1,6 @@
using Ryujinx.Graphics.GAL;
using System;
+using System.Collections.Generic;
using VkFormat = Silk.NET.Vulkan.Format;
namespace Ryujinx.Graphics.Vulkan
@@ -7,10 +8,12 @@ namespace Ryujinx.Graphics.Vulkan
static class FormatTable
{
private static readonly VkFormat[] _table;
+ private static readonly Dictionary<VkFormat, Format> _reverseMap;
static FormatTable()
{
_table = new VkFormat[Enum.GetNames(typeof(Format)).Length];
+ _reverseMap = new Dictionary<VkFormat, Format>();
#pragma warning disable IDE0055 // Disable formatting
Add(Format.R8Unorm, VkFormat.R8Unorm);
@@ -164,6 +167,7 @@ namespace Ryujinx.Graphics.Vulkan
private static void Add(Format format, VkFormat vkFormat)
{
_table[(int)format] = vkFormat;
+ _reverseMap[vkFormat] = format;
}
public static VkFormat GetFormat(Format format)
@@ -171,6 +175,16 @@ namespace Ryujinx.Graphics.Vulkan
return _table[(int)format];
}
+ public static Format GetFormat(VkFormat format)
+ {
+ if (!_reverseMap.TryGetValue(format, out Format result))
+ {
+ return Format.B8G8R8A8Unorm;
+ }
+
+ return result;
+ }
+
public static Format ConvertRgba8SrgbToUnorm(Format format)
{
return format switch
diff --git a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs
index 458a1646..af22f265 100644
--- a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs
+++ b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs
@@ -12,6 +12,8 @@ namespace Ryujinx.Graphics.Vulkan
private readonly Auto<DisposableImageView>[] _attachments;
private readonly TextureView[] _colors;
private readonly TextureView _depthStencil;
+ private readonly TextureView[] _colorsCanonical;
+ private readonly TextureView _baseAttachment;
private readonly uint _validColorAttachments;
public uint Width { get; }
@@ -28,25 +30,31 @@ namespace Ryujinx.Graphics.Vulkan
public bool HasDepthStencil { get; }
public int ColorAttachmentsCount => AttachmentsCount - (HasDepthStencil ? 1 : 0);
- public FramebufferParams(
- Device device,
- Auto<DisposableImageView> view,
- uint width,
- uint height,
- uint samples,
- bool isDepthStencil,
- VkFormat format)
+ public FramebufferParams(Device device, TextureView view, uint width, uint height)
{
+ bool isDepthStencil = view.Info.Format.IsDepthOrStencil();
+
_device = device;
- _attachments = new[] { view };
+ _attachments = new[] { view.GetImageViewForAttachment() };
_validColorAttachments = isDepthStencil ? 0u : 1u;
+ _baseAttachment = view;
+
+ if (isDepthStencil)
+ {
+ _depthStencil = view;
+ }
+ else
+ {
+ _colors = new TextureView[] { view };
+ _colorsCanonical = _colors;
+ }
Width = width;
Height = height;
Layers = 1;
- AttachmentSamples = new[] { samples };
- AttachmentFormats = new[] { format };
+ AttachmentSamples = new[] { (uint)view.Info.Samples };
+ AttachmentFormats = new[] { view.VkFormat };
AttachmentIndices = isDepthStencil ? Array.Empty<int>() : new[] { 0 };
AttachmentsCount = 1;
@@ -64,6 +72,7 @@ namespace Ryujinx.Graphics.Vulkan
_attachments = new Auto<DisposableImageView>[count];
_colors = new TextureView[colorsCount];
+ _colorsCanonical = colors.Select(color => color is TextureView view && view.Valid ? view : null).ToArray();
AttachmentSamples = new uint[count];
AttachmentFormats = new VkFormat[count];
@@ -86,6 +95,7 @@ namespace Ryujinx.Graphics.Vulkan
_attachments[index] = texture.GetImageViewForAttachment();
_colors[index] = texture;
_validColorAttachments |= 1u << bindIndex;
+ _baseAttachment = texture;
AttachmentSamples[index] = (uint)texture.Info.Samples;
AttachmentFormats[index] = texture.VkFormat;
@@ -115,6 +125,7 @@ namespace Ryujinx.Graphics.Vulkan
{
_attachments[count - 1] = dsTexture.GetImageViewForAttachment();
_depthStencil = dsTexture;
+ _baseAttachment ??= dsTexture;
AttachmentSamples[count - 1] = (uint)dsTexture.Info.Samples;
AttachmentFormats[count - 1] = dsTexture.VkFormat;
@@ -251,19 +262,11 @@ namespace Ryujinx.Graphics.Vulkan
public void InsertClearBarrier(CommandBufferScoped cbs, int index)
{
- if (_colors != null)
- {
- int realIndex = Array.IndexOf(AttachmentIndices, index);
-
- if (realIndex != -1)
- {
- _colors[realIndex].Storage?.InsertReadToWriteBarrier(
- cbs,
- AccessFlags.ColorAttachmentWriteBit,
- PipelineStageFlags.ColorAttachmentOutputBit,
- insideRenderPass: true);
- }
- }
+ _colorsCanonical?[index]?.Storage?.InsertReadToWriteBarrier(
+ cbs,
+ AccessFlags.ColorAttachmentWriteBit,
+ PipelineStageFlags.ColorAttachmentOutputBit,
+ insideRenderPass: true);
}
public void InsertClearBarrierDS(CommandBufferScoped cbs)
@@ -274,5 +277,61 @@ namespace Ryujinx.Graphics.Vulkan
PipelineStageFlags.LateFragmentTestsBit,
insideRenderPass: true);
}
+
+ public TextureView[] GetAttachmentViews()
+ {
+ var result = new TextureView[_attachments.Length];
+
+ _colors?.CopyTo(result, 0);
+
+ if (_depthStencil != null)
+ {
+ result[^1] = _depthStencil;
+ }
+
+ return result;
+ }
+
+ public RenderPassCacheKey GetRenderPassCacheKey()
+ {
+ return new RenderPassCacheKey(_depthStencil, _colorsCanonical);
+ }
+
+ public void InsertLoadOpBarriers(CommandBufferScoped cbs)
+ {
+ if (_colors != null)
+ {
+ foreach (var color in _colors)
+ {
+ // If Clear or DontCare were used, this would need to be write bit.
+ color.Storage?.InsertWriteToReadBarrier(cbs, AccessFlags.ColorAttachmentReadBit, PipelineStageFlags.ColorAttachmentOutputBit);
+ color.Storage?.SetModification(AccessFlags.ColorAttachmentWriteBit, PipelineStageFlags.ColorAttachmentOutputBit);
+ }
+ }
+
+ if (_depthStencil != null)
+ {
+ _depthStencil.Storage?.InsertWriteToReadBarrier(cbs, AccessFlags.DepthStencilAttachmentReadBit, PipelineStageFlags.EarlyFragmentTestsBit);
+ _depthStencil.Storage?.SetModification(AccessFlags.DepthStencilAttachmentWriteBit, PipelineStageFlags.LateFragmentTestsBit);
+ }
+ }
+
+ public (Auto<DisposableRenderPass> renderPass, 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;
+ }
}
}
diff --git a/src/Ryujinx.Graphics.Vulkan/HashTableSlim.cs b/src/Ryujinx.Graphics.Vulkan/HashTableSlim.cs
index ff4eb789..3796e3c5 100644
--- a/src/Ryujinx.Graphics.Vulkan/HashTableSlim.cs
+++ b/src/Ryujinx.Graphics.Vulkan/HashTableSlim.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Runtime.CompilerServices;
namespace Ryujinx.Graphics.Vulkan
{
@@ -20,20 +21,29 @@ namespace Ryujinx.Graphics.Vulkan
public TValue Value;
}
- private readonly Entry[][] _hashTable = new Entry[TotalBuckets][];
+ private struct Bucket
+ {
+ public int Length;
+ public Entry[] Entries;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public readonly Span<Entry> AsSpan()
+ {
+ return Entries == null ? Span<Entry>.Empty : Entries.AsSpan(0, Length);
+ }
+ }
+
+ private readonly Bucket[] _hashTable = new Bucket[TotalBuckets];
public IEnumerable<TKey> Keys
{
get
{
- foreach (Entry[] bucket in _hashTable)
+ foreach (Bucket bucket in _hashTable)
{
- if (bucket != null)
+ for (int i = 0; i < bucket.Length; i++)
{
- foreach (Entry entry in bucket)
- {
- yield return entry.Key;
- }
+ yield return bucket.Entries[i].Key;
}
}
}
@@ -43,14 +53,11 @@ namespace Ryujinx.Graphics.Vulkan
{
get
{
- foreach (Entry[] bucket in _hashTable)
+ foreach (Bucket bucket in _hashTable)
{
- if (bucket != null)
+ for (int i = 0; i < bucket.Length; i++)
{
- foreach (Entry entry in bucket)
- {
- yield return entry.Value;
- }
+ yield return bucket.Entries[i].Value;
}
}
}
@@ -68,40 +75,64 @@ namespace Ryujinx.Graphics.Vulkan
int hashCode = key.GetHashCode();
int bucketIndex = hashCode & TotalBucketsMask;
- var bucket = _hashTable[bucketIndex];
- if (bucket != null)
+ ref var bucket = ref _hashTable[bucketIndex];
+ if (bucket.Entries != null)
{
int index = bucket.Length;
- Array.Resize(ref _hashTable[bucketIndex], index + 1);
+ if (index >= bucket.Entries.Length)
+ {
+ Array.Resize(ref bucket.Entries, index + 1);
+ }
- _hashTable[bucketIndex][index] = entry;
+ bucket.Entries[index] = entry;
}
else
{
- _hashTable[bucketIndex] = new[]
+ bucket.Entries = new[]
{
entry,
};
}
+
+ bucket.Length++;
}
- public bool TryGetValue(ref TKey key, out TValue value)
+ public bool Remove(ref TKey key)
{
int hashCode = key.GetHashCode();
- var bucket = _hashTable[hashCode & TotalBucketsMask];
- if (bucket != null)
+ ref var bucket = ref _hashTable[hashCode & TotalBucketsMask];
+ var entries = bucket.AsSpan();
+ for (int i = 0; i < entries.Length; i++)
{
- for (int i = 0; i < bucket.Length; i++)
+ ref var entry = ref entries[i];
+
+ if (entry.Hash == hashCode && entry.Key.Equals(ref key))
{
- ref var entry = ref bucket[i];
+ entries[(i + 1)..].CopyTo(entries[i..]);
+ bucket.Length--;
- if (entry.Hash == hashCode && entry.Key.Equals(ref key))
- {
- value = entry.Value;
- return true;
- }
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public bool TryGetValue(ref TKey key, out TValue value)
+ {
+ int hashCode = key.GetHashCode();
+
+ var entries = _hashTable[hashCode & TotalBucketsMask].AsSpan();
+ for (int i = 0; i < entries.Length; i++)
+ {
+ ref var entry = ref entries[i];
+
+ if (entry.Hash == hashCode && entry.Key.Equals(ref key))
+ {
+ value = entry.Value;
+ return true;
}
}
diff --git a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs
index ce84f752..c0ded5b3 100644
--- a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs
+++ b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs
@@ -256,17 +256,8 @@ namespace Ryujinx.Graphics.Vulkan
using var cbs = gd.CommandBufferPool.Rent();
- var dstFormat = dst.VkFormat;
- var dstSamples = dst.Info.Samples;
-
for (int l = 0; l < levels; l++)
{
- int srcWidth = Math.Max(1, src.Width >> l);
- int srcHeight = Math.Max(1, src.Height >> l);
-
- int dstWidth = Math.Max(1, dst.Width >> l);
- int dstHeight = Math.Max(1, dst.Height >> l);
-
var mipSrcRegion = new Extents2D(
srcRegion.X1 >> l,
srcRegion.Y1 >> l,
@@ -290,11 +281,7 @@ namespace Ryujinx.Graphics.Vulkan
gd,
cbs,
srcView,
- dst.GetImageViewForAttachment(),
- dstWidth,
- dstHeight,
- dstSamples,
- dstFormat,
+ dstView,
mipSrcRegion,
mipDstRegion);
}
@@ -304,12 +291,7 @@ namespace Ryujinx.Graphics.Vulkan
gd,
cbs,
srcView,
- dst.GetImageViewForAttachment(),
- dstWidth,
- dstHeight,
- dstSamples,
- dstFormat,
- false,
+ dstView,
mipSrcRegion,
mipDstRegion,
linearFilter,
@@ -367,12 +349,7 @@ namespace Ryujinx.Graphics.Vulkan
gd,
cbs,
srcView,
- dstView.GetImageViewForAttachment(),
- dstView.Width,
- dstView.Height,
- dstView.Info.Samples,
- dstView.VkFormat,
- dstView.Info.Format.IsDepthOrStencil(),
+ dstView,
extents,
extents,
false);
@@ -394,12 +371,7 @@ namespace Ryujinx.Graphics.Vulkan
VulkanRenderer gd,
CommandBufferScoped cbs,
TextureView src,
- Auto<DisposableImageView> dst,
- int dstWidth,
- int dstHeight,
- int dstSamples,
- VkFormat dstFormat,
- bool dstIsDepthOrStencil,
+ TextureView dst,
Extents2D srcRegion,
Extents2D dstRegion,
bool linearFilter,
@@ -453,6 +425,8 @@ namespace Ryujinx.Graphics.Vulkan
0f,
1f);
+ bool dstIsDepthOrStencil = dst.Info.Format.IsDepthOrStencil();
+
if (dstIsDepthOrStencil)
{
_pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit);
@@ -471,7 +445,10 @@ namespace Ryujinx.Graphics.Vulkan
_pipeline.SetProgram(_programColorBlit);
}
- _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, (uint)dstSamples, dstIsDepthOrStencil, dstFormat);
+ int dstWidth = dst.Width;
+ int dstHeight = dst.Height;
+
+ _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight);
_pipeline.SetRenderTargetColorMasks(new uint[] { 0xf });
_pipeline.SetScissors(stackalloc Rectangle<int>[] { new Rectangle<int>(0, 0, dstWidth, dstHeight) });
@@ -496,11 +473,7 @@ namespace Ryujinx.Graphics.Vulkan
VulkanRenderer gd,
CommandBufferScoped cbs,
TextureView src,
- Auto<DisposableImageView> dst,
- int dstWidth,
- int dstHeight,
- int dstSamples,
- VkFormat dstFormat,
+ TextureView dst,
Extents2D srcRegion,
Extents2D dstRegion)
{
@@ -548,7 +521,10 @@ namespace Ryujinx.Graphics.Vulkan
0f,
1f);
- _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, (uint)dstSamples, true, dstFormat);
+ int dstWidth = dst.Width;
+ int dstHeight = dst.Height;
+
+ _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight);
_pipeline.SetScissors(stackalloc Rectangle<int>[] { new Rectangle<int>(0, 0, dstWidth, dstHeight) });
_pipeline.SetViewports(viewports);
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
@@ -660,12 +636,11 @@ namespace Ryujinx.Graphics.Vulkan
public void Clear(
VulkanRenderer gd,
- Auto<DisposableImageView> dst,
+ TextureView dst,
ReadOnlySpan<float> clearColor,
uint componentMask,
int dstWidth,
int dstHeight,
- VkFormat dstFormat,
ComponentType type,
Rectangle<int> scissor)
{
@@ -710,7 +685,7 @@ namespace Ryujinx.Graphics.Vulkan
}
_pipeline.SetProgram(program);
- _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false, dstFormat);
+ _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight);
_pipeline.SetRenderTargetColorMasks(new[] { componentMask });
_pipeline.SetViewports(viewports);
_pipeline.SetScissors(stackalloc Rectangle<int>[] { scissor });
@@ -721,7 +696,7 @@ namespace Ryujinx.Graphics.Vulkan
public void Clear(
VulkanRenderer gd,
- Auto<DisposableImageView> dst,
+ TextureView dst,
float depthValue,
bool depthMask,
int stencilValue,
@@ -757,7 +732,7 @@ namespace Ryujinx.Graphics.Vulkan
1f);
_pipeline.SetProgram(_programDepthStencilClear);
- _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, true, dstFormat);
+ _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight);
_pipeline.SetViewports(viewports);
_pipeline.SetScissors(stackalloc Rectangle<int>[] { scissor });
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
@@ -1163,12 +1138,7 @@ namespace Ryujinx.Graphics.Vulkan
var srcView = Create2DLayerView(src, srcLayer + z, 0);
var dstView = Create2DLayerView(dst, dstLayer + z, 0);
- _pipeline.SetRenderTarget(
- dstView.GetImageViewForAttachment(),
- (uint)dst.Width,
- (uint)dst.Height,
- true,
- dst.VkFormat);
+ _pipeline.SetRenderTarget(dstView, (uint)dst.Width, (uint)dst.Height);
CopyMSDraw(srcView, aspectFlags, fromMS: true);
@@ -1294,13 +1264,7 @@ namespace Ryujinx.Graphics.Vulkan
var srcView = Create2DLayerView(src, srcLayer + z, 0);
var dstView = Create2DLayerView(dst, dstLayer + z, 0);
- _pipeline.SetRenderTarget(
- dstView.GetImageViewForAttachment(),
- (uint)dst.Width,
- (uint)dst.Height,
- (uint)samples,
- true,
- dst.VkFormat);
+ _pipeline.SetRenderTarget(dstView, (uint)dst.Width, (uint)dst.Height);
CopyMSDraw(srcView, aspectFlags, fromMS: false);
@@ -1328,13 +1292,7 @@ namespace Ryujinx.Graphics.Vulkan
var dstView = Create2DLayerView(dst, dstLayer + z, 0);
_pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Fragment, 0, srcView, null);
- _pipeline.SetRenderTarget(
- dstView.GetView(format).GetImageViewForAttachment(),
- (uint)dst.Width,
- (uint)dst.Height,
- (uint)samples,
- false,
- vkFormat);
+ _pipeline.SetRenderTarget(dstView.GetView(format), (uint)dst.Width, (uint)dst.Height);
_pipeline.Draw(4, 1, 0, 0);
@@ -1471,9 +1429,9 @@ namespace Ryujinx.Graphics.Vulkan
};
var info = new TextureCreateInfo(
- from.Info.Width,
- from.Info.Height,
- from.Info.Depth,
+ Math.Max(1, from.Info.Width >> level),
+ Math.Max(1, from.Info.Height >> level),
+ 1,
1,
from.Info.Samples,
from.Info.BlockWidth,
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
index 61215b67..3aef1317 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
@@ -55,6 +55,7 @@ namespace Ryujinx.Graphics.Vulkan
protected FramebufferParams FramebufferParams;
private Auto<DisposableFramebuffer> _framebuffer;
private Auto<DisposableRenderPass> _renderPass;
+ private RenderPassHolder _nullRenderPass;
private int _writtenAttachmentCount;
private bool _framebufferUsingColorWriteMask;
@@ -1488,98 +1489,22 @@ namespace Ryujinx.Graphics.Vulkan
protected unsafe void CreateRenderPass()
{
- const int MaxAttachments = Constants.MaxRenderTargets + 1;
-
- AttachmentDescription[] attachmentDescs = null;
-
- var subpass = new SubpassDescription
- {
- PipelineBindPoint = PipelineBindPoint.Graphics,
- };
-
- AttachmentReference* attachmentReferences = stackalloc AttachmentReference[MaxAttachments];
-
var hasFramebuffer = FramebufferParams != null;
- if (hasFramebuffer && FramebufferParams.AttachmentsCount != 0)
- {
- attachmentDescs = new AttachmentDescription[FramebufferParams.AttachmentsCount];
-
- for (int i = 0; i < FramebufferParams.AttachmentsCount; i++)
- {
- attachmentDescs[i] = new AttachmentDescription(
- 0,
- FramebufferParams.AttachmentFormats[i],
- TextureStorage.ConvertToSampleCountFlags(Gd.Capabilities.SupportedSampleCounts, FramebufferParams.AttachmentSamples[i]),
- AttachmentLoadOp.Load,
- AttachmentStoreOp.Store,
- AttachmentLoadOp.Load,
- AttachmentStoreOp.Store,
- ImageLayout.General,
- ImageLayout.General);
- }
-
- int colorAttachmentsCount = FramebufferParams.ColorAttachmentsCount;
-
- if (colorAttachmentsCount > MaxAttachments - 1)
- {
- colorAttachmentsCount = MaxAttachments - 1;
- }
-
- if (colorAttachmentsCount != 0)
- {
- int maxAttachmentIndex = FramebufferParams.MaxColorAttachmentIndex;
- subpass.ColorAttachmentCount = (uint)maxAttachmentIndex + 1;
- subpass.PColorAttachments = &attachmentReferences[0];
-
- // Fill with VK_ATTACHMENT_UNUSED to cover any gaps.
- for (int i = 0; i <= maxAttachmentIndex; i++)
- {
- subpass.PColorAttachments[i] = new AttachmentReference(Vk.AttachmentUnused, ImageLayout.Undefined);
- }
-
- for (int i = 0; i < colorAttachmentsCount; i++)
- {
- int bindIndex = FramebufferParams.AttachmentIndices[i];
-
- subpass.PColorAttachments[bindIndex] = new AttachmentReference((uint)i, ImageLayout.General);
- }
- }
+ EndRenderPass();
- if (FramebufferParams.HasDepthStencil)
- {
- uint dsIndex = (uint)FramebufferParams.AttachmentsCount - 1;
+ if (!hasFramebuffer || FramebufferParams.AttachmentsCount == 0)
+ {
+ // Use the null framebuffer.
+ _nullRenderPass ??= new RenderPassHolder(Gd, Device, new RenderPassCacheKey(), FramebufferParams);
- subpass.PDepthStencilAttachment = &attachmentReferences[MaxAttachments - 1];
- *subpass.PDepthStencilAttachment = new AttachmentReference(dsIndex, ImageLayout.General);
- }
+ _renderPass = _nullRenderPass.GetRenderPass();
+ _framebuffer = _nullRenderPass.GetFramebuffer(Gd, Cbs, FramebufferParams);
}
-
- var subpassDependency = PipelineConverter.CreateSubpassDependency();
-
- fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs)
+ else
{
- var renderPassCreateInfo = new RenderPassCreateInfo
- {
- SType = StructureType.RenderPassCreateInfo,
- PAttachments = pAttachmentDescs,
- AttachmentCount = attachmentDescs != null ? (uint)attachmentDescs.Length : 0,
- PSubpasses = &subpass,
- SubpassCount = 1,
- PDependencies = &subpassDependency,
- DependencyCount = 1,
- };
-
- Gd.Api.CreateRenderPass(Device, renderPassCreateInfo, null, out var renderPass).ThrowOnError();
-
- _renderPass?.Dispose();
- _renderPass = new Auto<DisposableRenderPass>(new DisposableRenderPass(Gd.Api, Device, renderPass));
+ (_renderPass, _framebuffer) = FramebufferParams.GetPassAndFramebuffer(Gd, Device, Cbs);
}
-
- EndRenderPass();
-
- _framebuffer?.Dispose();
- _framebuffer = hasFramebuffer ? FramebufferParams.Create(Gd.Api, Cbs, _renderPass) : null;
}
protected void SignalStateChange()
@@ -1770,8 +1695,7 @@ namespace Ryujinx.Graphics.Vulkan
{
if (disposing)
{
- _renderPass?.Dispose();
- _framebuffer?.Dispose();
+ _nullRenderPass?.Dispose();
_newState.Dispose();
_descriptorSetUpdater.Dispose();
_vertexBufferUpdater.Dispose();
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs b/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs
index a3e6818f..6c4419cd 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs
@@ -51,7 +51,7 @@ namespace Ryujinx.Graphics.Vulkan
{
// We can't use CmdClearAttachments if not writing all components,
// because on Vulkan, the pipeline state does not affect clears.
- var dstTexture = FramebufferParams.GetAttachment(index);
+ var dstTexture = FramebufferParams.GetColorView(index);
if (dstTexture == null)
{
return;
@@ -71,7 +71,6 @@ namespace Ryujinx.Graphics.Vulkan
componentMask,
(int)FramebufferParams.Width,
(int)FramebufferParams.Height,
- FramebufferParams.AttachmentFormats[index],
FramebufferParams.GetAttachmentComponentType(index),
ClearScissor);
}
@@ -92,7 +91,7 @@ namespace Ryujinx.Graphics.Vulkan
{
// We can't use CmdClearAttachments if not clearing all (mask is all ones, 0xFF) or none (mask is 0) of the stencil bits,
// because on Vulkan, the pipeline state does not affect clears.
- var dstTexture = FramebufferParams.GetDepthStencilAttachment();
+ var dstTexture = FramebufferParams.GetDepthStencilView();
if (dstTexture == null)
{
return;
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineHelperShader.cs b/src/Ryujinx.Graphics.Vulkan/PipelineHelperShader.cs
index 0a871a5c..dfbf1901 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineHelperShader.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineHelperShader.cs
@@ -9,21 +9,16 @@ namespace Ryujinx.Graphics.Vulkan
{
}
- public void SetRenderTarget(Auto<DisposableImageView> view, uint width, uint height, bool isDepthStencil, VkFormat format)
+ public void SetRenderTarget(TextureView view, uint width, uint height)
{
- SetRenderTarget(view, width, height, 1u, isDepthStencil, format);
- }
-
- public void SetRenderTarget(Auto<DisposableImageView> view, uint width, uint height, uint samples, bool isDepthStencil, VkFormat format)
- {
- CreateFramebuffer(view, width, height, samples, isDepthStencil, format);
+ CreateFramebuffer(view, width, height);
CreateRenderPass();
SignalStateChange();
}
- private void CreateFramebuffer(Auto<DisposableImageView> view, uint width, uint height, uint samples, bool isDepthStencil, VkFormat format)
+ private void CreateFramebuffer(TextureView view, uint width, uint height)
{
- FramebufferParams = new FramebufferParams(Device, view, width, height, samples, isDepthStencil, format);
+ FramebufferParams = new FramebufferParams(Device, view, width, height);
UpdatePipelineAttachmentFormats();
}
diff --git a/src/Ryujinx.Graphics.Vulkan/RenderPassCacheKey.cs b/src/Ryujinx.Graphics.Vulkan/RenderPassCacheKey.cs
new file mode 100644
index 00000000..7c57b8fe
--- /dev/null
+++ b/src/Ryujinx.Graphics.Vulkan/RenderPassCacheKey.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Linq;
+
+namespace Ryujinx.Graphics.Vulkan
+{
+ internal readonly struct RenderPassCacheKey : IRefEquatable<RenderPassCacheKey>
+ {
+ private readonly TextureView _depthStencil;
+ private readonly TextureView[] _colors;
+
+ public RenderPassCacheKey(TextureView depthStencil, TextureView[] colors)
+ {
+ _depthStencil = depthStencil;
+ _colors = colors;
+ }
+
+ public override int GetHashCode()
+ {
+ HashCode hc = new();
+
+ hc.Add(_depthStencil);
+
+ if (_colors != null)
+ {
+ foreach (var color in _colors)
+ {
+ hc.Add(color);
+ }
+ }
+
+ return hc.ToHashCode();
+ }
+
+ public bool Equals(ref RenderPassCacheKey other)
+ {
+ bool colorsNull = _colors == null;
+ bool otherNull = other._colors == null;
+ return other._depthStencil == _depthStencil &&
+ colorsNull == otherNull &&
+ (colorsNull || other._colors.SequenceEqual(_colors));
+ }
+ }
+}
diff --git a/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs b/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs
new file mode 100644
index 00000000..3d883b2d
--- /dev/null
+++ b/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs
@@ -0,0 +1,180 @@
+using Silk.NET.Vulkan;
+using System;
+
+namespace Ryujinx.Graphics.Vulkan
+{
+ internal class RenderPassHolder
+ {
+ private readonly struct FramebufferCacheKey : IRefEquatable<FramebufferCacheKey>
+ {
+ private readonly uint _width;
+ private readonly uint _height;
+ private readonly uint _layers;
+
+ public FramebufferCacheKey(uint width, uint height, uint layers)
+ {
+ _width = width;
+ _height = height;
+ _layers = layers;
+ }
+
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(_width, _height, _layers);
+ }
+
+ public bool Equals(ref FramebufferCacheKey other)
+ {
+ return other._width == _width && other._height == _height && other._layers == _layers;
+ }
+ }
+
+ private readonly TextureView[] _textures;
+ private readonly Auto<DisposableRenderPass> _renderPass;
+ private readonly HashTableSlim<FramebufferCacheKey, Auto<DisposableFramebuffer>> _framebuffers;
+ private readonly RenderPassCacheKey _key;
+
+ public unsafe RenderPassHolder(VulkanRenderer gd, Device device, RenderPassCacheKey key, FramebufferParams fb)
+ {
+ // Create render pass using framebuffer params.
+
+ const int MaxAttachments = Constants.MaxRenderTargets + 1;
+
+ AttachmentDescription[] attachmentDescs = null;
+
+ var subpass = new SubpassDescription
+ {
+ PipelineBindPoint = PipelineBindPoint.Graphics,
+ };
+
+ AttachmentReference* attachmentReferences = stackalloc AttachmentReference[MaxAttachments];
+
+ var hasFramebuffer = fb != null;
+
+ if (hasFramebuffer && fb.AttachmentsCount != 0)
+ {
+ attachmentDescs = new AttachmentDescription[fb.AttachmentsCount];
+
+ for (int i = 0; i < fb.AttachmentsCount; i++)
+ {
+ attachmentDescs[i] = new AttachmentDescription(
+ 0,
+ fb.AttachmentFormats[i],
+ TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, fb.AttachmentSamples[i]),
+ AttachmentLoadOp.Load,
+ AttachmentStoreOp.Store,
+ AttachmentLoadOp.Load,
+ AttachmentStoreOp.Store,
+ ImageLayout.General,
+ ImageLayout.General);
+ }
+
+ int colorAttachmentsCount = fb.ColorAttachmentsCount;
+
+ if (colorAttachmentsCount > MaxAttachments - 1)
+ {
+ colorAttachmentsCount = MaxAttachments - 1;
+ }
+
+ if (colorAttachmentsCount != 0)
+ {
+ int maxAttachmentIndex = fb.MaxColorAttachmentIndex;
+ subpass.ColorAttachmentCount = (uint)maxAttachmentIndex + 1;
+ subpass.PColorAttachments = &attachmentReferences[0];
+
+ // Fill with VK_ATTACHMENT_UNUSED to cover any gaps.
+ for (int i = 0; i <= maxAttachmentIndex; i++)
+ {
+ subpass.PColorAttachments[i] = new AttachmentReference(Vk.AttachmentUnused, ImageLayout.Undefined);
+ }
+
+ for (int i = 0; i < colorAttachmentsCount; i++)
+ {
+ int bindIndex = fb.AttachmentIndices[i];
+
+ subpass.PColorAttachments[bindIndex] = new AttachmentReference((uint)i, ImageLayout.General);
+ }
+ }
+
+ if (fb.HasDepthStencil)
+ {
+ uint dsIndex = (uint)fb.AttachmentsCount - 1;
+
+ subpass.PDepthStencilAttachment = &attachmentReferences[MaxAttachments - 1];
+ *subpass.PDepthStencilAttachment = new AttachmentReference(dsIndex, ImageLayout.General);
+ }
+ }
+
+ var subpassDependency = PipelineConverter.CreateSubpassDependency();
+
+ fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs)
+ {
+ var renderPassCreateInfo = new RenderPassCreateInfo
+ {
+ SType = StructureType.RenderPassCreateInfo,
+ PAttachments = pAttachmentDescs,
+ AttachmentCount = attachmentDescs != null ? (uint)attachmentDescs.Length : 0,
+ PSubpasses = &subpass,
+ SubpassCount = 1,
+ PDependencies = &subpassDependency,
+ DependencyCount = 1,
+ };
+
+ gd.Api.CreateRenderPass(device, renderPassCreateInfo, null, out var renderPass).ThrowOnError();
+
+ _renderPass?.Dispose();
+ _renderPass = new Auto<DisposableRenderPass>(new DisposableRenderPass(gd.Api, device, renderPass));
+ }
+
+ _framebuffers = new HashTableSlim<FramebufferCacheKey, Auto<DisposableFramebuffer>>();
+
+ // Register this render pass with all render target views.
+
+ var textures = fb.GetAttachmentViews();
+
+ foreach (var texture in textures)
+ {
+ texture.AddRenderPass(key, this);
+ }
+
+ _textures = textures;
+ _key = key;
+ }
+
+ public Auto<DisposableFramebuffer> GetFramebuffer(VulkanRenderer gd, CommandBufferScoped cbs, FramebufferParams fb)
+ {
+ var key = new FramebufferCacheKey(fb.Width, fb.Height, fb.Layers);
+
+ if (!_framebuffers.TryGetValue(ref key, out Auto<DisposableFramebuffer> result))
+ {
+ result = fb.Create(gd.Api, cbs, _renderPass);
+
+ _framebuffers.Add(ref key, result);
+ }
+
+ return result;
+ }
+
+ public Auto<DisposableRenderPass> GetRenderPass()
+ {
+ return _renderPass;
+ }
+
+ public void Dispose()
+ {
+ // Dispose all framebuffers
+
+ foreach (var fb in _framebuffers.Values)
+ {
+ fb.Dispose();
+ }
+
+ // Notify all texture views that this render pass has been disposed.
+
+ foreach (var texture in _textures)
+ {
+ texture.RemoveRenderPass(_key);
+ }
+ }
+ }
+}
diff --git a/src/Ryujinx.Graphics.Vulkan/TextureView.cs b/src/Ryujinx.Graphics.Vulkan/TextureView.cs
index f5b80f94..393db261 100644
--- a/src/Ryujinx.Graphics.Vulkan/TextureView.cs
+++ b/src/Ryujinx.Graphics.Vulkan/TextureView.cs
@@ -3,6 +3,7 @@ using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan;
using System;
using System.Collections.Generic;
+using System.Linq;
using Format = Ryujinx.Graphics.GAL.Format;
using VkBuffer = Silk.NET.Vulkan.Buffer;
using VkFormat = Silk.NET.Vulkan.Format;
@@ -23,6 +24,8 @@ namespace Ryujinx.Graphics.Vulkan
private readonly TextureCreateInfo _info;
+ private HashTableSlim<RenderPassCacheKey, RenderPassHolder> _renderPasses;
+
public TextureCreateInfo Info => _info;
public TextureStorage Storage { get; }
@@ -158,6 +161,26 @@ namespace Ryujinx.Graphics.Vulkan
Valid = true;
}
+ /// <summary>
+ /// Create a texture view for an existing swapchain image view.
+ /// Does not set storage, so only appropriate for swapchain use.
+ /// </summary>
+ /// <remarks>Do not use this for normal textures, and make sure uses do not try to read storage.</remarks>
+ public TextureView(VulkanRenderer gd, Device device, DisposableImageView view, TextureCreateInfo info, VkFormat format)
+ {
+ _gd = gd;
+ _device = device;
+
+ _imageView = new Auto<DisposableImageView>(view);
+ _imageViewDraw = _imageView;
+ _imageViewIdentity = _imageView;
+ _info = info;
+
+ VkFormat = format;
+
+ Valid = true;
+ }
+
public Auto<DisposableImage> GetImage()
{
return Storage.GetImage();
@@ -939,6 +962,34 @@ namespace Ryujinx.Graphics.Vulkan
throw new NotImplementedException();
}
+ public (Auto<DisposableRenderPass> renderPass, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
+ VulkanRenderer gd,
+ Device device,
+ CommandBufferScoped cbs,
+ FramebufferParams fb)
+ {
+ var key = fb.GetRenderPassCacheKey();
+
+ if (_renderPasses == null || !_renderPasses.TryGetValue(ref key, out RenderPassHolder rpHolder))
+ {
+ rpHolder = new RenderPassHolder(gd, device, key, fb);
+ }
+
+ return (rpHolder.GetRenderPass(), rpHolder.GetFramebuffer(gd, cbs, fb));
+ }
+
+ public void AddRenderPass(RenderPassCacheKey key, RenderPassHolder renderPass)
+ {
+ _renderPasses ??= new HashTableSlim<RenderPassCacheKey, RenderPassHolder>();
+
+ _renderPasses.Add(ref key, renderPass);
+ }
+
+ public void RemoveRenderPass(RenderPassCacheKey key)
+ {
+ _renderPasses.Remove(ref key);
+ }
+
protected virtual void Dispose(bool disposing)
{
if (disposing)
@@ -948,15 +999,29 @@ namespace Ryujinx.Graphics.Vulkan
if (_gd.Textures.Remove(this))
{
_imageView.Dispose();
- _imageViewIdentity.Dispose();
_imageView2dArray?.Dispose();
+ if (_imageViewIdentity != _imageView)
+ {
+ _imageViewIdentity.Dispose();
+ }
+
if (_imageViewDraw != _imageViewIdentity)
{
_imageViewDraw.Dispose();
}
Storage.DecrementViewsCount();
+
+ if (_renderPasses != null)
+ {
+ var renderPasses = _renderPasses.Values.ToArray();
+
+ foreach (var pass in renderPasses)
+ {
+ pass.Dispose();
+ }
+ }
}
}
}
diff --git a/src/Ryujinx.Graphics.Vulkan/Window.cs b/src/Ryujinx.Graphics.Vulkan/Window.cs
index 2c5764a9..5ddb6eed 100644
--- a/src/Ryujinx.Graphics.Vulkan/Window.cs
+++ b/src/Ryujinx.Graphics.Vulkan/Window.cs
@@ -20,7 +20,7 @@ namespace Ryujinx.Graphics.Vulkan
private SwapchainKHR _swapchain;
private Image[] _swapchainImages;
- private Auto<DisposableImageView>[] _swapchainImageViews;
+ private TextureView[] _swapchainImageViews;
private Semaphore[] _imageAvailableSemaphores;
private Semaphore[] _renderFinishedSemaphores;
@@ -143,6 +143,23 @@ namespace Ryujinx.Graphics.Vulkan
Clipped = true,
};
+ var textureCreateInfo = new TextureCreateInfo(
+ _width,
+ _height,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ FormatTable.GetFormat(surfaceFormat.Format),
+ DepthStencilMode.Depth,
+ Target.Texture2D,
+ SwizzleComponent.Red,
+ SwizzleComponent.Green,
+ SwizzleComponent.Blue,
+ SwizzleComponent.Alpha);
+
_gd.SwapchainApi.CreateSwapchain(_device, swapchainCreateInfo, null, out _swapchain).ThrowOnError();
_gd.SwapchainApi.GetSwapchainImages(_device, _swapchain, &imageCount, null);
@@ -154,11 +171,11 @@ namespace Ryujinx.Graphics.Vulkan
_gd.SwapchainApi.GetSwapchainImages(_device, _swapchain, &imageCount, pSwapchainImages);
}
- _swapchainImageViews = new Auto<DisposableImageView>[imageCount];
+ _swapchainImageViews = new TextureView[imageCount];
for (int i = 0; i < _swapchainImageViews.Length; i++)
{
- _swapchainImageViews[i] = CreateSwapchainImageView(_swapchainImages[i], surfaceFormat.Format);
+ _swapchainImageViews[i] = CreateSwapchainImageView(_swapchainImages[i], surfaceFormat.Format, textureCreateInfo);
}
var semaphoreCreateInfo = new SemaphoreCreateInfo
@@ -181,7 +198,7 @@ namespace Ryujinx.Graphics.Vulkan
}
}
- private unsafe Auto<DisposableImageView> CreateSwapchainImageView(Image swapchainImage, VkFormat format)
+ private unsafe TextureView CreateSwapchainImageView(Image swapchainImage, VkFormat format, TextureCreateInfo info)
{
var componentMapping = new ComponentMapping(
ComponentSwizzle.R,
@@ -204,7 +221,8 @@ namespace Ryujinx.Graphics.Vulkan
};
_gd.Api.CreateImageView(_device, imageCreateInfo, null, out var imageView).ThrowOnError();
- return new Auto<DisposableImageView>(new DisposableImageView(_gd.Api, _device, imageView));
+
+ return new TextureView(_gd, _device, new DisposableImageView(_gd.Api, _device, imageView), info, format);
}
private static SurfaceFormatKHR ChooseSwapSurfaceFormat(SurfaceFormatKHR[] availableFormats, bool colorSpacePassthroughEnabled)
@@ -406,7 +424,7 @@ namespace Ryujinx.Graphics.Vulkan
_scalingFilter.Run(
view,
cbs,
- _swapchainImageViews[nextImage],
+ _swapchainImageViews[nextImage].GetImageViewForAttachment(),
_format,
_width,
_height,
@@ -421,11 +439,6 @@ namespace Ryujinx.Graphics.Vulkan
cbs,
view,
_swapchainImageViews[nextImage],
- _width,
- _height,
- 1,
- _format,
- false,
new Extents2D(srcX0, srcY0, srcX1, srcY1),
new Extents2D(dstX0, dstY1, dstX1, dstY0),
_isLinear,