/ src / Ryujinx.Graphics.Vulkan / RenderPassHolder.cs
RenderPassHolder.cs
  1  using Silk.NET.Vulkan;
  2  using System;
  3  using System.Collections.Generic;
  4  using System.Linq;
  5  
  6  namespace Ryujinx.Graphics.Vulkan
  7  {
  8      internal class RenderPassHolder
  9      {
 10          private readonly struct FramebufferCacheKey : IRefEquatable<FramebufferCacheKey>
 11          {
 12              private readonly uint _width;
 13              private readonly uint _height;
 14              private readonly uint _layers;
 15  
 16              public FramebufferCacheKey(uint width, uint height, uint layers)
 17              {
 18                  _width = width;
 19                  _height = height;
 20                  _layers = layers;
 21              }
 22  
 23              public override int GetHashCode()
 24              {
 25                  return HashCode.Combine(_width, _height, _layers);
 26              }
 27  
 28              public bool Equals(ref FramebufferCacheKey other)
 29              {
 30                  return other._width == _width && other._height == _height && other._layers == _layers;
 31              }
 32          }
 33  
 34          private readonly record struct ForcedFence(TextureStorage Texture, PipelineStageFlags StageFlags);
 35  
 36          private readonly TextureView[] _textures;
 37          private readonly Auto<DisposableRenderPass> _renderPass;
 38          private readonly HashTableSlim<FramebufferCacheKey, Auto<DisposableFramebuffer>> _framebuffers;
 39          private readonly RenderPassCacheKey _key;
 40          private readonly List<ForcedFence> _forcedFences;
 41  
 42          public unsafe RenderPassHolder(VulkanRenderer gd, Device device, RenderPassCacheKey key, FramebufferParams fb)
 43          {
 44              // Create render pass using framebuffer params.
 45  
 46              const int MaxAttachments = Constants.MaxRenderTargets + 1;
 47  
 48              AttachmentDescription[] attachmentDescs = null;
 49  
 50              var subpass = new SubpassDescription
 51              {
 52                  PipelineBindPoint = PipelineBindPoint.Graphics,
 53              };
 54  
 55              AttachmentReference* attachmentReferences = stackalloc AttachmentReference[MaxAttachments];
 56  
 57              var hasFramebuffer = fb != null;
 58  
 59              if (hasFramebuffer && fb.AttachmentsCount != 0)
 60              {
 61                  attachmentDescs = new AttachmentDescription[fb.AttachmentsCount];
 62  
 63                  for (int i = 0; i < fb.AttachmentsCount; i++)
 64                  {
 65                      attachmentDescs[i] = new AttachmentDescription(
 66                          0,
 67                          fb.AttachmentFormats[i],
 68                          TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, fb.AttachmentSamples[i]),
 69                          AttachmentLoadOp.Load,
 70                          AttachmentStoreOp.Store,
 71                          AttachmentLoadOp.Load,
 72                          AttachmentStoreOp.Store,
 73                          ImageLayout.General,
 74                          ImageLayout.General);
 75                  }
 76  
 77                  int colorAttachmentsCount = fb.ColorAttachmentsCount;
 78  
 79                  if (colorAttachmentsCount > MaxAttachments - 1)
 80                  {
 81                      colorAttachmentsCount = MaxAttachments - 1;
 82                  }
 83  
 84                  if (colorAttachmentsCount != 0)
 85                  {
 86                      int maxAttachmentIndex = fb.MaxColorAttachmentIndex;
 87                      subpass.ColorAttachmentCount = (uint)maxAttachmentIndex + 1;
 88                      subpass.PColorAttachments = &attachmentReferences[0];
 89  
 90                      // Fill with VK_ATTACHMENT_UNUSED to cover any gaps.
 91                      for (int i = 0; i <= maxAttachmentIndex; i++)
 92                      {
 93                          subpass.PColorAttachments[i] = new AttachmentReference(Vk.AttachmentUnused, ImageLayout.Undefined);
 94                      }
 95  
 96                      for (int i = 0; i < colorAttachmentsCount; i++)
 97                      {
 98                          int bindIndex = fb.AttachmentIndices[i];
 99  
100                          subpass.PColorAttachments[bindIndex] = new AttachmentReference((uint)i, ImageLayout.General);
101                      }
102                  }
103  
104                  if (fb.HasDepthStencil)
105                  {
106                      uint dsIndex = (uint)fb.AttachmentsCount - 1;
107  
108                      subpass.PDepthStencilAttachment = &attachmentReferences[MaxAttachments - 1];
109                      *subpass.PDepthStencilAttachment = new AttachmentReference(dsIndex, ImageLayout.General);
110                  }
111              }
112  
113              var subpassDependency = PipelineConverter.CreateSubpassDependency(gd);
114  
115              fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs)
116              {
117                  var renderPassCreateInfo = new RenderPassCreateInfo
118                  {
119                      SType = StructureType.RenderPassCreateInfo,
120                      PAttachments = pAttachmentDescs,
121                      AttachmentCount = attachmentDescs != null ? (uint)attachmentDescs.Length : 0,
122                      PSubpasses = &subpass,
123                      SubpassCount = 1,
124                      PDependencies = &subpassDependency,
125                      DependencyCount = 1,
126                  };
127  
128                  gd.Api.CreateRenderPass(device, in renderPassCreateInfo, null, out var renderPass).ThrowOnError();
129  
130                  _renderPass = new Auto<DisposableRenderPass>(new DisposableRenderPass(gd.Api, device, renderPass));
131              }
132  
133              _framebuffers = new HashTableSlim<FramebufferCacheKey, Auto<DisposableFramebuffer>>();
134  
135              // Register this render pass with all render target views.
136  
137              var textures = fb.GetAttachmentViews();
138  
139              foreach (var texture in textures)
140              {
141                  texture.AddRenderPass(key, this);
142              }
143  
144              _textures = textures;
145              _key = key;
146  
147              _forcedFences = new List<ForcedFence>();
148          }
149  
150          public Auto<DisposableFramebuffer> GetFramebuffer(VulkanRenderer gd, CommandBufferScoped cbs, FramebufferParams fb)
151          {
152              var key = new FramebufferCacheKey(fb.Width, fb.Height, fb.Layers);
153  
154              if (!_framebuffers.TryGetValue(ref key, out Auto<DisposableFramebuffer> result))
155              {
156                  result = fb.Create(gd.Api, cbs, _renderPass);
157  
158                  _framebuffers.Add(ref key, result);
159              }
160  
161              return result;
162          }
163  
164          public Auto<DisposableRenderPass> GetRenderPass()
165          {
166              return _renderPass;
167          }
168  
169          public void AddForcedFence(TextureStorage storage, PipelineStageFlags stageFlags)
170          {
171              if (!_forcedFences.Any(fence => fence.Texture == storage))
172              {
173                  _forcedFences.Add(new ForcedFence(storage, stageFlags));
174              }
175          }
176  
177          public void InsertForcedFences(CommandBufferScoped cbs)
178          {
179              if (_forcedFences.Count > 0)
180              {
181                  _forcedFences.RemoveAll((entry) =>
182                  {
183                      if (entry.Texture.Disposed)
184                      {
185                          return true;
186                      }
187  
188                      entry.Texture.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, entry.StageFlags);
189  
190                      return false;
191                  });
192              }
193          }
194  
195          public bool ContainsAttachment(TextureStorage storage)
196          {
197              return _textures.Any(view => view.Storage == storage);
198          }
199  
200          public void Dispose()
201          {
202              // Dispose all framebuffers.
203  
204              foreach (var fb in _framebuffers.Values)
205              {
206                  fb.Dispose();
207              }
208  
209              // Notify all texture views that this render pass has been disposed.
210  
211              foreach (var texture in _textures)
212              {
213                  texture.RemoveRenderPass(_key);
214              }
215  
216              // Dispose render pass.
217  
218              _renderPass.Dispose();
219          }
220      }
221  }