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}