A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using UnityEditor.Graphing;
5using UnityEditor.Graphing.Util;
6using UnityEditor.Rendering;
7using UnityEditor.ShaderGraph.Drawing;
8using UnityEditor.ShaderGraph.Internal;
9using UnityEngine;
10using UnityEngine.Rendering;
11using UnityEngine.UIElements;
12
13namespace UnityEditor.ShaderGraph
14{
15 // TODO: rename this file to VirtualTexturingFeedbackUtils
16 static class VirtualTexturingFeedbackUtils
17 {
18 // TODO: could get rid of this if we could run a codegen prepass (with proper keyword #ifdef)
19 public static void GenerateVirtualTextureFeedback(
20 List<AbstractMaterialNode> downstreamNodesIncludingRoot,
21 List<int>[] keywordPermutationsPerNode,
22 ShaderStringBuilder surfaceDescriptionFunction,
23 KeywordCollector shaderKeywords)
24 {
25 // A note on how we handle vt feedback in combination with keywords:
26 // We essentially generate a fully separate feedback path for each permutation of keywords
27 // so per permutation we gather variables contribution to feedback and we generate
28 // feedback gathering for each permutation individually.
29
30 var feedbackVariablesPerPermutation = PooledList<PooledList<string>>.Get();
31 try
32 {
33 if (shaderKeywords.permutations.Count >= 1)
34 {
35 for (int i = 0; i < shaderKeywords.permutations.Count; i++)
36 {
37 feedbackVariablesPerPermutation.Add(PooledList<string>.Get());
38 }
39 }
40 else
41 {
42 // Create a dummy single permutation
43 feedbackVariablesPerPermutation.Add(PooledList<string>.Get());
44 }
45
46 int index = 0; //for keywordPermutationsPerNode
47 foreach (var node in downstreamNodesIncludingRoot)
48 {
49 if (node is SampleVirtualTextureNode vtNode)
50 {
51 if (vtNode.noFeedback) continue;
52 if (keywordPermutationsPerNode[index] == null)
53 {
54 Debug.Assert(shaderKeywords.permutations.Count == 0, $"Shader has {shaderKeywords.permutations.Count} permutations but keywordPermutationsPerNode of some nodes are null.");
55 feedbackVariablesPerPermutation[0].Add(vtNode.GetFeedbackVariableName());
56 }
57 else
58 {
59 foreach (int perm in keywordPermutationsPerNode[index])
60 {
61 feedbackVariablesPerPermutation[perm].Add(vtNode.GetFeedbackVariableName());
62 }
63 }
64 }
65
66 if (node is SubGraphNode sgNode)
67 {
68 if (sgNode.asset == null) continue;
69 if (keywordPermutationsPerNode[index] == null)
70 {
71 Debug.Assert(shaderKeywords.permutations.Count == 0, $"Shader has {shaderKeywords.permutations.Count} permutations but keywordPermutationsPerNode of some nodes are null.");
72 foreach (var feedbackSlot in sgNode.asset.vtFeedbackVariables)
73 {
74 feedbackVariablesPerPermutation[0].Add(node.GetVariableNameForNode() + "_" + feedbackSlot);
75 }
76 }
77 else
78 {
79 foreach (var feedbackSlot in sgNode.asset.vtFeedbackVariables)
80 {
81 foreach (int perm in keywordPermutationsPerNode[index])
82 {
83 feedbackVariablesPerPermutation[perm].Add(node.GetVariableNameForNode() + "_" + feedbackSlot);
84 }
85 }
86 }
87 }
88
89 index++;
90 }
91
92 index = 0;
93 foreach (var feedbackVariables in feedbackVariablesPerPermutation)
94 {
95 // If it's a dummy single always-on permutation don't put an ifdef around the code
96 if (shaderKeywords.permutations.Count >= 1)
97 {
98 surfaceDescriptionFunction.AppendLine(KeywordUtil.GetKeywordPermutationConditional(index));
99 }
100
101 using (surfaceDescriptionFunction.BlockScope())
102 {
103 if (feedbackVariables.Count == 0)
104 {
105 string feedBackCode = "surface.VTPackedFeedback = float4(1.0f,1.0f,1.0f,1.0f);";
106 surfaceDescriptionFunction.AppendLine(feedBackCode);
107 }
108 else if (feedbackVariables.Count == 1)
109 {
110 string feedBackCode = "surface.VTPackedFeedback = GetPackedVTFeedback(" + feedbackVariables[0] + ");";
111 surfaceDescriptionFunction.AppendLine(feedBackCode);
112 }
113 else if (feedbackVariables.Count > 1)
114 {
115 surfaceDescriptionFunction.AppendLine("float4 VTFeedback_array[" + feedbackVariables.Count + "];");
116
117 int arrayIndex = 0;
118 foreach (var variable in feedbackVariables)
119 {
120 surfaceDescriptionFunction.AppendLine("VTFeedback_array[" + arrayIndex + "] = " + variable + ";");
121 arrayIndex++;
122 }
123
124 // TODO: should read from NDCPosition instead...
125 surfaceDescriptionFunction.AppendLine("uint pixelColumn = (IN.ScreenPosition.x / IN.ScreenPosition.w) * _ScreenParams.x;");
126 surfaceDescriptionFunction.AppendLine(
127 "surface.VTPackedFeedback = GetPackedVTFeedback(VTFeedback_array[(pixelColumn + _FrameCount) % (uint)" + feedbackVariables.Count + "]);");
128 }
129 }
130
131 if (shaderKeywords.permutations.Count >= 1)
132 {
133 surfaceDescriptionFunction.AppendLine("#endif");
134 }
135
136 index++;
137 }
138 }
139 finally
140 {
141 foreach (var list in feedbackVariablesPerPermutation)
142 {
143 list.Dispose();
144 }
145 feedbackVariablesPerPermutation.Dispose();
146 }
147 }
148
149 // Automatically add a streaming feedback node and correctly connect it to stack samples are connected to it and it is connected to the master node output
150 public static List<string> GetFeedbackVariables(SubGraphOutputNode masterNode)
151 {
152 // TODO: make use a generic interface instead of hard-coding the node types that we need to look at here
153 var VTNodes = GraphUtil.FindDownStreamNodesOfType<SampleVirtualTextureNode>(masterNode);
154 var subGraphNodes = GraphUtil.FindDownStreamNodesOfType<SubGraphNode>(masterNode);
155
156 List<string> result = new List<string>();
157
158 // Early out if there are no nodes we care about in the graph
159 if (subGraphNodes.Count <= 0 && VTNodes.Count <= 0)
160 {
161 return result;
162 }
163
164 // Add inputs to feedback node
165 foreach (var node in VTNodes)
166 {
167 if (node.noFeedback) continue;
168 result.Add(node.GetFeedbackVariableName());
169 }
170
171 foreach (var node in subGraphNodes)
172 {
173 if (node.asset == null) continue;
174 // TODO: subgraph.GetFeedbackVariableNames(...)
175 foreach (var feedbackSlot in node.asset.vtFeedbackVariables)
176 {
177 result.Add(node.GetVariableNameForNode() + "_" + feedbackSlot);
178 }
179 }
180
181 return result;
182 }
183 }
184}