2 using System
.Collections
.Generic
;
3 using UnityEngine
.Rendering
;
8 namespace UnityEngine
.Experimental
.Rendering
10 // 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
11 public abstract class TextureCache
13 // Name that identifies the texture cache (Mainly used to generate the storage texture name)
14 protected string m_CacheName
;
15 // The number of mipmap that is deduced from the maximal resolution
16 protected int m_NumMipLevels
;
17 // 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"
18 protected int m_SliceSize
;
19 // Counter of input texture that have been fed to this structure
20 private int m_NumTextures
;
21 // Array that maps input textureIDs into the slices indexes
22 Dictionary
<uint, int> m_LocatorInSliceDictionnary
;
24 // This structure defines the mapping between an input texture and the internal structure
25 private struct SliceEntry
27 // ID of the internal structure
29 // 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
31 // Hash that tracks the version of the input texture (allows us to know if it needs an update)
32 public uint sliceEntryHash
;
34 // The array of slices that the cache holds
35 private SliceEntry
[] m_SliceArray
;
37 // Array with the slices sorted according to their countLRU
38 private int[] m_SortedIdxArray
;
40 // Array used when we use the texture as itself's representative in the slices
41 private Texture
[] m_autoContentArray
= new Texture
[1];
44 private static uint g_MaxFrameCount
= unchecked((uint)(-1));
45 private static uint g_InvalidTexID
= (uint)0;
47 protected const int k_FP16SizeInByte
= 2;
48 protected const int k_NbChannel
= 4;
49 protected const float k_MipmapFactorApprox
= 1.33f
;
50 internal const int k_MaxSupported
= 250; //vary along hardware and cube/2D but 250 should be always safe
52 protected TextureCache(string cacheName
, int sliceSize
= 1)
54 m_CacheName
= cacheName
;
55 m_SliceSize
= sliceSize
;
60 // Function that initialize the texture cache with a maximal number of textures in the cache
61 protected bool AllocTextureArray(int numTextures
)
63 if (numTextures
>= m_SliceSize
)
65 m_SliceArray
= new SliceEntry
[numTextures
];
66 m_SortedIdxArray
= new int[numTextures
];
67 m_LocatorInSliceDictionnary
= new Dictionary
<uint, int>();
69 m_NumTextures
= numTextures
/ m_SliceSize
;
70 for (int i
= 0; i
< m_NumTextures
; i
++)
72 m_SliceArray
[i
].countLRU
= g_MaxFrameCount
; // never used before
73 m_SliceArray
[i
].texId
= g_InvalidTexID
;
74 m_SortedIdxArray
[i
] = i
;
77 return numTextures
>= m_SliceSize
;
80 // This function returns the internal storage texture of the cache. It is specified by the child class.
81 abstract public Texture
GetTexCache();
83 // Function that allows us to do the mapping between a texture value and an identifier
84 public uint GetTextureHash(Texture texture
)
86 uint textureHash
= texture
.updateCount
;
87 // 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.
88 // 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.
90 textureHash
+= (uint)texture
.imageContentsHash
.GetHashCode();
95 // Function that reserves a slice using a texture and it returns a update flag that tells if the stored value matches the input one
96 public int ReserveSlice(Texture texture
, out bool needUpdate
)
98 // Reset the update flag
101 // Check the validity of the input texture
104 var texId
= (uint)texture
.GetInstanceID();
105 if (texId
== g_InvalidTexID
)
108 // Search for existing copy in the texId to slice index dictionary
110 if (m_LocatorInSliceDictionnary
.TryGetValue(texId
, out sliceIndex
))
112 // Compute the new hash of the texture
113 var textureHash
= GetTextureHash(texture
);
115 // We need to update the texture if the hash does not match the one in the slice
116 needUpdate
|= (m_SliceArray
[sliceIndex
].sliceEntryHash
!= textureHash
);
118 Debug
.Assert(m_SliceArray
[sliceIndex
].texId
== texId
);
122 // This texture was not in the slice array. We need to look for first non zero entry.
123 // Will by the least recently used entry since the array was pre-sorted (in linear time) in NewFrame()
126 while ((!bFound
) && j
< m_NumTextures
)
128 idx
= m_SortedIdxArray
[j
];
129 if (m_SliceArray
[idx
].countLRU
== 0)
130 ++j
; // if entry already snagged by a new texture in this frame then ++j
138 // if we are replacing an existing entry delete it from m_locatorInSliceArray.
139 if (m_SliceArray
[idx
].texId
!= g_InvalidTexID
)
141 m_LocatorInSliceDictionnary
.Remove(m_SliceArray
[idx
].texId
);
144 m_LocatorInSliceDictionnary
.Add(texId
, idx
);
145 m_SliceArray
[idx
].texId
= texId
;
151 if (sliceIndex
!= -1)
153 m_SliceArray
[sliceIndex
].countLRU
= 0; // mark slice as in use this frame
159 // In case the texture content with which we update the cache is not the input texture, we need to provide the right update count.
160 public bool UpdateSlice(CommandBuffer cmd
, int sliceIndex
, Texture
[] contentArray
, uint textureHash
)
162 // Make sure the content matches the size of the texture cache
163 Debug
.Assert(contentArray
.Length
== m_SliceSize
);
166 SetSliceHash(sliceIndex
, textureHash
);
168 // transfer new slice to sliceIndex from source texture
169 return TransferToSlice(cmd
, sliceIndex
, contentArray
);
172 public bool UpdateSlice(CommandBuffer cmd
, int sliceIndex
, Texture texture
, uint textureHash
)
174 // Make sure the content matches the size of the texture cache
175 Debug
.Assert(m_SliceSize
== 1);
178 SetSliceHash(sliceIndex
, textureHash
);
180 // transfer new slice to sliceIndex from source texture
181 m_autoContentArray
[0] = texture
;
182 return TransferToSlice(cmd
, sliceIndex
, m_autoContentArray
);
185 public void SetSliceHash(int sliceIndex
, uint hash
)
187 // transfer new slice to sliceIndex from source texture
188 m_SliceArray
[sliceIndex
].sliceEntryHash
= hash
;
191 // 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)
192 protected abstract bool TransferToSlice(CommandBuffer cmd
, int sliceIndex
, Texture
[] textureArray
);
194 public int FetchSlice(CommandBuffer cmd
, Texture texture
, bool forceReinject
= false)
196 bool needUpdate
= false;
197 var sliceIndex
= ReserveSlice(texture
, out needUpdate
);
199 var bSwapSlice
= forceReinject
|| needUpdate
;
202 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.");
203 if (sliceIndex
!= -1 && bSwapSlice
)
205 m_autoContentArray
[0] = texture
;
206 UpdateSlice(cmd
, sliceIndex
, m_autoContentArray
, GetTextureHash(texture
));
212 private static List
<int> s_TempIntList
= new List
<int>();
213 public void NewFrame()
216 s_TempIntList
.Clear();
217 for (int i
= 0; i
< m_NumTextures
; i
++)
219 s_TempIntList
.Add(m_SortedIdxArray
[i
]); // copy buffer
220 if (m_SliceArray
[m_SortedIdxArray
[i
]].countLRU
!= 0) ++numNonZeros
;
222 int nonZerosBase
= 0, zerosBase
= 0;
223 for (int i
= 0; i
< m_NumTextures
; i
++)
225 if (m_SliceArray
[s_TempIntList
[i
]].countLRU
== 0)
227 m_SortedIdxArray
[zerosBase
+ numNonZeros
] = s_TempIntList
[i
];
232 m_SortedIdxArray
[nonZerosBase
] = s_TempIntList
[i
];
237 for (int i
= 0; i
< m_NumTextures
; i
++)
239 if (m_SliceArray
[i
].countLRU
< g_MaxFrameCount
) ++m_SliceArray
[i
].countLRU
; // next frame
242 //for(int q=1; q<m_numTextures; q++)
243 // assert(m_SliceArray[m_SortedIdxArray[q-1]].CountLRU>=m_SliceArray[m_SortedIdxArray[q]].CountLRU);
246 // should not really be used in general. Assuming lights are culled properly entries will automatically be replaced efficiently.
247 public void RemoveEntryFromSlice(Texture texture
)
249 var texId
= (uint)texture
.GetInstanceID();
251 //assert(TexID!=g_InvalidTexID);
252 if (texId
== g_InvalidTexID
) return;
254 // search for existing copy
255 if (!m_LocatorInSliceDictionnary
.ContainsKey(texId
))
258 var sliceIndex
= m_LocatorInSliceDictionnary
[texId
];
260 //assert(m_SliceArray[sliceIndex].TexID==TexID);
262 // locate entry sorted by uCountLRU in m_pSortedIdxArray
263 var foundIdxSortLRU
= false;
265 while ((!foundIdxSortLRU
) && i
< m_NumTextures
)
267 if (m_SortedIdxArray
[i
] == sliceIndex
) foundIdxSortLRU
= true;
271 if (!foundIdxSortLRU
)
274 // relocate sliceIndex to front of m_pSortedIdxArray since uCountLRU will be set to maximum.
275 for (int j
= 0; j
< i
; j
++)
277 m_SortedIdxArray
[j
+ 1] = m_SortedIdxArray
[j
];
279 m_SortedIdxArray
[0] = sliceIndex
;
281 // delete from m_locatorInSliceArray and m_pSliceArray.
282 m_LocatorInSliceDictionnary
.Remove(texId
);
283 m_SliceArray
[sliceIndex
].countLRU
= g_MaxFrameCount
; // never used before
284 m_SliceArray
[sliceIndex
].texId
= g_InvalidTexID
;
287 protected int GetNumMips(int width
, int height
)
289 return GetNumMips(width
> height
? width
: height
);
292 protected int GetNumMips(int dim
)
294 var uDim
= (uint)dim
;
297 { ++iNumMips; uDim >>= 1; }
301 public static bool isMobileBuildTarget
306 switch (EditorUserBuildSettings
.activeBuildTarget
)
308 case BuildTarget
.iOS
:
309 case BuildTarget
.Android
:
315 return Application
.isMobilePlatform
;
320 public static TextureFormat GetPreferredHDRCompressedTextureFormat
324 var format
= TextureFormat
.RGBAHalf
;
325 var probeFormat
= TextureFormat
.BC6H
;
327 if (SystemInfo
.SupportsTextureFormat(probeFormat
) && !UnityEngine
.Rendering
.GraphicsSettings
.HasShaderDefine(UnityEngine
.Rendering
.BuiltinShaderDefine
.UNITY_NO_DXT5nm
))
328 format
= probeFormat
;
334 public static bool supportsCubemapArrayTextures
338 return !UnityEngine
.Rendering
.GraphicsSettings
.HasShaderDefine(UnityEngine
.Rendering
.BuiltinShaderDefine
.UNITY_NO_CUBEMAP_ARRAY
);