Brak opisu
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DebugMipmapStreaming.hlsl 35KB

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