/ src / Ryujinx.Graphics.Vulkan / DescriptorSetTemplate.cs
DescriptorSetTemplate.cs
  1  using Ryujinx.Graphics.GAL;
  2  using Silk.NET.Vulkan;
  3  using System;
  4  using System.Numerics;
  5  using System.Runtime.CompilerServices;
  6  
  7  namespace Ryujinx.Graphics.Vulkan
  8  {
  9      class DescriptorSetTemplate : IDisposable
 10      {
 11          /// <summary>
 12          /// Renderdoc seems to crash when doing a templated uniform update with count > 1 on a push descriptor.
 13          /// When this is true, consecutive buffers are always updated individually.
 14          /// </summary>
 15          private const bool RenderdocPushCountBug = true;
 16  
 17          private readonly VulkanRenderer _gd;
 18          private readonly Device _device;
 19  
 20          public readonly DescriptorUpdateTemplate Template;
 21          public readonly int Size;
 22  
 23          public unsafe DescriptorSetTemplate(
 24              VulkanRenderer gd,
 25              Device device,
 26              ResourceBindingSegment[] segments,
 27              PipelineLayoutCacheEntry plce,
 28              PipelineBindPoint pbp,
 29              int setIndex)
 30          {
 31              _gd = gd;
 32              _device = device;
 33  
 34              // Create a template from the set usages. Assumes the descriptor set is updated in segment order then binding order.
 35  
 36              DescriptorUpdateTemplateEntry* entries = stackalloc DescriptorUpdateTemplateEntry[segments.Length];
 37              nuint structureOffset = 0;
 38  
 39              for (int seg = 0; seg < segments.Length; seg++)
 40              {
 41                  ResourceBindingSegment segment = segments[seg];
 42  
 43                  int binding = segment.Binding;
 44                  int count = segment.Count;
 45  
 46                  if (IsBufferType(segment.Type))
 47                  {
 48                      entries[seg] = new DescriptorUpdateTemplateEntry()
 49                      {
 50                          DescriptorType = segment.Type.Convert(),
 51                          DstBinding = (uint)binding,
 52                          DescriptorCount = (uint)count,
 53                          Offset = structureOffset,
 54                          Stride = (nuint)Unsafe.SizeOf<DescriptorBufferInfo>()
 55                      };
 56  
 57                      structureOffset += (nuint)(Unsafe.SizeOf<DescriptorBufferInfo>() * count);
 58                  }
 59                  else if (IsBufferTextureType(segment.Type))
 60                  {
 61                      entries[seg] = new DescriptorUpdateTemplateEntry()
 62                      {
 63                          DescriptorType = segment.Type.Convert(),
 64                          DstBinding = (uint)binding,
 65                          DescriptorCount = (uint)count,
 66                          Offset = structureOffset,
 67                          Stride = (nuint)Unsafe.SizeOf<BufferView>()
 68                      };
 69  
 70                      structureOffset += (nuint)(Unsafe.SizeOf<BufferView>() * count);
 71                  }
 72                  else
 73                  {
 74                      entries[seg] = new DescriptorUpdateTemplateEntry()
 75                      {
 76                          DescriptorType = segment.Type.Convert(),
 77                          DstBinding = (uint)binding,
 78                          DescriptorCount = (uint)count,
 79                          Offset = structureOffset,
 80                          Stride = (nuint)Unsafe.SizeOf<DescriptorImageInfo>()
 81                      };
 82  
 83                      structureOffset += (nuint)(Unsafe.SizeOf<DescriptorImageInfo>() * count);
 84                  }
 85              }
 86  
 87              Size = (int)structureOffset;
 88  
 89              var info = new DescriptorUpdateTemplateCreateInfo()
 90              {
 91                  SType = StructureType.DescriptorUpdateTemplateCreateInfo,
 92                  DescriptorUpdateEntryCount = (uint)segments.Length,
 93                  PDescriptorUpdateEntries = entries,
 94  
 95                  TemplateType = DescriptorUpdateTemplateType.DescriptorSet,
 96                  DescriptorSetLayout = plce.DescriptorSetLayouts[setIndex],
 97                  PipelineBindPoint = pbp,
 98                  PipelineLayout = plce.PipelineLayout,
 99                  Set = (uint)setIndex,
100              };
101  
102              DescriptorUpdateTemplate result;
103              gd.Api.CreateDescriptorUpdateTemplate(device, &info, null, &result).ThrowOnError();
104  
105              Template = result;
106          }
107  
108          public unsafe DescriptorSetTemplate(
109              VulkanRenderer gd,
110              Device device,
111              ResourceDescriptorCollection descriptors,
112              long updateMask,
113              PipelineLayoutCacheEntry plce,
114              PipelineBindPoint pbp,
115              int setIndex)
116          {
117              _gd = gd;
118              _device = device;
119  
120              // Create a template from the set usages. Assumes the descriptor set is updated in segment order then binding order.
121              int segmentCount = BitOperations.PopCount((ulong)updateMask);
122  
123              DescriptorUpdateTemplateEntry* entries = stackalloc DescriptorUpdateTemplateEntry[segmentCount];
124              int entry = 0;
125              nuint structureOffset = 0;
126  
127              void AddBinding(int binding, int count)
128              {
129                  entries[entry++] = new DescriptorUpdateTemplateEntry()
130                  {
131                      DescriptorType = DescriptorType.UniformBuffer,
132                      DstBinding = (uint)binding,
133                      DescriptorCount = (uint)count,
134                      Offset = structureOffset,
135                      Stride = (nuint)Unsafe.SizeOf<DescriptorBufferInfo>()
136                  };
137  
138                  structureOffset += (nuint)(Unsafe.SizeOf<DescriptorBufferInfo>() * count);
139              }
140  
141              int startBinding = 0;
142              int bindingCount = 0;
143  
144              foreach (ResourceDescriptor descriptor in descriptors.Descriptors)
145              {
146                  for (int i = 0; i < descriptor.Count; i++)
147                  {
148                      int binding = descriptor.Binding + i;
149  
150                      if ((updateMask & (1L << binding)) != 0)
151                      {
152                          if (bindingCount > 0 && (RenderdocPushCountBug || startBinding + bindingCount != binding))
153                          {
154                              AddBinding(startBinding, bindingCount);
155  
156                              bindingCount = 0;
157                          }
158  
159                          if (bindingCount == 0)
160                          {
161                              startBinding = binding;
162                          }
163  
164                          bindingCount++;
165                      }
166                  }
167              }
168  
169              if (bindingCount > 0)
170              {
171                  AddBinding(startBinding, bindingCount);
172              }
173  
174              Size = (int)structureOffset;
175  
176              var info = new DescriptorUpdateTemplateCreateInfo()
177              {
178                  SType = StructureType.DescriptorUpdateTemplateCreateInfo,
179                  DescriptorUpdateEntryCount = (uint)entry,
180                  PDescriptorUpdateEntries = entries,
181  
182                  TemplateType = DescriptorUpdateTemplateType.PushDescriptorsKhr,
183                  DescriptorSetLayout = plce.DescriptorSetLayouts[setIndex],
184                  PipelineBindPoint = pbp,
185                  PipelineLayout = plce.PipelineLayout,
186                  Set = (uint)setIndex,
187              };
188  
189              DescriptorUpdateTemplate result;
190              gd.Api.CreateDescriptorUpdateTemplate(device, &info, null, &result).ThrowOnError();
191  
192              Template = result;
193          }
194  
195          private static bool IsBufferType(ResourceType type)
196          {
197              return type == ResourceType.UniformBuffer || type == ResourceType.StorageBuffer;
198          }
199  
200          private static bool IsBufferTextureType(ResourceType type)
201          {
202              return type == ResourceType.BufferTexture || type == ResourceType.BufferImage;
203          }
204  
205          public unsafe void Dispose()
206          {
207              _gd.Api.DestroyDescriptorUpdateTemplate(_device, Template, null);
208          }
209      }
210  }