Skip to content

Commit 26a1096

Browse files
author
Chris Tchou
authored
[2020.2] [10.x.x] [Bugfix 1296776] Enforce SRP Batcher and Hybrid renderer compatibility by always using a consolidated property list (#3577)
* Make all passes in a subshader use the same HLSL proeprty declarations * Adding changelog * Better fix for Targets accessing the current blocks
1 parent 66924d5 commit 26a1096

File tree

7 files changed

+218
-40
lines changed

7 files changed

+218
-40
lines changed

com.unity.shadergraph/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this package are documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
55
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
66

7+
## [10.5.0] - 2021-02-20
8+
9+
### Fixed
10+
- Fixed an issue where the exposed checkbox was removed from keyword inspectors [1312779] (https://issuetracker.unity3d.com/issues/shader-graph-exposed-parameter-for-keywords-removed-by-accident)
11+
- Fixed issue with SRP Batcher compatibility [1310624]
12+
- Fixed issue with Hybrid renderer compatibility [1296776]
713

814
## [10.4.0] - 2021-01-27
915

com.unity.shadergraph/Editor/Data/Implementation/NodeUtils.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ public static SlotReference DepthFirstCollectRedirectNodeFromNode(RedirectNodeDa
101101
}
102102

103103
public static void DepthFirstCollectNodesFromNode(List<AbstractMaterialNode> nodeList, AbstractMaterialNode node,
104-
IncludeSelf includeSelf = IncludeSelf.Include, List<KeyValuePair<ShaderKeyword, int>> keywordPermutation = null)
104+
IncludeSelf includeSelf = IncludeSelf.Include, List<KeyValuePair<ShaderKeyword, int>> keywordPermutation = null, bool ignoreActiveState = false)
105105
{
106106
// no where to start
107107
if (node == null)
@@ -131,11 +131,11 @@ public static void DepthFirstCollectNodesFromNode(List<AbstractMaterialNode> nod
131131
{
132132
var outputNode = edge.outputSlot.node;
133133
if (outputNode != null)
134-
DepthFirstCollectNodesFromNode(nodeList, outputNode, keywordPermutation: keywordPermutation);
134+
DepthFirstCollectNodesFromNode(nodeList, outputNode, keywordPermutation: keywordPermutation, ignoreActiveState: ignoreActiveState);
135135
}
136136
}
137137

138-
if (includeSelf == IncludeSelf.Include && node.isActive)
138+
if (includeSelf == IncludeSelf.Include && (node.isActive || ignoreActiveState))
139139
nodeList.Add(node);
140140
}
141141

com.unity.shadergraph/Editor/Drawing/PreviewManager.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ void OnNodeModified(AbstractMaterialNode node, ModificationScope scope)
217217
static HashSet<AbstractMaterialNode> m_TempAddedToNodeWave = new HashSet<AbstractMaterialNode>();
218218

219219
// cache the Action to avoid GC
220-
Action<AbstractMaterialNode> AddNextLevelNodesToWave =
220+
static Action<AbstractMaterialNode> AddNextLevelNodesToWave =
221221
nextLevelNode =>
222222
{
223223
if (!m_TempAddedToNodeWave.Contains(nextLevelNode))
@@ -227,7 +227,7 @@ void OnNodeModified(AbstractMaterialNode node, ModificationScope scope)
227227
}
228228
};
229229

