Assignment for RMIT Mixed Reality in 2020
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.

284 lines
9.6 KiB

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using Oculus.Avatar;
  4. using UnityEngine;
  5. public class OvrAvatarTextureCopyManager : MonoBehaviour
  6. {
  7. [System.Serializable]
  8. public struct FallbackTextureSet
  9. {
  10. public bool Initialized;
  11. public Texture2D DiffuseRoughness;
  12. public Texture2D Normal;
  13. }
  14. // Fallback texture sets are indexed with ovrAvatarAssetLevelOfDetail.
  15. // We currently only use 1, 3 (mobile default), 5 (PC default).
  16. public FallbackTextureSet[] FallbackTextureSets = new FallbackTextureSet[(int)ovrAvatarAssetLevelOfDetail.Highest + 1];
  17. struct CopyTextureParams
  18. {
  19. public Texture Src;
  20. public Texture Dst;
  21. public int Mip;
  22. public int SrcSize;
  23. public int DstElement;
  24. public CopyTextureParams(
  25. Texture src,
  26. Texture dst,
  27. int mip,
  28. int srcSize,
  29. int dstElement)
  30. {
  31. Src = src;
  32. Dst = dst;
  33. Mip = mip;
  34. SrcSize = srcSize;
  35. DstElement = dstElement;
  36. }
  37. }
  38. private Queue<CopyTextureParams> texturesToCopy;
  39. public struct TextureSet
  40. {
  41. // Contains all texture asset IDs that are part of an avatar spec.
  42. // Used by DeleteTextureSet().
  43. // Textures that are part of combined mesh avatars can be safely deleted once they have been
  44. // uploaded to the texture arrays.
  45. // Textures that are part of single component meshes will remain in memory.
  46. public Dictionary<ulong, bool> TextureIDSingleMeshPair;
  47. public bool IsProcessed;
  48. public TextureSet(
  49. Dictionary<ulong, bool> textureIDSingleMeshPair,
  50. bool isProcessed)
  51. {
  52. TextureIDSingleMeshPair = textureIDSingleMeshPair;
  53. IsProcessed = isProcessed;
  54. }
  55. }
  56. private Dictionary<int, TextureSet> textureSets;
  57. private const int TEXTURES_TO_COPY_QUEUE_CAPACITY = 256;
  58. private const int COPIES_PER_FRAME = 8;
  59. // Fallback texture paths are indexed with ovrAvatarAssetLevelOfDetail
  60. // We currently only use 1, 3 (mobile default), 5 (PC default)
  61. private readonly string[] FALLBACK_TEXTURE_PATHS_DIFFUSE_ROUGHNESS = new string[]
  62. {
  63. "null",
  64. PATH_LOWEST_DIFFUSE_ROUGHNESS,
  65. "null",
  66. PATH_MEDIUM_DIFFUSE_ROUGHNESS,
  67. "null",
  68. PATH_HIGHEST_DIFFUSE_ROUGHNESS,
  69. };
  70. private readonly string[] FALLBACK_TEXTURE_PATHS_NORMAL = new string[]
  71. {
  72. "null",
  73. PATH_LOWEST_NORMAL,
  74. "null",
  75. PATH_MEDIUM_NORMAL,
  76. "null",
  77. PATH_HIGHEST_NORMAL,
  78. };
  79. private const string PATH_HIGHEST_DIFFUSE_ROUGHNESS = "FallbackTextures/fallback_diffuse_roughness_2048";
  80. private const string PATH_MEDIUM_DIFFUSE_ROUGHNESS = "FallbackTextures/fallback_diffuse_roughness_1024";
  81. private const string PATH_LOWEST_DIFFUSE_ROUGHNESS = "FallbackTextures/fallback_diffuse_roughness_256";
  82. private const string PATH_HIGHEST_NORMAL = "FallbackTextures/fallback_normal_2048";
  83. private const string PATH_MEDIUM_NORMAL = "FallbackTextures/fallback_normal_1024";
  84. private const string PATH_LOWEST_NORMAL = "FallbackTextures/fallback_normal_256";
  85. private const int GPU_TEXTURE_COPY_WAIT_TIME = 10;
  86. public OvrAvatarTextureCopyManager()
  87. {
  88. texturesToCopy = new Queue<CopyTextureParams>(TEXTURES_TO_COPY_QUEUE_CAPACITY);
  89. textureSets = new Dictionary<int, TextureSet>();
  90. }
  91. public void Update()
  92. {
  93. if (texturesToCopy.Count == 0)
  94. {
  95. return;
  96. }
  97. lock (texturesToCopy)
  98. {
  99. for (int i = 0; i < Mathf.Min(COPIES_PER_FRAME, texturesToCopy.Count); ++i)
  100. {
  101. CopyTexture(texturesToCopy.Dequeue());
  102. }
  103. }
  104. }
  105. public int GetTextureCount()
  106. {
  107. return texturesToCopy.Count;
  108. }
  109. public void CopyTexture(
  110. Texture src,
  111. Texture dst,
  112. int mipLevel,
  113. int mipSize,
  114. int dstElement,
  115. bool useQueue = true)
  116. {
  117. var copyTextureParams = new CopyTextureParams(src, dst, mipLevel, mipSize, dstElement);
  118. if (useQueue)
  119. {
  120. lock (texturesToCopy)
  121. {
  122. if (texturesToCopy.Count < TEXTURES_TO_COPY_QUEUE_CAPACITY)
  123. {
  124. texturesToCopy.Enqueue(copyTextureParams);
  125. }
  126. else
  127. {
  128. // Queue is full so copy texture immediately
  129. CopyTexture(copyTextureParams);
  130. }
  131. }
  132. }
  133. else
  134. {
  135. CopyTexture(copyTextureParams);
  136. }
  137. }
  138. private void CopyTexture(CopyTextureParams copyTextureParams)
  139. {
  140. Graphics.CopyTexture(
  141. copyTextureParams.Src,
  142. 0,
  143. copyTextureParams.Mip,
  144. copyTextureParams.Dst,
  145. copyTextureParams.DstElement,
  146. copyTextureParams.Mip);
  147. }
  148. public void AddTextureIDToTextureSet(int gameobjectID, ulong textureID, bool isSingleMesh)
  149. {
  150. if (!textureSets.ContainsKey(gameobjectID))
  151. {
  152. TextureSet newTextureSet = new TextureSet(new Dictionary<ulong, bool>(), false);
  153. newTextureSet.TextureIDSingleMeshPair.Add(textureID, isSingleMesh);
  154. textureSets.Add(gameobjectID, newTextureSet);
  155. }
  156. else
  157. {
  158. bool TexIDSingleMesh;
  159. if (textureSets[gameobjectID].TextureIDSingleMeshPair.TryGetValue(textureID, out TexIDSingleMesh))
  160. {
  161. if (!TexIDSingleMesh && isSingleMesh)
  162. {
  163. textureSets[gameobjectID].TextureIDSingleMeshPair[textureID] = true;
  164. }
  165. }
  166. else
  167. {
  168. textureSets[gameobjectID].TextureIDSingleMeshPair.Add(textureID, isSingleMesh);
  169. }
  170. }
  171. }
  172. // This is called by a fully loaded avatar using combined mesh to safely delete unused textures.
  173. public void DeleteTextureSet(int gameobjectID)
  174. {
  175. TextureSet textureSetToDelete;
  176. if (!textureSets.TryGetValue(gameobjectID, out textureSetToDelete))
  177. {
  178. return;
  179. };
  180. if (textureSetToDelete.IsProcessed)
  181. {
  182. return;
  183. }
  184. StartCoroutine(DeleteTextureSetCoroutine(textureSetToDelete, gameobjectID));
  185. }
  186. private IEnumerator DeleteTextureSetCoroutine(TextureSet textureSetToDelete, int gameobjectID)
  187. {
  188. // Wait a conservative amount of time for gpu upload to finish. Unity 2017 doesn't support async GPU calls,
  189. // so this 10 second time is a very conservative delay for this process to occur, which should be <1 sec.
  190. yield return new WaitForSeconds(GPU_TEXTURE_COPY_WAIT_TIME);
  191. // Spin if an avatar is loading
  192. while (OvrAvatarSDKManager.Instance.IsAvatarLoading())
  193. {
  194. yield return null;
  195. }
  196. // The avatar's texture set is compared against all other loaded or loading avatar texture sets.
  197. foreach (var textureIdAndSingleMeshFlag in textureSetToDelete.TextureIDSingleMeshPair)
  198. {
  199. bool triggerDelete = !textureIdAndSingleMeshFlag.Value;
  200. if (triggerDelete)
  201. {
  202. foreach (KeyValuePair<int, TextureSet> textureSet in textureSets)
  203. {
  204. if (textureSet.Key == gameobjectID)
  205. {
  206. continue;
  207. }
  208. foreach (var comparisonTextureIDSingleMeshPair in textureSet.Value.TextureIDSingleMeshPair)
  209. {
  210. // Mark the texture as not deletable if it's present in another set and that set hasn't been processed
  211. // or that texture ID is marked as part of a single mesh component.
  212. if (comparisonTextureIDSingleMeshPair.Key == textureIdAndSingleMeshFlag.Key &&
  213. (!textureSet.Value.IsProcessed || comparisonTextureIDSingleMeshPair.Value))
  214. {
  215. triggerDelete = false;
  216. break;
  217. }
  218. }
  219. if (!triggerDelete)
  220. {
  221. break;
  222. }
  223. }
  224. }
  225. if (triggerDelete)
  226. {
  227. Texture2D textureToDelete = OvrAvatarComponent.GetLoadedTexture(textureIdAndSingleMeshFlag.Key);
  228. if (textureToDelete != null)
  229. {
  230. AvatarLogger.Log("Deleting texture " + textureIdAndSingleMeshFlag.Key);
  231. OvrAvatarSDKManager.Instance.DeleteAssetFromCache(textureIdAndSingleMeshFlag.Key);
  232. Destroy(textureToDelete);
  233. }
  234. }
  235. }
  236. textureSetToDelete.IsProcessed = true;
  237. textureSets.Remove(gameobjectID);
  238. }
  239. public void CheckFallbackTextureSet(ovrAvatarAssetLevelOfDetail lod)
  240. {
  241. if (FallbackTextureSets[(int)lod].Initialized)
  242. {
  243. return;
  244. }
  245. InitFallbackTextureSet(lod);
  246. }
  247. private void InitFallbackTextureSet(ovrAvatarAssetLevelOfDetail lod)
  248. {
  249. FallbackTextureSets[(int)lod].DiffuseRoughness = FallbackTextureSets[(int)lod].DiffuseRoughness =
  250. Resources.Load<Texture2D>(FALLBACK_TEXTURE_PATHS_DIFFUSE_ROUGHNESS[(int)lod]);
  251. FallbackTextureSets[(int)lod].Normal = FallbackTextureSets[(int)lod].Normal =
  252. Resources.Load<Texture2D>(FALLBACK_TEXTURE_PATHS_NORMAL[(int)lod]);
  253. FallbackTextureSets[(int)lod].Initialized = true;
  254. }
  255. }