/ src / Ryujinx.Graphics.Vulkan / ImageArray.cs
ImageArray.cs
  1  using Ryujinx.Graphics.GAL;
  2  using Silk.NET.Vulkan;
  3  using System;
  4  using System.Collections.Generic;
  5  
  6  namespace Ryujinx.Graphics.Vulkan
  7  {
  8      class ImageArray : ResourceArray, IImageArray
  9      {
 10          private readonly VulkanRenderer _gd;
 11  
 12          private record struct TextureRef
 13          {
 14              public TextureStorage Storage;
 15              public TextureView View;
 16          }
 17  
 18          private readonly TextureRef[] _textureRefs;
 19          private readonly TextureBuffer[] _bufferTextureRefs;
 20  
 21          private readonly DescriptorImageInfo[] _textures;
 22          private readonly BufferView[] _bufferTextures;
 23  
 24          private HashSet<TextureStorage> _storages;
 25  
 26          private int _cachedCommandBufferIndex;
 27          private int _cachedSubmissionCount;
 28  
 29          private readonly bool _isBuffer;
 30  
 31          public ImageArray(VulkanRenderer gd, int size, bool isBuffer)
 32          {
 33              _gd = gd;
 34  
 35              if (isBuffer)
 36              {
 37                  _bufferTextureRefs = new TextureBuffer[size];
 38                  _bufferTextures = new BufferView[size];
 39              }
 40              else
 41              {
 42                  _textureRefs = new TextureRef[size];
 43                  _textures = new DescriptorImageInfo[size];
 44              }
 45  
 46              _storages = null;
 47  
 48              _cachedCommandBufferIndex = -1;
 49              _cachedSubmissionCount = 0;
 50  
 51              _isBuffer = isBuffer;
 52          }
 53  
 54          public void SetImages(int index, ITexture[] images)
 55          {
 56              for (int i = 0; i < images.Length; i++)
 57              {
 58                  ITexture image = images[i];
 59  
 60                  if (image is TextureBuffer textureBuffer)
 61                  {
 62                      _bufferTextureRefs[index + i] = textureBuffer;
 63                  }
 64                  else if (image is TextureView view)
 65                  {
 66                      _textureRefs[index + i].Storage = view.Storage;
 67                      _textureRefs[index + i].View = view;
 68                  }
 69                  else if (!_isBuffer)
 70                  {
 71                      _textureRefs[index + i].Storage = null;
 72                      _textureRefs[index + i].View = default;
 73                  }
 74                  else
 75                  {
 76                      _bufferTextureRefs[index + i] = null;
 77                  }
 78              }
 79  
 80              SetDirty();
 81          }
 82  
 83          private void SetDirty()
 84          {
 85              _cachedCommandBufferIndex = -1;
 86              _storages = null;
 87              SetDirty(_gd, isImage: true);
 88          }
 89  
 90          public void QueueWriteToReadBarriers(CommandBufferScoped cbs, PipelineStageFlags stageFlags)
 91          {
 92              HashSet<TextureStorage> storages = _storages;
 93  
 94              if (storages == null)
 95              {
 96                  storages = new HashSet<TextureStorage>();
 97  
 98                  for (int index = 0; index < _textureRefs.Length; index++)
 99                  {
100                      if (_textureRefs[index].Storage != null)
101                      {
102                          storages.Add(_textureRefs[index].Storage);
103                      }
104                  }
105  
106                  _storages = storages;
107              }
108  
109              foreach (TextureStorage storage in storages)
110              {
111                  storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stageFlags);
112              }
113          }
114  
115          public ReadOnlySpan<DescriptorImageInfo> GetImageInfos(VulkanRenderer gd, CommandBufferScoped cbs, TextureView dummyTexture)
116          {
117              int submissionCount = gd.CommandBufferPool.GetSubmissionCount(cbs.CommandBufferIndex);
118  
119              Span<DescriptorImageInfo> textures = _textures;
120  
121              if (cbs.CommandBufferIndex == _cachedCommandBufferIndex && submissionCount == _cachedSubmissionCount)
122              {
123                  return textures;
124              }
125  
126              _cachedCommandBufferIndex = cbs.CommandBufferIndex;
127              _cachedSubmissionCount = submissionCount;
128  
129              for (int i = 0; i < textures.Length; i++)
130              {
131                  ref var texture = ref textures[i];
132                  ref var refs = ref _textureRefs[i];
133  
134                  if (i > 0 && _textureRefs[i - 1].View == refs.View)
135                  {
136                      texture = textures[i - 1];
137  
138                      continue;
139                  }
140  
141                  texture.ImageLayout = ImageLayout.General;
142                  texture.ImageView = refs.View?.GetIdentityImageView().Get(cbs).Value ?? default;
143  
144                  if (texture.ImageView.Handle == 0)
145                  {
146                      texture.ImageView = dummyTexture.GetImageView().Get(cbs).Value;
147                  }
148              }
149  
150              return textures;
151          }
152  
153          public ReadOnlySpan<BufferView> GetBufferViews(CommandBufferScoped cbs)
154          {
155              Span<BufferView> bufferTextures = _bufferTextures;
156  
157              for (int i = 0; i < bufferTextures.Length; i++)
158              {
159                  bufferTextures[i] = _bufferTextureRefs[i]?.GetBufferView(cbs, true) ?? default;
160              }
161  
162              return bufferTextures;
163          }
164  
165          public DescriptorSet[] GetDescriptorSets(
166              Device device,
167              CommandBufferScoped cbs,
168              DescriptorSetTemplateUpdater templateUpdater,
169              ShaderCollection program,
170              int setIndex,
171              TextureView dummyTexture)
172          {
173              if (TryGetCachedDescriptorSets(cbs, program, setIndex, out DescriptorSet[] sets))
174              {
175                  // We still need to ensure the current command buffer holds a reference to all used textures.
176  
177                  if (!_isBuffer)
178                  {
179                      GetImageInfos(_gd, cbs, dummyTexture);
180                  }
181                  else
182                  {
183                      GetBufferViews(cbs);
184                  }
185  
186                  return sets;
187              }
188  
189              DescriptorSetTemplate template = program.Templates[setIndex];
190  
191              DescriptorSetTemplateWriter tu = templateUpdater.Begin(template);
192  
193              if (!_isBuffer)
194              {
195                  tu.Push(GetImageInfos(_gd, cbs, dummyTexture));
196              }
197              else
198              {
199                  tu.Push(GetBufferViews(cbs));
200              }
201  
202              templateUpdater.Commit(_gd, device, sets[0]);
203  
204              return sets;
205          }
206      }
207  }