A game about forced loneliness, made by TACStudios
at master 375 lines 20 kB view raw
1using System; 2using System.Collections.Generic; 3using System.Text; 4 5namespace UnityEngine.Rendering.RenderGraphModule.NativeRenderPassCompiler 6{ 7 internal partial class NativePassCompiler 8 { 9 static RenderGraph.DebugData.PassData.NRPInfo.NativeRenderPassInfo.AttachmentInfo MakeAttachmentInfo( 10 CompilerContextData ctx, 11 in NativePassData nativePass, 12 int attachmentIndex) 13 { 14 var attachment = nativePass.attachments[attachmentIndex]; 15 var pointTo = ctx.UnversionedResourceData(attachment.handle); 16 17 LoadAudit loadAudit = nativePass.loadAudit[attachmentIndex]; 18 string loadReason = LoadAudit.LoadReasonMessages[(int) loadAudit.reason]; 19 if (loadAudit.passId >= 0) 20 loadReason = loadReason.Replace("{pass}", $"<b>{ctx.passNames[loadAudit.passId].name}</b>"); 21 22 StoreAudit storeAudit = nativePass.storeAudit[attachmentIndex]; 23 string storeReason = StoreAudit.StoreReasonMessages[(int) storeAudit.reason]; 24 if (storeAudit.passId >= 0) 25 storeReason = storeReason.Replace("{pass}", $"<b>{ctx.passNames[storeAudit.passId].name}</b>"); 26 27 string storeMsaaReason = string.Empty; 28 if (storeAudit.msaaReason != StoreReason.InvalidReason && storeAudit.msaaReason != StoreReason.NoMSAABuffer) 29 { 30 storeMsaaReason = StoreAudit.StoreReasonMessages[(int) storeAudit.msaaReason]; 31 if (storeAudit.msaaPassId >= 0) 32 storeMsaaReason = storeMsaaReason.Replace("{pass}", $"<b>{ctx.passNames[storeAudit.msaaPassId].name}</b>"); 33 } 34 35 return new RenderGraph.DebugData.PassData.NRPInfo.NativeRenderPassInfo.AttachmentInfo 36 { 37 resourceName = pointTo.GetName(ctx, attachment.handle), 38 attachmentIndex = attachmentIndex, 39 loadAction = attachment.loadAction.ToString(), 40 loadReason = loadReason, 41 storeAction = attachment.storeAction.ToString(), 42 storeReason = storeReason, 43 storeMsaaReason = storeMsaaReason 44 }; 45 } 46 47 internal static string MakePassBreakInfoMessage(CompilerContextData ctx, in NativePassData nativePass) 48 { 49 string msg = ""; 50 if (nativePass.breakAudit.breakPass >= 0) 51 { 52 msg += $"Failed to merge {ctx.passNames[nativePass.breakAudit.breakPass].name} into this native pass.\n"; 53 } 54 55 msg += PassBreakAudit.BreakReasonMessages[(int) nativePass.breakAudit.reason]; 56 return msg; 57 } 58 59 internal static string MakePassMergeMessage(CompilerContextData ctx, in PassData pass, in PassData prevPass, PassBreakAudit mergeResult) 60 { 61 string message = mergeResult.reason == PassBreakReason.Merged ? 62 "The passes are <b>compatible</b> to be merged.\n\n" : 63 "The passes are <b>incompatible</b> to be merged.\n\n"; 64 string passName = InjectSpaces(pass.GetName(ctx).name); 65 string prevPassName = InjectSpaces(prevPass.GetName(ctx).name); 66 switch (mergeResult.reason) 67 { 68 case (PassBreakReason.Merged): 69 if (pass.nativePassIndex == prevPass.nativePassIndex && pass.mergeState != PassMergeState.None) 70 message += "Passes are merged."; 71 else 72 message += "Passes can be merged but are not recorded consecutively."; 73 break; 74 case PassBreakReason.TargetSizeMismatch: 75 message += "The fragment attachments of the passes have different sizes or sample counts.\n" + 76 $"- {prevPassName}: {prevPass.fragmentInfoWidth}x{prevPass.fragmentInfoHeight}, {prevPass.fragmentInfoSamples} sample(s).\n" + 77 $"- {passName}: {pass.fragmentInfoWidth}x{pass.fragmentInfoHeight}, {pass.fragmentInfoSamples} sample(s)."; 78 break; 79 case PassBreakReason.NextPassReadsTexture: 80 message += "The next pass reads one of the outputs as a regular texture, the pass needs to break."; 81 break; 82 case PassBreakReason.NonRasterPass: 83 message += $"{prevPassName} is type {prevPass.type}. Only Raster passes can be merged."; 84 break; 85 case PassBreakReason.DifferentDepthTextures: 86 message += $"{prevPassName} uses a different depth buffer than {passName}."; 87 break; 88 case PassBreakReason.AttachmentLimitReached: 89 message += $"Merging the passes would use more than {FixedAttachmentArray<PassFragmentData>.MaxAttachments} attachments."; 90 break; 91 case PassBreakReason.SubPassLimitReached: 92 message += $"Merging the passes would use more than {k_MaxSubpass} native subpasses."; 93 break; 94 case PassBreakReason.EndOfGraph: 95 message += "The pass is the last pass in the graph."; 96 break; 97 default: 98 throw new ArgumentOutOfRangeException(); 99 } 100 101 return message; 102 } 103 104 static string InjectSpaces(string camelCaseString) 105 { 106 var bld = new StringBuilder(); 107 108 for (var i = 0; i< camelCaseString.Length; i++) 109 { 110 if (char.IsUpper(camelCaseString[i]) 111 && (i!=0 && char.IsLower(camelCaseString[i-1]) ) ) 112 { 113 bld.Append(" "); 114 } 115 116 bld.Append(camelCaseString[i]); 117 } 118 return bld.ToString(); 119 } 120 121 internal void GenerateNativeCompilerDebugData(ref RenderGraph.DebugData debugData) 122 { 123 ref var ctx = ref contextData; 124 125 debugData.isNRPCompiler = true; 126 127 // Resolve read/write lists per resource 128 Dictionary<(RenderGraphResourceType, int), List<int>> resourceReadLists = new Dictionary<(RenderGraphResourceType, int), List<int>>(); 129 Dictionary<(RenderGraphResourceType, int), List<int>> resourceWriteLists = new Dictionary<(RenderGraphResourceType, int), List<int>>(); 130 131 foreach (var renderGraphPass in graph.m_RenderPasses) 132 { 133 for (int type = 0; type < (int)RenderGraphResourceType.Count; ++type) 134 { 135 int numResources = ctx.resources.unversionedData[type].Length; 136 137 for (int resIndex = 0; resIndex < numResources; ++resIndex) 138 { 139 foreach (var read in renderGraphPass.resourceReadLists[type]) 140 { 141 if (renderGraphPass.implicitReadsList.Contains(read)) 142 continue; // Implicit read - do not display them in RG Viewer 143 144 if (read.type == (RenderGraphResourceType) type && read.index == resIndex) 145 { 146 var pair = ((RenderGraphResourceType) type, resIndex); 147 if (!resourceReadLists.ContainsKey(pair)) 148 resourceReadLists[pair] = new List<int>(); 149 resourceReadLists[pair].Add(renderGraphPass.index); 150 } 151 } 152 foreach (var read in renderGraphPass.resourceWriteLists[type]) 153 { 154 if (read.type == (RenderGraphResourceType) type && read.index == resIndex) 155 { 156 var pair = ((RenderGraphResourceType) type, resIndex); 157 if (!resourceWriteLists.ContainsKey(pair)) 158 resourceWriteLists[pair] = new List<int>(); 159 resourceWriteLists[pair].Add(renderGraphPass.index); 160 } 161 } 162 } 163 } 164 } 165 166 // Create resource debug data 167 for (int t = 0; t < (int)RenderGraphResourceType.Count; ++t) 168 { 169 var numResources = ctx.resources.unversionedData[t].Length; 170 for (int i = 0; i < numResources; ++i) 171 { 172 ref var resourceUnversioned = ref ctx.resources.unversionedData[t].ElementAt(i); 173 RenderGraph.DebugData.ResourceData debugResource = new RenderGraph.DebugData.ResourceData(); 174 RenderGraphResourceType type = (RenderGraphResourceType) t; 175 bool isNullResource = i == 0; 176 177 if (!isNullResource) 178 { 179 string resourceName = ctx.resources.resourceNames[t][i].name; 180 debugResource.name = !string.IsNullOrEmpty(resourceName) ? resourceName : "(unnamed)"; 181 debugResource.imported = resourceUnversioned.isImported; 182 } 183 else 184 { 185 // The above functions will throw exceptions when used with the null argument so just use a dummy instead 186 debugResource.name = "<null>"; 187 debugResource.imported = true; 188 } 189 190 var info = new RenderTargetInfo(); 191 if (type == RenderGraphResourceType.Texture && !isNullResource) 192 { 193 var handle = new ResourceHandle(i, type, false); 194 try 195 { 196 graph.m_ResourcesForDebugOnly.GetRenderTargetInfo(handle, out info); 197 } 198 catch (Exception) { } 199 } 200 201 debugResource.creationPassIndex = resourceUnversioned.firstUsePassID; 202 debugResource.releasePassIndex = resourceUnversioned.lastUsePassID; 203 204 debugResource.textureData = new RenderGraph.DebugData.TextureResourceData(); 205 debugResource.textureData.width = resourceUnversioned.width; 206 debugResource.textureData.height = resourceUnversioned.height; 207 debugResource.textureData.depth = resourceUnversioned.volumeDepth; 208 debugResource.textureData.samples = resourceUnversioned.msaaSamples; 209 debugResource.textureData.format = info.format; 210 debugResource.memoryless = resourceUnversioned.memoryLess; 211 212 debugResource.consumerList = new List<int>(); 213 debugResource.producerList = new List<int>(); 214 215 if (resourceReadLists.ContainsKey(((RenderGraphResourceType) t, i))) 216 debugResource.consumerList = resourceReadLists[((RenderGraphResourceType) t, i)]; 217 218 if (resourceWriteLists.ContainsKey(((RenderGraphResourceType) t, i))) 219 debugResource.producerList = resourceWriteLists[((RenderGraphResourceType) t, i)]; 220 221 debugData.resourceLists[t].Add(debugResource); 222 } 223 } 224 225 // Create pass debug data 226 for (int passId = 0; passId < ctx.passData.Length; passId++) 227 { 228 var graphPass = graph.m_RenderPasses[passId]; 229 ref var passData = ref ctx.passData.ElementAt(passId); 230 string passName = passData.GetName(ctx).name; 231 string passDisplayName = InjectSpaces(passName); 232 233 RenderGraph.DebugData.PassData debugPass = new RenderGraph.DebugData.PassData(); 234 debugPass.name = passDisplayName; 235 debugPass.type = passData.type; 236 debugPass.culled = passData.culled; 237 debugPass.async = passData.asyncCompute; 238 debugPass.nativeSubPassIndex = passData.nativeSubPassIndex; 239 debugPass.generateDebugData = graphPass.generateDebugData; 240 debugPass.resourceReadLists = new List<int>[(int)RenderGraphResourceType.Count]; 241 debugPass.resourceWriteLists = new List<int>[(int)RenderGraphResourceType.Count]; 242 243 RenderGraph.DebugData.s_PassScriptMetadata.TryGetValue(passName, out debugPass.scriptInfo); 244 245 debugPass.syncFromPassIndex = -1; // TODO async compute support 246 debugPass.syncToPassIndex = -1; // TODO async compute support 247 248 debugPass.nrpInfo = new RenderGraph.DebugData.PassData.NRPInfo(); 249 250 debugPass.nrpInfo.width = passData.fragmentInfoWidth; 251 debugPass.nrpInfo.height = passData.fragmentInfoHeight; 252 debugPass.nrpInfo.volumeDepth = passData.fragmentInfoVolumeDepth; 253 debugPass.nrpInfo.samples = passData.fragmentInfoSamples; 254 debugPass.nrpInfo.hasDepth = passData.fragmentInfoHasDepth; 255 256 foreach (var setGlobal in graphPass.setGlobalsList) 257 debugPass.nrpInfo.setGlobals.Add(setGlobal.Item1.handle.index); 258 259 for (int type = 0; type < (int)RenderGraphResourceType.Count; ++type) 260 { 261 debugPass.resourceReadLists[type] = new List<int>(); 262 debugPass.resourceWriteLists[type] = new List<int>(); 263 264 foreach (var resRead in graphPass.resourceReadLists[type]) 265 { 266 if (graphPass.implicitReadsList.Contains(resRead)) 267 continue; // Implicit read - do not display them in RG Viewer 268 debugPass.resourceReadLists[type].Add(resRead.index); 269 } 270 271 foreach (var resWrite in graphPass.resourceWriteLists[type]) 272 debugPass.resourceWriteLists[type].Add(resWrite.index); 273 } 274 275 foreach (var fragmentInput in passData.FragmentInputs(ctx)) 276 { 277 Debug.Assert(fragmentInput.resource.type == RenderGraphResourceType.Texture); 278 debugPass.nrpInfo.textureFBFetchList.Add(fragmentInput.resource.index); 279 } 280 281 debugData.passList.Add(debugPass); 282 } 283 284 // Native pass info 285 foreach (ref readonly var nativePassData in ctx.NativePasses) 286 { 287 List<int> mergedPassIds = new List<int>(); 288 for (int graphPassId = nativePassData.firstGraphPass; graphPassId < nativePassData.lastGraphPass + 1; ++graphPassId) 289 mergedPassIds.Add(graphPassId); 290 291 if (nativePassData.numGraphPasses > 0) 292 { 293 var nativePassInfo = new RenderGraph.DebugData.PassData.NRPInfo.NativeRenderPassInfo(); 294 nativePassInfo.passBreakReasoning = MakePassBreakInfoMessage(ctx, in nativePassData); 295 nativePassInfo.attachmentInfos = new (); 296 for (int a = 0; a < nativePassData.attachments.size; a++) 297 nativePassInfo.attachmentInfos.Add(MakeAttachmentInfo(ctx, in nativePassData, a)); 298 nativePassInfo.passCompatibility = new Dictionary<int, RenderGraph.DebugData.PassData.NRPInfo.NativeRenderPassInfo.PassCompatibilityInfo>(); 299 nativePassInfo.mergedPassIds = mergedPassIds; 300 301 for (int i = 0; i < mergedPassIds.Count; ++i) 302 { 303 var mergedPassId = mergedPassIds[i]; 304 var debugPass = debugData.passList[mergedPassId]; 305 debugPass.nrpInfo.nativePassInfo = nativePassInfo; 306 debugData.passList[mergedPassId] = debugPass; 307 } 308 } 309 } 310 311 // Pass compatibility info 312 for (int passIndex = 0; passIndex < ctx.passData.Length; passIndex++) 313 { 314 ref var pass = ref ctx.passData.ElementAt(passIndex); 315 var nativePassInfo = debugData.passList[pass.passId].nrpInfo.nativePassInfo; 316 if (nativePassInfo == null) 317 continue; 318 319 // Input dependencies 320 foreach (ref readonly var input in pass.Inputs(ctx)) 321 { 322 ref var inputDataVersioned = ref ctx.VersionedResourceData(input.resource); 323 if (inputDataVersioned.written) 324 { 325 var inputDependencyPass = ctx.passData[inputDataVersioned.writePassId]; 326 PassBreakAudit mergeResult = inputDependencyPass.nativePassIndex >= 0 327 ? NativePassData.CanMerge(ctx, inputDependencyPass.nativePassIndex, pass.passId) 328 : new PassBreakAudit(PassBreakReason.NonRasterPass, pass.passId); 329 330 string mergeMessage = "This pass writes to a resource that is read by the currently selected pass.\n\n" 331 + MakePassMergeMessage(ctx, pass, inputDependencyPass, mergeResult); 332 nativePassInfo.passCompatibility.TryAdd(inputDependencyPass.passId, 333 new RenderGraph.DebugData.PassData.NRPInfo.NativeRenderPassInfo.PassCompatibilityInfo 334 { 335 message = mergeMessage, 336 isCompatible = mergeResult.reason == PassBreakReason.Merged 337 }); 338 } 339 } 340 341 // Output dependencies (only relevant if current pass is part of a native pass, and therefore candidate for merging) 342 if (pass.nativePassIndex >= 0) 343 { 344 foreach (ref readonly var output in pass.Outputs(ctx)) 345 { 346 ref var outputDataUnversioned = ref ctx.UnversionedResourceData(output.resource); 347 if (outputDataUnversioned.lastUsePassID != pass.passId) // Someone else is using this resource 348 { 349 ref var outputDataVersioned = ref ctx.VersionedResourceData(output.resource); 350 var numReaders = outputDataVersioned.numReaders; 351 for (var i = 0; i < numReaders; ++i) 352 { 353 var depIdx = ResourcesData.IndexReader(output.resource, i); 354 ref var dep = ref ctx.resources.readerData[output.resource.iType].ElementAt(depIdx); 355 356 var outputDependencyPass = ctx.passData[dep.passId]; 357 PassBreakAudit mergeResult = NativePassData.CanMerge(ctx, pass.nativePassIndex, 358 outputDependencyPass.passId); 359 360 string mergeMessage = "This pass reads a resource that is written to by the currently selected pass.\n\n" 361 + MakePassMergeMessage(ctx, outputDependencyPass, pass, mergeResult); 362 nativePassInfo.passCompatibility.TryAdd(outputDependencyPass.passId, 363 new RenderGraph.DebugData.PassData.NRPInfo.NativeRenderPassInfo.PassCompatibilityInfo 364 { 365 message = mergeMessage, 366 isCompatible = mergeResult.reason == PassBreakReason.Merged 367 }); 368 } 369 } 370 } 371 } 372 } 373 } 374 } 375}