A game about forced loneliness, made by TACStudios
at master 1362 lines 64 kB view raw
1using NUnit.Framework; 2using System; 3using UnityEngine.Experimental.Rendering; 4using UnityEngine.Rendering.RenderGraphModule; 5using System.Collections.Generic; 6using UnityEngine.TestTools; 7using Unity.Collections; 8 9#if UNITY_EDITOR 10using UnityEditor; 11using UnityEditor.Rendering; 12#endif 13namespace UnityEngine.Rendering.Tests 14{ 15 [InitializeOnLoad] 16 class RenderGraphTestsOnLoad 17 { 18 static bool IsGraphicsAPISupported() 19 { 20 var gfxAPI = SystemInfo.graphicsDeviceType; 21 if (gfxAPI == GraphicsDeviceType.OpenGLCore) 22 return false; 23 return true; 24 } 25 26 static RenderGraphTestsOnLoad() 27 { 28 ConditionalIgnoreAttribute.AddConditionalIgnoreMapping("IgnoreGraphicsAPI", !IsGraphicsAPISupported()); 29 } 30 } 31 32 class RenderGraphTests 33 { 34 // For RG Record/Hash/Compile testing, use m_RenderGraph 35 RenderGraph m_RenderGraph; 36 37 RenderPipelineAsset m_OldDefaultRenderPipeline; 38 RenderPipelineAsset m_OldQualityRenderPipeline; 39 40 // For RG Execute/Submit testing with rendering, use m_RenderGraphTestPipeline and m_RenderGraph in its recordRenderGraphBody 41 RenderGraphTestPipelineAsset m_RenderGraphTestPipeline; 42 RenderGraphTestGlobalSettings m_RenderGraphTestGlobalSettings; 43 44 // For the testing of the following RG steps: Execute and Submit (native) with camera rendering, use this custom RenderGraph render pipeline 45 // through a camera render call to test the RG with a real ScriptableRenderContext 46 class RenderGraphTestPipelineAsset : RenderPipelineAsset<RenderGraphTestPipelineInstance> 47 { 48 public Action<ScriptableRenderContext, Camera, CommandBuffer> recordRenderGraphBody; 49 public RenderGraph renderGraph; 50 protected override RenderPipeline CreatePipeline() 51 { 52 return new RenderGraphTestPipelineInstance(this); 53 } 54 55 // Called only once per UTR 56 void OnEnable() 57 { 58 renderGraph = new(); 59 } 60 } 61 62 class RenderGraphTestPipelineInstance : RenderPipeline 63 { 64 RenderGraphTestPipelineAsset asset; 65 66 // Having the RG at this level allows us to handle RG framework within Render() for easier testing 67 RenderGraph m_RenderGraph; 68 69 public RenderGraphTestPipelineInstance(RenderGraphTestPipelineAsset asset) 70 { 71 this.asset = asset; 72 this.m_RenderGraph = asset.renderGraph; 73 } 74 75 protected override void Render(ScriptableRenderContext renderContext, Camera[] cameras) 76 { 77 foreach (var camera in cameras) 78 { 79 if (!camera.enabled) 80 continue; 81 82 var cmd = new CommandBuffer { name = "Rendering command buffer" }; 83 84 RenderGraphParameters rgParams = new() 85 { 86 commandBuffer = cmd, 87 scriptableRenderContext = renderContext, 88 currentFrameIndex = Time.frameCount, 89 invalidContextForTesting = false 90 }; 91 92 m_RenderGraph.BeginRecording(rgParams); 93 94 asset.recordRenderGraphBody?.Invoke(renderContext, camera, cmd); 95 96 m_RenderGraph.EndRecordingAndExecute(); 97 98 renderContext.ExecuteCommandBuffer(cmd); 99 } 100 renderContext.Submit(); 101 } 102 } 103 104 [SupportedOnRenderPipeline(typeof(RenderGraphTestPipelineAsset))] 105 [System.ComponentModel.DisplayName("RenderGraphTest")] 106 class RenderGraphTestGlobalSettings : RenderPipelineGlobalSettings<RenderGraphTestGlobalSettings, RenderGraphTestPipelineInstance> 107 { 108 [SerializeField] RenderPipelineGraphicsSettingsContainer m_Settings = new(); 109 protected override List<IRenderPipelineGraphicsSettings> settingsList => m_Settings.settingsList; 110 } 111 112 [OneTimeSetUp] 113 public void Setup() 114 { 115 // Setting default global settings to the custom RG render pipeline type, no quality settings so we can rely on the default RP 116 m_RenderGraphTestGlobalSettings = ScriptableObject.CreateInstance<RenderGraphTestGlobalSettings>(); 117#if UNITY_EDITOR 118 EditorGraphicsSettings.SetRenderPipelineGlobalSettingsAsset<RenderGraphTestPipelineInstance>(m_RenderGraphTestGlobalSettings); 119#endif 120 // Saving old render pipelines to set them back after testing 121 m_OldDefaultRenderPipeline = GraphicsSettings.defaultRenderPipeline; 122 m_OldQualityRenderPipeline = QualitySettings.renderPipeline; 123 124 // Setting the custom RG render pipeline 125 m_RenderGraphTestPipeline = ScriptableObject.CreateInstance<RenderGraphTestPipelineAsset>(); 126 GraphicsSettings.defaultRenderPipeline = m_RenderGraphTestPipeline; 127 QualitySettings.renderPipeline = m_RenderGraphTestPipeline; 128 129 // Getting the RG from the custom asset pipeline 130 m_RenderGraph = m_RenderGraphTestPipeline.renderGraph; 131 } 132 133 [OneTimeTearDown] 134 public void Cleanup() 135 { 136 GraphicsSettings.defaultRenderPipeline = m_OldDefaultRenderPipeline; 137 m_OldDefaultRenderPipeline = null; 138 139 QualitySettings.renderPipeline = m_OldQualityRenderPipeline; 140 m_OldQualityRenderPipeline = null; 141 142 m_RenderGraph.Cleanup(); 143 144 Object.DestroyImmediate(m_RenderGraphTestPipeline); 145 146#if UNITY_EDITOR 147 EditorGraphicsSettings.SetRenderPipelineGlobalSettingsAsset<RenderGraphTestPipelineInstance>(null); 148#endif 149 Object.DestroyImmediate(m_RenderGraphTestGlobalSettings); 150 } 151 152 [SetUp] 153 public void SetupRenderGraph() 154 { 155 m_RenderGraph.ClearCompiledGraph(); 156 } 157 158 class RenderGraphTestPassData 159 { 160 public TextureHandle[] textures = new TextureHandle[8]; 161 public BufferHandle[] buffers = new BufferHandle[8]; 162 } 163 164 // Final output (back buffer) of render graph needs to be explicitly imported in order to know that the chain of dependency should not be culled. 165 [Test] 166 public void WriteToBackBufferNotCulled() 167 { 168 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData)) 169 { 170 builder.WriteTexture(m_RenderGraph.ImportBackbuffer(0)); 171 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 172 } 173 174 m_RenderGraph.CompileRenderGraph(m_RenderGraph.ComputeGraphHash()); 175 176 var compiledPasses = m_RenderGraph.GetCompiledPassInfos(); 177 Assert.AreEqual(1, compiledPasses.size); 178 Assert.AreEqual(false, compiledPasses[0].culled); 179 } 180 181 // If no back buffer is ever written to, everything should be culled. 182 [Test] 183 public void NoWriteToBackBufferCulled() 184 { 185 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData)) 186 { 187 builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm })); 188 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 189 } 190 191 m_RenderGraph.CompileRenderGraph(m_RenderGraph.ComputeGraphHash()); 192 193 var compiledPasses = m_RenderGraph.GetCompiledPassInfos(); 194 Assert.AreEqual(1, compiledPasses.size); 195 Assert.AreEqual(true, compiledPasses[0].culled); 196 } 197 198 // Writing to imported resource is considered as a side effect so passes should not be culled. 199 [Test] 200 public void WriteToImportedTextureNotCulled() 201 { 202 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData)) 203 { 204 builder.WriteTexture(m_RenderGraph.ImportTexture(null)); 205 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 206 } 207 208 m_RenderGraph.CompileRenderGraph(m_RenderGraph.ComputeGraphHash()); 209 210 var compiledPasses = m_RenderGraph.GetCompiledPassInfos(); 211 Assert.AreEqual(1, compiledPasses.size); 212 Assert.AreEqual(false, compiledPasses[0].culled); 213 } 214 215 [Test] 216 public void WriteToImportedComputeBufferNotCulled() 217 { 218 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData)) 219 { 220 builder.WriteBuffer(m_RenderGraph.ImportBuffer(null)); 221 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 222 } 223 224 m_RenderGraph.CompileRenderGraph(m_RenderGraph.ComputeGraphHash()); 225 226 var compiledPasses = m_RenderGraph.GetCompiledPassInfos(); 227 Assert.AreEqual(1, compiledPasses.size); 228 Assert.AreEqual(false, compiledPasses[0].culled); 229 } 230 231 [Test] 232 public void PassWriteResourcePartialNotReadAfterNotCulled() 233 { 234 // If a pass writes to a resource that is not unused globally by the graph but not read ever AFTER the pass then the pass should be culled unless it writes to another used resource. 235 TextureHandle texture0; 236 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData)) 237 { 238 texture0 = builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm })); 239 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 240 } 241 242 TextureHandle texture1; 243 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass1", out var passData)) 244 { 245 builder.ReadTexture(texture0); 246 texture1 = builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm })); 247 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 248 } 249 250 // This pass writes to texture0 which is used so will not be culled out. 251 // Since texture0 is never read after this pass, we should decrement refCount for this pass and potentially cull it. 252 // However, it also writes to texture1 which is used in the last pass so we mustn't cull it. 253 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass2", out var passData)) 254 { 255 builder.WriteTexture(texture0); 256 builder.WriteTexture(texture1); 257 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 258 } 259 260 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass3", out var passData)) 261 { 262 builder.ReadTexture(texture1); 263 builder.WriteTexture(m_RenderGraph.ImportBackbuffer(0)); // Needed for the passes to not be culled 264 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 265 } 266 267 m_RenderGraph.CompileRenderGraph(m_RenderGraph.ComputeGraphHash()); 268 269 var compiledPasses = m_RenderGraph.GetCompiledPassInfos(); 270 Assert.AreEqual(4, compiledPasses.size); 271 Assert.AreEqual(false, compiledPasses[0].culled); 272 Assert.AreEqual(false, compiledPasses[1].culled); 273 Assert.AreEqual(false, compiledPasses[2].culled); 274 Assert.AreEqual(false, compiledPasses[3].culled); 275 } 276 277 [Test] 278 public void PassDisallowCullingNotCulled() 279 { 280 // This pass does nothing so should be culled but we explicitly disallow it. 281 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData)) 282 { 283 builder.AllowPassCulling(false); 284 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 285 } 286 287 m_RenderGraph.CompileRenderGraph(m_RenderGraph.ComputeGraphHash()); 288 289 var compiledPasses = m_RenderGraph.GetCompiledPassInfos(); 290 Assert.AreEqual(1, compiledPasses.size); 291 Assert.AreEqual(false, compiledPasses[0].culled); 292 } 293 294 // First pass produces two textures and second pass only read one of the two. Pass one should not be culled. 295 [Test] 296 public void PartialUnusedProductNotCulled() 297 { 298 TextureHandle texture; 299 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData)) 300 { 301 texture = builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm })); 302 builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm })); 303 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 304 } 305 306 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass1", out var passData)) 307 { 308 builder.ReadTexture(texture); 309 builder.WriteTexture(m_RenderGraph.ImportBackbuffer(0)); 310 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 311 } 312 313 m_RenderGraph.CompileRenderGraph(m_RenderGraph.ComputeGraphHash()); 314 315 var compiledPasses = m_RenderGraph.GetCompiledPassInfos(); 316 Assert.AreEqual(2, compiledPasses.size); 317 Assert.AreEqual(false, compiledPasses[0].culled); 318 Assert.AreEqual(false, compiledPasses[1].culled); 319 } 320 321 // Simple cycle of create/release of a texture across multiple passes. 322 [Test] 323 public void SimpleCreateReleaseTexture() 324 { 325 TextureHandle texture; 326 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData)) 327 { 328 texture = builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm })); 329 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 330 } 331 332 // Add dummy passes 333 for (int i = 0; i < 2; ++i) 334 { 335 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass1", out var passData)) 336 { 337 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 338 } 339 } 340 341 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass2", out var passData)) 342 { 343 builder.ReadTexture(texture); 344 builder.WriteTexture(m_RenderGraph.ImportBackbuffer(0)); // Needed for the passes to not be culled 345 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 346 } 347 348 m_RenderGraph.CompileRenderGraph(m_RenderGraph.ComputeGraphHash()); 349 350 var compiledPasses = m_RenderGraph.GetCompiledPassInfos(); 351 Assert.AreEqual(4, compiledPasses.size); 352 Assert.Contains(texture.handle.index, compiledPasses[0].resourceCreateList[(int)RenderGraphResourceType.Texture]); 353 Assert.Contains(texture.handle.index, compiledPasses[3].resourceReleaseList[(int)RenderGraphResourceType.Texture]); 354 } 355 356 [Test] 357 public void UseTransientOutsidePassRaiseException() 358 { 359 Assert.Catch<System.ArgumentException>(() => 360 { 361 TextureHandle texture; 362 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData)) 363 { 364 texture = builder.CreateTransientTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }); 365 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 366 } 367 368 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass1", out var passData)) 369 { 370 builder.ReadTexture(texture); // This is illegal (transient resource was created in previous pass) 371 builder.WriteTexture(m_RenderGraph.ImportBackbuffer(0)); // Needed for the passes to not be culled 372 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 373 } 374 375 m_RenderGraph.CompileRenderGraph(m_RenderGraph.ComputeGraphHash()); 376 }); 377 } 378 379 [Test] 380 public void TransientCreateReleaseInSamePass() 381 { 382 TextureHandle texture; 383 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData)) 384 { 385 texture = builder.CreateTransientTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }); 386 builder.WriteTexture(m_RenderGraph.ImportBackbuffer(0)); // Needed for the passes to not be culled 387 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 388 } 389 390 m_RenderGraph.CompileRenderGraph(m_RenderGraph.ComputeGraphHash()); 391 392 var compiledPasses = m_RenderGraph.GetCompiledPassInfos(); 393 Assert.AreEqual(1, compiledPasses.size); 394 Assert.Contains(texture.handle.index, compiledPasses[0].resourceCreateList[(int)RenderGraphResourceType.Texture]); 395 Assert.Contains(texture.handle.index, compiledPasses[0].resourceReleaseList[(int)RenderGraphResourceType.Texture]); 396 } 397 398 // Texture that should be released during an async pass should have their release delayed until the first pass that syncs with the compute pipe. 399 // Otherwise they may be reused by the graphics pipe even if the async pipe is not done executing. 400 [Test] 401 public void AsyncPassReleaseTextureOnGraphicsPipe() 402 { 403 TextureHandle texture0; 404 TextureHandle texture1; 405 TextureHandle texture2; 406 TextureHandle texture3; 407 // First pass creates and writes two textures. 408 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("Async_TestPass0", out var passData)) 409 { 410 texture0 = builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm })); 411 texture1 = builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm })); 412 builder.EnableAsyncCompute(true); 413 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 414 } 415 416 // Second pass creates a transient texture => Create/Release should happen in this pass but we want to delay the release until the first graphics pipe pass that sync with async queue. 417 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("Async_TestPass1", out var passData)) 418 { 419 texture2 = builder.CreateTransientTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }); 420 builder.WriteTexture(texture0); 421 builder.EnableAsyncCompute(true); 422 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 423 } 424 425 // This pass is the last to read texture0 => Release should happen in this pass but we want to delay the release until the first graphics pipe pass that sync with async queue. 426 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("Async_TestPass2", out var passData)) 427 { 428 texture0 = builder.ReadTexture(texture0); 429 builder.WriteTexture(texture1); 430 builder.EnableAsyncCompute(true); 431 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 432 } 433 434 // Just here to add "padding" to the number of passes to ensure resources are not released right at the first sync pass. 435 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass3", out var passData)) 436 { 437 texture3 = builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm })); 438 builder.EnableAsyncCompute(false); 439 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 440 } 441 442 // Pass prior to synchronization should be where textures are released. 443 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass4", out var passData)) 444 { 445 builder.WriteTexture(texture3); 446 builder.EnableAsyncCompute(false); 447 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 448 } 449 450 // Graphics pass that reads texture1. This will request a sync with compute pipe. The previous pass should be the one releasing async textures. 451 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass5", out var passData)) 452 { 453 builder.ReadTexture(texture1); 454 builder.ReadTexture(texture3); 455 builder.WriteTexture(m_RenderGraph.ImportBackbuffer(0)); // Needed for the passes to not be culled 456 builder.EnableAsyncCompute(false); 457 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 458 } 459 460 m_RenderGraph.CompileRenderGraph(m_RenderGraph.ComputeGraphHash()); 461 462 var compiledPasses = m_RenderGraph.GetCompiledPassInfos(); 463 Assert.AreEqual(6, compiledPasses.size); 464 Assert.Contains(texture0.handle.index, compiledPasses[4].resourceReleaseList[(int)RenderGraphResourceType.Texture]); 465 Assert.Contains(texture2.handle.index, compiledPasses[4].resourceReleaseList[(int)RenderGraphResourceType.Texture]); 466 } 467 468 [Test] 469 public void TransientResourceNotCulled() 470 { 471 TextureHandle texture0; 472 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData)) 473 { 474 texture0 = builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm })); 475 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 476 } 477 478 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass1", out var passData)) 479 { 480 builder.CreateTransientTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }); 481 builder.WriteTexture(texture0); 482 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 483 } 484 485 // Graphics pass that reads texture1. This will request a sync with compute pipe. The previous pass should be the one releasing async textures. 486 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass5", out var passData)) 487 { 488 builder.ReadTexture(texture0); 489 builder.WriteTexture(m_RenderGraph.ImportBackbuffer(0)); // Needed for the passes to not be culled 490 builder.EnableAsyncCompute(false); 491 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 492 } 493 494 m_RenderGraph.CompileRenderGraph(m_RenderGraph.ComputeGraphHash()); 495 496 var compiledPasses = m_RenderGraph.GetCompiledPassInfos(); 497 Assert.AreEqual(3, compiledPasses.size); 498 Assert.AreEqual(false, compiledPasses[1].culled); 499 } 500 501 [Test] 502 public void AsyncPassWriteWaitOnGraphicsPipe() 503 { 504 TextureHandle texture0; 505 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData)) 506 { 507 texture0 = builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm })); 508 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 509 } 510 511 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("Async_TestPass1", out var passData)) 512 { 513 texture0 = builder.WriteTexture(texture0); 514 builder.EnableAsyncCompute(true); 515 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 516 } 517 518 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass2", out var passData)) 519 { 520 builder.ReadTexture(texture0); 521 builder.WriteTexture(m_RenderGraph.ImportBackbuffer(0)); // Needed for the passes to not be culled 522 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 523 } 524 525 m_RenderGraph.CompileRenderGraph(m_RenderGraph.ComputeGraphHash()); 526 527 var compiledPasses = m_RenderGraph.GetCompiledPassInfos(); 528 Assert.AreEqual(3, compiledPasses.size); 529 Assert.AreEqual(0, compiledPasses[1].syncToPassIndex); 530 Assert.AreEqual(1, compiledPasses[2].syncToPassIndex); 531 } 532 533 [Test] 534 public void AsyncPassReadWaitOnGraphicsPipe() 535 { 536 TextureHandle texture0; 537 TextureHandle texture1; 538 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData)) 539 { 540 texture0 = builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm })); 541 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 542 } 543 544 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("Async_TestPass1", out var passData)) 545 { 546 builder.ReadTexture(texture0); 547 texture1 = builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm })); 548 builder.EnableAsyncCompute(true); 549 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 550 } 551 552 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass2", out var passData)) 553 { 554 builder.ReadTexture(texture1); 555 builder.WriteTexture(m_RenderGraph.ImportBackbuffer(0)); // Needed for the passes to not be culled 556 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 557 } 558 559 m_RenderGraph.CompileRenderGraph(m_RenderGraph.ComputeGraphHash()); 560 561 var compiledPasses = m_RenderGraph.GetCompiledPassInfos(); 562 Assert.AreEqual(3, compiledPasses.size); 563 Assert.AreEqual(0, compiledPasses[1].syncToPassIndex); 564 Assert.AreEqual(1, compiledPasses[2].syncToPassIndex); 565 } 566 567 [Test] 568 public void GraphicsPassWriteWaitOnAsyncPipe() 569 { 570 TextureHandle texture0; 571 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("Async_TestPass0", out var passData)) 572 { 573 texture0 = builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm })); 574 builder.EnableAsyncCompute(true); 575 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 576 } 577 578 // This pass should sync with the "Async_TestPass0" 579 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass1", out var passData)) 580 { 581 texture0 = builder.WriteTexture(texture0); 582 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 583 } 584 585 // Read result and output to backbuffer to avoid culling passes. 586 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass2", out var passData)) 587 { 588 builder.ReadTexture(texture0); 589 builder.WriteTexture(m_RenderGraph.ImportBackbuffer(0)); // Needed for the passes to not be culled 590 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 591 } 592 593 m_RenderGraph.CompileRenderGraph(m_RenderGraph.ComputeGraphHash()); 594 595 var compiledPasses = m_RenderGraph.GetCompiledPassInfos(); 596 Assert.AreEqual(3, compiledPasses.size); 597 Assert.AreEqual(0, compiledPasses[1].syncToPassIndex); 598 } 599 600 [Test] 601 public void GraphicsPassReadWaitOnAsyncPipe() 602 { 603 TextureHandle texture0; 604 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("Async_TestPass0", out var passData)) 605 { 606 texture0 = builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm })); 607 builder.EnableAsyncCompute(true); 608 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 609 } 610 611 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass1", out var passData)) 612 { 613 builder.ReadTexture(texture0); 614 builder.WriteTexture(m_RenderGraph.ImportBackbuffer(0)); // Needed for the passes to not be culled 615 builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { }); 616 } 617 618 m_RenderGraph.CompileRenderGraph(m_RenderGraph.ComputeGraphHash()); 619 620 var compiledPasses = m_RenderGraph.GetCompiledPassInfos(); 621 Assert.AreEqual(2, compiledPasses.size); 622 Assert.AreEqual(0, compiledPasses[1].syncToPassIndex); 623 } 624 625 [Test] 626 public void SetRenderAttachmentValidation() 627 { 628 TextureHandle texture0; 629 texture0 = m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }); 630 631 TextureHandle texture1; 632 texture1 = m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }); 633 634 // Using two different textures on the same slot not allowed 635 using (var builder = m_RenderGraph.AddRasterRenderPass<RenderGraphTestPassData>("Async_TestPass0", out var passData)) 636 { 637 builder.SetRenderAttachment(texture0, 0, AccessFlags.ReadWrite); 638 Assert.Throws<System.InvalidOperationException>(() => 639 { 640 builder.SetRenderAttachment(texture1, 0, AccessFlags.ReadWrite); 641 }); 642 builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { }); 643 } 644 645 // Using the same texture on two slots, not allowed 646 // TODO: Would this be allowed if read-only? Likely an edge case possibly hardware dependent... let's not bother with it 647 using (var builder = m_RenderGraph.AddRasterRenderPass<RenderGraphTestPassData>("Async_TestPass0", out var passData)) 648 { 649 builder.SetRenderAttachment(texture0, 0, AccessFlags.ReadWrite); 650 Assert.Throws<System.InvalidOperationException>(() => 651 { 652 builder.SetRenderAttachment(texture0, 1, AccessFlags.ReadWrite); 653 }); 654 builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { }); 655 } 656 657 // Using a texture both as a texture and as a fragment, not allowed 658 using (var builder = m_RenderGraph.AddRasterRenderPass<RenderGraphTestPassData>("Async_TestPass0", out var passData)) 659 { 660 builder.UseTexture(texture0, AccessFlags.ReadWrite); 661 Assert.Throws<System.InvalidOperationException>(() => 662 { 663 builder.SetRenderAttachment(texture0, 0, AccessFlags.ReadWrite); 664 }); 665 builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { }); 666 } 667 668 669 // Using a texture both as a texture and as a fragment, not allowed (reversed) 670 using (var builder = m_RenderGraph.AddRasterRenderPass<RenderGraphTestPassData>("Async_TestPass0", out var passData)) 671 { 672 builder.UseTexture(texture0, AccessFlags.ReadWrite); 673 Assert.Throws<System.InvalidOperationException>(() => 674 { 675 builder.SetRenderAttachment(texture0, 0, AccessFlags.ReadWrite); 676 }); 677 builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { }); 678 } 679 } 680 681 [Test] 682 public void UseTextureValidation() 683 { 684 TextureHandle texture0; 685 texture0 = m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }); 686 687 TextureHandle texture1; 688 texture1 = m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }); 689 690 // Writing the same texture twice is not allowed 691 using (var builder = m_RenderGraph.AddRasterRenderPass<RenderGraphTestPassData>("Async_TestPass0", out var passData)) 692 { 693 builder.UseTexture(texture0, AccessFlags.ReadWrite); 694 Assert.Throws<System.InvalidOperationException>(() => 695 { 696 builder.UseTexture(texture0, AccessFlags.ReadWrite); 697 }); 698 builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { }); 699 } 700 701 // Reading the same texture twice is allowed 702 using (var builder = m_RenderGraph.AddRasterRenderPass<RenderGraphTestPassData>("Async_TestPass0", out var passData)) 703 { 704 builder.UseTexture(texture0, AccessFlags.Read); 705 builder.UseTexture(texture0, AccessFlags.Read); 706 builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { }); 707 } 708 } 709 710 [Test] 711 public void ComputeHashDifferentPerResolution() 712 { 713 static void RenderFunc(RenderGraphTestPassData data, RenderGraphContext context) { } 714 715 TextureHandle texture0 = m_RenderGraph.CreateTexture(new TextureDesc(256, 256) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }); 716 717 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData)) 718 { 719 builder.UseColorBuffer(texture0, 0); 720 builder.SetRenderFunc<RenderGraphTestPassData>(RenderFunc); 721 } 722 723 var hash0 = m_RenderGraph.ComputeGraphHash(); 724 m_RenderGraph.ClearCompiledGraph(); 725 726 TextureHandle texture1 = m_RenderGraph.CreateTexture(new TextureDesc(512, 512) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }); 727 728 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData)) 729 { 730 builder.UseColorBuffer(texture1, 0); 731 builder.SetRenderFunc<RenderGraphTestPassData>(RenderFunc); 732 } 733 734 var hash1 = m_RenderGraph.ComputeGraphHash(); 735 m_RenderGraph.ClearCompiledGraph(); 736 737 Assert.AreNotEqual(hash0, hash1); 738 } 739 740 [Test] 741 public void ComputeHashDifferentForMSAA() 742 { 743 static void RenderFunc(RenderGraphTestPassData data, RenderGraphContext context) { } 744 745 TextureHandle texture0 = m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm, msaaSamples = MSAASamples.None }); 746 747 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData)) 748 { 749 builder.UseColorBuffer(texture0, 0); 750 builder.SetRenderFunc<RenderGraphTestPassData>(RenderFunc); 751 } 752 753 var hash0 = m_RenderGraph.ComputeGraphHash(); 754 m_RenderGraph.ClearCompiledGraph(); 755 756 texture0 = m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm, msaaSamples = MSAASamples.MSAA4x }); 757 758 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData)) 759 { 760 builder.UseColorBuffer(texture0, 0); 761 builder.SetRenderFunc<RenderGraphTestPassData>(RenderFunc); 762 } 763 764 var hash1 = m_RenderGraph.ComputeGraphHash(); 765 m_RenderGraph.ClearCompiledGraph(); 766 767 Assert.AreNotEqual(hash0, hash1); 768 } 769 770 [Test] 771 public void ComputeHashDifferentForRenderFunc() 772 { 773 static void RenderFunc(RenderGraphTestPassData data, RenderGraphContext context) { } 774 static void RenderFunc2(RenderGraphTestPassData data, RenderGraphContext context) { } 775 776 TextureHandle texture0 = m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }); 777 778 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData)) 779 { 780 builder.UseColorBuffer(texture0, 0); 781 builder.SetRenderFunc<RenderGraphTestPassData>(RenderFunc); 782 } 783 784 var hash0 = m_RenderGraph.ComputeGraphHash(); 785 m_RenderGraph.ClearCompiledGraph(); 786 787 texture0 = m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }); 788 789 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData)) 790 { 791 builder.UseColorBuffer(texture0, 0); 792 builder.SetRenderFunc<RenderGraphTestPassData>(RenderFunc2); 793 } 794 795 var hash1 = m_RenderGraph.ComputeGraphHash(); 796 m_RenderGraph.ClearCompiledGraph(); 797 798 Assert.AreNotEqual(hash0, hash1); 799 } 800 801 [Test] 802 public void ComputeHashDifferentForMorePasses() 803 { 804 static void RenderFunc(RenderGraphTestPassData data, RenderGraphContext context) { } 805 static void RenderFunc2(RenderGraphTestPassData data, RenderGraphContext context) { } 806 static void RenderFunc3(RenderGraphTestPassData data, RenderGraphContext context) { } 807 808 TextureHandle texture0 = m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }); 809 810 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData)) 811 { 812 builder.UseColorBuffer(texture0, 0); 813 builder.SetRenderFunc<RenderGraphTestPassData>(RenderFunc); 814 } 815 816 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass1", out var passData)) 817 { 818 builder.WriteTexture(m_RenderGraph.ImportBackbuffer(0)); 819 builder.ReadTexture(texture0); 820 builder.SetRenderFunc<RenderGraphTestPassData>(RenderFunc2); 821 } 822 823 var hash0 = m_RenderGraph.ComputeGraphHash(); 824 m_RenderGraph.ClearCompiledGraph(); 825 826 texture0 = m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { format = GraphicsFormat.R8G8B8A8_UNorm }); 827 828 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData)) 829 { 830 builder.UseColorBuffer(texture0, 0); 831 builder.SetRenderFunc<RenderGraphTestPassData>(RenderFunc); 832 } 833 834 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass1", out var passData)) 835 { 836 builder.UseColorBuffer(texture0, 0); 837 builder.SetRenderFunc<RenderGraphTestPassData>(RenderFunc3); 838 } 839 840 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass2", out var passData)) 841 { 842 builder.WriteTexture(m_RenderGraph.ImportBackbuffer(0)); 843 builder.ReadTexture(texture0); 844 builder.SetRenderFunc<RenderGraphTestPassData>(RenderFunc2); 845 } 846 847 var hash1 = m_RenderGraph.ComputeGraphHash(); 848 m_RenderGraph.ClearCompiledGraph(); 849 850 Assert.AreNotEqual(hash0, hash1); 851 } 852 853 [Test] 854 public void ComputeHashSameForOneSetup() 855 { 856 static void RenderFunc(RenderGraphTestPassData data, RenderGraphContext context) { } 857 static void RenderFunc2(RenderGraphTestPassData data, RenderGraphContext context) { } 858 static void RenderFunc3(RenderGraphTestPassData data, RenderGraphContext context) { } 859 860 static void RecordRenderGraph(RenderGraph renderGraph) 861 { 862 TextureHandle texture0 = renderGraph.CreateTexture(new TextureDesc(Vector2.one) { format = GraphicsFormat.R8G8B8A8_UNorm }); 863 864 using (var builder = renderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData)) 865 { 866 builder.UseColorBuffer(texture0, 0); 867 builder.SetRenderFunc<RenderGraphTestPassData>(RenderFunc); 868 } 869 870 using (var builder = renderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass1", out var passData)) 871 { 872 builder.UseColorBuffer(texture0, 0); 873 builder.SetRenderFunc<RenderGraphTestPassData>(RenderFunc3); 874 } 875 876 using (var builder = renderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass2", out var passData)) 877 { 878 builder.WriteTexture(renderGraph.ImportBackbuffer(0)); 879 builder.ReadTexture(texture0); 880 builder.SetRenderFunc<RenderGraphTestPassData>(RenderFunc2); 881 } 882 } 883 884 RecordRenderGraph(m_RenderGraph); 885 886 var hash0 = m_RenderGraph.ComputeGraphHash(); 887 m_RenderGraph.ClearCompiledGraph(); 888 889 RecordRenderGraph(m_RenderGraph); 890 891 var hash1 = m_RenderGraph.ComputeGraphHash(); 892 m_RenderGraph.ClearCompiledGraph(); 893 894 Assert.AreEqual(hash0, hash1); 895 } 896 897 [Test] 898 public void GetDescAndInfoForImportedTextureWorks() 899 { 900 RenderTextureDescriptor desc = new RenderTextureDescriptor(37, 53, GraphicsFormat.R16G16_SNorm, GraphicsFormat.None, 4); 901 RenderTexture renderTexture = new RenderTexture(desc); 902 RTHandle renderTextureHandle = RTHandles.Alloc(renderTexture); 903 904 var importedTexture = m_RenderGraph.ImportTexture(renderTextureHandle); 905 var renderGraphDesc = m_RenderGraph.GetTextureDesc(importedTexture); 906 907 Assert.AreEqual(desc.width, renderGraphDesc.width); 908 Assert.AreEqual(desc.height, renderGraphDesc.height); 909 Assert.AreEqual(desc.graphicsFormat, renderGraphDesc.colorFormat); 910 Assert.AreEqual(DepthBits.None, renderGraphDesc.depthBufferBits); 911 912 913 var renderTargetInfo = m_RenderGraph.GetRenderTargetInfo(importedTexture); 914 Assert.AreEqual(desc.width, renderTargetInfo.width); 915 Assert.AreEqual(desc.height, renderTargetInfo.height); 916 Assert.AreEqual(desc.graphicsFormat, renderTargetInfo.format); 917 918 CoreUtils.Destroy(renderTexture); 919 } 920 921 [Test] 922 public void TextureDescFormatPropertiesWork() 923 { 924 var formatR32 = GraphicsFormat.R32_SFloat; 925 926 var textureDesc = new TextureDesc(16, 16); 927 textureDesc.format = formatR32; 928 929 Assert.AreEqual(textureDesc.colorFormat,formatR32); 930 Assert.AreEqual(textureDesc.depthBufferBits, DepthBits.None); 931 932 textureDesc.depthBufferBits = DepthBits.None; 933 934 //No change expected 935 Assert.AreEqual(textureDesc.colorFormat, formatR32); 936 Assert.AreEqual(textureDesc.depthBufferBits, DepthBits.None); 937 938 textureDesc.depthBufferBits = DepthBits.Depth32; 939 940 //Not entirely sure what the platform will select but at least it should be 24 or more (not 0) 941 Assert.IsTrue((int)textureDesc.depthBufferBits >= 24); 942 Assert.AreEqual(textureDesc.colorFormat, GraphicsFormat.None); 943 944 textureDesc.format = formatR32; 945 946 Assert.AreEqual(textureDesc.colorFormat, formatR32); 947 Assert.AreEqual(textureDesc.depthBufferBits, DepthBits.None); 948 949 textureDesc.format = GraphicsFormat.D16_UNorm; 950 951 Assert.AreEqual(textureDesc.depthBufferBits, DepthBits.Depth16); 952 Assert.AreEqual(textureDesc.colorFormat, GraphicsFormat.None); 953 954 { 955 var importedTexture = m_RenderGraph.CreateTexture(textureDesc); 956 957 var importedDesc = importedTexture.GetDescriptor(m_RenderGraph); 958 Assert.AreEqual(textureDesc.format, importedDesc.format); 959 } 960 961 textureDesc.colorFormat = formatR32; 962 Assert.AreEqual(textureDesc.depthBufferBits, DepthBits.None); 963 Assert.AreEqual(textureDesc.colorFormat, textureDesc.format); 964 965 { 966 var importedTexture = m_RenderGraph.CreateTexture(textureDesc); 967 968 var importedDesc = importedTexture.GetDescriptor(m_RenderGraph); 969 Assert.AreEqual(textureDesc.format, importedDesc.format); 970 } 971 } 972 973 [Test] 974 public void ImportingBuiltinRenderTextureTypeWithNoInfoThrows() 975 { 976 RenderTargetIdentifier renderTargetIdentifier = new RenderTargetIdentifier(BuiltinRenderTextureType.CameraTarget); 977 RTHandle renderTextureHandle = RTHandles.Alloc(renderTargetIdentifier); 978 979 Assert.Throws<Exception>(() => 980 { 981 var importedTexture = m_RenderGraph.ImportTexture(renderTextureHandle); 982 }); 983 984 renderTextureHandle.Release(); 985 } 986 987 [Test] 988 public void ImportingRenderTextureWithColorAndDepthThrows() 989 { 990 // Create a new RTHandle texture 991 var desc = new RenderTextureDescriptor(16, 16, GraphicsFormat.R8G8B8A8_UNorm, GraphicsFormat.D32_SFloat_S8_UInt); 992 var rt = new RenderTexture(desc) { name = "RenderTextureWithColorAndDepth"}; 993 994 var renderTextureHandle = RTHandles.Alloc(rt); 995 996 Assert.Throws<Exception>(() => 997 { 998 var importedTexture = m_RenderGraph.ImportTexture(renderTextureHandle); 999 }); 1000 1001 renderTextureHandle.Release(); 1002 rt.Release(); 1003 } 1004 1005 [Test] 1006 public void ImportingBuiltinRenderTextureTypeWithInfoHasNoDesc() 1007 { 1008 RenderTargetIdentifier renderTargetIdentifier = new RenderTargetIdentifier(BuiltinRenderTextureType.CameraTarget); 1009 RTHandle renderTextureHandle = RTHandles.Alloc(renderTargetIdentifier); 1010 1011 var importedInfo = new RenderTargetInfo(); 1012 importedInfo.width = 128; 1013 importedInfo.height = 128; 1014 importedInfo.volumeDepth = 1; 1015 importedInfo.msaaSamples = 1; 1016 importedInfo.format = GraphicsFormat.B8G8R8A8_SNorm; 1017 var importedTexture = m_RenderGraph.ImportTexture(renderTextureHandle, importedInfo); 1018 1019 Assert.Throws<ArgumentException>(() => 1020 { 1021 var renderGraphDesc = m_RenderGraph.GetTextureDesc(importedTexture); 1022 }); 1023 1024 var renderTargetInfo = m_RenderGraph.GetRenderTargetInfo(importedTexture); 1025 1026 // It just needs to return what was fed in. 1027 Assert.AreEqual(importedInfo, renderTargetInfo); 1028 } 1029 1030 [Test] 1031 public void CreateLegacyRendererLists() 1032 { 1033 // We need a real ScriptableRenderContext and a camera to call correctly the legacy RendererLists API 1034 1035 // add the default camera 1036 var gameObject = new GameObject("testGameObject") 1037 { 1038 hideFlags = HideFlags.HideAndDontSave 1039 }; 1040 gameObject.tag = "MainCamera"; 1041 var camera = gameObject.AddComponent<Camera>(); 1042 1043 // record and execute render graph calls 1044 m_RenderGraphTestPipeline.recordRenderGraphBody = (context, camera, cmd) => 1045 { 1046 var rendererListHandle = m_RenderGraph.CreateUIOverlayRendererList(camera); 1047 Assert.IsTrue(rendererListHandle.IsValid()); 1048 1049 rendererListHandle = m_RenderGraph.CreateWireOverlayRendererList(camera); 1050 Assert.IsTrue(rendererListHandle.IsValid()); 1051 1052 rendererListHandle = m_RenderGraph.CreateGizmoRendererList(camera, GizmoSubset.PostImageEffects); 1053 Assert.IsTrue(rendererListHandle.IsValid()); 1054 1055 rendererListHandle = m_RenderGraph.CreateSkyboxRendererList(camera); 1056 Assert.IsTrue(rendererListHandle.IsValid()); 1057 1058 rendererListHandle = m_RenderGraph.CreateSkyboxRendererList(camera, Matrix4x4.identity, Matrix4x4.identity); 1059 Assert.IsTrue(rendererListHandle.IsValid()); 1060 1061 rendererListHandle = m_RenderGraph.CreateSkyboxRendererList(camera, Matrix4x4.identity, Matrix4x4.identity, Matrix4x4.identity, Matrix4x4.identity); 1062 Assert.IsTrue(rendererListHandle.IsValid()); 1063 }; 1064 camera.Render(); 1065 1066 GameObject.DestroyImmediate(gameObject); 1067 } 1068 1069 [Test] 1070 public void RenderPassWithNoRenderFuncThrows() 1071 { 1072 // We need a real ScriptableRenderContext and a camera to execute the render graph 1073 // add the default camera 1074 var gameObject = new GameObject("testGameObject") 1075 { 1076 hideFlags = HideFlags.HideAndDontSave 1077 }; 1078 gameObject.tag = "MainCamera"; 1079 var camera = gameObject.AddComponent<Camera>(); 1080 1081 // record and execute render graph calls 1082 m_RenderGraphTestPipeline.recordRenderGraphBody = (context, camera, cmd) => 1083 { 1084 using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPassWithNoRenderFunc", out var passData)) 1085 { 1086 builder.AllowPassCulling(false); 1087 1088 // no render func 1089 } 1090 }; 1091 LogAssert.Expect(LogType.Error, "Render Graph Execution error"); 1092 LogAssert.Expect(LogType.Exception, "InvalidOperationException: RenderPass TestPassWithNoRenderFunc was not provided with an execute function."); 1093 camera.Render(); 1094 1095 GameObject.DestroyImmediate(gameObject); 1096 } 1097 1098 /* 1099 // Disabled for now as version management is not exposed to user code 1100 [Test] 1101 public void VersionManagement() 1102 { 1103 1104 TextureHandle texture0; 1105 TextureHandle texture0v1; 1106 TextureHandle texture0v2; 1107 texture0 = m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }); 1108 1109 TextureHandle texture1; 1110 texture1 = m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }); 1111 1112 // Handles are unversioned by default. Unversioned handles use an implicit version "the latest version" depending on their 1113 // usage context. 1114 Assert.AreEqual(false, texture0.handle.IsVersioned); 1115 1116 // Writing bumps the version 1117 using (var builder = m_RenderGraph.AddRasterRenderPass<RenderGraphTestPassData>("Async_TestPass0", out var passData)) 1118 { 1119 texture0v1 = builder.UseTexture(texture0, AccessFlags.ReadWrite); 1120 Assert.AreEqual(true, texture0v1.handle.IsVersioned); 1121 Assert.AreEqual(1, texture0v1.handle.version); 1122 builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { }); 1123 } 1124 1125 // Writing again bumps again 1126 using (var builder = m_RenderGraph.AddRasterRenderPass<RenderGraphTestPassData>("Async_TestPass1", out var passData)) 1127 { 1128 texture0v2 = builder.UseTexture(texture0, AccessFlags.ReadWrite); 1129 Assert.AreEqual(true, texture0v2.handle.IsVersioned); 1130 Assert.AreEqual(2, texture0v2.handle.version); 1131 builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { }); 1132 } 1133 1134 // Reading leaves the version alone 1135 using (var builder = m_RenderGraph.AddRasterRenderPass<RenderGraphTestPassData>("Async_TestPass2", out var passData)) 1136 { 1137 var versioned = builder.UseTexture(texture0, AccessFlags.Read); 1138 Assert.AreEqual(true, versioned.handle.IsVersioned); 1139 Assert.AreEqual(2, versioned.handle.version); 1140 builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { }); 1141 } 1142 1143 // Writing to an old version is not supported it would lead to a divergent version timeline for the resource 1144 using (var builder = m_RenderGraph.AddRasterRenderPass<RenderGraphTestPassData>("Async_TestPass2", out var passData)) 1145 { 1146 // If you want do achieve this and avoid copying the move should be used 1147 Assert.Throws<System.InvalidOperationException>(() => 1148 { 1149 var versioned = builder.UseTexture(texture0v1, AccessFlags.ReadWrite); 1150 }); 1151 builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { }); 1152 } 1153 }*/ 1154 1155 class RenderGraphAsyncRequestTestData 1156 { 1157 public TextureHandle texture; 1158 public NativeArray<byte> pixels; 1159 } 1160 1161 private bool m_AsyncReadbackDone = false; 1162 1163 [Test] 1164 public void RequestAsyncReadbackIntoNativeArrayWorks() 1165 { 1166 const int kWidth = 4; 1167 const int kHeight = 4; 1168 const GraphicsFormat format = GraphicsFormat.R8G8B8A8_SRGB; 1169 1170 // We need a real ScriptableRenderContext and a camera to execute the render graph 1171 // add the default camera 1172 var gameObject = new GameObject("testGameObject") 1173 { 1174 hideFlags = HideFlags.HideAndDontSave, 1175 tag = "MainCamera" 1176 }; 1177 var camera = gameObject.AddComponent<Camera>(); 1178 1179 NativeArray<byte> pixels = default; 1180 bool passExecuted = false; 1181 1182 m_RenderGraphTestPipeline.recordRenderGraphBody = (context, camera, cmd) => 1183 { 1184 // Avoid performing the same request multiple frames for nothing 1185 if (passExecuted) 1186 return; 1187 1188 passExecuted = true; 1189 1190 var redTexture = CreateRedTexture(kWidth, kHeight); 1191 var texture0 = m_RenderGraph.ImportTexture(redTexture); 1192 1193 pixels = new NativeArray<byte>(kWidth * kHeight * 4, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); 1194 1195 using (var builder = m_RenderGraph.AddUnsafePass<RenderGraphAsyncRequestTestData>("ReadbackPass", out var passData)) 1196 { 1197 builder.AllowPassCulling(false); 1198 1199 builder.UseTexture(texture0, AccessFlags.ReadWrite); 1200 1201 passData.texture = texture0; 1202 passData.pixels = pixels; 1203 1204 builder.SetRenderFunc((RenderGraphAsyncRequestTestData data, UnsafeGraphContext context) => 1205 { 1206 context.cmd.RequestAsyncReadbackIntoNativeArray(ref data.pixels, data.texture, 0, format, RenderGraphTest_AsyncReadbackCallback); 1207 }); 1208 } 1209 }; 1210 1211 camera.Render(); 1212 1213 AsyncGPUReadback.WaitAllRequests(); 1214 1215 Assert.True(m_AsyncReadbackDone); 1216 1217 for (int i = 0; i < kWidth * kHeight; i += 4) 1218 { 1219 Assert.True(pixels[i] / 255.0f == Color.red.r); 1220 Assert.True(pixels[i+1] / 255.0f == Color.red.g); 1221 Assert.True(pixels[i+2] / 255.0f == Color.red.b); 1222 Assert.True(pixels[i+3] / 255.0f == Color.red.a); 1223 } 1224 1225 pixels.Dispose(); 1226 GameObject.DestroyImmediate(gameObject); 1227 } 1228 1229 void RenderGraphTest_AsyncReadbackCallback(AsyncGPUReadbackRequest request) 1230 { 1231 if (request.hasError) 1232 { 1233 // We shouldn't have any error, asserting. 1234 Assert.True(m_AsyncReadbackDone); 1235 } 1236 else if (request.done) 1237 { 1238 m_AsyncReadbackDone = true; 1239 } 1240 } 1241 1242 RTHandle CreateRedTexture(int width, int height) 1243 { 1244 // Create a red color 1245 Color redColor = Color.red; 1246 1247 // Initialize the RTHandle system if necessary 1248 RTHandles.Initialize(width, height); 1249 1250 // Create a new RTHandle texture 1251 var redTextureHandle = RTHandles.Alloc(width, height, 1252 GraphicsFormat.R8G8B8A8_UNorm, 1253 dimension: TextureDimension.Tex2D, 1254 useMipMap: false, 1255 autoGenerateMips: false, 1256 name: "RedTexture"); 1257 1258 // Set the texture to red 1259 Texture2D tempTexture = new Texture2D(width, height, TextureFormat.RGBA32, false); 1260 for (int y = 0; y < tempTexture.height; y++) 1261 { 1262 for (int x = 0; x < tempTexture.width; x++) 1263 { 1264 tempTexture.SetPixel(x, y, redColor); 1265 } 1266 } 1267 tempTexture.Apply(); 1268 1269 // Copy the temporary Texture2D to the RTHandle 1270 Graphics.Blit(tempTexture, redTextureHandle.rt); 1271 1272 Texture2D.DestroyImmediate(tempTexture); 1273 1274 // Cleanup the temporary texture 1275 return redTextureHandle; 1276 } 1277 1278 class TestBuffersImport 1279 { 1280 public BufferHandle bufferHandle; 1281 public ComputeShader computeShader; 1282 } 1283 1284 private const string kPathToComputeShader = "Packages/com.unity.render-pipelines.core/Tests/Editor/BufferCopyTest.compute"; 1285 1286 [Test, ConditionalIgnore("IgnoreGraphicsAPI", "Compute Shaders are not supported for this Graphics API.")] 1287 public void ImportingBufferWorks() 1288 { 1289 // We need a real ScriptableRenderContext and a camera to execute the render graph 1290 // add the default camera 1291 var gameObject = new GameObject("testGameObject") 1292 { 1293 hideFlags = HideFlags.HideAndDontSave, 1294 tag = "MainCamera" 1295 }; 1296 var camera = gameObject.AddComponent<Camera>(); 1297#if UNITY_EDITOR 1298 var computeShader = AssetDatabase.LoadAssetAtPath<ComputeShader>(kPathToComputeShader); 1299#else 1300 var computeShader = Resources.Load<ComputeShader>("_" + Path.GetFileNameWithoutExtension(kPathToComputeShader)); 1301#endif 1302 // Check if the compute shader was loaded successfully 1303 if (computeShader == null) 1304 { 1305 Debug.LogError("Compute Shader not found!"); 1306 return; 1307 } 1308 1309 // Define the size of the buffer (number of elements) 1310 int bufferSize = 4; // We are only interested in the first four values 1311 1312 // Allocate the buffer with the given size and format 1313 var buffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, bufferSize, sizeof(float)); 1314 1315 // Initialize the buffer with zeros 1316 float[] initialData = new float[bufferSize]; 1317 buffer.SetData(initialData); 1318 1319 // Ensure the data is set to 0.0f 1320 for (int i = 0; i < bufferSize; i++) 1321 { 1322 Assert.IsTrue(initialData[i] == 0.0f); 1323 } 1324 1325 m_RenderGraphTestPipeline.recordRenderGraphBody = (context, camera, cmd) => 1326 { 1327 using (var builder = m_RenderGraph.AddComputePass<TestBuffersImport>("TestPass0", out var passData)) 1328 { 1329 builder.AllowPassCulling(false); 1330 1331 passData.bufferHandle = m_RenderGraph.ImportBuffer(buffer); 1332 1333 builder.UseBuffer(passData.bufferHandle, AccessFlags.Write); 1334 1335 passData.computeShader = computeShader; 1336 1337 builder.SetRenderFunc((TestBuffersImport data, ComputeGraphContext ctx) => 1338 { 1339 int kernel = data.computeShader.FindKernel("CSMain"); 1340 1341 ctx.cmd.SetComputeBufferParam(data.computeShader, kernel, "resultBuffer", data.bufferHandle); 1342 ctx.cmd.DispatchCompute(data.computeShader, kernel, 1, 1, 1); 1343 }); 1344 } 1345 }; 1346 1347 camera.Render(); 1348 1349 // Read back the data from the buffer 1350 float[] result2 = new float[bufferSize]; 1351 buffer.GetData(result2); 1352 1353 buffer.Release(); 1354 1355 // Ensure the data has been updated 1356 for (int i = 0; i < bufferSize; i++) 1357 { 1358 Assert.IsTrue(result2[i] == 1.0f); 1359 } 1360 } 1361 } 1362}