From c94f0fbb8307873f68df982c100d3fb01aa6ccf5 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Wed, 31 Jan 2024 22:49:50 +0000 Subject: 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. --- src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs | 180 ++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs (limited to 'src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs') 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 + { + 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 _renderPass; + private readonly HashTableSlim> _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(new DisposableRenderPass(gd.Api, device, renderPass)); + } + + _framebuffers = new HashTableSlim>(); + + // 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 GetFramebuffer(VulkanRenderer gd, CommandBufferScoped cbs, FramebufferParams fb) + { + var key = new FramebufferCacheKey(fb.Width, fb.Height, fb.Layers); + + if (!_framebuffers.TryGetValue(ref key, out Auto result)) + { + result = fb.Create(gd.Api, cbs, _renderPass); + + _framebuffers.Add(ref key, result); + } + + return result; + } + + public Auto 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); + } + } + } +} -- cgit v1.2.3