A game about forced loneliness, made by TACStudios
1using System; 2using System.Diagnostics; 3using System.Collections.Generic; 4using System.Runtime.CompilerServices; 5 6namespace UnityEngine.Rendering.RenderGraphModule 7{ 8 [DebuggerDisplay("RenderPass: {name} (Index:{index} Async:{enableAsyncCompute})")] 9 abstract class RenderGraphPass 10 { 11 public abstract void Execute(InternalRenderGraphContext renderGraphContext); 12 public abstract void Release(RenderGraphObjectPool pool); 13 public abstract bool HasRenderFunc(); 14 public abstract int GetRenderFuncHash(); 15 16 public string name { get; protected set; } 17 public int index { get; protected set; } 18 public RenderGraphPassType type { get; internal set; } 19 public ProfilingSampler customSampler { get; protected set; } 20 public bool enableAsyncCompute { get; protected set; } 21 public bool allowPassCulling { get; protected set; } 22 public bool allowGlobalState { get; protected set; } 23 public bool enableFoveatedRasterization { get; protected set; } 24 25 // Before using the AccessFlags use resourceHandle.isValid() 26 // to make sure that the data in the colorBuffer/fragmentInput/randomAccessResource buffers are up to date 27 public TextureAccess depthAccess { get; protected set; } 28 29 public TextureAccess[] colorBufferAccess { get; protected set; } = new TextureAccess[RenderGraph.kMaxMRTCount]; 30 public int colorBufferMaxIndex { get; protected set; } = -1; 31 32 // Used by native pass compiler only 33 public TextureAccess[] fragmentInputAccess { get; protected set; } = new TextureAccess[RenderGraph.kMaxMRTCount]; 34 public int fragmentInputMaxIndex { get; protected set; } = -1; 35 36 public struct RandomWriteResourceInfo 37 { 38 public ResourceHandle h; 39 public bool preserveCounterValue; 40 } 41 42 // This list can contain both texture and buffer resources based on their binding index. 43 public RandomWriteResourceInfo[] randomAccessResource { get; protected set; } = new RandomWriteResourceInfo[RenderGraph.kMaxMRTCount]; 44 public int randomAccessResourceMaxIndex { get; protected set; } = -1; 45 46 public bool generateDebugData { get; protected set; } 47 48 public bool allowRendererListCulling { get; protected set; } 49 50 public List<ResourceHandle>[] resourceReadLists = new List<ResourceHandle>[(int)RenderGraphResourceType.Count]; 51 public List<ResourceHandle>[] resourceWriteLists = new List<ResourceHandle>[(int)RenderGraphResourceType.Count]; 52 public List<ResourceHandle>[] transientResourceList = new List<ResourceHandle>[(int)RenderGraphResourceType.Count]; 53 54 public List<RendererListHandle> usedRendererListList = new List<RendererListHandle>(); 55 56 public List<ValueTuple<TextureHandle, int>> setGlobalsList = new List<ValueTuple<TextureHandle, int>>(); 57 public bool useAllGlobalTextures; 58 59 public List<ResourceHandle> implicitReadsList = new List<ResourceHandle>(); 60 61 public RenderGraphPass() 62 { 63 for (int i = 0; i < (int)RenderGraphResourceType.Count; ++i) 64 { 65 resourceReadLists[i] = new List<ResourceHandle>(); 66 resourceWriteLists[i] = new List<ResourceHandle>(); 67 transientResourceList[i] = new List<ResourceHandle>(); 68 } 69 } 70 71 public void Clear() 72 { 73 name = ""; 74 index = -1; 75 customSampler = null; 76 for (int i = 0; i < (int)RenderGraphResourceType.Count; ++i) 77 { 78 resourceReadLists[i].Clear(); 79 resourceWriteLists[i].Clear(); 80 transientResourceList[i].Clear(); 81 } 82 83 usedRendererListList.Clear(); 84 setGlobalsList.Clear(); 85 useAllGlobalTextures = false; 86 implicitReadsList.Clear(); 87 enableAsyncCompute = false; 88 allowPassCulling = true; 89 allowRendererListCulling = true; 90 allowGlobalState = false; 91 enableFoveatedRasterization = false; 92 generateDebugData = true; 93 94 // Invalidate the buffers without clearing them, as it is too costly 95 // Use IsValid() to make sure that the data in the colorBuffer/fragmentInput/randomAccessResource buffers are up to date 96 colorBufferMaxIndex = -1; 97 fragmentInputMaxIndex = -1; 98 randomAccessResourceMaxIndex = -1; 99 } 100 101 // Check if the pass has any render targets set-up 102 [MethodImpl(MethodImplOptions.AggressiveInlining)] 103 public bool HasRenderAttachments() 104 { 105 return depthAccess.textureHandle.IsValid() || colorBufferAccess[0].textureHandle.IsValid() || colorBufferMaxIndex > 0; 106 } 107 108 // Checks if the resource is involved in this pass 109 [MethodImpl(MethodImplOptions.AggressiveInlining)] 110 public bool IsTransient(in ResourceHandle res) 111 { 112 return transientResourceList[res.iType].Contains(res); 113 } 114 115 [MethodImpl(MethodImplOptions.AggressiveInlining)] 116 public bool IsWritten(in ResourceHandle res) 117 { 118 // You can only ever write to the latest version so we ignore it when looking in the list 119 for (int i = 0; i < resourceWriteLists[res.iType].Count; i++) 120 { 121 if (resourceWriteLists[res.iType][i].index == res.index) 122 { 123 return true; 124 } 125 } 126 return false; 127 } 128 129 [MethodImpl(MethodImplOptions.AggressiveInlining)] 130 public bool IsRead(in ResourceHandle res) 131 { 132 if (res.IsVersioned) 133 { 134 return resourceReadLists[res.iType].Contains(res); 135 } 136 else 137 { 138 // Just look if we are reading any version of this texture. 139 // Note that in theory this pass could read from several versions of the same texture 140 // e.g. ColorBuffer,v3 and ColorBuffer,v5 so this check is always conservative 141 for (int i = 0; i < resourceReadLists[res.iType].Count; i++) 142 { 143 if (resourceReadLists[res.iType][i].index == res.index) 144 { 145 return true; 146 } 147 } 148 return false; 149 } 150 } 151 152 [MethodImpl(MethodImplOptions.AggressiveInlining)] 153 public bool IsAttachment(in TextureHandle res) 154 { 155 // We ignore the version when checking, if any version is used it is considered a match 156 157 if (depthAccess.textureHandle.IsValid() && depthAccess.textureHandle.handle.index == res.handle.index) return true; 158 for (int i = 0; i < colorBufferAccess.Length; i++) 159 { 160 if (colorBufferAccess[i].textureHandle.IsValid() && colorBufferAccess[i].textureHandle.handle.index == res.handle.index) return true; 161 } 162 163 return false; 164 } 165 166 167 [MethodImpl(MethodImplOptions.AggressiveInlining)] 168 public void AddResourceWrite(in ResourceHandle res) 169 { 170 resourceWriteLists[res.iType].Add(res); 171 } 172 173 [MethodImpl(MethodImplOptions.AggressiveInlining)] 174 public void AddResourceRead(in ResourceHandle res) 175 { 176 resourceReadLists[res.iType].Add(res); 177 } 178 179 [MethodImpl(MethodImplOptions.AggressiveInlining)] 180 public void AddTransientResource(in ResourceHandle res) 181 { 182 transientResourceList[res.iType].Add(res); 183 } 184 185 [MethodImpl(MethodImplOptions.AggressiveInlining)] 186 public void UseRendererList(in RendererListHandle rendererList) 187 { 188 usedRendererListList.Add(rendererList); 189 } 190 191 [MethodImpl(MethodImplOptions.AggressiveInlining)] 192 public void EnableAsyncCompute(bool value) 193 { 194 enableAsyncCompute = value; 195 } 196 197 [MethodImpl(MethodImplOptions.AggressiveInlining)] 198 public void AllowPassCulling(bool value) 199 { 200 allowPassCulling = value; 201 } 202 203 [MethodImpl(MethodImplOptions.AggressiveInlining)] 204 public void EnableFoveatedRasterization(bool value) 205 { 206 enableFoveatedRasterization = value; 207 } 208 209 [MethodImpl(MethodImplOptions.AggressiveInlining)] 210 public void AllowRendererListCulling(bool value) 211 { 212 allowRendererListCulling = value; 213 } 214 215 [MethodImpl(MethodImplOptions.AggressiveInlining)] 216 public void AllowGlobalState(bool value) 217 { 218 allowGlobalState = value; 219 } 220 221 [MethodImpl(MethodImplOptions.AggressiveInlining)] 222 public void GenerateDebugData(bool value) 223 { 224 generateDebugData = value; 225 } 226 227 [MethodImpl(MethodImplOptions.AggressiveInlining)] 228 public void SetColorBuffer(in TextureHandle resource, int index) 229 { 230 Debug.Assert(index < RenderGraph.kMaxMRTCount && index >= 0); 231 colorBufferMaxIndex = Math.Max(colorBufferMaxIndex, index); 232 colorBufferAccess[index].textureHandle = resource; 233 AddResourceWrite(resource.handle); 234 } 235 236 // Sets up the color buffer for this pass but not any resource Read/Writes for it 237 [MethodImpl(MethodImplOptions.AggressiveInlining)] 238 public void SetColorBufferRaw(in TextureHandle resource, int index, AccessFlags accessFlags, int mipLevel, int depthSlice) 239 { 240 Debug.Assert(index < RenderGraph.kMaxMRTCount && index >= 0); 241 if (colorBufferAccess[index].textureHandle.handle.Equals(resource.handle) || !colorBufferAccess[index].textureHandle.IsValid()) 242 { 243 colorBufferMaxIndex = Math.Max(colorBufferMaxIndex, index); 244 colorBufferAccess[index].textureHandle = resource; 245 colorBufferAccess[index].flags = accessFlags; 246 colorBufferAccess[index].mipLevel = mipLevel; 247 colorBufferAccess[index].depthSlice = depthSlice; 248 } 249 else 250 { 251#if DEVELOPMENT_BUILD || UNITY_EDITOR 252 // You tried to do SetRenderAttachment(tex1, 1, ..); SetRenderAttachment(tex2, 1, ..); that is not valid for different textures on the same index 253 throw new InvalidOperationException("You can only bind a single texture to an MRT index. Verify your indexes are correct."); 254#endif 255 } 256 } 257 258 // Sets up the color buffer for this pass but not any resource Read/Writes for it 259 [MethodImpl(MethodImplOptions.AggressiveInlining)] 260 public void SetFragmentInputRaw(in TextureHandle resource, int index, AccessFlags accessFlags, int mipLevel, int depthSlice) 261 { 262 Debug.Assert(index < RenderGraph.kMaxMRTCount && index >= 0); 263 if (fragmentInputAccess[index].textureHandle.handle.Equals(resource.handle) || !fragmentInputAccess[index].textureHandle.IsValid()) 264 { 265 fragmentInputMaxIndex = Math.Max(fragmentInputMaxIndex, index); 266 fragmentInputAccess[index].textureHandle = resource; 267 fragmentInputAccess[index].flags = accessFlags; 268 fragmentInputAccess[index].mipLevel = mipLevel; 269 fragmentInputAccess[index].depthSlice = depthSlice; 270 } 271 else 272 { 273#if DEVELOPMENT_BUILD || UNITY_EDITOR 274 // You tried to do SetRenderAttachment(tex1, 1, ..); SetRenderAttachment(tex2, 1, ..); that is not valid for different textures on the same index 275 throw new InvalidOperationException("You can only bind a single texture to an fragment input index. Verify your indexes are correct."); 276#endif 277 } 278 } 279 280 // Sets up the color buffer for this pass but not any resource Read/Writes for it 281 [MethodImpl(MethodImplOptions.AggressiveInlining)] 282 public void SetRandomWriteResourceRaw(in ResourceHandle resource, int index, bool preserveCounterValue, AccessFlags accessFlags) 283 { 284 Debug.Assert(index < RenderGraph.kMaxMRTCount && index >= 0); 285 if (randomAccessResource[index].h.Equals(resource) || !randomAccessResource[index].h.IsValid()) 286 { 287 randomAccessResourceMaxIndex = Math.Max(randomAccessResourceMaxIndex, index); 288 ref var info = ref randomAccessResource[index]; 289 info.h = resource; 290 info.preserveCounterValue = preserveCounterValue; 291 } 292 else 293 { 294 // You tried to do SetRenderAttachment(tex1, 1, ..); SetRenderAttachment(tex2, 1, ..); that is not valid for different textures on the same index 295 throw new InvalidOperationException("You can only bind a single texture to an random write input index. Verify your indexes are correct."); 296 } 297 } 298 299 300 [MethodImpl(MethodImplOptions.AggressiveInlining)] 301 public void SetDepthBuffer(in TextureHandle resource, DepthAccess flags) 302 { 303 depthAccess = new TextureAccess(resource, (AccessFlags)flags, 0, 0); 304 if ((flags & DepthAccess.Read) != 0) 305 AddResourceRead(resource.handle); 306 if ((flags & DepthAccess.Write) != 0) 307 AddResourceWrite(resource.handle); 308 } 309 310 // Sets up the depth buffer for this pass but not any resource Read/Writes for it 311 [MethodImpl(MethodImplOptions.AggressiveInlining)] 312 public void SetDepthBufferRaw(in TextureHandle resource, AccessFlags accessFlags, int mipLevel, int depthSlice) 313 { 314 // If no depth buffer yet or if it is the same one as the previous one, allow the call otherwise log an error. 315 if (depthAccess.textureHandle.handle.Equals(resource.handle) || !depthAccess.textureHandle.IsValid()) 316 { 317 depthAccess = new TextureAccess(resource, accessFlags, mipLevel, depthSlice); 318 } 319#if DEVELOPMENT_BUILD || UNITY_EDITOR 320 else 321 { 322 throw new InvalidOperationException("You can only set a single depth texture per pass."); 323 } 324#endif 325 } 326 327 328 // Here we want to keep computation to a minimum and only hash what will influence NRP compiler: Pass merging, load/store actions etc. 329 [MethodImpl(MethodImplOptions.AggressiveInlining)] 330 void ComputeTextureHash(ref HashFNV1A32 generator, in ResourceHandle handle, RenderGraphResourceRegistry resources) 331 { 332 if (handle.index == 0) 333 return; 334 335 if (resources.IsRenderGraphResourceImported(handle)) 336 { 337 var res = resources.GetTextureResource(handle); 338 if (res.graphicsResource.externalTexture != null) // External texture 339 { 340 var externalTexture = res.graphicsResource.externalTexture; 341 generator.Append((int) externalTexture.graphicsFormat); 342 generator.Append((int) externalTexture.dimension); 343 generator.Append(externalTexture.width); 344 generator.Append(externalTexture.height); 345 if (externalTexture is RenderTexture externalRT) 346 generator.Append(externalRT.antiAliasing); 347 } 348 else if (res.graphicsResource.rt != null) // Regular RTHandle 349 { 350 var rt = res.graphicsResource.rt; 351 generator.Append((int) rt.graphicsFormat); 352 generator.Append((int) rt.dimension); 353 generator.Append(rt.antiAliasing); 354 if (res.graphicsResource.useScaling) 355 if (res.graphicsResource.scaleFunc != null) 356 generator.Append(res.graphicsResource.scaleFunc); 357 else 358 generator.Append(res.graphicsResource.scaleFactor); 359 else 360 { 361 generator.Append(rt.width); 362 generator.Append(rt.height); 363 } 364 } 365 else if (res.graphicsResource.nameID != default) // External RTI 366 { 367 // The only info we have is from the provided desc upon importing. 368 ref var desc = ref res.desc; 369 generator.Append((int) desc.format); 370 generator.Append((int) desc.dimension); 371 generator.Append((int) desc.msaaSamples); 372 generator.Append(desc.width); 373 generator.Append(desc.height); 374 } 375 376 // Add the clear/discard buffer flags to the hash (used in all the cases above) 377 generator.Append(res.desc.clearBuffer); 378 generator.Append(res.desc.discardBuffer); 379 } 380 else 381 { 382 var desc = resources.GetTextureResourceDesc(handle); 383 generator.Append((int) desc.format); 384 generator.Append((int) desc.dimension); 385 generator.Append((int) desc.msaaSamples); 386 generator.Append(desc.clearBuffer); 387 generator.Append(desc.discardBuffer); 388 switch (desc.sizeMode) 389 { 390 case TextureSizeMode.Explicit: 391 generator.Append(desc.width); 392 generator.Append(desc.height); 393 break; 394 case TextureSizeMode.Scale: 395 generator.Append(desc.scale); 396 break; 397 case TextureSizeMode.Functor: 398 generator.Append(desc.func); 399 break; 400 } 401 } 402 } 403 404 // This function is performance sensitive. 405 // Avoid mass function calls to get the hashCode and compute locally instead. 406 public void ComputeHash(ref HashFNV1A32 generator, RenderGraphResourceRegistry resources) 407 { 408 generator.Append((int) type); 409 generator.Append(enableAsyncCompute); 410 generator.Append(allowPassCulling); 411 generator.Append(allowGlobalState); 412 generator.Append(enableFoveatedRasterization); 413 414 var depthHandle = depthAccess.textureHandle.handle; 415 if (depthHandle.IsValid()) 416 { 417 ComputeTextureHash(ref generator, depthHandle, resources); 418 ComputeHashForTextureAccess(ref generator, depthHandle, depthAccess); 419 } 420 421 for (int i = 0; i < colorBufferMaxIndex + 1; ++i) 422 { 423 var colorBufferAccessElement = colorBufferAccess[i]; 424 var handle = colorBufferAccessElement.textureHandle.handle; 425 if (!handle.IsValid()) 426 continue; 427 428 ComputeTextureHash(ref generator, handle, resources); 429 ComputeHashForTextureAccess(ref generator, handle, colorBufferAccessElement); 430 } 431 432 generator.Append(colorBufferMaxIndex); 433 434 for (int i = 0; i < fragmentInputMaxIndex + 1; ++i) 435 { 436 var fragmentInputAccessElement = fragmentInputAccess[i]; 437 var handle = fragmentInputAccessElement.textureHandle.handle; 438 if (!handle.IsValid()) 439 continue; 440 441 ComputeTextureHash(ref generator, handle, resources); 442 ComputeHashForTextureAccess(ref generator, handle, fragmentInputAccessElement); 443 } 444 445 for (int i = 0; i < randomAccessResourceMaxIndex + 1; ++i) 446 { 447 var rar = randomAccessResource[i]; 448 if (!rar.h.IsValid()) 449 continue; 450 451 generator.Append(rar.h.index); 452 generator.Append(rar.preserveCounterValue); 453 } 454 generator.Append(randomAccessResourceMaxIndex); 455 generator.Append(fragmentInputMaxIndex); 456 generator.Append(generateDebugData); 457 generator.Append(allowRendererListCulling); 458 459 for (int resType = 0; resType < (int)RenderGraphResourceType.Count; resType++) 460 { 461 var resourceReads = resourceReadLists[resType]; 462 for (int i = 0; i < resourceReads.Count; ++i) 463 generator.Append(resourceReads[i].index); 464 465 var resourceWrites = resourceWriteLists[resType]; 466 for (int i = 0; i < resourceWrites.Count; ++i) 467 generator.Append(resourceWrites[i].index); 468 469 var resourceTransient = transientResourceList[resType]; 470 for (int i = 0; i < resourceTransient.Count; ++i) 471 generator.Append(resourceTransient[i].index); 472 } 473 474 for (int i = 0; i < usedRendererListList.Count; ++i) 475 generator.Append(usedRendererListList[i].handle); 476 477 for (int i = 0; i < setGlobalsList.Count; ++i) 478 { 479 var global = setGlobalsList[i]; 480 generator.Append(global.Item1.handle.index); 481 generator.Append(global.Item2); 482 } 483 generator.Append(useAllGlobalTextures); 484 485 for (int i = 0; i < implicitReadsList.Count; ++i) 486 generator.Append(implicitReadsList[i].index); 487 488 generator.Append(GetRenderFuncHash()); 489 } 490 491 [MethodImpl(MethodImplOptions.AggressiveInlining)] 492 static void ComputeHashForTextureAccess(ref HashFNV1A32 generator, in ResourceHandle handle, in TextureAccess textureAccess) 493 { 494 generator.Append(handle.index); 495 generator.Append((int) textureAccess.flags); 496 generator.Append(textureAccess.mipLevel); 497 generator.Append(textureAccess.depthSlice); 498 } 499 } 500 501 // This used to have an extra generic argument 'RenderGraphContext' abstracting the context and avoiding 502 // the RenderGraphPass/ComputeRenderGraphPass/RasterRenderGraphPass/UnsafeRenderGraphPass classes below 503 // but this confuses IL2CPP and causes garbage when boxing the context created (even though they are structs) 504 [DebuggerDisplay("RenderPass: {name} (Index:{index} Async:{enableAsyncCompute})")] 505 internal abstract class BaseRenderGraphPass<PassData, TRenderGraphContext> : RenderGraphPass 506 where PassData : class, new() 507 { 508 internal PassData data; 509 internal BaseRenderFunc<PassData, TRenderGraphContext> renderFunc; 510 511 [MethodImpl(MethodImplOptions.AggressiveInlining)] 512 public void Initialize(int passIndex, PassData passData, string passName, RenderGraphPassType passType, ProfilingSampler sampler) 513 { 514 Clear(); 515 index = passIndex; 516 data = passData; 517 name = passName; 518 type = passType; 519 customSampler = sampler; 520 } 521 522 [MethodImpl(MethodImplOptions.AggressiveInlining)] 523 public override void Release(RenderGraphObjectPool pool) 524 { 525 pool.Release(data); 526 data = null; 527 renderFunc = null; 528 } 529 530 [MethodImpl(MethodImplOptions.AggressiveInlining)] 531 public override bool HasRenderFunc() 532 { 533 return renderFunc != null; 534 } 535 536 [MethodImpl(MethodImplOptions.AggressiveInlining)] 537 public override int GetRenderFuncHash() 538 { 539 return renderFunc != null ? HashFNV1A32.GetFuncHashCode(renderFunc) : 0; 540 } 541 } 542 543 [DebuggerDisplay("RenderPass: {name} (Index:{index} Async:{enableAsyncCompute})")] 544 internal sealed class RenderGraphPass<PassData> : BaseRenderGraphPass<PassData, RenderGraphContext> 545 where PassData : class, new() 546 { 547 internal static RenderGraphContext c = new RenderGraphContext(); 548 549 [MethodImpl(MethodImplOptions.AggressiveInlining)] 550 public override void Execute(InternalRenderGraphContext renderGraphContext) 551 { 552 c.FromInternalContext(renderGraphContext); 553 renderFunc(data, c); 554 } 555 556 [MethodImpl(MethodImplOptions.AggressiveInlining)] 557 public override void Release(RenderGraphObjectPool pool) 558 { 559 base.Release(pool); 560 561 // We need to do the release from here because we need the final type. 562 pool.Release(this); 563 } 564 } 565 566 [DebuggerDisplay("RenderPass: {name} (Index:{index} Async:{enableAsyncCompute})")] 567 internal sealed class ComputeRenderGraphPass<PassData> : BaseRenderGraphPass<PassData, ComputeGraphContext> 568 where PassData : class, new() 569 { 570 internal static ComputeGraphContext c = new ComputeGraphContext(); 571 572 [MethodImpl(MethodImplOptions.AggressiveInlining)] 573 public override void Execute(InternalRenderGraphContext renderGraphContext) 574 { 575 c.FromInternalContext(renderGraphContext); 576 renderFunc(data, c); 577 } 578 579 [MethodImpl(MethodImplOptions.AggressiveInlining)] 580 public override void Release(RenderGraphObjectPool pool) 581 { 582 base.Release(pool); 583 584 // We need to do the release from here because we need the final type. 585 pool.Release(this); 586 } 587 } 588 589 [DebuggerDisplay("RenderPass: {name} (Index:{index} Async:{enableAsyncCompute})")] 590 internal sealed class RasterRenderGraphPass<PassData> : BaseRenderGraphPass<PassData, RasterGraphContext> 591 where PassData : class, new() 592 { 593 internal static RasterGraphContext c = new RasterGraphContext(); 594 595 [MethodImpl(MethodImplOptions.AggressiveInlining)] 596 public override void Execute(InternalRenderGraphContext renderGraphContext) 597 { 598 c.FromInternalContext(renderGraphContext); 599 renderFunc(data, c); 600 } 601 602 [MethodImpl(MethodImplOptions.AggressiveInlining)] 603 public override void Release(RenderGraphObjectPool pool) 604 { 605 base.Release(pool); 606 607 // We need to do the release from here because we need the final type. 608 pool.Release(this); 609 } 610 } 611 612 [DebuggerDisplay("RenderPass: {name} (Index:{index} Async:{enableAsyncCompute})")] 613 internal sealed class UnsafeRenderGraphPass<PassData> : BaseRenderGraphPass<PassData, UnsafeGraphContext> 614 where PassData : class, new() 615 { 616 internal static UnsafeGraphContext c = new UnsafeGraphContext(); 617 618 [MethodImpl(MethodImplOptions.AggressiveInlining)] 619 public override void Execute(InternalRenderGraphContext renderGraphContext) 620 { 621 c.FromInternalContext(renderGraphContext); 622 renderFunc(data, c); 623 } 624 625 [MethodImpl(MethodImplOptions.AggressiveInlining)] 626 public override void Release(RenderGraphObjectPool pool) 627 { 628 base.Release(pool); 629 630 // We need to do the release from here because we need the final type. 631 pool.Release(this); 632 } 633 } 634}