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