A game about forced loneliness, made by TACStudios
1#include "GraniteShaderLibBase.hlsl"
2
3#define VtAddressMode_Wrap 0
4#define VtAddressMode_Clamp 1
5#define VtAddressMode_Udim 2
6
7#define VtFilter_Anisotropic 0
8
9#define VtLevel_Automatic 0
10#define VtLevel_Lod 1
11#define VtLevel_Bias 2
12#define VtLevel_Derivatives 3
13
14#define VtUvSpace_Regular 0
15#define VtUvSpace_PreTransformed 1
16
17#define VtSampleQuality_Low 0
18#define VtSampleQuality_High 1
19
20struct VtInputParameters
21{
22 float2 uv;
23 float lodOrOffset;
24 float2 dx;
25 float2 dy;
26 int addressMode;
27 int filterMode;
28 int levelMode;
29 int uvMode;
30 int sampleQuality;
31 int enableGlobalMipBias;
32};
33
34int VirtualTexturingLookup(
35 in GraniteConstantBuffers grCB,
36 in GraniteTranslationTexture translationTable,
37 in VtInputParameters input,
38 out GraniteLookupData graniteLookupData,
39 out float4 resolveResult
40)
41{
42 GraniteStreamingTextureConstantBuffer grSTCB = grCB.streamingTextureBuffer;
43 GraniteTilesetConstantBuffer tsCB = grCB.tilesetBuffer;
44
45 float2 texCoord = input.uv;
46 float2 dx;
47 float2 dy;
48 float mipLevel; //interger
49
50 if (input.levelMode == VtLevel_Automatic)
51 {
52 dx = ddx(texCoord);
53 dy = ddy(texCoord);
54 }
55 else if (input.levelMode == VtLevel_Bias)
56 {
57 // We can't simply add the bias after the mip-calculation since the derivatives
58 // are also used when sampling the cache so make sure we apply bias by scaling derivatives
59 if ( input.sampleQuality == VtSampleQuality_High )
60 {
61 float offsetPow2 = pow(2.0f, input.lodOrOffset);
62 dx = ddx(texCoord) * offsetPow2;
63 dy = ddy(texCoord) * offsetPow2;
64 }
65 // In low quality we don't care about cache derivatives and will add the bias later
66 else
67 {
68 dx = ddx(texCoord);
69 dy = ddy(texCoord);
70 }
71 }
72 else if (input.levelMode == VtLevel_Derivatives)
73 {
74 dx = input.dx;
75 dy = input.dy;
76 }
77 else /*input.levelMode == VtLevel_Lod*/
78 {
79 //gra_TrilinearOffset ensures we do round-nearest for no-trilinear and
80 //round-floor for trilinear.
81 float clampedLevel = clamp(input.lodOrOffset + gra_TrilinearOffset, 0.0f, gra_NumLevels);
82 mipLevel = floor(clampedLevel);
83 dx = float2(frac(clampedLevel), 0.0f); // trilinear blend ratio
84 dy = float2(0.0f,0.0f);
85 }
86
87 // Transform the derivatives to atlas space if needed
88 if (input.uvMode == VtUvSpace_Regular && input.levelMode != VtLevel_Lod)
89 {
90 dx = gra_Transform.zw * dx;
91 dy = gra_Transform.zw * dy;
92 }
93
94 if (input.levelMode != VtLevel_Lod)
95 {
96 #ifdef VT_GLOBAL_MIP_BIAS_MULTIPLIER
97 if (input.enableGlobalMipBias)
98 {
99 dx *= VT_GLOBAL_MIP_BIAS_MULTIPLIER;
100 dy *= VT_GLOBAL_MIP_BIAS_MULTIPLIER;
101 }
102 #endif
103
104 mipLevel = GranitePrivate_CalcMiplevelAnisotropic(grCB.tilesetBuffer, grCB.streamingTextureBuffer, dx, dy);
105
106 // Simply add it here derivatives are wrong from this point onwards but not used anymore
107 if ( input.sampleQuality == VtSampleQuality_Low && input.levelMode == VtLevel_Bias)
108 {
109 mipLevel += input.lodOrOffset;
110 // GranitePrivate_CalcMiplevelAnisotropic will already clamp between 0 gra_NumLevels
111 // But we need to do it again here. The alternative is modifying dx,dy before passing to
112 // GranitePrivate_CalcMiplevelAnisotropic adding a pow2 + 4 fmuls so probably
113 // the exra clamp is more appropriate here.
114 mipLevel = clamp(mipLevel, 0.0f, gra_NumLevels);
115 }
116
117 mipLevel = floor(mipLevel + 0.5f); //round nearest
118 }
119
120 // Apply clamp/wrap mode if needed and transform into atlas space
121 // If the user passes in pre-transformed texture coords clamping and wrapping should be handled by the user
122 if (input.uvMode == VtUvSpace_Regular)
123 {
124 if (input.addressMode == VtAddressMode_Wrap)
125 {
126 texCoord = frac(input.uv);
127 }
128 else if (input.addressMode == VtAddressMode_Clamp)
129 {
130 float2 epsilon2 = float2(gra_AssetWidthRcp, gra_AssetHeightRcp);
131 texCoord = clamp(input.uv, epsilon2, float2(1,1) - epsilon2);
132 }
133 else if (input.addressMode == VtAddressMode_Udim)
134 {
135 // not modified (i.e outside of the 0-1 range, atlas transform below will take care of it)
136 texCoord = input.uv;
137 }
138
139 texCoord = Granite_Transform(gra_StreamingTextureCB, texCoord);
140 }
141
142 // calculate resolver data
143 float2 level0NumTiles = float2(gra_Level0NumTilesX, gra_Level0NumTilesX*gra_NumTilesYScale);
144 float2 virtualTilesUv = floor(texCoord * level0NumTiles * pow(0.5, mipLevel));
145 resolveResult = GranitePrivate_MakeResolveOutput(tsCB, virtualTilesUv, mipLevel);
146
147 float4 translationTableData;
148 if (input.levelMode != VtLevel_Lod)
149 {
150 // Look up the physical page indexes and the number of pages on the mipmap
151 // level of the page in the translation texture
152 // Note: this is equal for both anisotropic and linear sampling
153 // We could use a sample bias here for 'auto' mip level detection
154#if (GRA_LOAD_INSTR==0)
155 translationTableData = GranitePrivate_SampleLevel_Translation(translationTable, texCoord, mipLevel);
156#else
157 translationTableData = GranitePrivate_Load(translationTable, gra_Int3(virtualTilesUv, mipLevel));
158#endif
159 }
160 else
161 {
162 // Look up the physical page indexes and the number of pages on the mipmap
163 // level of the page in the translation texture
164 // Note: this is equal for both anisotropic and linear sampling
165 // We could use a sample bias here for 'auto' mip level detection
166#if (GRA_LOAD_INSTR==0)
167 translationTableData = GranitePrivate_SampleLevel_Translation(translationTable, texCoord, mipLevel);
168#else
169 translationTableData = GranitePrivate_Load(translationTable, gra_Int3(virtualTilesUv, mipLevel));
170#endif
171 }
172
173 graniteLookupData.translationTableData = translationTableData;
174 graniteLookupData.textureCoordinates = texCoord;
175 graniteLookupData.dX = dx;
176 graniteLookupData.dY = dy;
177
178 return 1;
179}
180
181int VirtualTexturingSample(
182 in GraniteTilesetConstantBuffer tsCB,
183 in GraniteLookupData graniteLookupData,
184 in GraniteCacheTexture cacheTexture,
185 in int layer,
186 in int levelMode,
187 in int quality,
188 out float4 result)
189{
190 // Convert from pixels to [0-1] and look up in the physical page texture
191 float2 deltaScale;
192 float3 cacheCoord = GranitePrivate_TranslateCoord(tsCB, graniteLookupData.textureCoordinates, graniteLookupData.translationTableData, layer, deltaScale);
193
194 if ( levelMode != VtLevel_Lod )
195 {
196 if ( quality == VtSampleQuality_Low )
197 {
198 // This leads to small artefacts at tile borders but is generally not noticable unless the texture
199 // is greatly magnified
200 result = GranitePrivate_SampleArray(cacheTexture, cacheCoord);
201 }
202 else /* quality == VtSampleQuality_High */
203 {
204 deltaScale *= gra_LodBiasPow2;
205
206 // Calculate the delta scale this works by first converting the [0-1] texcoord deltas to
207 // pixel deltas on the current mip level, then dividing by the cache size to convert to [0-1] cache deltas
208 float2 sampDeltaX = graniteLookupData.dX*deltaScale;
209 float2 sampDeltaY = graniteLookupData.dY*deltaScale;
210
211 result = GranitePrivate_SampleGradArray(cacheTexture, cacheCoord, sampDeltaX, sampDeltaY);
212 }
213 }
214 else
215 {
216 result = GranitePrivate_SampleLevelArray(cacheTexture, cacheCoord, graniteLookupData.dX.x);
217 }
218
219 return 1;
220}