A game about forced loneliness, made by TACStudios
1using System; 2using System.Collections.Generic; 3using System.Linq; 4using UnityEngine; 5using UnityEditor.Graphing; 6using UnityEditor.Graphing.Util; 7using UnityEngine.Assertions; 8using UnityEngine.Rendering; 9using UnityEditor.ShaderGraph.Internal; 10using Debug = UnityEngine.Debug; 11using Object = UnityEngine.Object; 12using Unity.Profiling; 13 14namespace UnityEditor.ShaderGraph.Drawing 15{ 16 delegate void OnPrimaryMasterChanged(); 17 18 class PreviewManager : IDisposable 19 { 20 GraphData m_Graph; 21 MessageManager m_Messenger; 22 23 MaterialPropertyBlock m_SharedPreviewPropertyBlock; // stores preview properties (shared among ALL preview nodes) 24 25 Dictionary<AbstractMaterialNode, PreviewRenderData> m_RenderDatas = new Dictionary<AbstractMaterialNode, PreviewRenderData>(); // stores all of the PreviewRendererData, mapped by node 26 PreviewRenderData m_MasterRenderData; // ref to preview renderer data for the master node 27 28 int m_MaxPreviewsCompiling = 2; // max preview shaders we want to async compile at once 29 30 // state trackers 31 HashSet<AbstractMaterialNode> m_NodesShaderChanged = new HashSet<AbstractMaterialNode>(); // nodes whose shader code has changed, this node and nodes that read from it are put into NeedRecompile 32 HashSet<AbstractMaterialNode> m_NodesPropertyChanged = new HashSet<AbstractMaterialNode>(); // nodes whose property values have changed, the properties will need to be updated and all nodes that use that property re-rendered 33 34 HashSet<PreviewRenderData> m_PreviewsNeedsRecompile = new HashSet<PreviewRenderData>(); // previews we need to recompile the preview shader 35 HashSet<PreviewRenderData> m_PreviewsCompiling = new HashSet<PreviewRenderData>(); // previews currently being compiled 36 HashSet<PreviewRenderData> m_PreviewsToDraw = new HashSet<PreviewRenderData>(); // previews to re-render the texture (either because shader compile changed or property changed) 37 HashSet<PreviewRenderData> m_TimedPreviews = new HashSet<PreviewRenderData>(); // previews that are dependent on a time node -- i.e. animated / need to redraw every frame 38 39 double m_LastTimedUpdateTime = 0.0f; 40 41 bool m_TopologyDirty; // indicates topology changed, used to rebuild timed node list and preview type (2D/3D) inheritance. 42 43 HashSet<BlockNode> m_MasterNodeTempBlocks = new HashSet<BlockNode>(); // temp blocks used by the most recent master node preview generation. 44 45 // used to detect when texture assets have been modified 46 HashSet<string> m_PreviewTextureGUIDs = new HashSet<string>(); 47 PreviewSceneResources m_SceneResources; 48 Texture2D m_ErrorTexture; 49 Vector2? m_NewMasterPreviewSize; 50 51 const AbstractMaterialNode kMasterProxyNode = null; 52 53 public PreviewRenderData masterRenderData 54 { 55 get { return m_MasterRenderData; } 56 } 57 58 public PreviewManager(GraphData graph, MessageManager messenger) 59 { 60 m_SharedPreviewPropertyBlock = new MaterialPropertyBlock(); 61 m_Graph = graph; 62 m_Messenger = messenger; 63 m_ErrorTexture = GenerateFourSquare(Color.magenta, Color.black); 64 m_SceneResources = new PreviewSceneResources(); 65 66 foreach (var node in m_Graph.GetNodes<AbstractMaterialNode>()) 67 AddPreview(node); 68 69 AddMasterPreview(); 70 } 71 72 static Texture2D GenerateFourSquare(Color c1, Color c2) 73 { 74 var tex = new Texture2D(2, 2); 75 tex.SetPixel(0, 0, c1); 76 tex.SetPixel(0, 1, c2); 77 tex.SetPixel(1, 0, c2); 78 tex.SetPixel(1, 1, c1); 79 tex.filterMode = FilterMode.Point; 80 tex.Apply(); 81 return tex; 82 } 83 84 public void ResizeMasterPreview(Vector2 newSize) 85 { 86 m_NewMasterPreviewSize = newSize; 87 } 88 89 public PreviewRenderData GetPreviewRenderData(AbstractMaterialNode node) 90 { 91 PreviewRenderData result = null; 92 if (node == kMasterProxyNode || 93 node is BlockNode || 94 node == m_Graph.outputNode) // the outputNode, if it exists, is mapped to master 95 { 96 result = m_MasterRenderData; 97 } 98 else 99 { 100 m_RenderDatas.TryGetValue(node, out result); 101 } 102 103 return result; 104 } 105 106 void AddMasterPreview() 107 { 108 m_MasterRenderData = new PreviewRenderData 109 { 110 previewName = "Master Preview", 111 renderTexture = 112 new RenderTexture(400, 400, 16, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default) 113 { 114 hideFlags = HideFlags.HideAndDontSave 115 }, 116 previewMode = PreviewMode.Preview3D, 117 }; 118 119 m_MasterRenderData.renderTexture.Create(); 120 121 var shaderData = new PreviewShaderData 122 { 123 // even though a SubGraphOutputNode can be directly mapped to master (via m_Graph.outputNode) 124 // we always keep master node associated with kMasterProxyNode instead 125 // just easier if the association is always dynamic 126 node = kMasterProxyNode, 127 passesCompiling = 0, 128 isOutOfDate = true, 129 hasError = false, 130 }; 131 m_MasterRenderData.shaderData = shaderData; 132 133 m_PreviewsNeedsRecompile.Add(m_MasterRenderData); 134 m_PreviewsToDraw.Add(m_MasterRenderData); 135 m_TopologyDirty = true; 136 } 137 138 public void UpdateMasterPreview(ModificationScope scope) 139 { 140 if (scope == ModificationScope.Topological || 141 scope == ModificationScope.Graph) 142 { 143 // mark the master preview for recompile if it exists 144 // if not, no need to do it here, because it is always marked for recompile on creation 145 if (m_MasterRenderData != null) 146 m_PreviewsNeedsRecompile.Add(m_MasterRenderData); 147 m_TopologyDirty = true; 148 } 149 else if (scope == ModificationScope.Node) 150 { 151 if (m_MasterRenderData != null) 152 m_PreviewsToDraw.Add(m_MasterRenderData); 153 } 154 } 155 156 void AddPreview(AbstractMaterialNode node) 157 { 158 Assert.IsNotNull(node); 159 160 // BlockNodes have no preview for themselves, but are mapped to the "Master" preview 161 // SubGraphOutput nodes have their own previews, but will use the "Master" preview if they are the m_Graph.outputNode 162 if (node is BlockNode) 163 { 164 node.RegisterCallback(OnNodeModified); 165 UpdateMasterPreview(ModificationScope.Topological); 166 m_NodesPropertyChanged.Add(node); 167 return; 168 } 169 170 var renderData = new PreviewRenderData 171 { 172 previewName = node.name ?? "UNNAMED NODE", 173 renderTexture = 174 new RenderTexture(200, 200, 16, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default) 175 { 176 hideFlags = HideFlags.HideAndDontSave 177 } 178 }; 179 180 renderData.renderTexture.Create(); 181 182 var shaderData = new PreviewShaderData 183 { 184 node = node, 185 passesCompiling = 0, 186 isOutOfDate = true, 187 hasError = false, 188 }; 189 renderData.shaderData = shaderData; 190 191 m_RenderDatas.Add(node, renderData); 192 node.RegisterCallback(OnNodeModified); 193 194 m_PreviewsNeedsRecompile.Add(renderData); 195 m_NodesPropertyChanged.Add(node); 196 m_TopologyDirty = true; 197 } 198 199 void OnNodeModified(AbstractMaterialNode node, ModificationScope scope) 200 { 201 Assert.IsNotNull(node); 202 203 if (scope == ModificationScope.Topological || 204 scope == ModificationScope.Graph) 205 { 206 m_NodesShaderChanged.Add(node); // shader code for this node changed, this will trigger m_PreviewsShaderChanged for all nodes downstream 207 m_NodesPropertyChanged.Add(node); // properties could also have changed at the same time and need to be re-collected 208 m_TopologyDirty = true; 209 } 210 else if (scope == ModificationScope.Node) 211 { 212 // if we only changed a constant on the node, we don't have to recompile the shader for it, just re-render it with the updated constant 213 // should instead flag m_NodesConstantChanged 214 m_NodesPropertyChanged.Add(node); 215 } 216 } 217 218 // temp structures that are kept around statically to avoid GC churn (not thread safe) 219 static Stack<AbstractMaterialNode> m_TempNodeWave = new Stack<AbstractMaterialNode>(); 220 static HashSet<AbstractMaterialNode> m_TempAddedToNodeWave = new HashSet<AbstractMaterialNode>(); 221 222 // cache the Action to avoid GC 223 static Action<AbstractMaterialNode> AddNextLevelNodesToWave = 224 nextLevelNode => 225 { 226 if (!m_TempAddedToNodeWave.Contains(nextLevelNode)) 227 { 228 m_TempNodeWave.Push(nextLevelNode); 229 m_TempAddedToNodeWave.Add(nextLevelNode); 230 } 231 }; 232 233 internal enum PropagationDirection 234 { 235 Upstream, 236 Downstream 237 } 238 239 // ADDs all nodes in sources, and all nodes in the given direction relative to them, into result 240 // sources and result can be the same HashSet 241 private static readonly ProfilerMarker PropagateNodesMarker = new ProfilerMarker("PropagateNodes"); 242 internal static void PropagateNodes(HashSet<AbstractMaterialNode> sources, PropagationDirection dir, HashSet<AbstractMaterialNode> result) 243 { 244 using (PropagateNodesMarker.Auto()) 245 if (sources.Count > 0) 246 { 247 // NodeWave represents the list of nodes we still have to process and add to result 248 m_TempNodeWave.Clear(); 249 m_TempAddedToNodeWave.Clear(); 250 foreach (var node in sources) 251 { 252 m_TempNodeWave.Push(node); 253 m_TempAddedToNodeWave.Add(node); 254 } 255 256 while (m_TempNodeWave.Count > 0) 257 { 258 var node = m_TempNodeWave.Pop(); 259 if (node == null) 260 continue; 261 262 result.Add(node); 263 264 // grab connected nodes in propagation direction, add them to the node wave 265 ForeachConnectedNode(node, dir, AddNextLevelNodesToWave); 266 } 267 268 // clean up any temp data 269 m_TempNodeWave.Clear(); 270 m_TempAddedToNodeWave.Clear(); 271 } 272 } 273 274 static void ForeachConnectedNode(AbstractMaterialNode node, PropagationDirection dir, Action<AbstractMaterialNode> action) 275 { 276 using (var tempEdges = PooledList<IEdge>.Get()) 277 using (var tempSlots = PooledList<MaterialSlot>.Get()) 278 { 279 // Loop through all nodes that the node feeds into. 280 if (dir == PropagationDirection.Downstream) 281 node.GetOutputSlots(tempSlots); 282 else 283 node.GetInputSlots(tempSlots); 284 285 foreach (var slot in tempSlots) 286 { 287 // get the edges out of each slot 288 tempEdges.Clear(); // and here we serialize another list, ouch! 289 node.owner.GetEdges(slot.slotReference, tempEdges); 290 foreach (var edge in tempEdges) 291 { 292 // We look at each node we feed into. 293 var connectedSlot = (dir == PropagationDirection.Downstream) ? edge.inputSlot : edge.outputSlot; 294 var connectedNode = connectedSlot.node; 295 296 action(connectedNode); 297 } 298 } 299 } 300 301 // Custom Interpolator Blocks have implied connections to their Custom Interpolator Nodes... 302 if (dir == PropagationDirection.Downstream && node is BlockNode bnode && bnode.isCustomBlock) 303 { 304 foreach (var cin in CustomInterpolatorUtils.GetCustomBlockNodeDependents(bnode)) 305 { 306 action(cin); 307 } 308 } 309 // ... Just as custom Interpolator Nodes have implied connections to their custom interpolator blocks 310 if (dir == PropagationDirection.Upstream && node is CustomInterpolatorNode ciNode && ciNode.e_targetBlockNode != null) 311 { 312 action(ciNode.e_targetBlockNode); 313 } 314 } 315 316 public void ReloadChangedFiles(string ChangedFileDependencyGUIDs) 317 { 318 if (m_PreviewTextureGUIDs.Contains(ChangedFileDependencyGUIDs)) 319 { 320 // have to setup the textures on the MaterialPropertyBlock again 321 // easiest is to just mark everything as needing property update 322 m_NodesPropertyChanged.UnionWith(m_RenderDatas.Keys); 323 } 324 } 325 326 public void HandleGraphChanges() 327 { 328 foreach (var node in m_Graph.addedNodes) 329 { 330 AddPreview(node); 331 m_TopologyDirty = true; 332 } 333 334 foreach (var edge in m_Graph.addedEdges) 335 { 336 var node = edge.inputSlot.node; 337 if (node != null) 338 { 339 if ((node is BlockNode) || (node is SubGraphOutputNode)) 340 UpdateMasterPreview(ModificationScope.Topological); 341 else 342 m_NodesShaderChanged.Add(node); 343 m_TopologyDirty = true; 344 } 345 } 346 347 foreach (var node in m_Graph.removedNodes) 348 { 349 DestroyPreview(node); 350 m_TopologyDirty = true; 351 } 352 353 foreach (var edge in m_Graph.removedEdges) 354 { 355 var node = edge.inputSlot.node; 356 if ((node is BlockNode) || (node is SubGraphOutputNode)) 357 { 358 UpdateMasterPreview(ModificationScope.Topological); 359 } 360 361 m_NodesShaderChanged.Add(node); 362 //When an edge gets deleted, if the node had the edge on creation, the properties would get out of sync and no value would get set. 363 //Fix for https://fogbugz.unity3d.com/f/cases/1284033/ 364 m_NodesPropertyChanged.Add(node); 365 366 m_TopologyDirty = true; 367 } 368 369 foreach (var edge in m_Graph.addedEdges) 370 { 371 var node = edge.inputSlot.node; 372 if (node != null) 373 { 374 if ((node is BlockNode) || (node is SubGraphOutputNode)) 375 { 376 UpdateMasterPreview(ModificationScope.Topological); 377 } 378 379 m_NodesShaderChanged.Add(node); 380 m_TopologyDirty = true; 381 } 382 } 383 384 // remove the nodes from the state trackers 385 m_NodesShaderChanged.ExceptWith(m_Graph.removedNodes); 386 m_NodesPropertyChanged.ExceptWith(m_Graph.removedNodes); 387 388 m_Messenger.ClearNodesFromProvider(this, m_Graph.removedNodes); 389 } 390 391 private static readonly ProfilerMarker CollectPreviewPropertiesMarker = new ProfilerMarker("CollectPreviewProperties"); 392 void CollectPreviewProperties(IEnumerable<AbstractMaterialNode> nodesToCollect, PooledList<PreviewProperty> perMaterialPreviewProperties) 393 { 394 using (CollectPreviewPropertiesMarker.Auto()) 395 using (var tempPreviewProps = PooledList<PreviewProperty>.Get()) 396 { 397 // collect from all of the changed nodes 398 foreach (var propNode in nodesToCollect) 399 propNode.CollectPreviewMaterialProperties(tempPreviewProps); 400 401 // also grab all graph properties (they are updated every frame) 402 foreach (var prop in m_Graph.properties) 403 tempPreviewProps.Add(prop.GetPreviewMaterialProperty()); 404 405 foreach (var previewProperty in tempPreviewProps) 406 { 407 previewProperty.SetValueOnMaterialPropertyBlock(m_SharedPreviewPropertyBlock); 408 409 // record guids for any texture properties 410 if ((previewProperty.propType >= PropertyType.Texture2D) && (previewProperty.propType <= PropertyType.Cubemap)) 411 { 412 413 if (previewProperty.propType != PropertyType.Cubemap) 414 { 415 if (previewProperty.textureValue != null) 416 if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier(previewProperty.textureValue, out string guid, out long localID)) 417 { 418 // Note, this never gets cleared, so we accumulate texture GUIDs over time, if the user keeps changing textures 419 m_PreviewTextureGUIDs.Add(guid); 420 } 421 } 422 else 423 { 424 if (previewProperty.cubemapValue != null) 425 if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier(previewProperty.cubemapValue, out string guid, out long localID)) 426 { 427 // Note, this never gets cleared, so we accumulate texture GUIDs over time, if the user keeps changing textures 428 m_PreviewTextureGUIDs.Add(guid); 429 } 430 } 431 432 } 433 // virtual texture assignments must be pushed to the materials themselves (MaterialPropertyBlocks not supported) 434 if ((previewProperty.propType == PropertyType.VirtualTexture) && 435 (previewProperty.vtProperty?.value?.layers != null)) 436 { 437 perMaterialPreviewProperties.Add(previewProperty); 438 } 439 } 440 } 441 } 442 443 void AssignPerMaterialPreviewProperties(Material mat, List<PreviewProperty> perMaterialPreviewProperties) 444 { 445 foreach (var prop in perMaterialPreviewProperties) 446 { 447 switch (prop.propType) 448 { 449 case PropertyType.VirtualTexture: 450 451 // setup the VT textures on the material 452 bool setAnyTextures = false; 453 var vt = prop.vtProperty.value; 454 for (int layer = 0; layer < vt.layers.Count; layer++) 455 { 456 var texture = vt.layers[layer].layerTexture?.texture; 457 int propIndex = mat.shader.FindPropertyIndex(vt.layers[layer].layerRefName); 458 if (propIndex != -1) 459 { 460 mat.SetTexture(vt.layers[layer].layerRefName, texture); 461 setAnyTextures = true; 462 } 463 } 464 // also put in a request for the VT tiles, since preview rendering does not have feedback enabled 465 if (setAnyTextures) 466 { 467#if ENABLE_VIRTUALTEXTURES 468 int stackPropertyId = Shader.PropertyToID(prop.vtProperty.referenceName); 469 try 470 { 471 // Ensure we always request the mip sized 256x256 472 int width, height; 473 UnityEngine.Rendering.VirtualTexturing.Streaming.GetTextureStackSize(mat, stackPropertyId, out width, out height); 474 int textureMip = (int)Math.Max(Mathf.Log(width, 2f), Mathf.Log(height, 2f)); 475 const int baseMip = 8; 476 int mip = Math.Max(textureMip - baseMip, 0); 477 UnityEngine.Rendering.VirtualTexturing.Streaming.RequestRegion(mat, stackPropertyId, new Rect(0.0f, 0.0f, 1.0f, 1.0f), mip, UnityEngine.Rendering.VirtualTexturing.System.AllMips); 478 } 479 catch (InvalidOperationException) 480 { 481 // This gets thrown when the system is in an indeterminate state (like a material with no textures assigned which can obviously never have a texture stack streamed). 482 // This is valid in this case as we're still authoring the material. 483 } 484#endif // ENABLE_VIRTUALTEXTURES 485 } 486 break; 487 } 488 } 489 } 490 491 bool TimedNodesShouldUpdate(EditorWindow editorWindow) 492 { 493 // get current screen FPS, clamp to what we consider a valid range 494 // this is probably not accurate for multi-monitor.. but should be relevant to at least one of the monitors 495 double monitorFPS = Screen.currentResolution.refreshRateRatio.value; 496 if (Double.IsInfinity(monitorFPS) || Double.IsNaN(monitorFPS)) 497 monitorFPS = 60.0f; 498 monitorFPS = Math.Min(monitorFPS, 144.0); 499 monitorFPS = Math.Max(monitorFPS, 30.0); 500 501 var curTime = EditorApplication.timeSinceStartup; 502 var deltaTime = curTime - m_LastTimedUpdateTime; 503 bool isFocusedWindow = (EditorWindow.focusedWindow == editorWindow); 504 505 // we throttle the update rate, based on whether the window is focused and if unity is active 506 const double k_AnimatedFPS_WhenNotFocused = 10.0; 507 const double k_AnimatedFPS_WhenInactive = 2.0; 508 double maxAnimatedFPS = 509 (UnityEditorInternal.InternalEditorUtility.isApplicationActive ? 510 (isFocusedWindow ? monitorFPS : k_AnimatedFPS_WhenNotFocused) : 511 k_AnimatedFPS_WhenInactive); 512 513 bool update = (deltaTime > (1.0 / maxAnimatedFPS)); 514 if (update) 515 m_LastTimedUpdateTime = curTime; 516 return update; 517 } 518 519 private static readonly ProfilerMarker RenderPreviewsMarker = new ProfilerMarker("RenderPreviews"); 520 private static int k_spriteProps = Shader.PropertyToID("unity_SpriteProps"); 521 private static int k_spriteColor = Shader.PropertyToID("unity_SpriteColor"); 522 private static int k_rendererColor = Shader.PropertyToID("_RendererColor"); 523 public void RenderPreviews(EditorWindow editorWindow, bool requestShaders = true) 524 { 525 using (RenderPreviewsMarker.Auto()) 526 using (var renderList2D = PooledList<PreviewRenderData>.Get()) 527 using (var renderList3D = PooledList<PreviewRenderData>.Get()) 528 using (var nodesToDraw = PooledHashSet<AbstractMaterialNode>.Get()) 529 using (var perMaterialPreviewProperties = PooledList<PreviewProperty>.Get()) 530 { 531 // update topology cached data 532 // including list of time-dependent previews, and the preview mode (2d/3d) 533 UpdateTopology(); 534 535 if (requestShaders) 536 UpdateShaders(); 537 538 // Need to late capture custom interpolators because of how their type changes 539 // can have downstream impacts on dynamic slots. 540 HashSet<AbstractMaterialNode> customProps = new HashSet<AbstractMaterialNode>(); 541 PropagateNodes( 542 new HashSet<AbstractMaterialNode>(m_NodesPropertyChanged.OfType<BlockNode>().Where(b => b.isCustomBlock)), 543 PropagationDirection.Downstream, 544 customProps); 545 546 m_NodesPropertyChanged.UnionWith(customProps); 547 548 // all nodes downstream of a changed property must be redrawn (to display the updated the property value) 549 PropagateNodes(m_NodesPropertyChanged, PropagationDirection.Downstream, nodesToDraw); 550 551 // always update properties from temporary blocks created by master node preview generation 552 m_NodesPropertyChanged.UnionWith(m_MasterNodeTempBlocks); 553 554 CollectPreviewProperties(m_NodesPropertyChanged, perMaterialPreviewProperties); 555 m_NodesPropertyChanged.Clear(); 556 557 // timed nodes are animated, so they should be updated regularly (but not necessarily on every update) 558 // (m_TimedPreviews has been pre-propagated downstream) 559 // HOWEVER they do not need to collect properties. (the only property changing is time..) 560 if (TimedNodesShouldUpdate(editorWindow)) 561 m_PreviewsToDraw.UnionWith(m_TimedPreviews); 562 563 ForEachNodesPreview(nodesToDraw, p => m_PreviewsToDraw.Add(p)); 564 565 // redraw master when it is resized 566 if (m_NewMasterPreviewSize.HasValue) 567 m_PreviewsToDraw.Add(m_MasterRenderData); 568 569 // apply filtering to determine what nodes really get drawn 570 bool renderMasterPreview = false; 571 int drawPreviewCount = 0; 572 foreach (var preview in m_PreviewsToDraw) 573 { 574 Assert.IsNotNull(preview); 575 576 { // skip if the node doesn't have a preview expanded (unless it's master) 577 var node = preview.shaderData.node; 578 if ((node != kMasterProxyNode) && (!node.hasPreview || !node.previewExpanded)) 579 continue; 580 } 581 582 // check that we've got shaders and materials generated 583 // if not ,replace the rendered texture with null 584 if ((preview.shaderData.shader == null) || 585 (preview.shaderData.mat == null)) 586 { 587 // avoid calling NotifyPreviewChanged repeatedly 588 if (preview.texture != null) 589 { 590 preview.texture = null; 591 preview.NotifyPreviewChanged(); 592 } 593 continue; 594 } 595 596 597 if (preview.shaderData.hasError) 598 { 599 preview.texture = m_ErrorTexture; 600 preview.NotifyPreviewChanged(); 601 continue; 602 } 603 604 // skip rendering while a preview shader is being compiled 605 if (m_PreviewsCompiling.Contains(preview)) 606 continue; 607 608 // we want to render this thing, now categorize what kind of render it is 609 if (preview == m_MasterRenderData) 610 renderMasterPreview = true; 611 else if (preview.previewMode == PreviewMode.Preview2D) 612 renderList2D.Add(preview); 613 else 614 renderList3D.Add(preview); 615 drawPreviewCount++; 616 } 617 618 // if we actually don't want to render anything at all, early out here 619 if (drawPreviewCount <= 0) 620 return; 621 622 var time = Time.realtimeSinceStartup; 623 var timeParameters = new Vector4(time, Mathf.Sin(time), Mathf.Cos(time), 0.0f); 624 m_SharedPreviewPropertyBlock.SetVector("_TimeParameters", timeParameters); 625 626 EditorUtility.SetCameraAnimateMaterialsTime(m_SceneResources.camera, time); 627 628 m_SceneResources.light0.enabled = true; 629 m_SceneResources.light0.intensity = 1.0f; 630 m_SceneResources.light0.transform.rotation = Quaternion.Euler(50f, 50f, 0); 631 m_SceneResources.light1.enabled = true; 632 m_SceneResources.light1.intensity = 1.0f; 633 m_SceneResources.camera.clearFlags = CameraClearFlags.Color; 634 635 // Render 2D previews 636 m_SceneResources.camera.transform.position = -Vector3.forward * 2; 637 m_SceneResources.camera.transform.rotation = Quaternion.identity; 638 m_SceneResources.camera.orthographicSize = 0.5f; 639 m_SceneResources.camera.orthographic = true; 640 641 foreach (var renderData in renderList2D) 642 RenderPreview(renderData, m_SceneResources.quad, Matrix4x4.identity, perMaterialPreviewProperties); 643 644 // Render 3D previews 645 m_SceneResources.camera.transform.position = -Vector3.forward * 5; 646 m_SceneResources.camera.transform.rotation = Quaternion.identity; 647 m_SceneResources.camera.orthographic = false; 648 649 foreach (var renderData in renderList3D) 650 RenderPreview(renderData, m_SceneResources.sphere, Matrix4x4.identity, perMaterialPreviewProperties); 651 652 if (renderMasterPreview) 653 { 654 if (m_NewMasterPreviewSize.HasValue) 655 { 656 if (masterRenderData.renderTexture != null) 657 Object.DestroyImmediate(masterRenderData.renderTexture, true); 658 masterRenderData.renderTexture = new RenderTexture((int)m_NewMasterPreviewSize.Value.x, (int)m_NewMasterPreviewSize.Value.y, 16, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default) { hideFlags = HideFlags.HideAndDontSave }; 659 masterRenderData.renderTexture.Create(); 660 masterRenderData.texture = masterRenderData.renderTexture; 661 m_NewMasterPreviewSize = null; 662 } 663 var mesh = m_Graph.previewData.serializedMesh.mesh; 664 var preventRotation = m_Graph.previewData.preventRotation; 665 if (!mesh) 666 { 667 var useSpritePreview = 668 m_Graph.activeTargets.LastOrDefault(t => t.IsActive())?.prefersSpritePreview ?? false; 669 mesh = useSpritePreview ? m_SceneResources.quad : m_SceneResources.sphere; 670 preventRotation = useSpritePreview; 671 } 672 673 var previewTransform = preventRotation ? Matrix4x4.identity : Matrix4x4.Rotate(m_Graph.previewData.rotation); 674 var scale = m_Graph.previewData.scale; 675 previewTransform *= Matrix4x4.Scale(scale * Vector3.one * (Vector3.one).magnitude / mesh.bounds.size.magnitude); 676 previewTransform *= Matrix4x4.Translate(-mesh.bounds.center); 677 678 //bugfix for some variables that need to be setup for URP Sprite material previews. Want a better isolated place to put them, 679 //but I dont believe such a place exists and would be too costly to add. 680 masterRenderData.shaderData.mat.SetVector(k_spriteProps, new Vector4(1, 1, -1, 0)); 681 masterRenderData.shaderData.mat.SetVector(k_spriteColor, new Vector4(1, 1, 1, 1)); 682 masterRenderData.shaderData.mat.SetVector(k_rendererColor, new Vector4(1, 1, 1, 1)); 683 RenderPreview(masterRenderData, mesh, previewTransform, perMaterialPreviewProperties); 684 } 685 686 m_SceneResources.light0.enabled = false; 687 m_SceneResources.light1.enabled = false; 688 689 foreach (var renderData in renderList2D) 690 renderData.NotifyPreviewChanged(); 691 foreach (var renderData in renderList3D) 692 renderData.NotifyPreviewChanged(); 693 if (renderMasterPreview) 694 masterRenderData.NotifyPreviewChanged(); 695 } 696 } 697 698 private static readonly ProfilerMarker ProcessCompletedShaderCompilationsMarker = new ProfilerMarker("ProcessCompletedShaderCompilations"); 699 private int compileFailRekicks = 0; 700 void ProcessCompletedShaderCompilations() 701 { 702 // Check for shaders that finished compiling and set them to redraw 703 using (ProcessCompletedShaderCompilationsMarker.Auto()) 704 using (var previewsCompiled = PooledHashSet<PreviewRenderData>.Get()) 705 { 706 foreach (var preview in m_PreviewsCompiling) 707 { 708 { 709 var node = preview.shaderData.node; 710 Assert.IsFalse(node is BlockNode); 711 } 712 713 PreviewRenderData renderData = preview; 714 PreviewShaderData shaderData = renderData.shaderData; 715 716 // Assert.IsTrue(shaderData.passesCompiling > 0); 717 if (shaderData.passesCompiling <= 0) 718 { 719 Debug.Log("Zero Passes: " + preview.previewName + " (" + shaderData.passesCompiling + " passes, " + renderData.shaderData.mat.passCount + " mat passes)"); 720 } 721 722 if (shaderData.passesCompiling != renderData.shaderData.mat.passCount) 723 { 724 // attempt to re-kick the compilation a few times 725 Debug.Log("Rekicking Compiling: " + preview.previewName + " (" + shaderData.passesCompiling + " passes, " + renderData.shaderData.mat.passCount + " mat passes)"); 726 compileFailRekicks++; 727 if (compileFailRekicks <= 3) 728 { 729 shaderData.passesCompiling = 0; 730 previewsCompiled.Add(renderData); 731 m_PreviewsNeedsRecompile.Add(renderData); 732 continue; 733 } 734 else if (compileFailRekicks == 4) 735 { 736 Debug.LogWarning("Unexpected error in compiling preview shaders: some previews might not update. You can try to re-open the Shader Graph window, or select <b>Help > Report a Bug</b> in the menu and report this bug."); 737 } 738 } 739 740 // check that all passes have compiled 741 var allPassesCompiled = true; 742 for (var i = 0; i < renderData.shaderData.mat.passCount; i++) 743 { 744 if (!ShaderUtil.IsPassCompiled(renderData.shaderData.mat, i)) 745 { 746 allPassesCompiled = false; 747 break; 748 } 749 } 750 751 if (!allPassesCompiled) 752 { 753 // keep waiting 754 continue; 755 } 756 757 // Force the material to re-generate all it's shader properties, by reassigning the shader 758 renderData.shaderData.mat.shader = renderData.shaderData.shader; 759 renderData.shaderData.passesCompiling = 0; 760 renderData.shaderData.isOutOfDate = false; 761 CheckForErrors(renderData.shaderData); 762 763 previewsCompiled.Add(renderData); 764 } 765 766 // removed compiled nodes from compiling list 767 m_PreviewsCompiling.ExceptWith(previewsCompiled); 768 769 // and add them to the draw list to display updated shader (note this will only redraw specifically this node, not any others) 770 m_PreviewsToDraw.UnionWith(previewsCompiled); 771 } 772 } 773 774 private static readonly ProfilerMarker KickOffShaderCompilationsMarker = new ProfilerMarker("KickOffShaderCompilations"); 775 void KickOffShaderCompilations() 776 { 777 // Start compilation for nodes that need to recompile 778 using (KickOffShaderCompilationsMarker.Auto()) 779 using (var previewsToCompile = PooledHashSet<PreviewRenderData>.Get()) 780 { 781 // master node compile is first in the priority list, as it takes longer than the other previews 782 if (m_PreviewsCompiling.Count + previewsToCompile.Count < m_MaxPreviewsCompiling) 783 { 784 if (m_PreviewsNeedsRecompile.Contains(m_MasterRenderData) && 785 !m_PreviewsCompiling.Contains(m_MasterRenderData)) 786 { 787 previewsToCompile.Add(m_MasterRenderData); 788 m_PreviewsNeedsRecompile.Remove(m_MasterRenderData); 789 } 790 } 791 792 // add each node to compile list if it needs a preview, is not already compiling, and we have room 793 // (we don't want to double kick compiles, so wait for the first one to get back before kicking another) 794 for (int i = 0; i < m_PreviewsNeedsRecompile.Count(); i++) 795 { 796 if (m_PreviewsCompiling.Count + previewsToCompile.Count >= m_MaxPreviewsCompiling) 797 break; 798 799 var preview = m_PreviewsNeedsRecompile.ElementAt(i); 800 if (preview == m_MasterRenderData) // master preview is handled specially above 801 continue; 802 803 var node = preview.shaderData.node; 804 Assert.IsNotNull(node); 805 Assert.IsFalse(node is BlockNode); 806 807 if (node.hasPreview && node.previewExpanded && !m_PreviewsCompiling.Contains(preview)) 808 { 809 previewsToCompile.Add(preview); 810 } 811 } 812 813 if (previewsToCompile.Count >= 0) 814 using (var nodesToCompile = PooledHashSet<AbstractMaterialNode>.Get()) 815 { 816 // remove the selected nodes from the recompile list 817 m_PreviewsNeedsRecompile.ExceptWith(previewsToCompile); 818 819 // Reset error states for the UI, the shader, and all render data for nodes we're recompiling 820 nodesToCompile.UnionWith(previewsToCompile.Select(x => x.shaderData.node)); 821 nodesToCompile.Remove(null); 822 823 // TODO: not sure if we need to clear BlockNodes when master gets rebuilt? 824 m_Messenger.ClearNodesFromProvider(this, nodesToCompile); 825 826 // Force async compile on 827 var wasAsyncAllowed = ShaderUtil.allowAsyncCompilation; 828 ShaderUtil.allowAsyncCompilation = true; 829 830 // kick async compiles for all nodes in m_NodeToCompile 831 foreach (var preview in previewsToCompile) 832 { 833 if (preview == m_MasterRenderData) 834 { 835 CompileMasterNodeShader(); 836 continue; 837 } 838 839 var node = preview.shaderData.node; 840 Assert.IsNotNull(node); // master preview is handled above 841 842 // Get shader code and compile 843 var generator = new Generator(node.owner, node, GenerationMode.Preview, $"hidden/preview/{node.GetVariableNameForNode()}"); 844 BeginCompile(preview, generator.generatedShader); 845 } 846 847 ShaderUtil.allowAsyncCompilation = wasAsyncAllowed; 848 } 849 } 850 } 851 852 private static readonly ProfilerMarker UpdateShadersMarker = new ProfilerMarker("UpdateShaders"); 853 void UpdateShaders() 854 { 855 using (UpdateShadersMarker.Auto()) 856 { 857 ProcessCompletedShaderCompilations(); 858 859 if (m_NodesShaderChanged.Count > 0) 860 { 861 // nodes with shader changes cause all downstream nodes to need recompilation 862 // (since they presumably include the code for these nodes) 863 using (var nodesToRecompile = PooledHashSet<AbstractMaterialNode>.Get()) 864 { 865 PropagateNodes(m_NodesShaderChanged, PropagationDirection.Downstream, nodesToRecompile); 866 ForEachNodesPreview(nodesToRecompile, p => m_PreviewsNeedsRecompile.Add(p)); 867 868 m_NodesShaderChanged.Clear(); 869 } 870 } 871 872 // if there's nothing to update, or if too many nodes are still compiling, then just return 873 if ((m_PreviewsNeedsRecompile.Count == 0) || (m_PreviewsCompiling.Count >= m_MaxPreviewsCompiling)) 874 return; 875 876 // flag all nodes in m_PreviewsNeedsRecompile as having out of date textures, and redraw them 877 foreach (var preview in m_PreviewsNeedsRecompile) 878 { 879 Assert.IsNotNull(preview); 880 if (!preview.shaderData.isOutOfDate) 881 { 882 preview.shaderData.isOutOfDate = true; 883 preview.NotifyPreviewChanged(); 884 } 885 } 886 887 InitializeSRPIfNeeded(); // SRP must be initialized to compile master node previews 888 889 KickOffShaderCompilations(); 890 } 891 } 892 893 private static readonly ProfilerMarker BeginCompileMarker = new ProfilerMarker("BeginCompile"); 894 void BeginCompile(PreviewRenderData renderData, string shaderStr) 895 { 896 using (BeginCompileMarker.Auto()) 897 { 898 var shaderData = renderData.shaderData; 899 900 // want to ensure this so we don't get confused with multiple compile versions in flight 901 Assert.IsTrue(shaderData.passesCompiling == 0); 902 903 if (shaderData.shader == null) 904 { 905 shaderData.shader = ShaderUtil.CreateShaderAsset(shaderStr, false); 906 shaderData.shader.hideFlags = HideFlags.HideAndDontSave; 907 } 908 else 909 { 910 ShaderUtil.ClearCachedData(shaderData.shader); 911 ShaderUtil.ClearShaderMessages(shaderData.shader); 912 ShaderUtil.UpdateShaderAsset(shaderData.shader, shaderStr, false); 913 } 914 915 // Set up the material we use for the preview 916 // Due to case 1259744, we have to re-create the material to update the preview material keywords 917 Object.DestroyImmediate(shaderData.mat); 918 { 919 shaderData.mat = new Material(shaderData.shader) { hideFlags = HideFlags.HideAndDontSave }; 920 if (renderData == m_MasterRenderData) 921 { 922 // apply active target settings to the Material 923 foreach (var target in m_Graph.activeTargets) 924 { 925 if (target.IsActive()) 926 target.ProcessPreviewMaterial(renderData.shaderData.mat); 927 } 928 } 929 } 930 931 int materialPassCount = shaderData.mat.passCount; 932 if (materialPassCount <= 0) 933 Debug.Log("Zero Passes ON COMPILE: " + shaderData.node.name + " (" + shaderData.passesCompiling + " passes, " + renderData.shaderData.mat.passCount + " mat passes)"); 934 else 935 { 936 shaderData.passesCompiling = materialPassCount; 937 for (var i = 0; i < materialPassCount; i++) 938 { 939 ShaderUtil.CompilePass(shaderData.mat, i); 940 } 941 m_PreviewsCompiling.Add(renderData); 942 } 943 } 944 } 945 946 private void ForEachNodesPreview( 947 IEnumerable<AbstractMaterialNode> nodes, 948 Action<PreviewRenderData> action) 949 { 950 foreach (var node in nodes) 951 { 952 var preview = GetPreviewRenderData(node); 953 if (preview != null) // some output nodes may have no preview 954 action(preview); 955 } 956 } 957 958 class NodeProcessor 959 { 960 // parameters 961 GraphData graphData; 962 Action<AbstractMaterialNode, IEnumerable<AbstractMaterialNode>> process; 963 964 // node tracking state 965 HashSet<AbstractMaterialNode> processing = new HashSet<AbstractMaterialNode>(); 966 HashSet<AbstractMaterialNode> processed = new HashSet<AbstractMaterialNode>(); 967 968 // iteration state stack 969 Stack<AbstractMaterialNode> nodeStack = new Stack<AbstractMaterialNode>(); 970 Stack<int> childStartStack = new Stack<int>(); 971 Stack<int> curChildStack = new Stack<int>(); 972 Stack<int> stateStack = new Stack<int>(); 973 974 List<AbstractMaterialNode> allChildren = new List<AbstractMaterialNode>(); 975 976 public NodeProcessor(GraphData graphData, Action<AbstractMaterialNode, IEnumerable<AbstractMaterialNode>> process) 977 { 978 this.graphData = graphData; 979 this.process = process; 980 } 981 982 public void ProcessInDependencyOrder(AbstractMaterialNode root) 983 { 984 // early out to skip a bit of work 985 if (processed.Contains(root)) 986 return; 987 988 // push root node in the initial state 989 stateStack.Push(0); 990 nodeStack.Push(root); 991 992 while (nodeStack.Count > 0) 993 { 994 // check the state of the top of the stack 995 switch (stateStack.Pop()) 996 { 997 case 0: // node initial state (valid stacks: nodeStack) 998 { 999 var node = nodeStack.Peek(); 1000 if (processed.Contains(node)) 1001 { 1002 // finished with this node, pop it off the stack 1003 nodeStack.Pop(); 1004 continue; 1005 } 1006 1007 if (processing.Contains(node)) 1008 { 1009 // not processed, but still processing.. means there was a circular dependency here 1010 throw new ArgumentException("ERROR: graph contains circular wire connections"); 1011 } 1012 1013 processing.Add(node); 1014 1015 int childStart = allChildren.Count; 1016 childStartStack.Push(childStart); 1017 1018 // add immediate children 1019 ForeachConnectedNode(node, PropagationDirection.Upstream, n => allChildren.Add(n)); 1020 1021 if (allChildren.Count == childStart) 1022 { 1023 // no children.. transition to state 2 (all children processed) 1024 stateStack.Push(2); 1025 } 1026 else 1027 { 1028 // transition to state 1 (processing children) 1029 stateStack.Push(1); 1030 curChildStack.Push(childStart); 1031 } 1032 } 1033 break; 1034 case 1: // processing children (valid stacks: nodeStack, childStartStack, curChildStack) 1035 { 1036 int curChild = curChildStack.Pop(); 1037 1038 // first update our state for when we return from the cur child 1039 int nextChild = curChild + 1; 1040 if (nextChild < allChildren.Count) 1041 { 1042 // we will process the next child 1043 stateStack.Push(1); 1044 curChildStack.Push(nextChild); 1045 } 1046 else 1047 { 1048 // we will be done iterating children, move to state 2 1049 stateStack.Push(2); 1050 } 1051 1052 // then push the current child in state 0 to process it 1053 stateStack.Push(0); 1054 nodeStack.Push(allChildren[curChild]); 1055 } 1056 break; 1057 case 2: // all children processed (valid stacks: nodeStack, childStartStack) 1058 { 1059 // read state, popping all 1060 var node = nodeStack.Pop(); 1061 int childStart = childStartStack.Pop(); 1062 1063 // process node 1064 process(node, allChildren.Slice(childStart, allChildren.Count)); 1065 processed.Add(node); 1066 1067 // remove the children that were added in state 0 1068 allChildren.RemoveRange(childStart, allChildren.Count - childStart); 1069 1070 // terminate node, stacks are popped to state of parent node 1071 } 1072 break; 1073 } 1074 } 1075 } 1076 1077 public void ProcessInDependencyOrderRecursive(AbstractMaterialNode node) 1078 { 1079 if (processed.Contains(node)) 1080 return; // already processed 1081 1082 if (processing.Contains(node)) 1083 throw new ArgumentException("ERROR: graph contains circular wire connections"); 1084 1085 processing.Add(node); 1086 1087 int childStart = allChildren.Count; 1088 1089 // add immediate children 1090 ForeachConnectedNode(node, PropagationDirection.Upstream, n => allChildren.Add(n)); 1091 1092 // process children 1093 var children = allChildren.Slice(childStart, allChildren.Count); 1094 foreach (var child in children) 1095 ProcessInDependencyOrderRecursive(child); 1096 1097 // process self 1098 process(node, children); 1099 processed.Add(node); 1100 1101 // remove the children 1102 allChildren.RemoveRange(childStart, allChildren.Count - childStart); 1103 } 1104 } 1105 1106 // Processes all the nodes in the upstream trees of rootNodes 1107 // Will only process each node once, even if the trees overlap 1108 // Processes a node ONLY after processing all of the nodes in its upstream subtree 1109 void ProcessUpstreamNodesInDependencyOrder( 1110 IEnumerable<AbstractMaterialNode> rootNodes, // root nodes can share subtrees, but cannot themselves exist in any others subtree 1111 Action<AbstractMaterialNode, IEnumerable<AbstractMaterialNode>> process) // process takes the node and it's list of immediate upstream children as parameters 1112 { 1113 if (rootNodes.Any()) 1114 { 1115 NodeProcessor processor = new NodeProcessor(rootNodes.First().owner, process); 1116 foreach (var node in rootNodes) 1117 processor.ProcessInDependencyOrderRecursive(node); 1118 } 1119 } 1120 1121 private static readonly ProfilerMarker UpdateTopologyMarker = new ProfilerMarker("UpdateTopology"); 1122 void UpdateTopology() 1123 { 1124 if (!m_TopologyDirty) 1125 return; 1126 1127 using (UpdateTopologyMarker.Auto()) 1128 using (var timedNodes = PooledHashSet<AbstractMaterialNode>.Get()) 1129 { 1130 timedNodes.UnionWith(m_Graph.GetNodes<AbstractMaterialNode>().Where(n => n.RequiresTime())); 1131 1132 // we pre-propagate timed nodes downstream, to reduce amount of propagation we have to do per frame 1133 PropagateNodes(timedNodes, PropagationDirection.Downstream, timedNodes); 1134 1135 m_TimedPreviews.Clear(); 1136 ForEachNodesPreview(timedNodes, p => m_TimedPreviews.Add(p)); 1137 } 1138 1139 // Calculate the PreviewMode from upstream nodes 1140 ProcessUpstreamNodesInDependencyOrder( 1141 // we just pass all the nodes we care about as the roots 1142 m_RenderDatas.Values.Select(p => p.shaderData.node).Where(n => n != null), 1143 (node, children) => 1144 { 1145 var preview = GetPreviewRenderData(node); 1146 1147 // set preview mode based on node preference 1148 preview.previewMode = node.previewMode; 1149 1150 // Inherit becomes 2D or 3D based on child state 1151 if (preview.previewMode == PreviewMode.Inherit) 1152 { 1153 if (children.Any(child => GetPreviewRenderData(child).previewMode == PreviewMode.Preview3D)) 1154 preview.previewMode = PreviewMode.Preview3D; 1155 else 1156 preview.previewMode = PreviewMode.Preview2D; 1157 } 1158 }); 1159 1160 m_TopologyDirty = false; 1161 } 1162 1163 private static readonly ProfilerMarker RenderPreviewMarker = new ProfilerMarker("RenderPreview"); 1164 void RenderPreview(PreviewRenderData renderData, Mesh mesh, Matrix4x4 transform, PooledList<PreviewProperty> perMaterialPreviewProperties) 1165 { 1166 using (RenderPreviewMarker.Auto()) 1167 { 1168 var wasAsyncAllowed = ShaderUtil.allowAsyncCompilation; 1169 ShaderUtil.allowAsyncCompilation = true; 1170 1171 AssignPerMaterialPreviewProperties(renderData.shaderData.mat, perMaterialPreviewProperties); 1172 1173 var previousRenderTexture = RenderTexture.active; 1174 1175 //Temp workaround for alpha previews... 1176 var temp = RenderTexture.GetTemporary(renderData.renderTexture.descriptor); 1177 RenderTexture.active = temp; 1178 Graphics.Blit(Texture2D.whiteTexture, temp, m_SceneResources.checkerboardMaterial); 1179 1180 // Mesh is invalid for VFXTarget 1181 // We should handle this more gracefully 1182 if (renderData != m_MasterRenderData || !m_Graph.isOnlyVFXTarget) 1183 { 1184 m_SceneResources.camera.targetTexture = temp; 1185 Graphics.DrawMesh(mesh, transform, renderData.shaderData.mat, 1, m_SceneResources.camera, 0, m_SharedPreviewPropertyBlock, ShadowCastingMode.Off, false, null, false); 1186 } 1187 1188 var previousUseSRP = Unsupported.useScriptableRenderPipeline; 1189 Unsupported.useScriptableRenderPipeline = (renderData == m_MasterRenderData); 1190 m_SceneResources.camera.Render(); 1191 Unsupported.useScriptableRenderPipeline = previousUseSRP; 1192 1193 Graphics.Blit(temp, renderData.renderTexture, m_SceneResources.blitNoAlphaMaterial); 1194 RenderTexture.ReleaseTemporary(temp); 1195 1196 RenderTexture.active = previousRenderTexture; 1197 renderData.texture = renderData.renderTexture; 1198 1199 m_PreviewsToDraw.Remove(renderData); 1200 1201 ShaderUtil.allowAsyncCompilation = wasAsyncAllowed; 1202 } 1203 } 1204 1205 void InitializeSRPIfNeeded() 1206 { 1207 if ((Shader.globalRenderPipeline != null) && (Shader.globalRenderPipeline.Length > 0)) 1208 { 1209 return; 1210 } 1211 1212 // issue a dummy SRP render to force SRP initialization, use the master node texture 1213 PreviewRenderData renderData = m_MasterRenderData; 1214 var previousRenderTexture = RenderTexture.active; 1215 1216 //Temp workaround for alpha previews... 1217 var temp = RenderTexture.GetTemporary(renderData.renderTexture.descriptor); 1218 RenderTexture.active = temp; 1219 Graphics.Blit(Texture2D.whiteTexture, temp, m_SceneResources.checkerboardMaterial); 1220 1221 m_SceneResources.camera.targetTexture = temp; 1222 1223 var previousUseSRP = Unsupported.useScriptableRenderPipeline; 1224 Unsupported.useScriptableRenderPipeline = true; 1225 m_SceneResources.camera.Render(); 1226 Unsupported.useScriptableRenderPipeline = previousUseSRP; 1227 1228 RenderTexture.ReleaseTemporary(temp); 1229 1230 RenderTexture.active = previousRenderTexture; 1231 } 1232 1233 void CheckForErrors(PreviewShaderData shaderData) 1234 { 1235 shaderData.hasError = ShaderUtil.ShaderHasError(shaderData.shader); 1236 if (shaderData.hasError) 1237 { 1238 var messages = ShaderUtil.GetShaderMessages(shaderData.shader); 1239 if (messages.Length > 0) 1240 { 1241 // TODO: Where to add errors to the stack?? 1242 if (shaderData.node == null) 1243 return; 1244 1245 m_Messenger.AddOrAppendError(this, shaderData.node.objectId, messages[0]); 1246 ShaderUtil.ClearShaderMessages(shaderData.shader); 1247 } 1248 } 1249 } 1250 1251 void CompileMasterNodeShader() 1252 { 1253 var shaderData = masterRenderData?.shaderData; 1254 1255 // Skip generation for VFXTarget 1256 if (!m_Graph.isOnlyVFXTarget) 1257 { 1258 var generator = new Generator(m_Graph, m_Graph.outputNode, GenerationMode.Preview, "Master"); 1259 shaderData.shaderString = generator.generatedShader; 1260 1261 // record the blocks temporarily created for missing stack blocks 1262 m_MasterNodeTempBlocks.Clear(); 1263 foreach (var block in generator.temporaryBlocks) 1264 { 1265 m_MasterNodeTempBlocks.Add(block); 1266 } 1267 } 1268 1269 if (string.IsNullOrEmpty(shaderData.shaderString)) 1270 { 1271 if (shaderData.shader != null) 1272 { 1273 ShaderUtil.ClearShaderMessages(shaderData.shader); 1274 Object.DestroyImmediate(shaderData.shader, true); 1275 shaderData.shader = null; 1276 } 1277 return; 1278 } 1279 1280 BeginCompile(masterRenderData, shaderData.shaderString); 1281 } 1282 1283 void DestroyRenderData(PreviewRenderData renderData) 1284 { 1285 if (renderData.shaderData != null) 1286 { 1287 if (renderData.shaderData.mat != null) 1288 { 1289 Object.DestroyImmediate(renderData.shaderData.mat, true); 1290 } 1291 if (renderData.shaderData.shader != null) 1292 { 1293 ShaderUtil.ClearShaderMessages(renderData.shaderData.shader); 1294 Object.DestroyImmediate(renderData.shaderData.shader, true); 1295 } 1296 } 1297 1298 // Clear render textures 1299 if (renderData.renderTexture != null) 1300 Object.DestroyImmediate(renderData.renderTexture, true); 1301 if(renderData.texture != null) 1302 Object.DestroyImmediate(renderData.texture, true); 1303 1304 // Clear callbacks 1305 renderData.onPreviewChanged = null; 1306 if (renderData.shaderData != null && renderData.shaderData.node != null) 1307 renderData.shaderData.node.UnregisterCallback(OnNodeModified); 1308 } 1309 1310 void DestroyPreview(AbstractMaterialNode node) 1311 { 1312 if (node is BlockNode) 1313 { 1314 // block nodes don't have preview render data 1315 Assert.IsFalse(m_RenderDatas.ContainsKey(node)); 1316 node.UnregisterCallback(OnNodeModified); 1317 UpdateMasterPreview(ModificationScope.Topological); 1318 return; 1319 } 1320 1321 if (!m_RenderDatas.TryGetValue(node, out var renderData)) 1322 { 1323 return; 1324 } 1325 1326 m_PreviewsNeedsRecompile.Remove(renderData); 1327 m_PreviewsCompiling.Remove(renderData); 1328 m_PreviewsToDraw.Remove(renderData); 1329 m_TimedPreviews.Remove(renderData); 1330 1331 DestroyRenderData(renderData); 1332 m_RenderDatas.Remove(node); 1333 } 1334 1335 void ReleaseUnmanagedResources() 1336 { 1337 if (m_ErrorTexture != null) 1338 { 1339 Object.DestroyImmediate(m_ErrorTexture); 1340 m_ErrorTexture = null; 1341 } 1342 if (m_SceneResources != null) 1343 { 1344 m_SceneResources.Dispose(); 1345 m_SceneResources = null; 1346 } 1347 foreach (var renderData in m_RenderDatas.Values) 1348 DestroyRenderData(renderData); 1349 m_RenderDatas.Clear(); 1350 m_SharedPreviewPropertyBlock.Clear(); 1351 } 1352 1353 public void Dispose() 1354 { 1355 ReleaseUnmanagedResources(); 1356 GC.SuppressFinalize(this); 1357 } 1358 1359 ~PreviewManager() 1360 { 1361 throw new Exception("PreviewManager was not disposed of properly."); 1362 } 1363 } 1364 1365 delegate void OnPreviewChanged(); 1366 1367 class PreviewShaderData 1368 { 1369 public AbstractMaterialNode node; 1370 public Shader shader; 1371 public Material mat; 1372 public string shaderString; 1373 public int passesCompiling; 1374 public bool isOutOfDate; 1375 public bool hasError; 1376 } 1377 1378 class PreviewRenderData 1379 { 1380 public string previewName; 1381 public PreviewShaderData shaderData; 1382 public RenderTexture renderTexture; 1383 public Texture texture; 1384 public PreviewMode previewMode; 1385 public OnPreviewChanged onPreviewChanged; 1386 1387 public void NotifyPreviewChanged() 1388 { 1389 if (onPreviewChanged != null) 1390 onPreviewChanged(); 1391 } 1392 } 1393}