您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

500 行
18 KiB

//#define NATIVE_CODE_FOR_CMD_CONVERT_TEXTURE
using UnityEngine;
using System.Collections.Generic;
using UnityEngine.Rendering;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace UnityEngine.Experimental.Rendering
{
public class TextureCache2D : TextureCache
{
private Texture2DArray m_Cache;
public override void TransferToSlice(CommandBuffer cmd, int sliceIndex, Texture texture)
{
var mismatch = (m_Cache.width != texture.width) || (m_Cache.height != texture.height);
if (texture is Texture2D)
{
mismatch |= (m_Cache.format != (texture as Texture2D).format);
}
if (mismatch)
{
#if NATIVE_CODE_FOR_CMD_CONVERT_TEXTURE
cmd.ConvertTexture(texture, 0, m_Cache, sliceIndex);
#else
UnityEngine.Graphics.ConvertTexture(texture, 0, m_Cache, sliceIndex);
#endif
}
else
{
cmd.CopyTexture(texture, 0, m_Cache, sliceIndex);
}
}
public override Texture GetTexCache()
{
return m_Cache;
}
public bool AllocTextureArray(int numTextures, int width, int height, TextureFormat format, bool isMipMapped)
{
var res = AllocTextureArray(numTextures);
m_NumMipLevels = GetNumMips(width, height);
m_Cache = new Texture2DArray(width, height, numTextures, format, isMipMapped)
{
hideFlags = HideFlags.HideAndDontSave,
wrapMode = TextureWrapMode.Clamp
};
return res;
}
public void Release()
{
Texture.DestroyImmediate(m_Cache); // do I need this?
}
}
public class TextureCacheCubemap : TextureCache
{
private CubemapArray m_Cache;
// the member variables below are only in use when TextureCache.supportsCubemapArrayTextures is false
private Texture2DArray m_CacheNoCubeArray;
private RenderTexture[] m_StagingRTs;
private int m_NumPanoMipLevels;
private Material m_CubeBlitMaterial;
private int m_CubeMipLevelPropName;
private int m_cubeSrcTexPropName;
public override void TransferToSlice(CommandBuffer cmd, int sliceIndex, Texture texture)
{
if (!TextureCache.supportsCubemapArrayTextures)
TransferToPanoCache(cmd, sliceIndex, texture);
else
{
var mismatch = (m_Cache.width != texture.width) || (m_Cache.height != texture.height);
if (texture is Cubemap)
{
mismatch |= (m_Cache.format != (texture as Cubemap).format);
}
if (mismatch)
{
for (int f = 0; f < 6; f++)
{
#if NATIVE_CODE_FOR_CMD_CONVERT_TEXTURE
cmd.ConvertTexture(texture, f, m_Cache, 6 * sliceIndex + f);
#else
UnityEngine.Graphics.ConvertTexture(texture, f, m_Cache, 6 * sliceIndex + f);
#endif
}
}
else
{
for (int f = 0; f < 6; f++)
cmd.CopyTexture(texture, f, m_Cache, 6 * sliceIndex + f);
}
}
}
public override Texture GetTexCache()
{
return !TextureCache.supportsCubemapArrayTextures ? (Texture)m_CacheNoCubeArray : m_Cache;
}
public bool AllocTextureArray(int numCubeMaps, int width, TextureFormat format, bool isMipMapped)
{
var res = AllocTextureArray(numCubeMaps);
m_NumMipLevels = GetNumMips(width, width); // will calculate same way whether we have cube array or not
if (!TextureCache.supportsCubemapArrayTextures)
{
if (!m_CubeBlitMaterial) m_CubeBlitMaterial = new Material(Shader.Find("Hidden/CubeToPano")) { hideFlags = HideFlags.HideAndDontSave };
int panoWidthTop = 4 * width;
int panoHeightTop = 2 * width;
// create panorama 2D array. Hardcoding the render target for now. No convenient way atm to
// map from TextureFormat to RenderTextureFormat and don't want to deal with sRGB issues for now.
m_CacheNoCubeArray = new Texture2DArray(panoWidthTop, panoHeightTop, numCubeMaps, TextureFormat.RGBAHalf, isMipMapped)
{
hideFlags = HideFlags.HideAndDontSave,
wrapMode = TextureWrapMode.Repeat,
wrapModeV = TextureWrapMode.Clamp,
filterMode = FilterMode.Trilinear,
anisoLevel = 0
};
m_NumPanoMipLevels = isMipMapped ? GetNumMips(panoWidthTop, panoHeightTop) : 1;
m_StagingRTs = new RenderTexture[m_NumPanoMipLevels];
for (int m = 0; m < m_NumPanoMipLevels; m++)
{
m_StagingRTs[m] = new RenderTexture(Mathf.Max(1, panoWidthTop >> m), Mathf.Max(1, panoHeightTop >> m), 0, RenderTextureFormat.ARGBHalf) { hideFlags = HideFlags.HideAndDontSave };
}
if (m_CubeBlitMaterial)
{
m_CubeMipLevelPropName = Shader.PropertyToID("_cubeMipLvl");
m_cubeSrcTexPropName = Shader.PropertyToID("_srcCubeTexture");
}
}
else
{
m_Cache = new CubemapArray(width, numCubeMaps, format, isMipMapped)
{
hideFlags = HideFlags.HideAndDontSave,
wrapMode = TextureWrapMode.Clamp,
filterMode = FilterMode.Trilinear,
anisoLevel = 0 // It is important to set 0 here, else unity force anisotropy filtering
};
}
return res;
}
public void Release()
{
if (m_CacheNoCubeArray)
{
Texture.DestroyImmediate(m_CacheNoCubeArray);
for (int m = 0; m < m_NumPanoMipLevels; m++)
{
m_StagingRTs[m].Release();
}
m_StagingRTs = null;
if (m_CubeBlitMaterial) Material.DestroyImmediate(m_CubeBlitMaterial);
}
if (m_Cache)
Texture.DestroyImmediate(m_Cache);
}
private void TransferToPanoCache(CommandBuffer cmd, int sliceIndex, Texture texture)
{
m_CubeBlitMaterial.SetTexture(m_cubeSrcTexPropName, texture);
for (int m = 0; m < m_NumPanoMipLevels; m++)
{
m_CubeBlitMaterial.SetInt(m_CubeMipLevelPropName, Mathf.Min(m_NumMipLevels - 1, m));
cmd.Blit(null, m_StagingRTs[m], m_CubeBlitMaterial, 0);
}
for (int m = 0; m < m_NumPanoMipLevels; m++)
cmd.CopyTexture(m_StagingRTs[m], 0, 0, m_CacheNoCubeArray, sliceIndex, m);
}
}
public abstract class TextureCache
{
protected int m_NumMipLevels;
public static bool isMobileBuildTarget
{
get
{
#if UNITY_EDITOR
switch (EditorUserBuildSettings.activeBuildTarget)
{
case BuildTarget.iOS:
case BuildTarget.Android:
case BuildTarget.Tizen:
case BuildTarget.WSAPlayer:
// Note: We return true on purpose even if Windows Store Apps are running on Desktop.
return true;
default:
return false;
}
#else
return Application.isMobilePlatform;
#endif
}
}
public static TextureFormat GetPreferredHDRCompressedTextureFormat
{
get
{
var format = TextureFormat.RGBAHalf;
var probeFormat = TextureFormat.BC6H;
// // On editor the texture is uncompressed when operating against mobile build targets
//#if UNITY_2017_2_OR_NEWER
if (SystemInfo.SupportsTextureFormat(probeFormat) && !UnityEngine.Rendering.GraphicsSettings.HasShaderDefine(UnityEngine.Rendering.BuiltinShaderDefine.UNITY_NO_DXT5nm))
format = probeFormat;
//#else
// if (SystemInfo.SupportsTextureFormat(probeFormat) && !TextureCache.isMobileBuildTarget)
// format = probeFormat;
//#endif
return format;
}
}
public static bool supportsCubemapArrayTextures
{
get
{
//#if UNITY_2017_2_OR_NEWER
return !UnityEngine.Rendering.GraphicsSettings.HasShaderDefine(UnityEngine.Rendering.BuiltinShaderDefine.UNITY_NO_CUBEMAP_ARRAY);
//#else
// return (SystemInfo.supportsCubemapArrayTextures && !TextureCache.isMobileBuildTarget);
//#endif
}
}
private struct SSliceEntry
{
public uint texId;
public uint countLRU;
public uint updateCount;
};
private int m_NumTextures;
private int[] m_SortedIdxArray;
private SSliceEntry[] m_SliceArray;
Dictionary<uint, int> m_LocatorInSliceArray;
private static uint g_MaxFrameCount = unchecked((uint)(-1));
private static uint g_InvalidTexID = (uint)0;
public uint GetTextureUpdateCount(Texture texture)
{
uint updateCount = texture.updateCount;
// For baked probes in the editor we need to factor in the actual hash of texture because we can't increment the update count of a texture that's baked on the disk.
// This code leaks logic from reflection probe baking into the texture cache which is not good... TODO: Find a way to do that outside of the texture cache.
#if UNITY_EDITOR
updateCount += (uint)texture.imageContentsHash.GetHashCode();
#endif
return updateCount;
}
public int ReserveSlice(Texture texture, out bool needUpdate)
{
needUpdate = false;
if (texture == null)
return -1;
var texId = (uint)texture.GetInstanceID();
if (texId == g_InvalidTexID)
return -1;
// search for existing copy
var sliceIndex = -1;
var foundIndex = -1;
if (m_LocatorInSliceArray.TryGetValue(texId, out foundIndex))
{
sliceIndex = foundIndex;
var updateCount = GetTextureUpdateCount(texture);
needUpdate |= (m_SliceArray[sliceIndex].updateCount != updateCount);
Debug.Assert(m_SliceArray[sliceIndex].texId == texId);
}
// If no existing copy found in the array
if(sliceIndex == -1)
{
// 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_LocatorInSliceArray.Remove(m_SliceArray[idx].texId);
}
m_LocatorInSliceArray.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
}
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 void UpdateSlice(CommandBuffer cmd, int sliceIndex, Texture content, uint updateCount)
{
// transfer new slice to sliceIndex from source texture
m_SliceArray[sliceIndex].updateCount = updateCount;
TransferToSlice(cmd, sliceIndex, content);
}
public void UpdateSlice(CommandBuffer cmd, int sliceIndex, Texture content)
{
UpdateSlice(cmd, sliceIndex, content, GetTextureUpdateCount(content));
}
public int FetchSlice(CommandBuffer cmd, Texture texture, bool forceReinject=false)
{
bool needUpdate = false;
var sliceIndex = ReserveSlice(texture, 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)
{
UpdateSlice(cmd, sliceIndex, texture);
}
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);
}
protected TextureCache()
{
m_NumTextures = 0;
m_NumMipLevels = 0;
}
public virtual void TransferToSlice(CommandBuffer cmd, int sliceIndex, Texture texture)
{
}
public virtual Texture GetTexCache()
{
return null;
}
protected bool AllocTextureArray(int numTextures)
{
if (numTextures > 0)
{
m_SliceArray = new SSliceEntry[numTextures];
m_SortedIdxArray = new int[numTextures];
m_LocatorInSliceArray = new Dictionary<uint, int>();
m_NumTextures = numTextures;
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 m_SliceArray != NULL && m_SortedIdxArray != NULL && numTextures > 0;
return numTextures > 0;
}
// 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_LocatorInSliceArray.ContainsKey(texId))
return;
var sliceIndex = m_LocatorInSliceArray[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_LocatorInSliceArray.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;
}
}
}