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.
 
 
 
 
 

328 lines
12 KiB

using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace UnityEngine.Rendering.HighDefinition
{
// This class allows us to map each of texture to an internal Slice structure. The set of slices that have been produced are then stored into a child-class specific structure
abstract class TextureCache
{
// Name that identifies the texture cache (Mainly used to generate the storage texture name)
protected string m_CacheName;
// The number of mipmap that is deduced from the maximal resolution
protected int m_NumMipLevels;
// In the texture cache, a given texture/texture hash can request more than one single slot in the cache. The set of slots that match a single texture hash is thus called a "slice"
protected int m_SliceSize;
// Counter of input texture that have been fed to this structure
private int m_NumTextures;
// Array that maps input textureIDs into the slices indexes
Dictionary<uint, int> m_LocatorInSliceDictionnary;
// This structure defines the mapping between an input texture and the internal structure
private struct SliceEntry
{
// ID of the internal structure
public uint texId;
// This counter tracks the number of frames since this slice was requested. The mechanic behind this is due to the fact that the number storage of the cache is limited
public uint countLRU;
// Hash that tracks the version of the input texture (allows us to know if it needs an update)
public uint sliceEntryHash;
}
// The array of slices that the cache holds
private SliceEntry[] m_SliceArray;
// Array with the slices sorted according to their countLRU
private int[] m_SortedIdxArray;
// Array used when we use the texture as itself's representative in the slices
private Texture[] m_autoContentArray = new Texture[1];
// Constant values
private static uint g_MaxFrameCount = unchecked((uint)(-1));
private static uint g_InvalidTexID = (uint)0;
protected const int k_FP16SizeInByte = 2;
protected const int k_NbChannel = 4;
protected const float k_MipmapFactorApprox = 1.33f;
internal const int k_MaxSupported = 250; //vary along hardware and cube/2D but 250 should be always safe
protected TextureCache(string cacheName, int sliceSize = 1)
{
m_CacheName = cacheName;
m_SliceSize = sliceSize;
m_NumTextures = 0;
m_NumMipLevels = 0;
}
virtual public bool IsCreated()
{
return true;
}
public string GetCacheName()
{
return m_CacheName;
}
public int GetNumMipLevels()
{
return m_NumMipLevels;
}
// Function that initialize the texture cache with a maximal number of textures in the cache
protected bool AllocTextureArray(int numTextures)
{
if (numTextures >= m_SliceSize)
{
m_SliceArray = new SliceEntry[numTextures];
m_SortedIdxArray = new int[numTextures];
m_LocatorInSliceDictionnary = new Dictionary<uint, int>();
m_NumTextures = numTextures / m_SliceSize;
for (int i = 0; i < m_NumTextures; i++)
{
m_SliceArray[i].countLRU = g_MaxFrameCount; // never used before
m_SliceArray[i].texId = g_InvalidTexID;
m_SortedIdxArray[i] = i;
}
}
return numTextures >= m_SliceSize;
}
// This function returns the internal storage texture of the cache. It is specified by the child class.
abstract public Texture GetTexCache();
// Function that reserves a slice using a texture and it returns a update flag that tells if the stored value matches the input one
public int ReserveSlice(Texture texture, uint textureHash, out bool needUpdate)
{
// Reset the update flag
needUpdate = false;
// Check the validity of the input texture
if (texture == null)
return -1;
var texId = (uint)texture.GetInstanceID();
if (texId == g_InvalidTexID)
return -1;
// Search for existing copy in the texId to slice index dictionary
var sliceIndex = -1;
if (m_LocatorInSliceDictionnary.TryGetValue(texId, out sliceIndex))
{
// We need to update the texture if the hash does not match the one in the slice
needUpdate |= (m_SliceArray[sliceIndex].sliceEntryHash != textureHash);
Debug.Assert(m_SliceArray[sliceIndex].texId == texId);
}
else
{
// This texture was not in the slice array. We need to look for first non zero entry.
// Will by the least recently used entry since the array was pre-sorted (in linear time) in NewFrame()
var bFound = false;
int j = 0, idx = 0;
while ((!bFound) && j < m_NumTextures)
{
idx = m_SortedIdxArray[j];
if (m_SliceArray[idx].countLRU == 0)
++j; // if entry already snagged by a new texture in this frame then ++j
else
bFound = true;
}
if (bFound)
{
needUpdate = true;
// if we are replacing an existing entry delete it from m_locatorInSliceArray.
if (m_SliceArray[idx].texId != g_InvalidTexID)
{
m_LocatorInSliceDictionnary.Remove(m_SliceArray[idx].texId);
}
m_LocatorInSliceDictionnary.Add(texId, idx);
m_SliceArray[idx].texId = texId;
sliceIndex = idx;
}
}
if (sliceIndex != -1)
{
m_SliceArray[sliceIndex].countLRU = 0; // mark slice as in use this frame
}
needUpdate |= !IsCreated();
return sliceIndex;
}
// In case the texture content with which we update the cache is not the input texture, we need to provide the right update count.
public bool UpdateSlice(CommandBuffer cmd, int sliceIndex, Texture[] contentArray, uint textureHash)
{
// Make sure the content matches the size of the texture cache
Debug.Assert(contentArray.Length == m_SliceSize);
// Update the hash
SetSliceHash(sliceIndex, textureHash);
// transfer new slice to sliceIndex from source texture
return TransferToSlice(cmd, sliceIndex, contentArray);
}
public bool UpdateSlice(CommandBuffer cmd, int sliceIndex, Texture texture, uint textureHash)
{
// Make sure the content matches the size of the texture cache
Debug.Assert(m_SliceSize == 1);
// Update the hash
SetSliceHash(sliceIndex, textureHash);
// transfer new slice to sliceIndex from source texture
m_autoContentArray[0] = texture;
return TransferToSlice(cmd, sliceIndex, m_autoContentArray);
}
public void SetSliceHash(int sliceIndex, uint hash)
{
// transfer new slice to sliceIndex from source texture
m_SliceArray[sliceIndex].sliceEntryHash = hash;
}
// Push the content to the internal target slice. Should be overridden by the child class. It will return fals if it fails to update (mainly sub-textures's size do not match)
protected abstract bool TransferToSlice(CommandBuffer cmd, int sliceIndex, Texture[] textureArray);
public int FetchSlice(CommandBuffer cmd, Texture texture, uint textureHash, bool forceReinject = false)
{
bool needUpdate = false;
var sliceIndex = ReserveSlice(texture, textureHash, out needUpdate);
var bSwapSlice = forceReinject || needUpdate;
// wrap up
Debug.Assert(sliceIndex != -1, "The texture cache doesn't have enough space to store all textures. Please either increase the size of the texture cache, or use fewer unique textures.");
if (sliceIndex != -1 && bSwapSlice)
{
m_autoContentArray[0] = texture;
UpdateSlice(cmd, sliceIndex, m_autoContentArray, textureHash);
}
return sliceIndex;
}
private static List<int> s_TempIntList = new List<int>();
public void NewFrame()
{
var numNonZeros = 0;
s_TempIntList.Clear();
for (int i = 0; i < m_NumTextures; i++)
{
s_TempIntList.Add(m_SortedIdxArray[i]); // copy buffer
if (m_SliceArray[m_SortedIdxArray[i]].countLRU != 0) ++numNonZeros;
}
int nonZerosBase = 0, zerosBase = 0;
for (int i = 0; i < m_NumTextures; i++)
{
if (m_SliceArray[s_TempIntList[i]].countLRU == 0)
{
m_SortedIdxArray[zerosBase + numNonZeros] = s_TempIntList[i];
++zerosBase;
}
else
{
m_SortedIdxArray[nonZerosBase] = s_TempIntList[i];
++nonZerosBase;
}
}
for (int i = 0; i < m_NumTextures; i++)
{
if (m_SliceArray[i].countLRU < g_MaxFrameCount) ++m_SliceArray[i].countLRU; // next frame
}
//for(int q=1; q<m_numTextures; q++)
// assert(m_SliceArray[m_SortedIdxArray[q-1]].CountLRU>=m_SliceArray[m_SortedIdxArray[q]].CountLRU);
}
// should not really be used in general. Assuming lights are culled properly entries will automatically be replaced efficiently.
public void RemoveEntryFromSlice(Texture texture)
{
var texId = (uint)texture.GetInstanceID();
//assert(TexID!=g_InvalidTexID);
if (texId == g_InvalidTexID) return;
// search for existing copy
if (!m_LocatorInSliceDictionnary.ContainsKey(texId))
return;
var sliceIndex = m_LocatorInSliceDictionnary[texId];
//assert(m_SliceArray[sliceIndex].TexID==TexID);
// locate entry sorted by uCountLRU in m_pSortedIdxArray
var foundIdxSortLRU = false;
var i = 0;
while ((!foundIdxSortLRU) && i < m_NumTextures)
{
if (m_SortedIdxArray[i] == sliceIndex) foundIdxSortLRU = true;
else ++i;
}
if (!foundIdxSortLRU)
return;
// relocate sliceIndex to front of m_pSortedIdxArray since uCountLRU will be set to maximum.
for (int j = 0; j < i; j++)
{
m_SortedIdxArray[j + 1] = m_SortedIdxArray[j];
}
m_SortedIdxArray[0] = sliceIndex;
// delete from m_locatorInSliceArray and m_pSliceArray.
m_LocatorInSliceDictionnary.Remove(texId);
m_SliceArray[sliceIndex].countLRU = g_MaxFrameCount; // never used before
m_SliceArray[sliceIndex].texId = g_InvalidTexID;
}
protected int GetNumMips(int width, int height)
{
return GetNumMips(width > height ? width : height);
}
protected int GetNumMips(int dim)
{
var uDim = (uint)dim;
var iNumMips = 0;
while (uDim > 0)
{ ++iNumMips; uDim >>= 1; }
return iNumMips;
}
public static bool isMobileBuildTarget
{
get
{
#if UNITY_EDITOR
switch (EditorUserBuildSettings.activeBuildTarget)
{
case BuildTarget.iOS:
case BuildTarget.Android:
return true;
default:
return false;
}
#else
return Application.isMobilePlatform;
#endif
}
}
public static bool supportsCubemapArrayTextures
{
get
{
return !UnityEngine.Rendering.GraphicsSettings.HasShaderDefine(UnityEngine.Rendering.BuiltinShaderDefine.UNITY_NO_CUBEMAP_ARRAY);
}
}
}
}