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.
713 lines
30 KiB
713 lines
30 KiB
using System;
|
|
using System.Linq;
|
|
using UnityEngine.Experimental.Rendering;
|
|
using System.Collections.Generic;
|
|
using UnityEngine.Assertions;
|
|
|
|
namespace UnityEngine.Rendering.HighDefinition
|
|
{
|
|
class ReflectionProbeTextureCache
|
|
{
|
|
IBLFilterBSDF[] m_IBLFiltersBSDF;
|
|
|
|
int m_AtlasWidth;
|
|
int m_AtlasHeight;
|
|
GraphicsFormat m_AtlasFormat;
|
|
int m_AtlasMipCount;
|
|
int m_AtlasSlicesCount;
|
|
|
|
RTHandle m_AtlasTexture;
|
|
Texture2DAtlasDynamic m_Atlas;
|
|
|
|
int m_CubeMipPadding;
|
|
int m_CubeTexelPadding;
|
|
int m_PlanarMipPadding;
|
|
int m_PlanarTexelPadding;
|
|
|
|
bool m_DecreaseResToFit;
|
|
|
|
int m_CubeFrameFetchIndex;
|
|
int m_PlanarFrameFetchIndex;
|
|
|
|
Dictionary<int, (uint, uint)> m_TextureLRUAndHash = new Dictionary<int, (uint, uint)>();
|
|
List<(int, uint)> m_TextureLRUSorted = new List<(int, uint)>();
|
|
|
|
Material m_ConvertTextureMaterial;
|
|
MaterialPropertyBlock m_ConvertTexturePropertyBlock = new MaterialPropertyBlock();
|
|
|
|
uint m_CurrentRender;
|
|
|
|
bool m_NoMoreSpaceErrorLogged;
|
|
|
|
RenderTexture m_ConvolvedPlanarReflectionTexture;
|
|
|
|
const int k_MaxFramesTmpUsage = 60;
|
|
int m_TempCubeTexturesLastFrameUsed;
|
|
int m_TmpTextureConvertedSize;
|
|
int m_TmpTextureConvolvedWidth;
|
|
int m_TmpTextureConvolvedHeight;
|
|
FilterMode m_TmpConvolvedFilterMode;
|
|
GraphicsFormat m_TmpTextureConvertedFormat;
|
|
GraphicsFormat m_TmpTextureConvolvedFormat;
|
|
FilterMode m_TmpTextureConvertedFilterMode;
|
|
FilterMode m_TmpTextureConvolvedFilterMode;
|
|
RenderTexture m_TempConvertedReflectionProbeTexture;
|
|
RenderTexture m_TempConvolvedReflectionProbeTexture;
|
|
|
|
public ReflectionProbeTextureCache(
|
|
HDRenderPipeline renderPipeline,
|
|
IBLFilterBSDF[] iblFiltersBSDF,
|
|
int width,
|
|
int height,
|
|
GraphicsFormat format,
|
|
bool decreaseResToFit,
|
|
int lastValidCubeMip,
|
|
int lastValidPlanarMip)
|
|
{
|
|
Assert.IsTrue(Mathf.IsPowerOfTwo(width) && Mathf.IsPowerOfTwo(height));
|
|
Assert.IsTrue(width <= (int)ReflectionProbeTextureCacheResolution.Resolution16384x16384);
|
|
Assert.IsTrue(height <= (int)ReflectionProbeTextureCacheResolution.Resolution16384x16384);
|
|
Assert.IsTrue(format == GraphicsFormat.B10G11R11_UFloatPack32 || format == GraphicsFormat.R16G16B16A16_SFloat, "Reflection Probe Cache format for HDRP can only be FP16 or R11G11B10.");
|
|
Assert.IsTrue(iblFiltersBSDF[0] is IBLFilterGGX);
|
|
|
|
m_IBLFiltersBSDF = iblFiltersBSDF;
|
|
|
|
m_AtlasWidth = width;
|
|
m_AtlasHeight = height;
|
|
m_AtlasFormat = format;
|
|
m_AtlasMipCount = Mathf.FloorToInt(Mathf.Log(Math.Max(m_AtlasWidth, m_AtlasHeight), 2)) + 1;
|
|
m_AtlasSlicesCount = m_IBLFiltersBSDF.Length;
|
|
|
|
m_AtlasTexture = RTHandles.Alloc(
|
|
width: width,
|
|
height: height,
|
|
slices: m_AtlasSlicesCount,
|
|
dimension: TextureDimension.Tex2DArray,
|
|
filterMode: FilterMode.Trilinear,
|
|
colorFormat: format,
|
|
wrapMode: TextureWrapMode.Clamp,
|
|
useMipMap: true,
|
|
autoGenerateMips: false,
|
|
name: "ReflectionProbeCacheTextureAtlas"
|
|
);
|
|
|
|
const int k_MaxTexturesInAtlas = 2048;
|
|
|
|
m_Atlas = new Texture2DAtlasDynamic(width, height, k_MaxTexturesInAtlas, m_AtlasTexture);
|
|
|
|
m_CubeMipPadding = Mathf.Clamp(lastValidCubeMip, 0, (int)EnvConstants.ConvolutionMipCount - 1);
|
|
m_CubeTexelPadding = (1 << m_CubeMipPadding) * 2;
|
|
m_PlanarMipPadding = Mathf.Clamp(lastValidPlanarMip, 0, (int)EnvConstants.ConvolutionMipCount - 1);
|
|
m_PlanarTexelPadding = (1 << m_PlanarMipPadding) * 2;
|
|
|
|
m_DecreaseResToFit = decreaseResToFit;
|
|
m_ConvertTextureMaterial = CoreUtils.CreateEngineMaterial(renderPipeline.runtimeShaders.blitCubeTextureFacePS);
|
|
}
|
|
|
|
private static int GetTextureID(HDProbe probe)
|
|
{
|
|
return GetTextureIDAndSize(probe, out int _);
|
|
}
|
|
|
|
private static int GetTextureIDAndSize(HDProbe probe, out int textureSize)
|
|
{
|
|
textureSize = GetTextureSizeInAtlas(probe);
|
|
|
|
int textureID = probe.texture.GetInstanceID();
|
|
|
|
// Include texture size in ID using simple hash
|
|
const int kPrime = 31;
|
|
textureID = kPrime * textureID + textureSize;
|
|
|
|
return textureID;
|
|
}
|
|
|
|
private static int GetTextureSizeInAtlas(HDProbe probe)
|
|
{
|
|
int textureSize = probe.texture.width;
|
|
|
|
switch (probe.type)
|
|
{
|
|
case ProbeSettings.ProbeType.ReflectionProbe:
|
|
textureSize = Mathf.Min(textureSize, (int)probe.cubeResolution);
|
|
textureSize = GetReflectionProbeSizeInAtlas(textureSize);
|
|
break;
|
|
}
|
|
|
|
return textureSize;
|
|
}
|
|
|
|
public static int GetReflectionProbeSizeInAtlas(int textureSize)
|
|
{
|
|
// Last mip level should be at least 4 texels wide for the prober octahedral border.
|
|
textureSize = Mathf.Max(textureSize, 32);
|
|
|
|
// In theory we should multiply by sqrt(6) to match the area but we can't do that due to pow of two constraint.
|
|
// So for cube resolutions less than 512 we multiply by 4 as otherwise quality suffers.
|
|
// But for resolutions more or equal than 512 it is enough to multiply by 2 as the difference is barely noticeable.
|
|
if (textureSize < 512)
|
|
textureSize *= 4;
|
|
else
|
|
textureSize *= 2;
|
|
|
|
return textureSize;
|
|
}
|
|
|
|
private static Vector2 GetTextureSizeWithoutPadding(int textureWidth, int textureHeight, int texelPadding)
|
|
{
|
|
int textureWidthWithoutPadding = Mathf.Max(textureWidth - texelPadding, 1);
|
|
int textureHeightWithoutPadding = Mathf.Max(textureHeight - texelPadding, 1);
|
|
return new Vector2(textureWidthWithoutPadding, textureHeightWithoutPadding);
|
|
}
|
|
|
|
internal static long GetApproxCacheSizeInByte(int elementsCount, int width, int height, GraphicsFormat format)
|
|
{
|
|
const double mipmapFactorApprox = 1.33;
|
|
return (long)(elementsCount * width * height * mipmapFactorApprox * GraphicsFormatUtility.GetBlockSize(format));
|
|
}
|
|
|
|
private RenderTexture EnsureConvolvedPlanarReflectionTexture(int textureSize)
|
|
{
|
|
if (m_ConvolvedPlanarReflectionTexture == null || m_ConvolvedPlanarReflectionTexture.width < textureSize)
|
|
{
|
|
m_ConvolvedPlanarReflectionTexture?.Release();
|
|
|
|
m_ConvolvedPlanarReflectionTexture = new RenderTexture(textureSize, textureSize, 0, m_AtlasFormat);
|
|
m_ConvolvedPlanarReflectionTexture.hideFlags = HideFlags.HideAndDontSave;
|
|
m_ConvolvedPlanarReflectionTexture.dimension = TextureDimension.Tex2D;
|
|
m_ConvolvedPlanarReflectionTexture.useMipMap = true;
|
|
m_ConvolvedPlanarReflectionTexture.autoGenerateMips = false;
|
|
m_ConvolvedPlanarReflectionTexture.filterMode = FilterMode.Point;
|
|
m_ConvolvedPlanarReflectionTexture.name = CoreUtils.GetRenderTargetAutoName(textureSize, textureSize, 0, m_AtlasFormat, "ConvolvedPlanarReflectionTexture", mips: true);
|
|
m_ConvolvedPlanarReflectionTexture.enableRandomWrite = true;
|
|
m_ConvolvedPlanarReflectionTexture.Create();
|
|
}
|
|
|
|
return m_ConvolvedPlanarReflectionTexture;
|
|
}
|
|
|
|
private void LogErrorNoMoreSpaceOnce()
|
|
{
|
|
if (m_NoMoreSpaceErrorLogged)
|
|
return;
|
|
|
|
m_NoMoreSpaceErrorLogged = true;
|
|
|
|
#if UNITY_EDITOR
|
|
// Avoid spamming the error frame too much, if we have more than 25 errors we dont display this issue.
|
|
UnityEditor.ConsoleWindowUtility.GetConsoleLogCounts(out var errorCounts, out _, out _);
|
|
if (errorCounts >= 25)
|
|
return;
|
|
#endif
|
|
Debug.LogError("No more space in Reflection Probe Atlas. To solve this issue, increase the size of the Reflection Probe Atlas in the HDRP settings.");
|
|
}
|
|
|
|
private bool NeedsUpdate(int textureId, uint textureHash, ref Vector4 scaleOffset)
|
|
{
|
|
bool needsUpdate = false;
|
|
|
|
if (!m_Atlas.IsCached(out scaleOffset, textureId))
|
|
needsUpdate = true;
|
|
else if (!m_TextureLRUAndHash.TryGetValue(textureId, out (uint, uint hash) entry) || entry.hash != textureHash)
|
|
needsUpdate = true;
|
|
|
|
m_TextureLRUAndHash[textureId] = (m_CurrentRender, textureHash);
|
|
|
|
return needsUpdate;
|
|
}
|
|
|
|
private RenderTexture GetTempConvertedReflectionProbeTexture(Texture texture)
|
|
{
|
|
int cubeSize = Math.Max(texture.width, (int)Mathf.Pow(2, (int)EnvConstants.ConvolutionMipCount - 1));
|
|
|
|
if ( m_TempConvertedReflectionProbeTexture == null
|
|
|| m_TmpTextureConvertedSize != cubeSize
|
|
|| m_TmpTextureConvertedFormat != m_AtlasFormat
|
|
|| m_TmpTextureConvertedFilterMode != texture.filterMode)
|
|
{
|
|
if (m_TempConvertedReflectionProbeTexture != null)
|
|
RenderTexture.ReleaseTemporary(m_TempConvertedReflectionProbeTexture);
|
|
|
|
RenderTexture convertedTextureTemp = RenderTexture.GetTemporary(cubeSize, cubeSize, 0, m_AtlasFormat);
|
|
convertedTextureTemp.dimension = TextureDimension.Cube;
|
|
convertedTextureTemp.filterMode = texture.filterMode;
|
|
convertedTextureTemp.useMipMap = true;
|
|
convertedTextureTemp.autoGenerateMips = false;
|
|
convertedTextureTemp.name = CoreUtils.GetRenderTargetAutoName(cubeSize, cubeSize, 0, m_AtlasFormat, "ConvertedReflectionProbeTemp", mips: true);
|
|
convertedTextureTemp.Create();
|
|
m_TempConvertedReflectionProbeTexture = convertedTextureTemp;
|
|
m_TmpTextureConvertedSize = cubeSize;
|
|
m_TmpTextureConvertedFormat = m_AtlasFormat;
|
|
m_TmpTextureConvertedFilterMode = texture.filterMode;
|
|
}
|
|
|
|
m_TempCubeTexturesLastFrameUsed = (int)m_CurrentRender;
|
|
return m_TempConvertedReflectionProbeTexture;
|
|
}
|
|
|
|
private RenderTexture PrepareCubeReflectionProbeTexture(CommandBuffer cmd, Texture texture, int textureSize)
|
|
{
|
|
RenderTexture renderTexture = texture as RenderTexture;
|
|
Cubemap cubemap = texture as Cubemap;
|
|
|
|
Assert.IsTrue((renderTexture && renderTexture.dimension == TextureDimension.Cube) || cubemap, "Cube Reflection Probe should always be a Cubemap Texture.");
|
|
|
|
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.ConvertReflectionProbe)))
|
|
{
|
|
int cubeSize = Math.Max(textureSize, (int)Mathf.Pow(2, (int)EnvConstants.ConvolutionMipCount - 1));
|
|
|
|
bool conversionRequired = texture.graphicsFormat != m_AtlasFormat;
|
|
conversionRequired |= (cubemap && cubemap.mipmapCount == 1);
|
|
conversionRequired |= (renderTexture && !renderTexture.useMipMap);
|
|
conversionRequired |= texture.width != cubeSize;
|
|
|
|
if (conversionRequired)
|
|
{
|
|
RenderTexture convertedTextureTemp = GetTempConvertedReflectionProbeTexture(texture);
|
|
|
|
m_ConvertTexturePropertyBlock.SetTexture(HDShaderIDs._InputTex, texture);
|
|
m_ConvertTexturePropertyBlock.SetFloat(HDShaderIDs._LoD, 0.0f);
|
|
|
|
for (int f = 0; f < 6; ++f)
|
|
{
|
|
m_ConvertTexturePropertyBlock.SetFloat(HDShaderIDs._FaceIndex, f);
|
|
CoreUtils.SetRenderTarget(cmd, convertedTextureTemp, ClearFlag.None, Color.black, 0, (CubemapFace)f);
|
|
CoreUtils.DrawFullScreen(cmd, m_ConvertTextureMaterial, m_ConvertTexturePropertyBlock);
|
|
}
|
|
|
|
cmd.GenerateMips(convertedTextureTemp);
|
|
|
|
return convertedTextureTemp;
|
|
}
|
|
else if (renderTexture && !renderTexture.autoGenerateMips)
|
|
{
|
|
cmd.GenerateMips(renderTexture);
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private RenderTexture GetTempConvolveReflectionProbeTexture(Texture texture)
|
|
{
|
|
if ( m_TempConvolvedReflectionProbeTexture == null
|
|
|| m_TmpTextureConvolvedWidth != texture.width
|
|
|| m_TmpTextureConvolvedHeight != texture.height
|
|
|| m_TmpTextureConvolvedFormat != m_AtlasFormat
|
|
|| m_TmpTextureConvolvedFilterMode != texture.filterMode)
|
|
{
|
|
if (m_TempConvolvedReflectionProbeTexture != null)
|
|
RenderTexture.ReleaseTemporary(m_TempConvolvedReflectionProbeTexture);
|
|
|
|
RenderTexture convolvedTextureTemp = RenderTexture.GetTemporary(texture.width, texture.height, 0, m_AtlasFormat);
|
|
convolvedTextureTemp.dimension = TextureDimension.Cube;
|
|
convolvedTextureTemp.filterMode = texture.filterMode;
|
|
convolvedTextureTemp.useMipMap = true;
|
|
convolvedTextureTemp.autoGenerateMips = false;
|
|
convolvedTextureTemp.anisoLevel = 0;
|
|
convolvedTextureTemp.name = "ConvolvedReflectionProbeTemp";
|
|
convolvedTextureTemp.Create();
|
|
m_TempConvolvedReflectionProbeTexture = convolvedTextureTemp;
|
|
m_TmpTextureConvolvedWidth = texture.width;
|
|
m_TmpTextureConvolvedHeight = texture.height;
|
|
m_TmpConvolvedFilterMode = texture.filterMode;
|
|
m_TmpTextureConvolvedFormat = m_AtlasFormat;
|
|
m_TmpTextureConvolvedFilterMode = texture.filterMode;
|
|
}
|
|
|
|
m_TempCubeTexturesLastFrameUsed = (int)m_CurrentRender;
|
|
return m_TempConvolvedReflectionProbeTexture;
|
|
}
|
|
|
|
private RenderTexture ConvolveCubeReflectionProbeTexture(CommandBuffer cmd, Texture texture, IBLFilterBSDF filter)
|
|
{
|
|
RenderTexture renderTexture = texture as RenderTexture;
|
|
|
|
Assert.IsTrue((renderTexture && renderTexture.dimension == TextureDimension.Cube), "Cube Reflection Probe should always be a Cubemap Texture.");
|
|
|
|
RenderTexture convolvedTextureTemp = GetTempConvolveReflectionProbeTexture(texture);
|
|
filter.FilterCubemap(cmd, texture, convolvedTextureTemp);
|
|
|
|
return convolvedTextureTemp;
|
|
}
|
|
|
|
private RenderTexture ConvolvePlanarReflectionProbeTexture(CommandBuffer cmd, Texture texture, ref IBLFilterBSDF.PlanarTextureFilteringParameters planarTextureFilteringParameters)
|
|
{
|
|
RenderTexture renderTexture = texture as RenderTexture;
|
|
|
|
Assert.IsTrue(renderTexture && renderTexture.dimension == TextureDimension.Tex2D, "Planar Reflection Probe must be a 2D RenderTexture.");
|
|
Assert.IsTrue(texture.graphicsFormat == m_AtlasFormat, "Planar Reflection Probe format and the cache format must be the same.");
|
|
|
|
RenderTexture convolvedPlanarReflectionTexture = EnsureConvolvedPlanarReflectionTexture(texture.width);
|
|
|
|
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.ConvolvePlanarReflectionProbe)))
|
|
{
|
|
// For planar reflection we only convolve with the GGX filter, otherwise it would be too expensive.
|
|
IBLFilterGGX IBLFiltersBSDF = (IBLFilterGGX)m_IBLFiltersBSDF[0];
|
|
IBLFiltersBSDF.FilterPlanarTexture(cmd, renderTexture, ref planarTextureFilteringParameters, convolvedPlanarReflectionTexture);
|
|
}
|
|
|
|
return convolvedPlanarReflectionTexture;
|
|
}
|
|
|
|
private void BlitTextureCube(CommandBuffer cmd, Vector4 scaleOffset, Texture texture, int arraySlice)
|
|
{
|
|
Assert.IsTrue(texture.dimension == TextureDimension.Cube);
|
|
|
|
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.BlitTextureToReflectionProbeAtlas)))
|
|
{
|
|
int texelPadding = m_CubeTexelPadding;
|
|
int textureWidthInAtlas = Mathf.CeilToInt(scaleOffset.x * m_AtlasWidth);
|
|
int textureHeightInAtlas = Mathf.CeilToInt(scaleOffset.y * m_AtlasHeight);
|
|
Assert.IsTrue(textureWidthInAtlas == textureHeightInAtlas);
|
|
|
|
Vector2 textureSizeWithoutPadding = GetTextureSizeWithoutPadding(textureWidthInAtlas, textureHeightInAtlas, texelPadding);
|
|
bool bilinear = texture.filterMode != FilterMode.Point;
|
|
|
|
for (int mipLevel = 0; mipLevel < m_AtlasMipCount; ++mipLevel)
|
|
{
|
|
// Every octahedral mip has at least one texel padding. This improves octahedral mapping as it always should have border for every mip.
|
|
if (mipLevel > m_CubeMipPadding)
|
|
texelPadding *= 2;
|
|
|
|
cmd.SetRenderTarget(m_AtlasTexture, mipLevel, CubemapFace.Unknown, arraySlice);
|
|
Blitter.BlitCubeToOctahedral2DQuadWithPadding(cmd, texture, textureSizeWithoutPadding, scaleOffset, mipLevel, bilinear, texelPadding);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void BlitTexture2D(CommandBuffer cmd, Vector4 scaleOffset, Vector4 sourceScaleOffset, Texture texture, int arraySlice)
|
|
{
|
|
Assert.IsTrue(texture.dimension == TextureDimension.Tex2D);
|
|
|
|
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.BlitTextureToReflectionProbeAtlas)))
|
|
{
|
|
int texelPadding = m_PlanarTexelPadding;
|
|
int textureWidthInAtlas = Mathf.CeilToInt(scaleOffset.x * m_AtlasWidth);
|
|
int textureHeightInAtlas = Mathf.CeilToInt(scaleOffset.y * m_AtlasHeight);
|
|
Assert.IsTrue(textureWidthInAtlas == textureHeightInAtlas);
|
|
|
|
Vector2 textureSizeWithoutPadding = GetTextureSizeWithoutPadding(textureWidthInAtlas, textureHeightInAtlas, texelPadding);
|
|
bool bilinear = texture.filterMode != FilterMode.Point;
|
|
|
|
for (int mipLevel = 0; mipLevel < m_AtlasMipCount; ++mipLevel)
|
|
{
|
|
cmd.SetRenderTarget(m_AtlasTexture, mipLevel, CubemapFace.Unknown, arraySlice);
|
|
Blitter.BlitQuadWithPadding(cmd, texture, textureSizeWithoutPadding, sourceScaleOffset, scaleOffset, mipLevel, bilinear, texelPadding);
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool RelayoutTextureAtlas()
|
|
{
|
|
using (ListPool<(int textureId, Vector4 scaleOffset)>.Get(out var atlasEntries))
|
|
{
|
|
atlasEntries.Capacity = m_TextureLRUAndHash.Count;
|
|
|
|
foreach (var pair in m_TextureLRUAndHash)
|
|
{
|
|
if (m_Atlas.IsCached(out Vector4 scaleOffset, pair.Key))
|
|
atlasEntries.Add((pair.Key, scaleOffset));
|
|
}
|
|
|
|
atlasEntries.Sort((a, b) => { return b.scaleOffset.x.CompareTo(a.scaleOffset.x); });
|
|
|
|
m_Atlas.ResetAllocator();
|
|
|
|
bool success = true;
|
|
|
|
foreach (var entry in atlasEntries)
|
|
{
|
|
int textureWidth = Mathf.CeilToInt(entry.scaleOffset.x * m_AtlasWidth);
|
|
int textureHeight = Mathf.CeilToInt(entry.scaleOffset.y * m_AtlasHeight);
|
|
|
|
if (m_Atlas.EnsureTextureSlot(out _, out Vector4 scaleOffset, entry.textureId, textureWidth, textureHeight))
|
|
{
|
|
var texturePos = new Vector2Int(Mathf.FloorToInt(entry.scaleOffset.z * m_AtlasWidth), Mathf.FloorToInt(entry.scaleOffset.w * m_AtlasHeight));
|
|
var newTexturePos = new Vector2Int(Mathf.FloorToInt(scaleOffset.z * m_AtlasWidth), Mathf.FloorToInt(scaleOffset.w * m_AtlasHeight));
|
|
|
|
// Invalidate texture only if its position actually changed after re-layout.
|
|
bool invalidateTexture = texturePos != newTexturePos;
|
|
|
|
if (invalidateTexture)
|
|
{
|
|
var LRUAndHash = m_TextureLRUAndHash[entry.textureId];
|
|
LRUAndHash.Item2 = 0;
|
|
m_TextureLRUAndHash[entry.textureId] = LRUAndHash;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_TextureLRUAndHash.Remove(entry.textureId);
|
|
|
|
success = false;
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
}
|
|
|
|
private bool TryAllocateTexture(int textureId, int textureSize, ref Vector4 scaleOffset)
|
|
{
|
|
Assert.IsTrue(Mathf.IsPowerOfTwo(textureSize));
|
|
Assert.IsTrue(!m_Atlas.IsCached(out _, textureId));
|
|
|
|
// 1.
|
|
// The first direct attempt to find space.
|
|
if (m_Atlas.EnsureTextureSlot(out _, out scaleOffset, textureId, textureSize, textureSize))
|
|
return true;
|
|
|
|
// 2.
|
|
// If no space try to remove least recently used entries and find space again.
|
|
for (int textureIndex = m_TextureLRUSorted.Count - 1; textureIndex >= 0; --textureIndex)
|
|
{
|
|
var textureLRU = m_TextureLRUSorted[textureIndex];
|
|
|
|
const int k_PreviousRender = 1;
|
|
|
|
// Preserve current and previous frame cached entries.
|
|
if (m_CurrentRender - textureLRU.Item2 > k_PreviousRender)
|
|
{
|
|
m_Atlas.ReleaseTextureSlot(textureLRU.Item1);
|
|
m_TextureLRUAndHash.Remove(textureLRU.Item1);
|
|
m_TextureLRUSorted.RemoveAt(textureIndex);
|
|
|
|
if (m_Atlas.EnsureTextureSlot(out _, out scaleOffset, textureId, textureSize, textureSize))
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 3.
|
|
// Try to downscale texture and find space.
|
|
if (m_DecreaseResToFit)
|
|
{
|
|
if (m_Atlas.EnsureTextureSlot(out _, out scaleOffset, textureId, textureSize / 2, textureSize / 2))
|
|
return true;
|
|
}
|
|
|
|
m_TextureLRUAndHash.Remove(textureId);
|
|
|
|
return false;
|
|
}
|
|
|
|
private bool UpdateTexture(CommandBuffer cmd, HDProbe probe, ref Vector4 scaleOffset)
|
|
{
|
|
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.UpdateReflectionProbeAtlas)))
|
|
{
|
|
Texture texture = probe.texture;
|
|
|
|
int textureId = GetTextureIDAndSize(probe, out int textureSize);
|
|
|
|
if (!m_Atlas.IsCached(out scaleOffset, textureId))
|
|
{
|
|
if (!TryAllocateTexture(textureId, textureSize, ref scaleOffset))
|
|
return false;
|
|
}
|
|
|
|
RenderTexture convertedTextureTemp = PrepareCubeReflectionProbeTexture(cmd, texture, textureSize);
|
|
|
|
for (int filterIndex = 0; filterIndex < m_IBLFiltersBSDF.Length; ++filterIndex)
|
|
{
|
|
RenderTexture convolvedTextureTemp = ConvolveCubeReflectionProbeTexture(cmd, convertedTextureTemp ? convertedTextureTemp : texture, m_IBLFiltersBSDF[filterIndex]);
|
|
BlitTextureCube(cmd, scaleOffset, convolvedTextureTemp, filterIndex);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
private bool UpdateTexture(CommandBuffer cmd, HDProbe probe, ref IBLFilterBSDF.PlanarTextureFilteringParameters planarTextureFilteringParameters, ref Vector4 scaleOffset)
|
|
{
|
|
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.UpdateReflectionProbeAtlas)))
|
|
{
|
|
Texture texture = probe.texture;
|
|
|
|
int textureId = GetTextureIDAndSize(probe, out int textureSize);
|
|
|
|
if (!m_Atlas.IsCached(out scaleOffset, textureId))
|
|
{
|
|
if (!TryAllocateTexture(textureId, textureSize, ref scaleOffset))
|
|
return false;
|
|
}
|
|
|
|
RenderTexture convolvedPlanarReflectionTexture = ConvolvePlanarReflectionProbeTexture(cmd, texture, ref planarTextureFilteringParameters);
|
|
|
|
float sourceScale = (float)texture.width / convolvedPlanarReflectionTexture.width;
|
|
Vector4 sourceScaleOffset = new Vector4(sourceScale, sourceScale, 0.0f, 0.0f);
|
|
|
|
BlitTexture2D(cmd, scaleOffset, sourceScaleOffset, convolvedPlanarReflectionTexture, 0);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public Vector4 GetTextureAtlasCubeData()
|
|
{
|
|
return new Vector4((float)m_CubeTexelPadding / m_AtlasWidth, (float)m_CubeTexelPadding / m_AtlasHeight, m_CubeMipPadding, 0.0f);
|
|
}
|
|
|
|
public Vector4 GetTextureAtlasPlanarData()
|
|
{
|
|
return new Vector4((float)m_PlanarTexelPadding / m_AtlasWidth, (float)m_PlanarTexelPadding / m_AtlasHeight, 1.0f / m_AtlasWidth, 1.0f / m_AtlasHeight);
|
|
}
|
|
|
|
public Texture GetAtlasTexture()
|
|
{
|
|
return m_AtlasTexture;
|
|
}
|
|
|
|
public int GetEnvSliceSize()
|
|
{
|
|
return m_AtlasSlicesCount;
|
|
}
|
|
|
|
public void Release()
|
|
{
|
|
foreach (var IBLFiltersBSDF in m_IBLFiltersBSDF)
|
|
IBLFiltersBSDF.Cleanup();
|
|
m_IBLFiltersBSDF = null;
|
|
|
|
m_AtlasTexture.Release();
|
|
m_AtlasTexture = null;
|
|
m_Atlas.Release();
|
|
m_Atlas = null;
|
|
|
|
m_TextureLRUAndHash = null;
|
|
|
|
m_ConvertTextureMaterial = null;
|
|
|
|
m_ConvolvedPlanarReflectionTexture?.Release();
|
|
if (m_TempConvertedReflectionProbeTexture != null)
|
|
{
|
|
RenderTexture.ReleaseTemporary(m_TempConvertedReflectionProbeTexture);
|
|
m_TempConvertedReflectionProbeTexture = null;
|
|
}
|
|
|
|
|
|
if (m_TempConvolvedReflectionProbeTexture != null)
|
|
{
|
|
RenderTexture.ReleaseTemporary(m_TempConvolvedReflectionProbeTexture);
|
|
m_TempConvolvedReflectionProbeTexture = null;
|
|
}
|
|
}
|
|
|
|
public Vector4 FetchCubeReflectionProbe(CommandBuffer cmd, HDProbe probe, out int fetchIndex)
|
|
{
|
|
Texture texture = probe.texture;
|
|
Assert.IsTrue(texture.width == texture.height);
|
|
Assert.IsTrue(texture.dimension == TextureDimension.Cube);
|
|
|
|
fetchIndex = m_CubeFrameFetchIndex++;
|
|
|
|
Vector4 scaleOffset = Vector4.zero;
|
|
|
|
int textureId = GetTextureID(probe);
|
|
|
|
if (NeedsUpdate(textureId, probe.GetTextureHash(), ref scaleOffset))
|
|
{
|
|
if(!UpdateTexture(cmd, probe, ref scaleOffset))
|
|
LogErrorNoMoreSpaceOnce();
|
|
}
|
|
|
|
return scaleOffset;
|
|
}
|
|
|
|
public Vector4 FetchPlanarReflectionProbe(CommandBuffer cmd, HDProbe probe, ref IBLFilterBSDF.PlanarTextureFilteringParameters planarTextureFilteringParameters, out int fetchIndex)
|
|
{
|
|
Texture texture = probe.texture;
|
|
Assert.IsTrue(texture.width == texture.height);
|
|
Assert.IsTrue(texture.dimension == TextureDimension.Tex2D);
|
|
|
|
fetchIndex = m_PlanarFrameFetchIndex++;
|
|
|
|
Vector4 scaleOffset = Vector4.zero;
|
|
|
|
int textureId = GetTextureID(probe);
|
|
|
|
if (NeedsUpdate(textureId, probe.GetTextureHash(), ref scaleOffset))
|
|
{
|
|
if (!UpdateTexture(cmd, probe, ref planarTextureFilteringParameters, ref scaleOffset))
|
|
LogErrorNoMoreSpaceOnce();
|
|
}
|
|
|
|
return scaleOffset;
|
|
}
|
|
|
|
public void ReserveReflectionProbeSlot(HDProbe probe)
|
|
{
|
|
Texture texture = probe.texture;
|
|
Assert.IsTrue(texture.width == texture.height);
|
|
Assert.IsTrue(texture.dimension == TextureDimension.Tex2D || texture.dimension == TextureDimension.Cube);
|
|
|
|
int textureId = GetTextureIDAndSize(probe, out int textureSize);
|
|
|
|
if (!m_Atlas.IsCached(out _, textureId))
|
|
{
|
|
Vector4 scaleOffset = Vector4.zero;
|
|
|
|
if (!TryAllocateTexture(textureId, textureSize, ref scaleOffset))
|
|
{
|
|
if (RelayoutTextureAtlas())
|
|
TryAllocateTexture(textureId, textureSize, ref scaleOffset);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void NewFrame()
|
|
{
|
|
m_CubeFrameFetchIndex = 0;
|
|
m_PlanarFrameFetchIndex = 0;
|
|
}
|
|
|
|
public void NewRender()
|
|
{
|
|
m_NoMoreSpaceErrorLogged = false;
|
|
|
|
++m_CurrentRender;
|
|
|
|
m_TextureLRUSorted.Clear();
|
|
|
|
foreach (var pair in m_TextureLRUAndHash)
|
|
m_TextureLRUSorted.Add((pair.Key, pair.Value.Item1));
|
|
|
|
m_TextureLRUSorted.Sort((a, b) => { return b.Item2.CompareTo(a.Item2); });
|
|
}
|
|
|
|
public void GarbageCollectTmpResources()
|
|
{
|
|
if (Math.Max((int)m_CurrentRender - m_TempCubeTexturesLastFrameUsed, 0) <= k_MaxFramesTmpUsage)
|
|
return;
|
|
|
|
m_TempConvertedReflectionProbeTexture?.Release();
|
|
m_TempConvolvedReflectionProbeTexture?.Release();
|
|
m_TempConvertedReflectionProbeTexture = null;
|
|
m_TempConvolvedReflectionProbeTexture = null;
|
|
}
|
|
|
|
public void ClearAtlasAllocator()
|
|
{
|
|
m_Atlas.ResetAllocator();
|
|
m_TextureLRUAndHash.Clear();
|
|
m_TextureLRUSorted.Clear();
|
|
}
|
|
|
|
public void Clear(CommandBuffer cmd)
|
|
{
|
|
ClearAtlasAllocator();
|
|
|
|
for (int sliceIndex = 0; sliceIndex < m_AtlasSlicesCount; ++sliceIndex)
|
|
{
|
|
for (int mipLevel = 0; mipLevel < m_AtlasMipCount; ++mipLevel)
|
|
{
|
|
cmd.SetRenderTarget(m_AtlasTexture, mipLevel, CubemapFace.Unknown, sliceIndex);
|
|
Blitter.BlitQuad(cmd, Texture2D.blackTexture, new Vector4(1.0f, 1.0f, 0.0f, 0.0f), new Vector4(1.0f, 1.0f, 0.0f, 0.0f), mipLevel, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|