A game about forced loneliness, made by TACStudios
1#ifndef TEXTURESTACK_include
2#define TEXTURESTACK_include
3
4#define GRA_HLSL_5 1
5#define GRA_ROW_MAJOR 1
6#define GRA_TEXTURE_ARRAY_SUPPORT 1
7#define GRA_PACK_RESOLVE_OUTPUT 0
8#if SHADER_API_PSSL
9#define GRA_NO_UNORM 1
10#endif
11#include "VirtualTexturing.hlsl"
12#include "Packing.hlsl"
13
14/*
15 Warning: the following guide is subject to change due to VT's experimental status.
16 For more information, visit https://docs.unity3d.com/ScriptReference/UnityEngine.VirtualTexturingModule.html.
17
18 This header adds the following pseudo definitions. Actual types etc may vary depending
19 on vt- being on or off.
20
21 struct StackInfo { opaque struct ... }
22 struct VTProperty { opaque struct ... }
23 struct VTPropertyWithTextureType { VTProperty + int layerTextureType[4] }
24
25 StackInfo PrepareVT(VTProperty vtProperty, VtInputParameters vtParams)
26 float4 SampleVTLayerWithTextureType(VTPropertyWithTextureType vtPropWithTexType, VtInputParameters vtParams, StackInfo info, [immediate] int layerIndex)
27 ("int layerIndex" cannot be a variable or expression, must be an immediate constant)
28
29 To use this in your materials add the following to various locations in the shader:
30
31 In shaderlab "Properties" section add:
32
33 [TextureStack.MyFancyStack] DiffuseTexture ("DiffuseTexture", 2D) = "white" {}
34 [TextureStack.MyFancyStack] NormalTexture ("NormalTexture", 2D) = "white" {}
35
36 This will declare a texture stack with two textures.
37
38 Then add the following to the PerMaterial constant buffer:
39
40 CBUFFER_START(UnityPerMaterial)
41 ...
42 DECLARE_STACK_CB(MyFancyStack)
43 ...
44 CBUFFER_END
45
46 Then in your shader root add the following:
47
48 ...
49
50 DECLARE_STACK(MyFancyStack, DiffuseTexture)
51 or
52 DECLARE_STACK2(MyFancyStack, DiffuseTexture, NormalTexture)
53 or
54 DECLARE_STACK3(MyFancyStack, TextureSlot1, TextureSlot2, TextureSlot2)
55 etc...
56
57 NOTE: The Stack shaderlab property and DECLARE_STACKn define need to match i.e. the same name and same texture slots.
58
59 Then in the pixel shader function (likely somewhere at the beginning) do:
60
61 VTPropertyWithTextureType vtPropWithTexType = AddTextureType(BuildVTProperties_MyFancyStack(), TEXTURETYPE_DEFAULT, TEXTURETYPE_DEFAULT, ...);
62 // or: TEXTURETYPE_NORMALTANGENTSPACE / TEXTURETYPE_NORMALOBJECTSPACE, match with each texture slot's actual texture type.
63
64 VtInputParameters vtParams;
65 vtParams.uv = uv;
66 vtParams.lodOrOffset = 0.0f;
67 ...
68 StackInfo info = PrepareVT(vtPropWithTexType.vtProperty, vtParams);
69
70 Then later on when you want to sample the actual texture do a call(s):
71
72 // LayerIndex must be an immediate constant, do not use a variable or expression.
73 float4 color1 = SampleVTLayerWithTextureType(vtPropWithTexType, vtParams, info, 0);
74 float4 color2 = SampleVTLayerWithTextureType(vtPropWithTexType, vtParams, info, 1);
75 ...
76
77 The above steps can be repeated for multiple stacks. But be sure that when using the SampleVTLayerWithTextureType you always
78 pass in the VtInputParameters + the result of the AddTextureType and PrepareVT for the correct stack the texture belongs to.
79
80 Also, for tiles to be automatically loaded, you need to write to the VT Feedback texture
81 (SV_Target1) by yourself in the "ForwardOnly" pass. For example:
82
83 #if defined(UNITY_VIRTUAL_TEXTURING) && defined(SHADER_API_PSSL)
84 // Prevent loss of precision on some Sony platforms.
85 #pragma PSSL_target_output_format(target 1 FMT_32_ABGR)
86 #endif
87
88 void Frag(PackedVaryingsToPS packedInput, out float4 outColor : SV_Target0
89 #ifdef UNITY_VIRTUAL_TEXTURING
90 , out float4 outVTFeedback : SV_Target1
91 #endif
92 , ...)
93 {
94 ... (PrepareVT and SampleVTLayerWithTextureType called at some point)
95
96 #ifdef UNITY_VIRTUAL_TEXTURING
97 float4 resolveOutput = GetResolveOutput(info);
98 float4 vtPackedFeedback = GetPackedVTFeedback(resolveOutput);
99 outVTFeedback = PackVTFeedbackWithAlpha(vtPackedFeedback, screenSpacePos.xy, color1.a);
100 // Include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl"
101 // for "PackVTFeedbackWithAlpha".
102 #endif
103
104 ...
105 }
106
107 If multiple stacks are present on the same pixel, alternate between resolve outputs in the following manner
108 and pass the result to "GetPackedVTFeedback", etc... to ensure that all relevant tiles get loaded properly:
109
110 ...
111 float4 resolveOutput = GetResolveOutput(info);
112 float4 resolveOutput2 = GetResolveOutput(info2);
113 float4 resolveOutputs[2] = { resolveOutput, resolveOutput2 };
114
115 uint pixelColumn = screenSpacePos.x;
116 float4 actualResolveOutput = resolveOutputs[(pixelColumn + _FrameCount) % 2];
117 float4 vtPackedFeedback = GetPackedVTFeedback(actualResolveOutput);
118 outVTFeedback = PackVTFeedbackWithAlpha(vtPackedFeedback, ...
119 ...
120*/
121
122#if defined(UNITY_VIRTUAL_TEXTURING) && !defined(FORCE_VIRTUAL_TEXTURING_OFF)
123
124struct StackInfo
125{
126 GraniteLookupData lookupData;
127 GraniteLODLookupData lookupDataLod;
128 float4 resolveOutput;
129};
130
131struct VTProperty
132{
133 GraniteConstantBuffers grCB;
134 GraniteTranslationTexture translationTable;
135 GraniteCacheTexture cacheLayer[4];
136 int layerCount;
137 int layerIndex[4];
138};
139
140#ifdef TEXTURESTACK_CLAMP
141 #define GR_LOOKUP Granite_Lookup_Clamp_Linear
142 #define GR_LOOKUP_LOD Granite_Lookup_Clamp
143#else
144 #define GR_LOOKUP Granite_Lookup_Anisotropic
145 #define GR_LOOKUP_LOD Granite_Lookup
146#endif
147
148// This can be used by certain resolver implementations to override screen space derivatives
149#ifndef RESOLVE_SCALE_OVERRIDE
150#define RESOLVE_SCALE_OVERRIDE float2(1,1)
151#endif
152
153#ifndef VT_CACHE_SAMPLER
154 #define VT_CACHE_SAMPLER sampler_clamp_trilinear_aniso4
155 SAMPLER(VT_CACHE_SAMPLER);
156#endif
157
158StructuredBuffer<GraniteTilesetConstantBuffer> _VTTilesetBuffer;
159
160#define DECLARE_STACK_CB(stackName) \
161 float4 stackName##_atlasparams[2]
162
163#define DECLARE_STACK_BASE(stackName) \
164TEXTURE2D(stackName##_transtab);\
165SAMPLER(sampler##stackName##_transtab);\
166\
167GraniteTilesetConstantBuffer GetConstantBuffer_##stackName() \
168{ \
169 int idx = (int)stackName##_atlasparams[1].w; \
170 GraniteTilesetConstantBuffer graniteParamBlock; \
171 graniteParamBlock = _VTTilesetBuffer[idx]; \
172 \
173 graniteParamBlock.data[0][2][0] *= RESOLVE_SCALE_OVERRIDE.x; \
174 graniteParamBlock.data[0][3][0] *= RESOLVE_SCALE_OVERRIDE.y; \
175 \
176 return graniteParamBlock; \
177} \
178StackInfo PrepareVT_##stackName(VtInputParameters par)\
179{\
180 GraniteStreamingTextureConstantBuffer textureParamBlock;\
181 textureParamBlock.data[0] = stackName##_atlasparams[0];\
182 textureParamBlock.data[1] = stackName##_atlasparams[1];\
183\
184 GraniteTilesetConstantBuffer graniteParamBlock = GetConstantBuffer_##stackName(); \
185\
186 GraniteConstantBuffers grCB;\
187 grCB.tilesetBuffer = graniteParamBlock;\
188 grCB.streamingTextureBuffer = textureParamBlock;\
189\
190 GraniteTranslationTexture translationTable;\
191 translationTable.Texture = stackName##_transtab;\
192 translationTable.Sampler = sampler##stackName##_transtab;\
193\
194 StackInfo info;\
195 VirtualTexturingLookup(grCB, translationTable, par, info.lookupData, info.resolveOutput);\
196 return info;\
197}
198
199// TODO: we could replace all uses of GetConstantBuffer_*() with this one:
200GraniteTilesetConstantBuffer GetConstantBuffer(GraniteStreamingTextureConstantBuffer textureParamBlock)
201{
202 int idx = (int)textureParamBlock.data[1].w;
203 GraniteTilesetConstantBuffer graniteParamBlock;
204 graniteParamBlock = _VTTilesetBuffer[idx];
205
206 graniteParamBlock.data[0][2][0] *= RESOLVE_SCALE_OVERRIDE.x;
207 graniteParamBlock.data[0][3][0] *= RESOLVE_SCALE_OVERRIDE.y;
208
209 return graniteParamBlock;
210}
211
212#define jj2(a, b) a##b
213#define jj(a, b) jj2(a, b)
214
215#define DECLARE_STACK_LAYER(stackName, layerSamplerName, layerIndex) \
216TEXTURE2D_ARRAY(stackName##_c##layerIndex);
217
218#define DECLARE_BUILD_PROPERTIES(stackName, layers, layer0Index, layer1Index, layer2Index, layer3Index)\
219 VTProperty BuildVTProperties_##stackName()\
220 {\
221 VTProperty vtProperty; \
222 \
223 GraniteStreamingTextureConstantBuffer textureParamBlock; \
224 textureParamBlock.data[0] = stackName##_atlasparams[0]; \
225 textureParamBlock.data[1] = stackName##_atlasparams[1]; \
226 \
227 vtProperty.grCB.tilesetBuffer = GetConstantBuffer(textureParamBlock); \
228 vtProperty.grCB.streamingTextureBuffer = textureParamBlock; \
229 \
230 vtProperty.translationTable.Texture = stackName##_transtab; \
231 vtProperty.translationTable.Sampler = sampler##stackName##_transtab; \
232 \
233 vtProperty.layerCount = layers; \
234 vtProperty.layerIndex[0] = layer0Index; \
235 vtProperty.layerIndex[1] = layer1Index; \
236 vtProperty.layerIndex[2] = layer2Index; \
237 vtProperty.layerIndex[3] = layer3Index; \
238 \
239 vtProperty.cacheLayer[0].TextureArray = stackName##_c##layer0Index; \
240 ASSIGN_SAMPLER(vtProperty.cacheLayer[0].Sampler, VT_CACHE_SAMPLER);\
241 vtProperty.cacheLayer[1].TextureArray = stackName##_c##layer1Index; \
242 ASSIGN_SAMPLER(vtProperty.cacheLayer[1].Sampler, VT_CACHE_SAMPLER);\
243 vtProperty.cacheLayer[2].TextureArray = stackName##_c##layer2Index; \
244 ASSIGN_SAMPLER(vtProperty.cacheLayer[2].Sampler, VT_CACHE_SAMPLER);\
245 vtProperty.cacheLayer[3].TextureArray = stackName##_c##layer3Index; \
246 ASSIGN_SAMPLER(vtProperty.cacheLayer[3].Sampler, VT_CACHE_SAMPLER);\
247 \
248 return vtProperty; \
249 }
250
251#define DECLARE_STACK(stackName, layer0SamplerName)\
252 DECLARE_STACK_BASE(stackName)\
253 DECLARE_STACK_LAYER(stackName, layer0SamplerName, 0)\
254 DECLARE_BUILD_PROPERTIES(stackName, 1, 0, 0, 0, 0)
255
256#define DECLARE_STACK2(stackName, layer0SamplerName, layer1SamplerName)\
257 DECLARE_STACK_BASE(stackName)\
258 DECLARE_STACK_LAYER(stackName, layer0SamplerName, 0)\
259 DECLARE_STACK_LAYER(stackName, layer1SamplerName, 1)\
260 DECLARE_BUILD_PROPERTIES(stackName, 2, 0, 1, 1, 1)
261
262#define DECLARE_STACK3(stackName, layer0SamplerName, layer1SamplerName, layer2SamplerName)\
263 DECLARE_STACK_BASE(stackName)\
264 DECLARE_STACK_LAYER(stackName, layer0SamplerName, 0)\
265 DECLARE_STACK_LAYER(stackName, layer1SamplerName, 1)\
266 DECLARE_STACK_LAYER(stackName, layer2SamplerName, 2)\
267 DECLARE_BUILD_PROPERTIES(stackName, 3, 0, 1, 2, 2)
268
269#define DECLARE_STACK4(stackName, layer0SamplerName, layer1SamplerName, layer2SamplerName, layer3SamplerName)\
270 DECLARE_STACK_BASE(stackName)\
271 DECLARE_STACK_LAYER(stackName, layer0SamplerName, 0)\
272 DECLARE_STACK_LAYER(stackName, layer1SamplerName, 1)\
273 DECLARE_STACK_LAYER(stackName, layer2SamplerName, 2)\
274 DECLARE_STACK_LAYER(stackName, layer3SamplerName, 3)\
275 DECLARE_BUILD_PROPERTIES(stackName, 4, 0, 1, 2, 3)
276
277#define PrepareStack(inputParams, stackName) PrepareVT_##stackName(inputParams)
278#define SampleStack(info, lodMode, quality, textureName) SampleVT_##textureName(info, lodMode, quality)
279#define GetResolveOutput(info) info.resolveOutput
280#define PackResolveOutput(output) Granite_PackTileId(output)
281
282StackInfo PrepareVT(VTProperty vtProperty, VtInputParameters vtParams)
283{
284 StackInfo info;
285 VirtualTexturingLookup(vtProperty.grCB, vtProperty.translationTable, vtParams, info.lookupData, info.resolveOutput);
286 return info;
287}
288
289// NOTE: layerIndex here can only be an immediate constant (i.e. 0,1,2, or 3) -- it CANNOT be a variable or expression
290// this is because we use macro concatentation on it when VT is disabled
291float4 SampleVTLayer(VTProperty vtProperty, VtInputParameters vtParams, StackInfo info, int layerIndex)
292{
293 float4 result;
294 VirtualTexturingSample(vtProperty.grCB.tilesetBuffer, info.lookupData, vtProperty.cacheLayer[layerIndex], vtProperty.layerIndex[layerIndex], vtParams.levelMode, vtParams.sampleQuality, result);
295 return result;
296}
297
298float4 GetPackedVTFeedback(float4 feedback)
299{
300 return Granite_PackTileId(feedback);
301}
302
303#define VIRTUAL_TEXTURING_SHADER_ENABLED
304
305#else
306// Virtual Texturing Disabled -- fallback to regular texture sampling
307
308#define DECLARE_BUILD_PROPERTIES(stackName, layers, layer0, layer1, layer2, layer3)\
309 VTProperty BuildVTProperties_##stackName()\
310 {\
311 VTProperty vtProperty; \
312 \
313 vtProperty.layerCount = layers; \
314 vtProperty.Layer0 = layer0; \
315 vtProperty.Layer1 = layer1; \
316 vtProperty.Layer2 = layer2; \
317 vtProperty.Layer3 = layer3; \
318 \
319 ASSIGN_SAMPLER(vtProperty.samplerLayer0, sampler##layer0); \
320 ASSIGN_SAMPLER(vtProperty.samplerLayer1, sampler##layer1); \
321 ASSIGN_SAMPLER(vtProperty.samplerLayer2, sampler##layer2); \
322 ASSIGN_SAMPLER(vtProperty.samplerLayer3, sampler##layer3); \
323 \
324 return vtProperty; \
325 }
326
327// Stacks amount to nothing when VT is off
328#define DECLARE_STACK(stackName, layer0) \
329 DECLARE_BUILD_PROPERTIES(stackName, 1, layer0, layer0, layer0, layer0)
330
331#define DECLARE_STACK2(stackName, layer0, layer1) \
332 DECLARE_BUILD_PROPERTIES(stackName, 2, layer0, layer1, layer1, layer1)
333
334#define DECLARE_STACK3(stackName, layer0, layer1, layer2) \
335 DECLARE_BUILD_PROPERTIES(stackName, 3, layer0, layer1, layer2, layer2)
336
337#define DECLARE_STACK4(stackName, layer0, layer1, layer2, layer3) \
338 DECLARE_BUILD_PROPERTIES(stackName, 4, layer0, layer1, layer2, layer3)
339
340#define DECLARE_STACK_CB(stackName)
341
342// Info is just the uv's
343// We could do a straight #define StackInfo float2 but this makes it a bit more type safe
344// and allows us to do things like function overloads,...
345struct StackInfo
346{
347 VtInputParameters vt;
348};
349
350struct VTProperty
351{
352 int layerCount;
353 TEXTURE2D(Layer0);
354 TEXTURE2D(Layer1);
355 TEXTURE2D(Layer2);
356 TEXTURE2D(Layer3);
357 SAMPLER(samplerLayer0);
358 SAMPLER(samplerLayer1);
359 SAMPLER(samplerLayer2);
360 SAMPLER(samplerLayer3);
361};
362
363StackInfo MakeStackInfo(VtInputParameters vt)
364{
365 StackInfo result;
366 result.vt = vt;
367 return result;
368}
369
370// Prepare just passes the texture coord around
371#define PrepareStack(inputParams, stackName) MakeStackInfo(inputParams)
372
373// Sample just samples the texture
374#define SampleStack(info, vtLevelMode, quality, texture) \
375 SampleVTFallbackToTexture(info, vtLevelMode, TEXTURE2D_ARGS(texture, sampler##texture))
376
377
378float4 SampleVTFallbackToTexture(StackInfo info, int vtLevelMode, TEXTURE2D_PARAM(layerTexture, layerSampler))
379{
380 if (info.vt.enableGlobalMipBias)
381 {
382 if (vtLevelMode == VtLevel_Automatic)
383 return SAMPLE_TEXTURE2D(layerTexture, layerSampler, info.vt.uv);
384 else if (vtLevelMode == VtLevel_Lod)
385 return SAMPLE_TEXTURE2D_LOD(layerTexture, layerSampler, info.vt.uv, info.vt.lodOrOffset);
386 else if (vtLevelMode == VtLevel_Bias)
387 return SAMPLE_TEXTURE2D_BIAS(layerTexture, layerSampler, info.vt.uv, info.vt.lodOrOffset);
388 else // vtLevelMode == VtLevel_Derivatives
389 return SAMPLE_TEXTURE2D_GRAD(layerTexture, layerSampler, info.vt.uv, info.vt.dx, info.vt.dy);
390 }
391 else
392 {
393 if (vtLevelMode == VtLevel_Automatic)
394 return PLATFORM_SAMPLE_TEXTURE2D(layerTexture, layerSampler, info.vt.uv);
395 else if (vtLevelMode == VtLevel_Lod)
396 return PLATFORM_SAMPLE_TEXTURE2D_LOD(layerTexture, layerSampler, info.vt.uv, info.vt.lodOrOffset);
397 else if (vtLevelMode == VtLevel_Bias)
398 return PLATFORM_SAMPLE_TEXTURE2D_BIAS(layerTexture, layerSampler, info.vt.uv, info.vt.lodOrOffset);
399 else // vtLevelMode == VtLevel_Derivatives
400 return PLATFORM_SAMPLE_TEXTURE2D_GRAD(layerTexture, layerSampler, info.vt.uv, info.vt.dx, info.vt.dy);
401 }
402}
403
404StackInfo PrepareVT(VTProperty vtProperty, VtInputParameters vtParams)
405{
406 StackInfo result;
407 result.vt = vtParams;
408 return result;
409}
410
411// NOTE: layerIndex here can only be an immediate constant (i.e. 0,1,2, or 3) -- it CANNOT be a variable or expression
412// this is because we use macro concatentation on it when VT is disabled
413#define SampleVTLayer(vtProperty, vtParams, info, layerIndex) \
414 SampleVTFallbackToTexture(info, vtParams.levelMode, TEXTURE2D_ARGS(vtProperty.Layer##layerIndex, vtProperty.samplerLayer##layerIndex))
415
416// Resolve does nothing
417#define GetResolveOutput(info) float4(1,1,1,1)
418#define PackResolveOutput(output) output
419#define GetPackedVTFeedback(feedback) feedback
420
421#endif
422
423
424
425// Shared code between VT enabled and VT disabled, adding TextureType handling
426
427// these texture types should be kept in sync with LayerTextureType in C# code
428#define TEXTURETYPE_DEFAULT 0 // LayerTextureType.Default
429#define TEXTURETYPE_NORMALTANGENTSPACE 1 // LayerTextureType.NormalTangentSpace
430#define TEXTURETYPE_NORMALOBJECTSPACE 2 // LayerTextureType.NormalObjectSpace
431
432struct VTPropertyWithTextureType
433{
434 VTProperty vtProperty;
435 int layerTextureType[4];
436};
437
438VTPropertyWithTextureType AddTextureType(VTProperty vtProperty, int layer0TextureType, int layer1TextureType = TEXTURETYPE_DEFAULT, int layer2TextureType = TEXTURETYPE_DEFAULT, int layer3TextureType = TEXTURETYPE_DEFAULT)
439{
440 VTPropertyWithTextureType result;
441 result.vtProperty = vtProperty;
442 result.layerTextureType[0] = layer0TextureType;
443 result.layerTextureType[1] = layer1TextureType;
444 result.layerTextureType[2] = layer2TextureType;
445 result.layerTextureType[3] = layer3TextureType;
446 return result;
447}
448
449float4 ApplyTextureType(float4 value, int textureType)
450{
451 // NOTE: when textureType is a compile-time constant, the branches compile out
452 if (textureType == TEXTURETYPE_NORMALTANGENTSPACE)
453 {
454 value.rgb = UnpackNormalmapRGorAG(value);
455 }
456 else if (textureType == TEXTURETYPE_NORMALOBJECTSPACE)
457 {
458 value.rgb = UnpackNormalRGB(value);
459 }
460 return value;
461}
462
463// if we _could_ express it as a function, the function signature would be:
464// float4 SampleVTLayerWithTextureType(VTPropertyWithTextureType vtPropWithTexType, VtInputParameters vtParams, StackInfo info, [immediate] int layerIndex)
465// NOTE: layerIndex here can only be an immediate constant (i.e. 0,1,2, or 3) -- it CANNOT be a variable or expression
466// this is because we use macro concatentation on it when VT is disabled
467
468#define SampleVTLayerWithTextureType(vtPropWithTexType, vtParams, info, layerIndex) \
469 ApplyTextureType(SampleVTLayer(vtPropWithTexType.vtProperty, vtParams, info, layerIndex), vtPropWithTexType.layerTextureType[layerIndex])
470
471#endif //TEXTURESTACK_include