forked from Mirror/Ryujinx
Vulkan: Add Render Pass / Framebuffer Cache (#6182)
* 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.
This commit is contained in:
parent
d1b30fbe08
commit
c94f0fbb83
12 changed files with 515 additions and 234 deletions
|
@ -257,7 +257,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||||
|
|
||||||
scissors[0] = new Rectangle<int>(0, 0, texture.Width, texture.Height);
|
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.SetRenderTargetColorMasks(colorMasks);
|
||||||
_pipeline.SetScissors(scissors);
|
_pipeline.SetScissors(scissors);
|
||||||
_pipeline.ClearRenderTargetColor(0, 0, 1, new ColorF(0f, 0f, 0f, 1f));
|
_pipeline.ClearRenderTargetColor(0, 0, 1, new ColorF(0f, 0f, 0f, 1f));
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using VkFormat = Silk.NET.Vulkan.Format;
|
using VkFormat = Silk.NET.Vulkan.Format;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
|
@ -7,10 +8,12 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
static class FormatTable
|
static class FormatTable
|
||||||
{
|
{
|
||||||
private static readonly VkFormat[] _table;
|
private static readonly VkFormat[] _table;
|
||||||
|
private static readonly Dictionary<VkFormat, Format> _reverseMap;
|
||||||
|
|
||||||
static FormatTable()
|
static FormatTable()
|
||||||
{
|
{
|
||||||
_table = new VkFormat[Enum.GetNames(typeof(Format)).Length];
|
_table = new VkFormat[Enum.GetNames(typeof(Format)).Length];
|
||||||
|
_reverseMap = new Dictionary<VkFormat, Format>();
|
||||||
|
|
||||||
#pragma warning disable IDE0055 // Disable formatting
|
#pragma warning disable IDE0055 // Disable formatting
|
||||||
Add(Format.R8Unorm, VkFormat.R8Unorm);
|
Add(Format.R8Unorm, VkFormat.R8Unorm);
|
||||||
|
@ -164,6 +167,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
private static void Add(Format format, VkFormat vkFormat)
|
private static void Add(Format format, VkFormat vkFormat)
|
||||||
{
|
{
|
||||||
_table[(int)format] = vkFormat;
|
_table[(int)format] = vkFormat;
|
||||||
|
_reverseMap[vkFormat] = format;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static VkFormat GetFormat(Format format)
|
public static VkFormat GetFormat(Format format)
|
||||||
|
@ -171,6 +175,16 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
return _table[(int)format];
|
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)
|
public static Format ConvertRgba8SrgbToUnorm(Format format)
|
||||||
{
|
{
|
||||||
return format switch
|
return format switch
|
||||||
|
|
|
@ -12,6 +12,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
private readonly Auto<DisposableImageView>[] _attachments;
|
private readonly Auto<DisposableImageView>[] _attachments;
|
||||||
private readonly TextureView[] _colors;
|
private readonly TextureView[] _colors;
|
||||||
private readonly TextureView _depthStencil;
|
private readonly TextureView _depthStencil;
|
||||||
|
private readonly TextureView[] _colorsCanonical;
|
||||||
|
private readonly TextureView _baseAttachment;
|
||||||
private readonly uint _validColorAttachments;
|
private readonly uint _validColorAttachments;
|
||||||
|
|
||||||
public uint Width { get; }
|
public uint Width { get; }
|
||||||
|
@ -28,25 +30,31 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
public bool HasDepthStencil { get; }
|
public bool HasDepthStencil { get; }
|
||||||
public int ColorAttachmentsCount => AttachmentsCount - (HasDepthStencil ? 1 : 0);
|
public int ColorAttachmentsCount => AttachmentsCount - (HasDepthStencil ? 1 : 0);
|
||||||
|
|
||||||
public FramebufferParams(
|
public FramebufferParams(Device device, TextureView view, uint width, uint height)
|
||||||
Device device,
|
|
||||||
Auto<DisposableImageView> view,
|
|
||||||
uint width,
|
|
||||||
uint height,
|
|
||||||
uint samples,
|
|
||||||
bool isDepthStencil,
|
|
||||||
VkFormat format)
|
|
||||||
{
|
{
|
||||||
|
bool isDepthStencil = view.Info.Format.IsDepthOrStencil();
|
||||||
|
|
||||||
_device = device;
|
_device = device;
|
||||||
_attachments = new[] { view };
|
_attachments = new[] { view.GetImageViewForAttachment() };
|
||||||
_validColorAttachments = isDepthStencil ? 0u : 1u;
|
_validColorAttachments = isDepthStencil ? 0u : 1u;
|
||||||
|
_baseAttachment = view;
|
||||||
|
|
||||||
|
if (isDepthStencil)
|
||||||
|
{
|
||||||
|
_depthStencil = view;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_colors = new TextureView[] { view };
|
||||||
|
_colorsCanonical = _colors;
|
||||||
|
}
|
||||||
|
|
||||||
Width = width;
|
Width = width;
|
||||||
Height = height;
|
Height = height;
|
||||||
Layers = 1;
|
Layers = 1;
|
||||||
|
|
||||||
AttachmentSamples = new[] { samples };
|
AttachmentSamples = new[] { (uint)view.Info.Samples };
|
||||||
AttachmentFormats = new[] { format };
|
AttachmentFormats = new[] { view.VkFormat };
|
||||||
AttachmentIndices = isDepthStencil ? Array.Empty<int>() : new[] { 0 };
|
AttachmentIndices = isDepthStencil ? Array.Empty<int>() : new[] { 0 };
|
||||||
|
|
||||||
AttachmentsCount = 1;
|
AttachmentsCount = 1;
|
||||||
|
@ -64,6 +72,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
_attachments = new Auto<DisposableImageView>[count];
|
_attachments = new Auto<DisposableImageView>[count];
|
||||||
_colors = new TextureView[colorsCount];
|
_colors = new TextureView[colorsCount];
|
||||||
|
_colorsCanonical = colors.Select(color => color is TextureView view && view.Valid ? view : null).ToArray();
|
||||||
|
|
||||||
AttachmentSamples = new uint[count];
|
AttachmentSamples = new uint[count];
|
||||||
AttachmentFormats = new VkFormat[count];
|
AttachmentFormats = new VkFormat[count];
|
||||||
|
@ -86,6 +95,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
_attachments[index] = texture.GetImageViewForAttachment();
|
_attachments[index] = texture.GetImageViewForAttachment();
|
||||||
_colors[index] = texture;
|
_colors[index] = texture;
|
||||||
_validColorAttachments |= 1u << bindIndex;
|
_validColorAttachments |= 1u << bindIndex;
|
||||||
|
_baseAttachment = texture;
|
||||||
|
|
||||||
AttachmentSamples[index] = (uint)texture.Info.Samples;
|
AttachmentSamples[index] = (uint)texture.Info.Samples;
|
||||||
AttachmentFormats[index] = texture.VkFormat;
|
AttachmentFormats[index] = texture.VkFormat;
|
||||||
|
@ -115,6 +125,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
_attachments[count - 1] = dsTexture.GetImageViewForAttachment();
|
_attachments[count - 1] = dsTexture.GetImageViewForAttachment();
|
||||||
_depthStencil = dsTexture;
|
_depthStencil = dsTexture;
|
||||||
|
_baseAttachment ??= dsTexture;
|
||||||
|
|
||||||
AttachmentSamples[count - 1] = (uint)dsTexture.Info.Samples;
|
AttachmentSamples[count - 1] = (uint)dsTexture.Info.Samples;
|
||||||
AttachmentFormats[count - 1] = dsTexture.VkFormat;
|
AttachmentFormats[count - 1] = dsTexture.VkFormat;
|
||||||
|
@ -251,19 +262,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
public void InsertClearBarrier(CommandBufferScoped cbs, int index)
|
public void InsertClearBarrier(CommandBufferScoped cbs, int index)
|
||||||
{
|
{
|
||||||
if (_colors != null)
|
_colorsCanonical?[index]?.Storage?.InsertReadToWriteBarrier(
|
||||||
{
|
cbs,
|
||||||
int realIndex = Array.IndexOf(AttachmentIndices, index);
|
AccessFlags.ColorAttachmentWriteBit,
|
||||||
|
PipelineStageFlags.ColorAttachmentOutputBit,
|
||||||
if (realIndex != -1)
|
insideRenderPass: true);
|
||||||
{
|
|
||||||
_colors[realIndex].Storage?.InsertReadToWriteBarrier(
|
|
||||||
cbs,
|
|
||||||
AccessFlags.ColorAttachmentWriteBit,
|
|
||||||
PipelineStageFlags.ColorAttachmentOutputBit,
|
|
||||||
insideRenderPass: true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InsertClearBarrierDS(CommandBufferScoped cbs)
|
public void InsertClearBarrierDS(CommandBufferScoped cbs)
|
||||||
|
@ -274,5 +277,61 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
PipelineStageFlags.LateFragmentTestsBit,
|
PipelineStageFlags.LateFragmentTestsBit,
|
||||||
insideRenderPass: true);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
|
@ -20,20 +21,29 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
public TValue Value;
|
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
|
public IEnumerable<TKey> Keys
|
||||||
{
|
{
|
||||||
get
|
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 bucket.Entries[i].Key;
|
||||||
{
|
|
||||||
yield return entry.Key;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,14 +53,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
get
|
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 bucket.Entries[i].Value;
|
||||||
{
|
|
||||||
yield return entry.Value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,40 +75,64 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
int hashCode = key.GetHashCode();
|
int hashCode = key.GetHashCode();
|
||||||
int bucketIndex = hashCode & TotalBucketsMask;
|
int bucketIndex = hashCode & TotalBucketsMask;
|
||||||
|
|
||||||
var bucket = _hashTable[bucketIndex];
|
ref var bucket = ref _hashTable[bucketIndex];
|
||||||
if (bucket != null)
|
if (bucket.Entries != null)
|
||||||
{
|
{
|
||||||
int index = bucket.Length;
|
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
|
else
|
||||||
{
|
{
|
||||||
_hashTable[bucketIndex] = new[]
|
bucket.Entries = new[]
|
||||||
{
|
{
|
||||||
entry,
|
entry,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bucket.Length++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Remove(ref TKey key)
|
||||||
|
{
|
||||||
|
int hashCode = key.GetHashCode();
|
||||||
|
|
||||||
|
ref var bucket = ref _hashTable[hashCode & TotalBucketsMask];
|
||||||
|
var entries = bucket.AsSpan();
|
||||||
|
for (int i = 0; i < entries.Length; i++)
|
||||||
|
{
|
||||||
|
ref var entry = ref entries[i];
|
||||||
|
|
||||||
|
if (entry.Hash == hashCode && entry.Key.Equals(ref key))
|
||||||
|
{
|
||||||
|
entries[(i + 1)..].CopyTo(entries[i..]);
|
||||||
|
bucket.Length--;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetValue(ref TKey key, out TValue value)
|
public bool TryGetValue(ref TKey key, out TValue value)
|
||||||
{
|
{
|
||||||
int hashCode = key.GetHashCode();
|
int hashCode = key.GetHashCode();
|
||||||
|
|
||||||
var bucket = _hashTable[hashCode & TotalBucketsMask];
|
var entries = _hashTable[hashCode & TotalBucketsMask].AsSpan();
|
||||||
if (bucket != null)
|
for (int i = 0; i < entries.Length; i++)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < bucket.Length; i++)
|
ref var entry = ref entries[i];
|
||||||
{
|
|
||||||
ref var entry = ref bucket[i];
|
|
||||||
|
|
||||||
if (entry.Hash == hashCode && entry.Key.Equals(ref key))
|
if (entry.Hash == hashCode && entry.Key.Equals(ref key))
|
||||||
{
|
{
|
||||||
value = entry.Value;
|
value = entry.Value;
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -256,17 +256,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
using var cbs = gd.CommandBufferPool.Rent();
|
using var cbs = gd.CommandBufferPool.Rent();
|
||||||
|
|
||||||
var dstFormat = dst.VkFormat;
|
|
||||||
var dstSamples = dst.Info.Samples;
|
|
||||||
|
|
||||||
for (int l = 0; l < levels; l++)
|
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(
|
var mipSrcRegion = new Extents2D(
|
||||||
srcRegion.X1 >> l,
|
srcRegion.X1 >> l,
|
||||||
srcRegion.Y1 >> l,
|
srcRegion.Y1 >> l,
|
||||||
|
@ -290,11 +281,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
gd,
|
gd,
|
||||||
cbs,
|
cbs,
|
||||||
srcView,
|
srcView,
|
||||||
dst.GetImageViewForAttachment(),
|
dstView,
|
||||||
dstWidth,
|
|
||||||
dstHeight,
|
|
||||||
dstSamples,
|
|
||||||
dstFormat,
|
|
||||||
mipSrcRegion,
|
mipSrcRegion,
|
||||||
mipDstRegion);
|
mipDstRegion);
|
||||||
}
|
}
|
||||||
|
@ -304,12 +291,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
gd,
|
gd,
|
||||||
cbs,
|
cbs,
|
||||||
srcView,
|
srcView,
|
||||||
dst.GetImageViewForAttachment(),
|
dstView,
|
||||||
dstWidth,
|
|
||||||
dstHeight,
|
|
||||||
dstSamples,
|
|
||||||
dstFormat,
|
|
||||||
false,
|
|
||||||
mipSrcRegion,
|
mipSrcRegion,
|
||||||
mipDstRegion,
|
mipDstRegion,
|
||||||
linearFilter,
|
linearFilter,
|
||||||
|
@ -367,12 +349,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
gd,
|
gd,
|
||||||
cbs,
|
cbs,
|
||||||
srcView,
|
srcView,
|
||||||
dstView.GetImageViewForAttachment(),
|
dstView,
|
||||||
dstView.Width,
|
|
||||||
dstView.Height,
|
|
||||||
dstView.Info.Samples,
|
|
||||||
dstView.VkFormat,
|
|
||||||
dstView.Info.Format.IsDepthOrStencil(),
|
|
||||||
extents,
|
extents,
|
||||||
extents,
|
extents,
|
||||||
false);
|
false);
|
||||||
|
@ -394,12 +371,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
VulkanRenderer gd,
|
VulkanRenderer gd,
|
||||||
CommandBufferScoped cbs,
|
CommandBufferScoped cbs,
|
||||||
TextureView src,
|
TextureView src,
|
||||||
Auto<DisposableImageView> dst,
|
TextureView dst,
|
||||||
int dstWidth,
|
|
||||||
int dstHeight,
|
|
||||||
int dstSamples,
|
|
||||||
VkFormat dstFormat,
|
|
||||||
bool dstIsDepthOrStencil,
|
|
||||||
Extents2D srcRegion,
|
Extents2D srcRegion,
|
||||||
Extents2D dstRegion,
|
Extents2D dstRegion,
|
||||||
bool linearFilter,
|
bool linearFilter,
|
||||||
|
@ -453,6 +425,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
0f,
|
0f,
|
||||||
1f);
|
1f);
|
||||||
|
|
||||||
|
bool dstIsDepthOrStencil = dst.Info.Format.IsDepthOrStencil();
|
||||||
|
|
||||||
if (dstIsDepthOrStencil)
|
if (dstIsDepthOrStencil)
|
||||||
{
|
{
|
||||||
_pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit);
|
_pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit);
|
||||||
|
@ -471,7 +445,10 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
_pipeline.SetProgram(_programColorBlit);
|
_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.SetRenderTargetColorMasks(new uint[] { 0xf });
|
||||||
_pipeline.SetScissors(stackalloc Rectangle<int>[] { new Rectangle<int>(0, 0, dstWidth, dstHeight) });
|
_pipeline.SetScissors(stackalloc Rectangle<int>[] { new Rectangle<int>(0, 0, dstWidth, dstHeight) });
|
||||||
|
|
||||||
|
@ -496,11 +473,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
VulkanRenderer gd,
|
VulkanRenderer gd,
|
||||||
CommandBufferScoped cbs,
|
CommandBufferScoped cbs,
|
||||||
TextureView src,
|
TextureView src,
|
||||||
Auto<DisposableImageView> dst,
|
TextureView dst,
|
||||||
int dstWidth,
|
|
||||||
int dstHeight,
|
|
||||||
int dstSamples,
|
|
||||||
VkFormat dstFormat,
|
|
||||||
Extents2D srcRegion,
|
Extents2D srcRegion,
|
||||||
Extents2D dstRegion)
|
Extents2D dstRegion)
|
||||||
{
|
{
|
||||||
|
@ -548,7 +521,10 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
0f,
|
0f,
|
||||||
1f);
|
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.SetScissors(stackalloc Rectangle<int>[] { new Rectangle<int>(0, 0, dstWidth, dstHeight) });
|
||||||
_pipeline.SetViewports(viewports);
|
_pipeline.SetViewports(viewports);
|
||||||
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
|
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
|
||||||
|
@ -660,12 +636,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
public void Clear(
|
public void Clear(
|
||||||
VulkanRenderer gd,
|
VulkanRenderer gd,
|
||||||
Auto<DisposableImageView> dst,
|
TextureView dst,
|
||||||
ReadOnlySpan<float> clearColor,
|
ReadOnlySpan<float> clearColor,
|
||||||
uint componentMask,
|
uint componentMask,
|
||||||
int dstWidth,
|
int dstWidth,
|
||||||
int dstHeight,
|
int dstHeight,
|
||||||
VkFormat dstFormat,
|
|
||||||
ComponentType type,
|
ComponentType type,
|
||||||
Rectangle<int> scissor)
|
Rectangle<int> scissor)
|
||||||
{
|
{
|
||||||
|
@ -710,7 +685,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
}
|
}
|
||||||
|
|
||||||
_pipeline.SetProgram(program);
|
_pipeline.SetProgram(program);
|
||||||
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false, dstFormat);
|
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight);
|
||||||
_pipeline.SetRenderTargetColorMasks(new[] { componentMask });
|
_pipeline.SetRenderTargetColorMasks(new[] { componentMask });
|
||||||
_pipeline.SetViewports(viewports);
|
_pipeline.SetViewports(viewports);
|
||||||
_pipeline.SetScissors(stackalloc Rectangle<int>[] { scissor });
|
_pipeline.SetScissors(stackalloc Rectangle<int>[] { scissor });
|
||||||
|
@ -721,7 +696,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
public void Clear(
|
public void Clear(
|
||||||
VulkanRenderer gd,
|
VulkanRenderer gd,
|
||||||
Auto<DisposableImageView> dst,
|
TextureView dst,
|
||||||
float depthValue,
|
float depthValue,
|
||||||
bool depthMask,
|
bool depthMask,
|
||||||
int stencilValue,
|
int stencilValue,
|
||||||
|
@ -757,7 +732,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
1f);
|
1f);
|
||||||
|
|
||||||
_pipeline.SetProgram(_programDepthStencilClear);
|
_pipeline.SetProgram(_programDepthStencilClear);
|
||||||
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, true, dstFormat);
|
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight);
|
||||||
_pipeline.SetViewports(viewports);
|
_pipeline.SetViewports(viewports);
|
||||||
_pipeline.SetScissors(stackalloc Rectangle<int>[] { scissor });
|
_pipeline.SetScissors(stackalloc Rectangle<int>[] { scissor });
|
||||||
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
|
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
|
||||||
|
@ -1163,12 +1138,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
var srcView = Create2DLayerView(src, srcLayer + z, 0);
|
var srcView = Create2DLayerView(src, srcLayer + z, 0);
|
||||||
var dstView = Create2DLayerView(dst, dstLayer + z, 0);
|
var dstView = Create2DLayerView(dst, dstLayer + z, 0);
|
||||||
|
|
||||||
_pipeline.SetRenderTarget(
|
_pipeline.SetRenderTarget(dstView, (uint)dst.Width, (uint)dst.Height);
|
||||||
dstView.GetImageViewForAttachment(),
|
|
||||||
(uint)dst.Width,
|
|
||||||
(uint)dst.Height,
|
|
||||||
true,
|
|
||||||
dst.VkFormat);
|
|
||||||
|
|
||||||
CopyMSDraw(srcView, aspectFlags, fromMS: true);
|
CopyMSDraw(srcView, aspectFlags, fromMS: true);
|
||||||
|
|
||||||
|
@ -1294,13 +1264,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
var srcView = Create2DLayerView(src, srcLayer + z, 0);
|
var srcView = Create2DLayerView(src, srcLayer + z, 0);
|
||||||
var dstView = Create2DLayerView(dst, dstLayer + z, 0);
|
var dstView = Create2DLayerView(dst, dstLayer + z, 0);
|
||||||
|
|
||||||
_pipeline.SetRenderTarget(
|
_pipeline.SetRenderTarget(dstView, (uint)dst.Width, (uint)dst.Height);
|
||||||
dstView.GetImageViewForAttachment(),
|
|
||||||
(uint)dst.Width,
|
|
||||||
(uint)dst.Height,
|
|
||||||
(uint)samples,
|
|
||||||
true,
|
|
||||||
dst.VkFormat);
|
|
||||||
|
|
||||||
CopyMSDraw(srcView, aspectFlags, fromMS: false);
|
CopyMSDraw(srcView, aspectFlags, fromMS: false);
|
||||||
|
|
||||||
|
@ -1328,13 +1292,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
var dstView = Create2DLayerView(dst, dstLayer + z, 0);
|
var dstView = Create2DLayerView(dst, dstLayer + z, 0);
|
||||||
|
|
||||||
_pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Fragment, 0, srcView, null);
|
_pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Fragment, 0, srcView, null);
|
||||||
_pipeline.SetRenderTarget(
|
_pipeline.SetRenderTarget(dstView.GetView(format), (uint)dst.Width, (uint)dst.Height);
|
||||||
dstView.GetView(format).GetImageViewForAttachment(),
|
|
||||||
(uint)dst.Width,
|
|
||||||
(uint)dst.Height,
|
|
||||||
(uint)samples,
|
|
||||||
false,
|
|
||||||
vkFormat);
|
|
||||||
|
|
||||||
_pipeline.Draw(4, 1, 0, 0);
|
_pipeline.Draw(4, 1, 0, 0);
|
||||||
|
|
||||||
|
@ -1471,9 +1429,9 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
};
|
};
|
||||||
|
|
||||||
var info = new TextureCreateInfo(
|
var info = new TextureCreateInfo(
|
||||||
from.Info.Width,
|
Math.Max(1, from.Info.Width >> level),
|
||||||
from.Info.Height,
|
Math.Max(1, from.Info.Height >> level),
|
||||||
from.Info.Depth,
|
1,
|
||||||
1,
|
1,
|
||||||
from.Info.Samples,
|
from.Info.Samples,
|
||||||
from.Info.BlockWidth,
|
from.Info.BlockWidth,
|
||||||
|
|
|
@ -55,6 +55,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
protected FramebufferParams FramebufferParams;
|
protected FramebufferParams FramebufferParams;
|
||||||
private Auto<DisposableFramebuffer> _framebuffer;
|
private Auto<DisposableFramebuffer> _framebuffer;
|
||||||
private Auto<DisposableRenderPass> _renderPass;
|
private Auto<DisposableRenderPass> _renderPass;
|
||||||
|
private RenderPassHolder _nullRenderPass;
|
||||||
private int _writtenAttachmentCount;
|
private int _writtenAttachmentCount;
|
||||||
|
|
||||||
private bool _framebufferUsingColorWriteMask;
|
private bool _framebufferUsingColorWriteMask;
|
||||||
|
@ -1488,98 +1489,22 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
protected unsafe void CreateRenderPass()
|
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;
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FramebufferParams.HasDepthStencil)
|
|
||||||
{
|
|
||||||
uint dsIndex = (uint)FramebufferParams.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));
|
|
||||||
}
|
|
||||||
|
|
||||||
EndRenderPass();
|
EndRenderPass();
|
||||||
|
|
||||||
_framebuffer?.Dispose();
|
if (!hasFramebuffer || FramebufferParams.AttachmentsCount == 0)
|
||||||
_framebuffer = hasFramebuffer ? FramebufferParams.Create(Gd.Api, Cbs, _renderPass) : null;
|
{
|
||||||
|
// Use the null framebuffer.
|
||||||
|
_nullRenderPass ??= new RenderPassHolder(Gd, Device, new RenderPassCacheKey(), FramebufferParams);
|
||||||
|
|
||||||
|
_renderPass = _nullRenderPass.GetRenderPass();
|
||||||
|
_framebuffer = _nullRenderPass.GetFramebuffer(Gd, Cbs, FramebufferParams);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
(_renderPass, _framebuffer) = FramebufferParams.GetPassAndFramebuffer(Gd, Device, Cbs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void SignalStateChange()
|
protected void SignalStateChange()
|
||||||
|
@ -1770,8 +1695,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
_renderPass?.Dispose();
|
_nullRenderPass?.Dispose();
|
||||||
_framebuffer?.Dispose();
|
|
||||||
_newState.Dispose();
|
_newState.Dispose();
|
||||||
_descriptorSetUpdater.Dispose();
|
_descriptorSetUpdater.Dispose();
|
||||||
_vertexBufferUpdater.Dispose();
|
_vertexBufferUpdater.Dispose();
|
||||||
|
|
|
@ -51,7 +51,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
// We can't use CmdClearAttachments if not writing all components,
|
// We can't use CmdClearAttachments if not writing all components,
|
||||||
// because on Vulkan, the pipeline state does not affect clears.
|
// because on Vulkan, the pipeline state does not affect clears.
|
||||||
var dstTexture = FramebufferParams.GetAttachment(index);
|
var dstTexture = FramebufferParams.GetColorView(index);
|
||||||
if (dstTexture == null)
|
if (dstTexture == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -71,7 +71,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
componentMask,
|
componentMask,
|
||||||
(int)FramebufferParams.Width,
|
(int)FramebufferParams.Width,
|
||||||
(int)FramebufferParams.Height,
|
(int)FramebufferParams.Height,
|
||||||
FramebufferParams.AttachmentFormats[index],
|
|
||||||
FramebufferParams.GetAttachmentComponentType(index),
|
FramebufferParams.GetAttachmentComponentType(index),
|
||||||
ClearScissor);
|
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,
|
// 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.
|
// because on Vulkan, the pipeline state does not affect clears.
|
||||||
var dstTexture = FramebufferParams.GetDepthStencilAttachment();
|
var dstTexture = FramebufferParams.GetDepthStencilView();
|
||||||
if (dstTexture == null)
|
if (dstTexture == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -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);
|
CreateFramebuffer(view, width, height);
|
||||||
}
|
|
||||||
|
|
||||||
public void SetRenderTarget(Auto<DisposableImageView> view, uint width, uint height, uint samples, bool isDepthStencil, VkFormat format)
|
|
||||||
{
|
|
||||||
CreateFramebuffer(view, width, height, samples, isDepthStencil, format);
|
|
||||||
CreateRenderPass();
|
CreateRenderPass();
|
||||||
SignalStateChange();
|
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();
|
UpdatePipelineAttachmentFormats();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
43
src/Ryujinx.Graphics.Vulkan/RenderPassCacheKey.cs
Normal file
43
src/Ryujinx.Graphics.Vulkan/RenderPassCacheKey.cs
Normal file
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
180
src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs
Normal file
180
src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,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.Linq;
|
||||||
using Format = Ryujinx.Graphics.GAL.Format;
|
using Format = Ryujinx.Graphics.GAL.Format;
|
||||||
using VkBuffer = Silk.NET.Vulkan.Buffer;
|
using VkBuffer = Silk.NET.Vulkan.Buffer;
|
||||||
using VkFormat = Silk.NET.Vulkan.Format;
|
using VkFormat = Silk.NET.Vulkan.Format;
|
||||||
|
@ -23,6 +24,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
private readonly TextureCreateInfo _info;
|
private readonly TextureCreateInfo _info;
|
||||||
|
|
||||||
|
private HashTableSlim<RenderPassCacheKey, RenderPassHolder> _renderPasses;
|
||||||
|
|
||||||
public TextureCreateInfo Info => _info;
|
public TextureCreateInfo Info => _info;
|
||||||
|
|
||||||
public TextureStorage Storage { get; }
|
public TextureStorage Storage { get; }
|
||||||
|
@ -158,6 +161,26 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
Valid = true;
|
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()
|
public Auto<DisposableImage> GetImage()
|
||||||
{
|
{
|
||||||
return Storage.GetImage();
|
return Storage.GetImage();
|
||||||
|
@ -939,6 +962,34 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
throw new NotImplementedException();
|
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)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
|
@ -948,15 +999,29 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
if (_gd.Textures.Remove(this))
|
if (_gd.Textures.Remove(this))
|
||||||
{
|
{
|
||||||
_imageView.Dispose();
|
_imageView.Dispose();
|
||||||
_imageViewIdentity.Dispose();
|
|
||||||
_imageView2dArray?.Dispose();
|
_imageView2dArray?.Dispose();
|
||||||
|
|
||||||
|
if (_imageViewIdentity != _imageView)
|
||||||
|
{
|
||||||
|
_imageViewIdentity.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
if (_imageViewDraw != _imageViewIdentity)
|
if (_imageViewDraw != _imageViewIdentity)
|
||||||
{
|
{
|
||||||
_imageViewDraw.Dispose();
|
_imageViewDraw.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
Storage.DecrementViewsCount();
|
Storage.DecrementViewsCount();
|
||||||
|
|
||||||
|
if (_renderPasses != null)
|
||||||
|
{
|
||||||
|
var renderPasses = _renderPasses.Values.ToArray();
|
||||||
|
|
||||||
|
foreach (var pass in renderPasses)
|
||||||
|
{
|
||||||
|
pass.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
private SwapchainKHR _swapchain;
|
private SwapchainKHR _swapchain;
|
||||||
|
|
||||||
private Image[] _swapchainImages;
|
private Image[] _swapchainImages;
|
||||||
private Auto<DisposableImageView>[] _swapchainImageViews;
|
private TextureView[] _swapchainImageViews;
|
||||||
|
|
||||||
private Semaphore[] _imageAvailableSemaphores;
|
private Semaphore[] _imageAvailableSemaphores;
|
||||||
private Semaphore[] _renderFinishedSemaphores;
|
private Semaphore[] _renderFinishedSemaphores;
|
||||||
|
@ -143,6 +143,23 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
Clipped = true,
|
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.CreateSwapchain(_device, swapchainCreateInfo, null, out _swapchain).ThrowOnError();
|
||||||
|
|
||||||
_gd.SwapchainApi.GetSwapchainImages(_device, _swapchain, &imageCount, null);
|
_gd.SwapchainApi.GetSwapchainImages(_device, _swapchain, &imageCount, null);
|
||||||
|
@ -154,11 +171,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
_gd.SwapchainApi.GetSwapchainImages(_device, _swapchain, &imageCount, pSwapchainImages);
|
_gd.SwapchainApi.GetSwapchainImages(_device, _swapchain, &imageCount, pSwapchainImages);
|
||||||
}
|
}
|
||||||
|
|
||||||
_swapchainImageViews = new Auto<DisposableImageView>[imageCount];
|
_swapchainImageViews = new TextureView[imageCount];
|
||||||
|
|
||||||
for (int i = 0; i < _swapchainImageViews.Length; i++)
|
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
|
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(
|
var componentMapping = new ComponentMapping(
|
||||||
ComponentSwizzle.R,
|
ComponentSwizzle.R,
|
||||||
|
@ -204,7 +221,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
};
|
};
|
||||||
|
|
||||||
_gd.Api.CreateImageView(_device, imageCreateInfo, null, out var imageView).ThrowOnError();
|
_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)
|
private static SurfaceFormatKHR ChooseSwapSurfaceFormat(SurfaceFormatKHR[] availableFormats, bool colorSpacePassthroughEnabled)
|
||||||
|
@ -406,7 +424,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
_scalingFilter.Run(
|
_scalingFilter.Run(
|
||||||
view,
|
view,
|
||||||
cbs,
|
cbs,
|
||||||
_swapchainImageViews[nextImage],
|
_swapchainImageViews[nextImage].GetImageViewForAttachment(),
|
||||||
_format,
|
_format,
|
||||||
_width,
|
_width,
|
||||||
_height,
|
_height,
|
||||||
|
@ -421,11 +439,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
cbs,
|
cbs,
|
||||||
view,
|
view,
|
||||||
_swapchainImageViews[nextImage],
|
_swapchainImageViews[nextImage],
|
||||||
_width,
|
|
||||||
_height,
|
|
||||||
1,
|
|
||||||
_format,
|
|
||||||
false,
|
|
||||||
new Extents2D(srcX0, srcY0, srcX1, srcY1),
|
new Extents2D(srcX0, srcY0, srcX1, srcY1),
|
||||||
new Extents2D(dstX0, dstY1, dstX1, dstY0),
|
new Extents2D(dstX0, dstY1, dstX1, dstY0),
|
||||||
_isLinear,
|
_isLinear,
|
||||||
|
|
Reference in a new issue