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 }