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

using System.Collections;
using System.Collections.Generic;
using Oculus.Avatar;
using UnityEngine;
public class OvrAvatarTextureCopyManager : MonoBehaviour
{
[System.Serializable]
public struct FallbackTextureSet
{
public bool Initialized;
public Texture2D DiffuseRoughness;
public Texture2D Normal;
}
// Fallback texture sets are indexed with ovrAvatarAssetLevelOfDetail.
// We currently only use 1, 3 (mobile default), 5 (PC default).
public FallbackTextureSet[] FallbackTextureSets = new FallbackTextureSet[(int)ovrAvatarAssetLevelOfDetail.Highest + 1];
struct CopyTextureParams
{
public Texture Src;
public Texture Dst;
public int Mip;
public int SrcSize;
public int DstElement;
public CopyTextureParams(
Texture src,
Texture dst,
int mip,
int srcSize,
int dstElement)
{
Src = src;
Dst = dst;
Mip = mip;
SrcSize = srcSize;
DstElement = dstElement;
}
}
private Queue<CopyTextureParams> texturesToCopy;
public struct TextureSet
{
// Contains all texture asset IDs that are part of an avatar spec.
// Used by DeleteTextureSet().
// Textures that are part of combined mesh avatars can be safely deleted once they have been
// uploaded to the texture arrays.
// Textures that are part of single component meshes will remain in memory.
public Dictionary<ulong, bool> TextureIDSingleMeshPair;
public bool IsProcessed;
public TextureSet(
Dictionary<ulong, bool> textureIDSingleMeshPair,
bool isProcessed)
{
TextureIDSingleMeshPair = textureIDSingleMeshPair;
IsProcessed = isProcessed;
}
}
private Dictionary<int, TextureSet> textureSets;
private const int TEXTURES_TO_COPY_QUEUE_CAPACITY = 256;
private const int COPIES_PER_FRAME = 8;
// Fallback texture paths are indexed with ovrAvatarAssetLevelOfDetail
// We currently only use 1, 3 (mobile default), 5 (PC default)
private readonly string[] FALLBACK_TEXTURE_PATHS_DIFFUSE_ROUGHNESS = new string[]
{
"null",
PATH_LOWEST_DIFFUSE_ROUGHNESS,
"null",
PATH_MEDIUM_DIFFUSE_ROUGHNESS,
"null",
PATH_HIGHEST_DIFFUSE_ROUGHNESS,
};
private readonly string[] FALLBACK_TEXTURE_PATHS_NORMAL = new string[]
{
"null",
PATH_LOWEST_NORMAL,
"null",
PATH_MEDIUM_NORMAL,
"null",
PATH_HIGHEST_NORMAL,
};
private const string PATH_HIGHEST_DIFFUSE_ROUGHNESS = "FallbackTextures/fallback_diffuse_roughness_2048";
private const string PATH_MEDIUM_DIFFUSE_ROUGHNESS = "FallbackTextures/fallback_diffuse_roughness_1024";
private const string PATH_LOWEST_DIFFUSE_ROUGHNESS = "FallbackTextures/fallback_diffuse_roughness_256";
private const string PATH_HIGHEST_NORMAL = "FallbackTextures/fallback_normal_2048";
private const string PATH_MEDIUM_NORMAL = "FallbackTextures/fallback_normal_1024";
private const string PATH_LOWEST_NORMAL = "FallbackTextures/fallback_normal_256";
private const int GPU_TEXTURE_COPY_WAIT_TIME = 10;
public OvrAvatarTextureCopyManager()
{
texturesToCopy = new Queue<CopyTextureParams>(TEXTURES_TO_COPY_QUEUE_CAPACITY);
textureSets = new Dictionary<int, TextureSet>();
}
public void Update()
{
if (texturesToCopy.Count == 0)
{
return;
}
lock (texturesToCopy)
{
for (int i = 0; i < Mathf.Min(COPIES_PER_FRAME, texturesToCopy.Count); ++i)
{
CopyTexture(texturesToCopy.Dequeue());
}
}
}
public int GetTextureCount()
{
return texturesToCopy.Count;
}
public void CopyTexture(
Texture src,
Texture dst,
int mipLevel,
int mipSize,
int dstElement,
bool useQueue = true)
{
var copyTextureParams = new CopyTextureParams(src, dst, mipLevel, mipSize, dstElement);
if (useQueue)
{
lock (texturesToCopy)
{
if (texturesToCopy.Count < TEXTURES_TO_COPY_QUEUE_CAPACITY)
{
texturesToCopy.Enqueue(copyTextureParams);
}
else
{
// Queue is full so copy texture immediately
CopyTexture(copyTextureParams);
}
}
}
else
{
CopyTexture(copyTextureParams);
}
}
private void CopyTexture(CopyTextureParams copyTextureParams)
{
Graphics.CopyTexture(
copyTextureParams.Src,
0,
copyTextureParams.Mip,
copyTextureParams.Dst,
copyTextureParams.DstElement,
copyTextureParams.Mip);
}
public void AddTextureIDToTextureSet(int gameobjectID, ulong textureID, bool isSingleMesh)
{
if (!textureSets.ContainsKey(gameobjectID))
{
TextureSet newTextureSet = new TextureSet(new Dictionary<ulong, bool>(), false);
newTextureSet.TextureIDSingleMeshPair.Add(textureID, isSingleMesh);
textureSets.Add(gameobjectID, newTextureSet);
}
else
{
bool TexIDSingleMesh;
if (textureSets[gameobjectID].TextureIDSingleMeshPair.TryGetValue(textureID, out TexIDSingleMesh))
{
if (!TexIDSingleMesh && isSingleMesh)
{
textureSets[gameobjectID].TextureIDSingleMeshPair[textureID] = true;
}
}
else
{
textureSets[gameobjectID].TextureIDSingleMeshPair.Add(textureID, isSingleMesh);
}
}
}
// This is called by a fully loaded avatar using combined mesh to safely delete unused textures.
public void DeleteTextureSet(int gameobjectID)
{
TextureSet textureSetToDelete;
if (!textureSets.TryGetValue(gameobjectID, out textureSetToDelete))
{
return;
};
if (textureSetToDelete.IsProcessed)
{
return;
}
StartCoroutine(DeleteTextureSetCoroutine(textureSetToDelete, gameobjectID));
}
private IEnumerator DeleteTextureSetCoroutine(TextureSet textureSetToDelete, int gameobjectID)
{
// Wait a conservative amount of time for gpu upload to finish. Unity 2017 doesn't support async GPU calls,
// so this 10 second time is a very conservative delay for this process to occur, which should be <1 sec.
yield return new WaitForSeconds(GPU_TEXTURE_COPY_WAIT_TIME);
// Spin if an avatar is loading
while (OvrAvatarSDKManager.Instance.IsAvatarLoading())
{
yield return null;
}
// The avatar's texture set is compared against all other loaded or loading avatar texture sets.
foreach (var textureIdAndSingleMeshFlag in textureSetToDelete.TextureIDSingleMeshPair)
{
bool triggerDelete = !textureIdAndSingleMeshFlag.Value;
if (triggerDelete)
{
foreach (KeyValuePair<int, TextureSet> textureSet in textureSets)
{
if (textureSet.Key == gameobjectID)
{
continue;
}
foreach (var comparisonTextureIDSingleMeshPair in textureSet.Value.TextureIDSingleMeshPair)
{
// Mark the texture as not deletable if it's present in another set and that set hasn't been processed
// or that texture ID is marked as part of a single mesh component.
if (comparisonTextureIDSingleMeshPair.Key == textureIdAndSingleMeshFlag.Key &&
(!textureSet.Value.IsProcessed || comparisonTextureIDSingleMeshPair.Value))
{
triggerDelete = false;
break;
}
}
if (!triggerDelete)
{
break;
}
}
}
if (triggerDelete)
{
Texture2D textureToDelete = OvrAvatarComponent.GetLoadedTexture(textureIdAndSingleMeshFlag.Key);
if (textureToDelete != null)
{
AvatarLogger.Log("Deleting texture " + textureIdAndSingleMeshFlag.Key);
OvrAvatarSDKManager.Instance.DeleteAssetFromCache(textureIdAndSingleMeshFlag.Key);
Destroy(textureToDelete);
}
}
}
textureSetToDelete.IsProcessed = true;
textureSets.Remove(gameobjectID);
}
public void CheckFallbackTextureSet(ovrAvatarAssetLevelOfDetail lod)
{
if (FallbackTextureSets[(int)lod].Initialized)
{
return;
}
InitFallbackTextureSet(lod);
}
private void InitFallbackTextureSet(ovrAvatarAssetLevelOfDetail lod)
{
FallbackTextureSets[(int)lod].DiffuseRoughness = FallbackTextureSets[(int)lod].DiffuseRoughness =
Resources.Load<Texture2D>(FALLBACK_TEXTURE_PATHS_DIFFUSE_ROUGHNESS[(int)lod]);
FallbackTextureSets[(int)lod].Normal = FallbackTextureSets[(int)lod].Normal =
Resources.Load<Texture2D>(FALLBACK_TEXTURE_PATHS_NORMAL[(int)lod]);
FallbackTextureSets[(int)lod].Initialized = true;
}
}