A game about forced loneliness, made by TACStudios
1#ifndef UNITY_DEBUG_MIPMAP_STREAMING_INCLUDED
2#define UNITY_DEBUG_MIPMAP_STREAMING_INCLUDED
3
4#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Debug.hlsl"
5
6// Indices for Mipmap Debug Legend Strings
7#define _kNotStreamingIndex 0
8#define _kStreamingIndex 1
9#define _kStreamingManuallyIndex 2
10#define _kStatusNoTextureIndex 3
11#define _kStatusWarningIndex 4
12#define _kStatusUnknownIndex 5
13#define _kStatusStreamerDisabledIndex 6
14#define _kStatusMessageNoMipMapIndex 7
15#define _kStatusMessageNotSetToStreamIndex 8
16#define _kStatusMessageNoAsyncIndex 9
17#define _kStatusMessageTerrainIndex 10
18#define _kStatusNoTexturesIndex 11
19#define _kStatusSomeTexturesHaveIssues 12
20#define _kStatusAllTexturesAreStreaming 13
21#define _kStatusAllTexturesAreStreamingSomeManually 14
22#define _kStatusNoTexturesAreStreaming 15
23#define _kStatusSomeTexturesAreStreaming 16
24#define _kStatusSomeTexturesAreStreamingSomeManually 17
25
26#define _kNoMipCountIndex 18
27#define _kTooManyMipsIndex 19
28
29#define _kHighPixelDensityIndex 20
30#define _kLowPixelDensityIndex 21
31
32#define _kLowPriorityIndex 22
33#define _kHighPriorityIndex 23
34
35#define _kBudgetSavingMips 24
36#define _kBudgetSavingMipsWithCache 25
37#define _kBudgetNothingSaved 26
38#define _kBudgetMissingMips 27
39
40#define _kRecentlyUpdated 28
41#define _kNotRecentlyUpdated 29
42
43static const uint kMipmapDebugLegendStrings[][32] =
44{
45 // Status
46 {13, 'N','o','t',' ','s','t','r','e','a','m','i','n','g','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.'},
47 { 9, 'S','t','r','e','a','m','i','n','g','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.'},
48 {31, 'S','t','r','e','a','m','i','n','g',' ','(','m','a','n','u','a','l','l','y',' ','v','i','a',' ','s','c','r','i','p','t',')'},
49 {18, 'N','o',' ','t','e','x','t','u','r','e',' ','i','n',' ','s','l','o','t','.','.','.','.','.','.','.','.','.','.','.','.','.'},
50 { 7, 'W','a','r','n','i','n','g','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.'},
51 {21, 'U','n','k','n','o','w','n',' ','(','n','o',' ','r','e','n','d','e','r','e','r',')','.','.','.','.','.','.','.','.','.','.'},
52 {24, 'T','e','x','t','u','r','e','S','t','r','e','a','m','e','r',' ','d','i','s','a','b','l','e','d','.','.','.','.','.','.','.'},
53 {19, 'N','o',' ','m','i','p','m','a','p',' ','g','e','n','e','r','a','t','e','d','.','.','.','.','.','.','.','.','.','.','.','.'},
54 {21, 'S','t','r','e','a','m','i','n','g',' ','n','o','t',' ','e','n','a','b','l','e','d','.','.','.','.','.','.','.','.','.','.'},
55 {19, 'C','a','n','n','o','t',' ','s','t','r','e','a','m',' ','a','s','y','n','c','.','.','.','.','.','.','.','.','.','.','.','.'},
56 { 7, 'T','e','r','r','a','i','n','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.'},
57
58 {23, 'N','o',' ','t','e','x','t','u','r','e','s',' ','o','n',' ','m','a','t','e','r','i','a','l','.','.','.','.','.','.','.','.'},
59 {15, 'I','s','s','u','e','s',' ','d','e','t','e','c','t','e','d','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.'},
60 {13, 'A','l','l',' ','s','t','r','e','a','m','i','n','g','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.'},
61 {29, 'A','l','l',' ','s','t','r','e','a','m','i','n','g',' ','(','s','o','m','e',' ','m','a','n','u','a','l','l','y',')','.','.'},
62 {21, 'N','o',' ','t','e','x','t','u','r','e','s',' ','s','t','r','e','a','m','i','n','g','.','.','.','.','.','.','.','.','.','.'},
63 {14, 'S','o','m','e',' ','s','t','r','e','a','m','i','n','g','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.'},
64 {30, 'S','o','m','e',' ','s','t','r','e','a','m','i','n','g',' ','(','s','o','m','e',' ','m','a','n','u','a','l','l','y',')','.'},
65
66 // MipCount
67 {16, 'I','n','v','a','l','i','d',' ','m','i','p','C','o','u','n','t','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.'},
68 {26, 'M','o','r','e',' ','t','h','a','n',' ','1','4',' ','m','i','p','s',' ','u','p','l','o','a','d','e','d','.','.','.','.','.'},
69
70 // Ratio
71 {18, 'H','i','g','h',' ','p','i','x','e','l',' ','d','e','n','s','i','t','y','.','.','.','.','.','.','.','.','.','.','.','.','.'},
72 {17, 'L','o','w',' ','p','i','x','e','l',' ','d','e','n','s','i','t','y','.','.','.','.','.','.','.','.','.','.','.','.','.','.'},
73
74 // Priorities
75 {10, 'L','o','w',' ','(','-','1','2','8',')','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.'},
76 {10, 'H','i','g','h',' ','(','1','2','7',')','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.'},
77
78 // Performance
79 {10, 'M','i','p','s',' ','s','a','v','e','d','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.'},
80 {31, 'M','i','p','s',' ','s','a','v','e','d',' ','(','s','o','m','e',' ','c','a','c','h','e','d',' ','o','n',' ','G','P','U',')'},
81 {30, 'N','o',' ','s','a','v','i','n','g','s',' ','(','a','l','l',' ','m','i','p','s',' ','r','e','q','u','i','r','e','d',')','.'},
82 {30, 'N','o','t',' ','a','l','l',' ','r','e','q','u','i','r','e','d',' ','m','i','p','s',' ','u','p','l','o','a','d','e','d','.'},
83
84 // RecentlyUpdated
85 {13, 'J','u','s','t',' ','s','t','r','e','a','m','e','d','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.'},
86 {21, 'N','o','t',' ','r','e','c','e','n','t','l','y',' ','s','t','r','e','a','m','e','d','.','.','.','.','.','.','.','.','.','.'},
87};
88
89void DrawString(int stringIdx, uint2 unormCoord, float3 textColor, uint2 textLocation, bool alignRight, inout float3 outputColor)
90{
91 const uint stringSize = kMipmapDebugLegendStrings[stringIdx][0];
92 const int direction = alignRight ? -1 : 1;
93 uint i = alignRight ? stringSize : 1;
94
95 [fastopt] for (; alignRight ? i > 0 : i <= stringSize; i += direction)
96 DrawCharacter(kMipmapDebugLegendStrings[stringIdx][i], textColor, unormCoord, textLocation, outputColor.rgb, direction);
97}
98
99void DrawString(int stringIdx, uint2 unormCoord, float3 textColor, uint2 textLocation, inout float3 outputColor)
100{
101 DrawString(stringIdx, unormCoord, textColor, textLocation, false, outputColor);
102}
103
104// mipInfo :
105// x = quality settings maxLevelReduction
106// y = original mip count for texture (if it has not been set, it's not a streamed texture)
107// z = desired on screen mip level
108// w = 0
109uint GetMaxLevelReduction(float4 mipInfo) { return max(1, uint(mipInfo.x)); } // Always has a minimum value of 1.
110uint GetTextureAssetMipCount(float4 mipInfo) { return uint(mipInfo.y); }
111uint GetDesiredMipLevel(float4 mipInfo) { return uint(mipInfo.z); }
112
113// Mipmap Debug Status Codes found in StreamInfo.z (Per-Texture)
114#define kMipmapDebugStatusCodeNotSet 0 // No status code has been set by the streamer
115#define kMipmapDebugStatusCodeStreamerDisabled 1 // Not streaming: streamer disabled
116#define kMipmapDebugStatusCodeNoTexture 2 // Nothing there, empty slot
117#define kMipmapDebugStatusCodeNoMipMap 3 // Not streaming: no mips
118#define kMipmapDebugStatusCodeNotSetToStream 4 // Not streaming: streaming not enabled for this texture
119#define kMipmapDebugStatusCodeNoAsync 5 // Not streaming: cannot asyncStream
120#define kMipmapDebugStatusCodeTerrain 6 // Not streaming: terrain
121#define kMipmapDebugStatusCodeInvalidStreamingIndex 7 // Not streaming: invalid streaming index (issue at Unity side?)
122#define kMipmapDebugStatusCodeManuallyRequested 8 // Streaming manually through script (RequestedMipmapLevel)
123
124// Mipmap Debug Status Code Flags found in StreamInfo.z (Per-Material)
125#define kMipmapDebugStatusCodeFlagNoTexturesSet 0 // No textures have been set on the material, all slots are empty
126#define kMipmapDebugStatusCodeFlagHasStreamingTextures 1 // 1 or more textures assigned to the material are streaming
127#define kMipmapDebugStatusCodeFlagHasNonStreamingTextures 2 // 1 or more textures assigned to the material aren't streaming
128#define kMipmapDebugStatusCodeFlagHasTexturesWithIssues 4 // 1 or more textures assigned to the material have issues preventing them from streaming
129#define kMipmapDebugStatusCodeFlagHasManualRequests 8 // 1 or more textures assigned to the material are streaming manually through script (RequestedMipmapLevel)
130
131// streamInfo :
132// x = streaming priority
133// y = time stamp of the latest texture upload
134// z = streaming status
135// w = 0
136int GetStreamingPriority(float4 streamInfo) { return int(streamInfo.x);}
137float GetUpdateTimestamp(float4 streamInfo) { return streamInfo.y;}
138bool IsStreaming(float4 streamInfo) { return streamInfo.z < 0 || (int)streamInfo.z == kMipmapDebugStatusCodeManuallyRequested; } // if manually set, that's also streaming
139int GetStatusCode(float4 streamInfo, bool perMaterial) { return perMaterial ? (int)streamInfo.z >> 4 : (int)streamInfo.z & 0xF;} // 0-15 are reserved for per-texture codes
140
141#define kMipmapDebugLowPixelDensity float3(0.049, 0.32, 0.751)
142#define kMipmapDebugHighPixelDensity float3(0.982, 0.32, 0)
143
144float3 GetDebugMipRatioColor(float value)
145{
146 if (value > 0.5)
147 return lerp(float3(0.5, 0.5, 0.5), kMipmapDebugHighPixelDensity, 2 * value - 1);
148 else
149 return lerp(kMipmapDebugLowPixelDensity, float3(0.5, 0.5, 0.5), 2 * value);
150}
151
152float3 GetMipLevelColor(float2 uv, float4 texelSize)
153{
154 // Push down into colors list to "optimal level" in following table.
155 // .zw is texture width,height so *2 is down one mip, *4 is down two mips
156 texelSize.zw *= 4.0;
157
158 float mipLevel = ComputeTextureLOD(uv, texelSize.zw);
159 mipLevel = clamp(mipLevel, 0.0, 5.0 - 0.0001);
160
161 return GetDebugMipRatioColor(mipLevel / 5.0);
162}
163
164float3 GetDebugMipColor(float3 originalColor, float4 texelSize, float2 uv)
165{
166 // https://aras-p.info/blog/2011/05/03/a-way-to-visualize-mip-levels/
167 return GetMipLevelColor(uv, texelSize);
168}
169
170#define kMipmapDebugTooManyMips float3(0.194, 0.007, 0.034)
171#define kMipmapDebugInvalidMipCount float3(0.716, 0.066, 0.9)
172
173float3 GetDebugMipCountColor(uint mipCount, out bool needsHatching)
174{
175 needsHatching = false;
176
177 if (mipCount == 0 || mipCount > 14)
178 {
179 needsHatching = true;
180 return mipCount == 0 ? kMipmapDebugInvalidMipCount : kMipmapDebugTooManyMips;
181 }
182
183 const float3 colors[14] = {
184 float3(0.349, 0.782, 0.965),
185 float3(0.188, 0.933, 0.847),
186 float3(0.034, 0.9, 0.442),
187 float3(0.027, 0.878, 0.035),
188 float3(0.4, 0.858, 0.023),
189 float3(0.694, 0.929, 0.047),
190 float3(1.0, 0.982, 0.072),
191 float3(0.996, 0.843, 0.039),
192 float3(0.991, 0.687, 0.004),
193 float3(0.988, 0.510, 0.004),
194 float3(0.982, 0.320, 0.0),
195 float3(0.992, 0.184, 0.016),
196 float3(1.0, 0.051, 0.029),
197 float3(1.0, 0.043, 0.180)
198 };
199
200 return colors[mipCount - 1];
201}
202
203float3 GetDebugMipCountHatchingColor(uint mipCount)
204{
205 if (mipCount == 0 || mipCount > 14)
206 return float3(0.9, 0.9, 0.9);
207 else
208 return float3(0.1, 0.1, 0.1);
209}
210
211
212#define kMipmapDebugBudgetSavingMips float3(0.036, 0.9, 0.442)
213#define kMipmapDebugBudgetMissing float3(1.0, 0.053, 0.029)
214#define kMipmapDebugBudgetFullResolution float3(0.5, 0.5, 0.5)
215#define kMipMapDebugStatusColorNotStreaming float3(0.0, 0.007, 0.731)
216
217float3 GetDebugStreamingMipColor(uint mipCount, float4 mipInfo, float4 streamInfo, out bool needsHatching)
218{
219 needsHatching = false;
220
221 if (!IsStreaming(streamInfo))
222 return kMipMapDebugStatusColorNotStreaming;
223
224 if (mipCount == 0)
225 return float3(1.0, 0.0, 1.0); // Magenta if mip count invalid
226
227 const uint originalTextureMipCount = GetTextureAssetMipCount(mipInfo);
228 const uint mipCountDesired = uint(originalTextureMipCount)-GetDesiredMipLevel(mipInfo);
229 const uint maxLevelReduction = GetMaxLevelReduction(mipInfo);
230
231 const float3 colorNeutral = float3(0.5, 0.5, 0.5);
232 if (mipCount < mipCountDesired)
233 {
234 const int missingMips = mipCountDesired - mipCount;
235 const float missingRatio = float(missingMips) / float(maxLevelReduction);
236
237 // red tones when not at the desired mip level (reduction due to budget). Full red means no more mips were allowed to be discarded
238 return lerp(colorNeutral, kMipmapDebugBudgetMissing, missingRatio);
239 }
240 else if (mipCountDesired < originalTextureMipCount)
241 {
242 const int savedMips = originalTextureMipCount - mipCountDesired;
243 const float savedRatio = float(savedMips) / float(maxLevelReduction);
244
245 // green tones when we require less mips than the original texture. Full green means we've dropped as many mips as allowed
246 if (mipCount > mipCountDesired) // some mips were cached
247 {
248 needsHatching = true;
249 return lerp(colorNeutral, kMipmapDebugBudgetSavingMips, savedRatio);
250 }
251 else
252 return lerp(colorNeutral, kMipmapDebugBudgetSavingMips, savedRatio);
253 }
254 else // so, (mipCount >= originalTextureMipCount)
255 {
256 return kMipmapDebugBudgetFullResolution;
257 }
258}
259
260#define kMipMapDebugStatusColorStreaming float3(0.036, 0.9, 0.442)
261#define kMipMapDebugStatusColorUnknown float3(0.349, 0.782, 0.965)
262#define kMipMapDebugStatusColorNoTexture float3(0.982, 0.320, 0)
263#define kMipMapDebugStatusColorWarning float3(1.0, 0.982, 0.072)
264
265float3 GetDebugStreamingStatusColor(float4 streamInfo, out bool needsHatching)
266{
267 needsHatching = false;
268
269 if (IsStreaming(streamInfo))
270 {
271 if (GetStatusCode(streamInfo, false) == kMipmapDebugStatusCodeManuallyRequested)
272 needsHatching = true;
273 return kMipMapDebugStatusColorStreaming;
274 }
275
276 int statusCode = GetStatusCode(streamInfo, false);
277 switch(statusCode)
278 {
279 case kMipmapDebugStatusCodeNoTexture:
280 return kMipMapDebugStatusColorNoTexture;
281 case kMipmapDebugStatusCodeStreamerDisabled:
282 case kMipmapDebugStatusCodeNoMipMap:
283 case kMipmapDebugStatusCodeNotSetToStream:
284 return kMipMapDebugStatusColorNotStreaming;
285 case kMipmapDebugStatusCodeNoAsync:
286 case kMipmapDebugStatusCodeTerrain:
287 return kMipMapDebugStatusColorWarning;
288 case kMipmapDebugStatusCodeInvalidStreamingIndex:
289 return float3(1.0, 0.0, 1.0);
290 case kMipmapDebugStatusCodeNotSet:
291 default:
292 return kMipMapDebugStatusColorUnknown;
293 }
294}
295
296#define kMipMapDebugMaterialStatusColorSomeStreaming float3(0.018, 0.454, 0.587)
297
298float3 GetDebugPerMaterialStreamingStatusColor(float4 streamInfo, out bool needsHatching)
299{
300 needsHatching = false;
301
302 int statusCode = GetStatusCode(streamInfo, true);
303 if (statusCode == kMipmapDebugStatusCodeFlagNoTexturesSet)
304 return kMipMapDebugStatusColorNoTexture;
305
306 const bool hasStreamingTextures = (statusCode & kMipmapDebugStatusCodeFlagHasStreamingTextures) != 0;
307 const bool hasNonStreamingTextures = (statusCode & kMipmapDebugStatusCodeFlagHasNonStreamingTextures) != 0;
308 const bool hasTexturesWithIssues = (statusCode & kMipmapDebugStatusCodeFlagHasTexturesWithIssues) != 0;
309 const bool hasManualRequests = (statusCode & kMipmapDebugStatusCodeFlagHasManualRequests) != 0;
310
311 if(hasTexturesWithIssues)
312 {
313 return kMipMapDebugStatusColorWarning;
314 }
315
316 // at this point, there are no issues to report
317 if(hasStreamingTextures && !hasNonStreamingTextures)
318 {
319 needsHatching = hasManualRequests;
320 return kMipMapDebugStatusColorStreaming;
321 }
322 else if(hasNonStreamingTextures && !hasStreamingTextures)
323 {
324 return kMipMapDebugStatusColorNotStreaming;
325 }
326 else
327 {
328 // mix of streaming and non-streaming
329 needsHatching = hasManualRequests;
330 return kMipMapDebugMaterialStatusColorSomeStreaming;
331 }
332}
333
334float3 GetDebugMipPriorityColor(float value)
335{
336 const float3 kMipMapDebugLowPriorityColor = float3(0.982, 0.32, 0);
337 const float3 kMipMapDebugHighPriorityColor = float3(0.4, 0.858, 0.023);
338
339 if(value < 0.5)
340 return lerp(kMipMapDebugLowPriorityColor, float3(0.5, 0.5, 0.5), 2 * value);
341 else
342 return lerp(float3(0.5, 0.5, 0.5), kMipMapDebugHighPriorityColor, 2 * (value - 0.5));
343}
344
345float3 GetDebugStreamingPriorityColor(float4 streamInfo)
346{
347 if (!IsStreaming(streamInfo))
348 return kMipMapDebugStatusColorNotStreaming;
349
350 const int textureStreamingPriority = GetStreamingPriority(streamInfo);
351 const float priorityValue = (textureStreamingPriority + 128) / 255.0;
352 return GetDebugMipPriorityColor(priorityValue);
353}
354
355float3 GetDebugMipRecentlyUpdatedColor(float value)
356{
357 const float3 kRecentlyUpdatedColor = float3(0.194, 0.007, 0.034);
358 return lerp(kRecentlyUpdatedColor, float3(0.766, 0.766, 0.766), value);
359}
360
361float3 GetDebugStreamingRecentlyUpdatedColor(float currentTime, float cooldownTime, bool perMaterial, float4 streamInfo)
362{
363 if (!perMaterial && !IsStreaming(streamInfo))
364 return kMipMapDebugStatusColorNotStreaming;
365
366 if (perMaterial)
367 {
368 // The other per-material status codes don't really matter here (users visualize these in the status view).
369 // Even if there are slots with issues, as long as there is one slot with a streaming texture, we can visualize
370 // recent updates here.
371 int statusCode = GetStatusCode(streamInfo, true);
372 const bool hasStreamingTextures = (statusCode & kMipmapDebugStatusCodeFlagHasStreamingTextures) != 0;
373 if (!hasStreamingTextures)
374 return kMipMapDebugStatusColorNotStreaming; // nothing there, all slots are empty
375 }
376
377 const float timeSinceUpdate = currentTime - GetUpdateTimestamp(streamInfo);
378 const float ratio = clamp(timeSinceUpdate / cooldownTime, 0.0, 1.0);
379 return GetDebugMipRecentlyUpdatedColor(ratio);
380}
381
382float3 GetDebugMipColorIncludingMipReduction(float3 originalColor, uint mipCount, float4 texelSize, float2 uv, float4 mipInfo)
383{
384 const uint originalTextureMipCount = GetTextureAssetMipCount(mipInfo);
385 if (originalTextureMipCount != 0)
386 {
387 // Mip count has been reduced but the texelSize was not updated to take that into account
388 const uint mipReductionLevel = originalTextureMipCount - mipCount;
389 const uint mipReductionFactor = 1U << mipReductionLevel;
390 if (mipReductionFactor)
391 {
392 const float oneOverMipReductionFactor = 1.0 / mipReductionFactor;
393 // texelSize.xy *= mipReductionRatio; // Unused in GetDebugMipColor so lets not re-calculate it
394 texelSize.zw *= oneOverMipReductionFactor;
395 }
396 }
397 return GetDebugMipColor(originalColor, texelSize, uv);
398}
399
400void HatchColor(uint2 unormCoord, real3 hatchingColor, inout real3 color)
401{
402 const uint spacing = 8;
403 const uint thickness = 3;
404 if((unormCoord.x + unormCoord.y) % spacing < thickness)
405 color = hatchingColor;
406}
407
408void HatchColor(uint2 unormCoord, inout real3 color)
409{
410 HatchColor(unormCoord, real3(0.1, 0.1, 0.1), color);
411}
412
413// Legend configuration
414static const float _kLegendBarPaddingHorizontal = 20.0;
415static const float _kLegendBarPaddingBottom = 20.0;
416static const float _kLegendBarHeight = 20.0;
417static const float _kLegendBarBorderThickness = 4.0;
418
419static const float _kLegendPaddingBottom = 5.0;
420static const float _kLegendMargin = 5.0;
421static const float _kLegendEntryBlockSize = 15.0;
422static const float _kLegendEntryBlockBorderSize = 1.0;
423static const float _kLegendBorderSize = 2.0;
424static const float _kLegendPaddingTextRight = 10.0;
425
426void DrawLegendEntry(uint2 unormCoord, uint stringIndex, int code, float3 entryColor, bool hatched, real3 hatchingColor, inout uint2 pos, inout real3 outputColor)
427{
428 const float3 borderColor = float3(1,1,1);
429 const float3 lightTextColor = float3(0.90, 0.90, 0.90);
430 const float3 darkTextColor = float3(0.10, 0.10, 0.10);
431
432 uint2 blockBottom = pos;
433 uint2 blockTop = blockBottom + uint2(_kLegendEntryBlockSize, _kLegendEntryBlockSize);
434
435 if (all(unormCoord > blockBottom) && all(unormCoord < blockTop))
436 {
437 if (all(unormCoord > blockBottom + uint(_kLegendEntryBlockBorderSize)) && all(unormCoord < blockTop - uint(_kLegendEntryBlockBorderSize)))
438 {
439 outputColor = entryColor;
440
441 if (hatched)
442 HatchColor(unormCoord, hatchingColor, outputColor);
443
444 if (code != -1)
445 {
446 bool invertColor = (code == kMipmapDebugStatusCodeNotSet || code == kMipmapDebugStatusCodeNoTexture || code == kMipmapDebugStatusCodeNoAsync || code == kMipmapDebugStatusCodeTerrain);
447
448 // draw "bold"
449 UNITY_LOOP for (uint i = 0; i < 4; ++i)
450 {
451 const bool isFont = SampleDebugFontNumber2Digits(unormCoord - pos + uint2(i % 2, i / 2), code);
452 UNITY_FLATTEN if (isFont)
453 {
454 outputColor = invertColor ? darkTextColor : lightTextColor;
455 break;
456 }
457 }
458 }
459 }
460 else
461 outputColor = borderColor;
462 }
463
464 uint2 textLocation = pos + uint2(_kLegendEntryBlockSize + _kLegendPaddingTextRight, 0);
465 DrawString(stringIndex, unormCoord, lightTextColor, textLocation, outputColor);
466
467 pos.y -= _kLegendEntryBlockSize + _kLegendMargin;
468}
469
470// Drawing a legend entry with an status code (no hatching)
471void DrawLegendEntry(uint2 unormCoord, uint stringIndex, int code, float3 entryColor, inout uint2 pos, inout real3 outputColor)
472{
473 DrawLegendEntry(unormCoord, stringIndex, code, entryColor, false, real3(0.1, 0.1, 0.1), pos, outputColor);
474}
475
476// Drawing a legend entry with just a color (no status code)
477void DrawLegendEntry(uint2 unormCoord, uint stringIndex, float3 entryColor, real3 hatchingColor, inout uint2 pos, inout real3 outputColor)
478{
479 DrawLegendEntry(unormCoord, stringIndex, -1, entryColor, true, hatchingColor, pos, outputColor);
480}
481
482// Drawing a legend entry with just a color (no status code)
483void DrawLegendEntry(uint2 unormCoord, uint stringIndex, float3 entryColor, bool hatched, inout uint2 pos, inout real3 outputColor)
484{
485 DrawLegendEntry(unormCoord, stringIndex, -1, entryColor, hatched, real3(0.1, 0.1, 0.1), pos, outputColor);
486}
487
488// Drawing a legend entry without hatching or status code
489void DrawLegendEntry(uint2 unormCoord, uint stringIndex, float3 entryColor, inout uint2 pos, inout real3 outputColor)
490{
491 DrawLegendEntry(unormCoord, stringIndex, -1, entryColor, false, real3(0.1, 0.1, 0.1), pos, outputColor);
492}
493
494void DrawLegendBackground(float2 texCoord, float4 screenSize, int numEntries, inout real3 color, out uint2 initialEntryPos)
495{
496 const int largestStringSize = 31;
497 const int requiredWidth = largestStringSize * DEBUG_FONT_TEXT_SCALE_WIDTH + _kLegendPaddingTextRight + _kLegendEntryBlockSize + 2 * _kLegendMargin;
498 const int requiredHeight = numEntries * (_kLegendEntryBlockSize + _kLegendMargin) + _kLegendMargin;
499
500 // Screen space (fixed x, fixed y, rel x, rel y)
501 const int heightOfLegendBar = _kLegendBarPaddingBottom + _kLegendBarHeight + _kLegendBarBorderThickness;
502 const float4 legendPosition = float4(screenSize.x - requiredWidth - _kLegendBarPaddingHorizontal, heightOfLegendBar + _kLegendPaddingBottom, 0, 0);
503 const float4 legendSize = float4(requiredWidth, requiredHeight, 0, 0);
504 const float4 legendBorderThickness = float4(_kLegendBorderSize, _kLegendBorderSize, 0, 0);
505 const float4 legendWithBorderPosition = legendPosition - legendBorderThickness;
506 const float4 legendWithBorderSize = legendSize + 2 * legendBorderThickness;
507
508 // Screen UV space
509 const float2 legendPositionUV = legendPosition.xy * screenSize.zw + legendPosition.zw;
510 const float2 legendSizeUV = legendSize.xy * screenSize.zw + legendSize.zw;
511 const float2 legendWithBorderPositionUV = legendWithBorderPosition.xy * screenSize.zw + legendWithBorderPosition.zw;
512 const float2 legendWithBorderSizeUV = legendWithBorderSize.xy * screenSize.zw + legendWithBorderSize.zw;
513
514 // Legend (with border) space
515 const float2 legendBorderCoord = (texCoord - legendWithBorderPositionUV) / legendWithBorderSizeUV;
516 const float2 legendCoord = (texCoord - legendPositionUV) / legendSizeUV;
517
518 // Draw Legend border
519 if (all(legendBorderCoord >= 0) && all(legendBorderCoord <= 1))
520 color = real3(0.1, 0.1, 0.1);
521
522 // Draw Legend background
523 if (all(legendCoord >= 0) && all(legendCoord <= 1))
524 color = real3(0.022, 0.022, 0.022);
525
526 initialEntryPos = uint2(legendPosition.xy) + uint2(_kLegendMargin, requiredHeight - _kLegendMargin - _kLegendEntryBlockSize);
527}
528
529void DrawLegendBar(float2 texCoord, float4 screenSize, inout real3 color)
530{
531 // Screen space (fixed x, fixed y, rel x, rel y)
532 const float4 legendBarPosition = float4(_kLegendBarPaddingHorizontal, _kLegendBarPaddingBottom, 0, 0);
533 const float4 legendBarSize = float4(-_kLegendBarPaddingHorizontal * 2, _kLegendBarHeight, 1, 0);
534 const float4 legendBarBorderThickness = float4(_kLegendBarBorderThickness, _kLegendBarBorderThickness, 0, 0);
535 const float4 legendBarWithBorderPosition = legendBarPosition - legendBarBorderThickness;
536 const float4 legendBarWithBorderSize = legendBarSize + 2 * legendBarBorderThickness;
537
538 // Screen UV space
539 const float2 legendBarWithBorderPositionUV = legendBarWithBorderPosition.xy * screenSize.zw + legendBarWithBorderPosition.zw;
540 const float2 legendBarWithBorderSizeUV = legendBarWithBorderSize.xy * screenSize.zw + legendBarWithBorderSize.zw;
541
542 // Legend bar (with border) space
543 const float2 legendBarBorderCoord = (texCoord - legendBarWithBorderPositionUV) / legendBarWithBorderSizeUV;
544
545 // Draw legend bar (still to be filled later)
546 if (all(legendBarBorderCoord >= 0) && all(legendBarBorderCoord <= 1))
547 color = real3(0.1, 0.1, 0.1);
548}
549
550bool InsideLegendBar(float2 texCoord, float4 screenSize, out float xCoord)
551{
552 // Screen space (fixed x, fixed y, rel x, rel y)
553 const float4 legendBarPosition = float4(_kLegendBarPaddingHorizontal, _kLegendBarPaddingBottom, 0, 0);
554 const float4 legendBarSize = float4(-_kLegendBarPaddingHorizontal * 2, _kLegendBarHeight, 1, 0);
555
556 // Screen UV space
557 const float2 legendBarPositionUV = legendBarPosition.xy * screenSize.zw + legendBarPosition.zw;
558 const float2 legendBarSizeUV = legendBarSize.xy * screenSize.zw + legendBarSize.zw;
559
560 // Legend bar space
561 const float2 legendBarCoord = (texCoord - legendBarPositionUV) / legendBarSizeUV;
562
563 // Check if inside the legend bar (and fill in the "legend bar space" horizontal coordinate)
564 xCoord = legendBarCoord.x;
565 return all(legendBarCoord >= 0) && all(legendBarCoord <= 1);
566}
567
568void DrawLabelBarBackground(float2 texCoord, float4 screenSize, inout real3 color)
569{
570 // Screen space (fixed x, fixed y, rel x, rel y)
571 const float4 labelBarPosition = float4(_kLegendBarPaddingHorizontal, 0, 0, 0);
572 const float4 labelBarSize = float4(-_kLegendBarPaddingHorizontal * 2, _kLegendBarPaddingBottom - _kLegendBarBorderThickness, 1, 0);
573
574 // Screen UV space
575 const float2 labelBarPositionUV = labelBarPosition.xy * screenSize.zw + labelBarPosition.zw;
576 const float2 labelBarSizeUV = labelBarSize.xy * screenSize.zw + labelBarSize.zw;
577
578 // Label bar space
579 const float2 labelBarCoord = (texCoord - labelBarPositionUV) / labelBarSizeUV;
580
581 // Draw label bar background
582 if (all(labelBarCoord >= 0) && all(labelBarCoord <= 1))
583 color = real3(0.022, 0.022, 0.022);
584}
585
586void DrawTwoValueLabelBar(uint2 unormCoord, float4 screenSize, uint leftStringId, uint rightStringId, inout real3 color)
587{
588 const float3 textColor = float3(1,1,1);
589
590 // Draw left and right labels
591 const uint2 startPosition = uint2(_kLegendBarPaddingHorizontal, 0);
592 const uint2 endPosition = uint2(screenSize.x - _kLegendBarPaddingHorizontal - DEBUG_FONT_TEXT_SCALE_WIDTH, 0);
593
594 DrawString(leftStringId, unormCoord, textColor, startPosition, color);
595 DrawString(rightStringId, unormCoord, textColor, endPosition, true, color);
596}
597
598void DrawUniformlySpreadValues(uint2 unormCoord, float4 screenSize, uint numValues, uint startValue, inout real3 color)
599{
600 const float bucketWidth = (screenSize.x - 2 * _kLegendBarPaddingHorizontal) / numValues;
601
602 for (uint i = 0; i < numValues; ++i)
603 {
604 const uint bucketLabel = uint(i + startValue);
605 const uint labelOffset = bucketLabel < 10 ? DEBUG_FONT_TEXT_SCALE_WIDTH / 2 : DEBUG_FONT_TEXT_SCALE_WIDTH;
606 const uint2 labelStartCoord = uint2(_kLegendBarPaddingHorizontal + i * bucketWidth + bucketWidth / 2 - labelOffset, 0);
607
608 const uint2 pixCoord = unormCoord - labelStartCoord;
609 if (SampleDebugFontNumberAllDigits(pixCoord, bucketLabel))
610 color = real3(1, 1, 1);
611 }
612}
613
614
615// Legend drawing functions
616// ------------------------
617
618void DrawMipCountLegend(float2 texCoord, float4 screenSize, inout real3 color)
619{
620 const uint2 unormCoord = texCoord * screenSize.xy;
621 const real maxMipCount = 14;
622
623 // Draw legend
624 uint2 pos;
625 DrawLegendBackground(texCoord, screenSize, 2, color, pos);
626 DrawLegendEntry(unormCoord, _kNoMipCountIndex, kMipmapDebugInvalidMipCount, float3(0.9, 0.9, 0.9), pos, color);
627 DrawLegendEntry(unormCoord, _kTooManyMipsIndex, kMipmapDebugTooManyMips, float3(0.9, 0.9, 0.9), pos, color);
628
629 // Draw legend bar
630 DrawLegendBar(texCoord, screenSize, color);
631
632 float xCoord;
633 if (InsideLegendBar(texCoord, screenSize, xCoord))
634 {
635 // Compute bucket index
636 const int bucket = ceil(xCoord * maxMipCount);
637 bool needsHatching;
638 color = GetDebugMipCountColor(bucket, needsHatching);
639 if (needsHatching)
640 // no need to get the hatching color (to keep in sync with rendering), it's always dark anyway inside the legend bar
641 HatchColor(unormCoord, color);
642 }
643
644 DrawLabelBarBackground(texCoord, screenSize, color);
645 DrawUniformlySpreadValues(unormCoord, screenSize, maxMipCount, 1, color);
646}
647
648void DrawMipRatioLegend(float2 texCoord, float4 screenSize, inout real3 color)
649{
650 const uint2 unormCoord = texCoord * screenSize.xy;
651
652 // Draw legend bar
653 DrawLegendBar(texCoord, screenSize, color);
654
655 float xCoord;
656 if (InsideLegendBar(texCoord, screenSize, xCoord))
657 color = GetDebugMipRatioColor(xCoord);
658
659 // Text labels
660 DrawLabelBarBackground(texCoord, screenSize, color);
661 DrawTwoValueLabelBar(unormCoord, screenSize, _kLowPixelDensityIndex, _kHighPixelDensityIndex, color);
662}
663
664void DrawMipPriorityLegend(float2 texCoord, float4 screenSize, inout real3 color)
665{
666 const uint2 unormCoord = texCoord * screenSize.xy;
667
668 // Draw the legend
669 uint2 pos;
670 DrawLegendBackground(texCoord, screenSize, 1, color, pos);
671 DrawLegendEntry(unormCoord, _kNotStreamingIndex, kMipMapDebugStatusColorNotStreaming, pos, color);
672
673 // Draw legend bar
674 DrawLegendBar(texCoord, screenSize, color);
675
676 float xCoord;
677 if (InsideLegendBar(texCoord, screenSize, xCoord))
678 color = GetDebugMipPriorityColor(xCoord);
679
680 // Text labels
681 DrawLabelBarBackground(texCoord, screenSize, color);
682 DrawTwoValueLabelBar(unormCoord, screenSize, _kLowPriorityIndex, _kHighPriorityIndex, color);
683}
684
685void DrawMipRecentlyUpdatedLegend(float2 texCoord, float4 screenSize, bool perMaterial, inout real3 color)
686{
687 const uint2 unormCoord = texCoord * screenSize.xy;
688
689 // Draw the legend
690 uint2 pos;
691 DrawLegendBackground(texCoord, screenSize, 1, color, pos);
692 if(perMaterial)
693 DrawLegendEntry(unormCoord, _kStatusNoTexturesAreStreaming, kMipMapDebugStatusColorNotStreaming, pos, color);
694 else
695 DrawLegendEntry(unormCoord, _kNotStreamingIndex, kMipMapDebugStatusColorNotStreaming, pos, color);
696
697 // Draw legend bar
698 DrawLegendBar(texCoord, screenSize, color);
699
700 float xCoord;
701 if (InsideLegendBar(texCoord, screenSize, xCoord))
702 color = GetDebugMipRecentlyUpdatedColor(xCoord);
703
704 // Text labels
705 DrawLabelBarBackground(texCoord, screenSize, color);
706 DrawTwoValueLabelBar(unormCoord, screenSize, _kRecentlyUpdated, _kNotRecentlyUpdated, color);
707}
708
709void DrawMipStreamingStatusLegend(float2 texCoord, float4 screenSize, bool needsStatusCodes, inout real3 color)
710{
711 const uint2 unormCoord = texCoord * screenSize.xy;
712
713 // Draw the legend
714 uint2 pos;
715 const int numEntries = needsStatusCodes ? 11 : 6;
716 DrawLegendBackground(texCoord, screenSize, numEntries, color, pos);
717
718 DrawLegendEntry(unormCoord, _kStatusNoTextureIndex, kMipMapDebugStatusColorNoTexture, pos, color);
719 DrawLegendEntry(unormCoord, _kNotStreamingIndex, kMipMapDebugStatusColorNotStreaming, pos, color);
720 if (needsStatusCodes)
721 {
722 DrawLegendEntry(unormCoord, _kStatusStreamerDisabledIndex, kMipmapDebugStatusCodeStreamerDisabled, kMipMapDebugStatusColorNotStreaming, pos, color);
723 DrawLegendEntry(unormCoord, _kStatusMessageNoMipMapIndex, kMipmapDebugStatusCodeNoMipMap, kMipMapDebugStatusColorNotStreaming, pos, color);
724 DrawLegendEntry(unormCoord, _kStatusMessageNotSetToStreamIndex, kMipmapDebugStatusCodeNotSetToStream, kMipMapDebugStatusColorNotStreaming, pos, color);
725 }
726 DrawLegendEntry(unormCoord, _kStreamingIndex, kMipMapDebugStatusColorStreaming, pos, color);
727 DrawLegendEntry(unormCoord, _kStreamingManuallyIndex, kMipMapDebugStatusColorStreaming, true, pos, color);
728
729 DrawLegendEntry(unormCoord, _kStatusWarningIndex, kMipMapDebugStatusColorWarning, pos, color);
730 if (needsStatusCodes)
731 {
732 DrawLegendEntry(unormCoord, _kStatusMessageNoAsyncIndex, kMipmapDebugStatusCodeNoAsync, kMipMapDebugStatusColorWarning, pos, color);
733 DrawLegendEntry(unormCoord, _kStatusMessageTerrainIndex, kMipmapDebugStatusCodeTerrain, kMipMapDebugStatusColorWarning, pos, color);
734 }
735
736 DrawLegendEntry(unormCoord, _kStatusUnknownIndex, kMipMapDebugStatusColorUnknown, pos, color);
737}
738
739void DrawMipStreamingStatusPerMaterialLegend(float2 texCoord, float4 screenSize, inout real3 color)
740{
741 const uint2 unormCoord = texCoord * screenSize.xy;
742
743 // Draw the legend
744 uint2 pos;
745 DrawLegendBackground(texCoord, screenSize, 7, color, pos);
746
747 DrawLegendEntry(unormCoord, _kStatusNoTexturesIndex, kMipMapDebugStatusColorNoTexture, pos, color);
748 DrawLegendEntry(unormCoord, _kStatusNoTexturesAreStreaming, kMipMapDebugStatusColorNotStreaming, pos, color);
749 DrawLegendEntry(unormCoord, _kStatusSomeTexturesAreStreaming, kMipMapDebugMaterialStatusColorSomeStreaming, pos, color);
750 DrawLegendEntry(unormCoord, _kStatusSomeTexturesAreStreamingSomeManually, kMipMapDebugMaterialStatusColorSomeStreaming, true, pos, color);
751 DrawLegendEntry(unormCoord, _kStatusAllTexturesAreStreaming, kMipMapDebugStatusColorStreaming, pos, color);
752 DrawLegendEntry(unormCoord, _kStatusAllTexturesAreStreamingSomeManually, kMipMapDebugStatusColorStreaming, true, pos, color);
753 DrawLegendEntry(unormCoord, _kStatusSomeTexturesHaveIssues, kMipMapDebugStatusColorWarning, pos, color);
754}
755
756void DrawTextureStreamingPerformanceLegend(float2 texCoord, float4 screenSize, inout real3 color)
757{
758 const uint2 unormCoord = texCoord * screenSize.xy;
759
760 // Draw the legend
761 uint2 pos;
762 DrawLegendBackground(texCoord, screenSize, 5, color, pos);
763
764 DrawLegendEntry(unormCoord, _kNotStreamingIndex, kMipMapDebugStatusColorNotStreaming, pos, color);
765 DrawLegendEntry(unormCoord, _kBudgetSavingMips, kMipmapDebugBudgetSavingMips, pos, color);
766 DrawLegendEntry(unormCoord, _kBudgetSavingMipsWithCache, kMipmapDebugBudgetSavingMips, true, pos, color);
767 DrawLegendEntry(unormCoord, _kBudgetNothingSaved, kMipmapDebugBudgetFullResolution, pos, color);
768 DrawLegendEntry(unormCoord, _kBudgetMissingMips, kMipmapDebugBudgetMissing, pos, color);
769}
770
771#endif // UNITY_DEBUG_MIPMAP_STREAMING_INCLUDED