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 }