230-
enum PropagationDirection
230+
internal enum PropagationDirection
231231
{
232232
Upstream,
233233
Downstream
@@ -236,7 +236,7 @@ enum PropagationDirection
236236
// ADDs all nodes in sources, and all nodes in the given direction relative to them, into result
237237
// sources and result can be the same HashSet
238238
private static readonly ProfilerMarker PropagateNodesMarker = new ProfilerMarker("PropagateNodes");
239-
void PropagateNodes(HashSet<AbstractMaterialNode> sources, PropagationDirection dir, HashSet<AbstractMaterialNode> result)
239+
internal static void PropagateNodes(HashSet<AbstractMaterialNode> sources, PropagationDirection dir, HashSet<AbstractMaterialNode> result)
240240
{
241241
using (PropagateNodesMarker.Auto())
242242
if (sources.Count > 0)

com.unity.shadergraph/Editor/Generation/Processors/Generator.cs

Lines changed: 76 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ public ActiveFields GatherActiveFieldsFromNode(AbstractMaterialNode outputNode,
8787
void BuildShader()
8888
{
8989
var activeNodeList = Graphing.ListPool<AbstractMaterialNode>.Get();
90-
if(m_OutputNode == null)
90+
bool ignoreActiveState = (m_Mode == GenerationMode.Preview); // for previews, we ignore node active state
91+
if (m_OutputNode == null)
9192
{
9293
foreach(var block in m_Blocks)
9394
{
@@ -96,12 +97,12 @@ void BuildShader()
9697
if(!block.isActive)
9798
continue;
9899

99-
NodeUtils.DepthFirstCollectNodesFromNode(activeNodeList, block, NodeUtils.IncludeSelf.Include);
100+
NodeUtils.DepthFirstCollectNodesFromNode(activeNodeList, block, NodeUtils.IncludeSelf.Include, ignoreActiveState: ignoreActiveState);
100101
}
101102
}
102103
else
103104
{
104-
NodeUtils.DepthFirstCollectNodesFromNode(activeNodeList, m_OutputNode);
105+
NodeUtils.DepthFirstCollectNodesFromNode(activeNodeList, m_OutputNode, ignoreActiveState: ignoreActiveState);
105106
}
106107

107108
var shaderProperties = new PropertyCollector();
@@ -131,6 +132,10 @@ void BuildShader()
131132
target.CollectShaderProperties(shaderProperties, m_Mode);
132133
}
133134

135+
// set the property collector to read only
136+
// (to ensure no rogue target or pass starts adding more properties later..)
137+
shaderProperties.SetReadOnly();
138+
134139
m_Builder.AppendLine(@"Shader ""{0}""", m_Name);
135140
using (m_Builder.BlockScope())
136141
{
@@ -143,9 +148,11 @@ void BuildShader()
143148
// Instead of setup target, we can also just do get context
144149
m_Targets[i].Setup(ref context);
145150

146-
foreach(var subShader in context.subShaders)
151+
var subShaderProperties = GetSubShaderPropertiesForTarget(m_Targets[i], m_GraphData, m_Mode, m_OutputNode);
152+
153+
foreach (var subShader in context.subShaders)
147154
{
148-
GenerateSubShader(i, subShader);
155+
GenerateSubShader(i, subShader, subShaderProperties);
149156
}
150157

151158
var customEditor = context.defaultShaderGUI;
@@ -161,7 +168,7 @@ void BuildShader()
161168
m_ConfiguredTextures = shaderProperties.GetConfiguredTexutres();
162169
}
163170

164-
void GenerateSubShader(int targetIndex, SubShaderDescriptor descriptor)
171+
void GenerateSubShader(int targetIndex, SubShaderDescriptor descriptor, PropertyCollector subShaderProperties)
165172
{
166173
if(descriptor.passes == null)
167174
return;
@@ -189,12 +196,70 @@ void GenerateSubShader(int targetIndex, SubShaderDescriptor descriptor)
189196

190197
// Check masternode fields for valid passes
191198
if(pass.TestActive(activeFields))
192-
GenerateShaderPass(targetIndex, pass.descriptor, activeFields, currentBlockDescriptors.Select(x => x.descriptor).ToList());
199+
GenerateShaderPass(targetIndex, pass.descriptor, activeFields, currentBlockDescriptors.Select(x => x.descriptor).ToList(), subShaderProperties);
193200
}
194201
}
195202
}
196203

197-
void GenerateShaderPass(int targetIndex, PassDescriptor pass, ActiveFields activeFields, List<BlockFieldDescriptor> currentBlockDescriptors)
204+
// this builds the list of properties for a Target / Graph combination
205+
static PropertyCollector GetSubShaderPropertiesForTarget(Target target, GraphData graph, GenerationMode generationMode, AbstractMaterialNode outputNode)
206+
{
207+
PropertyCollector subshaderProperties = new PropertyCollector();
208+
209+
// Collect shader properties declared by active nodes
210+
using (var activeNodes = PooledHashSet<AbstractMaterialNode>.Get())
211+
{
212+
if (outputNode == null)
213+
{
214+
// shader graph builds active nodes starting from the set of active blocks
215+
var currentBlocks = graph.GetNodes<BlockNode>();
216+
var activeBlockContext = new TargetActiveBlockContext(currentBlocks.Select(x => x.descriptor).ToList(), null);
217+
target.GetActiveBlocks(ref activeBlockContext);
218+
219+
foreach (var blockFieldDesc in activeBlockContext.activeBlocks)
220+
{
221+
// attempt to get BlockNode(s) from the stack
222+
var vertBlockNode = graph.vertexContext.blocks.FirstOrDefault(x => x.value.descriptor == blockFieldDesc).value;
223+
if (vertBlockNode != null)
224+
activeNodes.Add(vertBlockNode);
225+
226+
var fragBlockNode = graph.fragmentContext.blocks.FirstOrDefault(x => x.value.descriptor == blockFieldDesc).value;
227+
if (fragBlockNode != null)
228+
activeNodes.Add(fragBlockNode);
229+
}
230+
}
231+
else
232+
{
233+
// preview and/or subgraphs build their active node set based on the single output node
234+
activeNodes.Add(outputNode);
235+
}
236+
237+
PreviewManager.PropagateNodes(activeNodes, PreviewManager.PropagationDirection.Upstream, activeNodes);
238+
239+
// NOTE: this is NOT a deterministic ordering
240+
foreach (var node in activeNodes)
241+
node.CollectShaderProperties(subshaderProperties, generationMode);
242+
243+
// So we sort the properties after
244+
subshaderProperties.Sort();
245+
}
246+
247+
// Collect graph properties
248+
{
249+
graph.CollectShaderProperties(subshaderProperties, generationMode);
250+
}
251+
252+
// Collect shader properties declared by the Target
253+
{
254+
target.CollectShaderProperties(subshaderProperties, generationMode);
255+
}
256+
257+
subshaderProperties.SetReadOnly();
258+
259+
return subshaderProperties;
260+
}
261+
262+
void GenerateShaderPass(int targetIndex, PassDescriptor pass, ActiveFields activeFields, List<BlockFieldDescriptor> currentBlockDescriptors, PropertyCollector subShaderProperties)
198263
{
199264
// Early exit if pass is not used in preview
200265
if(m_Mode == GenerationMode.Preview && !pass.useInPreview)
@@ -647,7 +712,7 @@ void ProcessStackForPass(ContextData contextData, BlockFieldDescriptor[] passBlo
647712

648713
using (var propertyBuilder = new ShaderStringBuilder())
649714
{
650-
propertyCollector.GetPropertiesDeclaration(propertyBuilder, m_Mode, m_GraphData.concretePrecision);
715+
subShaderProperties.GetPropertiesDeclaration(propertyBuilder, m_Mode, m_GraphData.concretePrecision);
651716
if(propertyBuilder.length == 0)
652717
propertyBuilder.AppendLine("// GraphProperties: <None>");
653718
spliceCommands.Add("GraphProperties", propertyBuilder.ToCodeBlock());
@@ -656,17 +721,12 @@ void ProcessStackForPass(ContextData contextData, BlockFieldDescriptor[] passBlo
656721
// --------------------------------------------------
657722
// Dots Instanced Graph Properties
658723

659-
bool hasDotsProperties = false;
660-
m_GraphData.ForeachHLSLProperty(h =>
661-
{
662-
if (h.declaration == HLSLDeclaration.HybridPerInstance)
663-
hasDotsProperties = true;
664-
});
724+
bool hasDotsProperties = subShaderProperties.HasDotsProperties();
665725

666726
using (var dotsInstancedPropertyBuilder = new ShaderStringBuilder())
667727
{
668728
if (hasDotsProperties)
669-
dotsInstancedPropertyBuilder.AppendLines(propertyCollector.GetDotsInstancingPropertiesDeclaration(m_Mode));
729+
dotsInstancedPropertyBuilder.AppendLines(subShaderProperties.GetDotsInstancingPropertiesDeclaration(m_Mode));
670730
else
671731
dotsInstancedPropertyBuilder.AppendLine("// HybridV1InjectedBuiltinProperties: <None>");
672732
spliceCommands.Add("HybridV1InjectedBuiltinProperties", dotsInstancedPropertyBuilder.ToCodeBlock());

com.unity.shadergraph/Editor/Generation/Processors/PropertyCollector.cs

Lines changed: 124 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
using System;
12
using System.Collections.Generic;
23
using System.Linq;
34
using System.Text;
45
using UnityEditor.ShaderGraph.Internal;
6+
using UnityEngine;
57

68
namespace UnityEditor.ShaderGraph
79
{
@@ -14,13 +16,117 @@ public struct TextureInfo
1416
public bool modifiable;
1517
}
1618

17-
public readonly List<AbstractShaderProperty> properties = new List<AbstractShaderProperty>();
19+
bool m_ReadOnly;
20+
List<HLSLProperty> m_HLSLProperties = null;
1821

19-
public void AddShaderProperty(AbstractShaderProperty chunk)
22+
// reference name ==> property index in list
23+
Dictionary<string, int> m_ReferenceNames = new Dictionary<string, int>();
24+
25+
// list of properties (kept in a list to maintain deterministic declaration order)
26+
List<AbstractShaderProperty> m_Properties = new List<AbstractShaderProperty>();
27+
28+
public int propertyCount => m_Properties.Count;
29+
public IEnumerable<AbstractShaderProperty> properties => m_Properties;
30+
public AbstractShaderProperty GetProperty(int index) { return m_Properties[index]; }
31+
32+
public void Sort()
33+
{
34+
if (m_ReadOnly)
35+
{
36+
Debug.LogError("Cannot sort the properties when the PropertyCollector is already marked ReadOnly");
37+
return;
38+
}
39+
40+
m_Properties.Sort((a, b) => String.CompareOrdinal(a.referenceName, b.referenceName));
41+
}
42+
43+
public void SetReadOnly()
44+
{
45+
m_ReadOnly = true;
46+
}
47+
48+
private static bool EquivalentHLSLProperties(AbstractShaderProperty a, AbstractShaderProperty b)
49+
{
50+
bool equivalent = true;
51+
var bHLSLProps = new List<HLSLProperty>();
52+
b.ForeachHLSLProperty(bh => bHLSLProps.Add(bh));
53+
a.ForeachHLSLProperty(ah =>
54+
{
55+
var i = bHLSLProps.FindIndex(bh => bh.name == ah.name);
56+
if (i < 0)
57+
equivalent = false;
58+
else
59+
{
60+
var bh = bHLSLProps[i];
61+
if ((ah.name != bh.name) ||
62+
(ah.type != bh.type) ||
63+
(ah.precision != bh.precision) ||
64+
(ah.declaration != bh.declaration) ||
65+
((ah.customDeclaration == null) != (bh.customDeclaration == null)))
66+
{
67+
equivalent = false;
68+
}
69+
else if (ah.customDeclaration != null)
70+
{
71+
var ssba = new ShaderStringBuilder();
72+
var ssbb = new ShaderStringBuilder();
73+
ah.customDeclaration(ssba);
74+
bh.customDeclaration(ssbb);
75+
if (ssba.ToCodeBlock() != ssbb.ToCodeBlock())
76+
equivalent = false;
77+
}
78+
bHLSLProps.RemoveAt(i);
79+
}
80+
});
81+
return equivalent && (bHLSLProps.Count == 0);
82+
}
83+
84+
public void AddShaderProperty(AbstractShaderProperty prop)
2085
{
21-
if (properties.Any(x => x.referenceName == chunk.referenceName))
86+
if (m_ReadOnly)
87+
{
88+
Debug.LogError("ERROR attempting to add property to readonly collection");
2289
return;
23-
properties.Add(chunk);
90+
}
91+
92+
int propIndex = -1;
93+
if (m_ReferenceNames.TryGetValue(prop.referenceName, out propIndex))
94+
{
95+
// existing referenceName
96+
var existingProp = m_Properties[propIndex];
97+
if (existingProp != prop)
98+
{
99+
// duplicate reference name, but different property instances
100+
if (existingProp.GetType() != prop.GetType())
101+
{
102+
Debug.LogError("Two properties with the same reference name (" + prop.referenceName + ") using different types");
103+
}
104+
else
105+
{
106+
if (!EquivalentHLSLProperties(existingProp, prop))
107+
Debug.LogError("Two properties with the same reference name (" + prop.referenceName + ") produce different HLSL properties");
108+
}
109+
}
110+
}
111+
else
112+
{
113+
// new referenceName, new property
114+
propIndex = m_Properties.Count;
115+
m_Properties.Add(prop);
116+
m_ReferenceNames.Add(prop.referenceName, propIndex);
117+
}
118+
}
119+
120+
private List<HLSLProperty> BuildHLSLPropertyList()
121+
{
122+
SetReadOnly();
123+
if (m_HLSLProperties == null)
124+
{
125+
m_HLSLProperties = new List<HLSLProperty>();
126+
foreach (var p in m_Properties)
127+
p.ForeachHLSLProperty(h => m_HLSLProperties.Add(h));
128+
}
129+
return m_HLSLProperties;
24130
}
25131

26132
public void GetPropertiesDeclaration(ShaderStringBuilder builder, GenerationMode mode, ConcretePrecision inheritedPrecision)
@@ -31,8 +137,7 @@ public void GetPropertiesDeclaration(ShaderStringBuilder builder, GenerationMode
31137
}
32138

33139
// build a list of all HLSL properties
34-
var hlslProps = new List<HLSLProperty>();
35-
properties.ForEach(p => p.ForeachHLSLProperty(h => hlslProps.Add(h)));
140+
var hlslProps = BuildHLSLPropertyList();
36141

37142
if (mode == GenerationMode.Preview)
38143
{
@@ -150,6 +255,18 @@ public void GetPropertiesDeclaration(ShaderStringBuilder builder, GenerationMode
150255
h.AppendTo(builder);
151256
}
152257

258+
public bool HasDotsProperties()
259+
{
260+
var hlslProps = BuildHLSLPropertyList();
261+
bool hasDotsProperties = false;
262+
foreach (var h in hlslProps)
263+
{
264+
if (h.declaration == HLSLDeclaration.HybridPerInstance)
265+
hasDotsProperties = true;
266+
}
267+
return hasDotsProperties;
268+
}
269+
153270
public string GetDotsInstancingPropertiesDeclaration(GenerationMode mode)
154271
{
155272
// Hybrid V1 needs to declare a special macro to that is injected into
@@ -160,12 +277,7 @@ public string GetDotsInstancingPropertiesDeclaration(GenerationMode mode)
160277
var batchAll = (mode == GenerationMode.Preview);
161278

162279
// build a list of all HLSL properties
163-
var hybridHLSLProps = new List<HLSLProperty>();
164-
properties.ForEach(p => p.ForeachHLSLProperty(h =>
165-
{
166-
if (h.declaration == HLSLDeclaration.HybridPerInstance)
167-
hybridHLSLProps.Add(h);
168-
}));
280+
var hybridHLSLProps = BuildHLSLPropertyList().Where(h => h.declaration == HLSLDeclaration.HybridPerInstance);
169281

170282
if (hybridHLSLProps.Any())
171283
{

0 commit comments

Comments
 (0)