224 lines
9.9 KiB
C#
224 lines
9.9 KiB
C#
using System;
|
|
using UnityEngine.Experimental.Rendering;
|
|
using System.Collections.Generic;
|
|
|
|
namespace UnityEngine.Rendering.HighDefinition
|
|
{
|
|
class PlanarReflectionProbeCache
|
|
{
|
|
internal static readonly int s_InputTexID = Shader.PropertyToID("_InputTex");
|
|
internal static readonly int s_LoDID = Shader.PropertyToID("_LoD");
|
|
internal static readonly int s_FaceIndexID = Shader.PropertyToID("_FaceIndex");
|
|
|
|
enum ProbeFilteringState
|
|
{
|
|
Convolving,
|
|
Ready
|
|
}
|
|
|
|
int m_ProbeSize;
|
|
IBLFilterGGX m_IBLFilterGGX;
|
|
PowerOfTwoTextureAtlas m_TextureAtlas;
|
|
RenderTexture m_TempRenderTexture = null;
|
|
RenderTexture m_ConvolutionTargetTexture;
|
|
Dictionary<Vector4, ProbeFilteringState> m_ProbeBakingState = new Dictionary<Vector4, ProbeFilteringState>();
|
|
Material m_ConvertTextureMaterial;
|
|
MaterialPropertyBlock m_ConvertTextureMPB;
|
|
Dictionary<int, uint> m_TextureHashes = new Dictionary<int, uint>();
|
|
int m_FrameProbeIndex;
|
|
GraphicsFormat m_ProbeFormat;
|
|
|
|
public PlanarReflectionProbeCache(RenderPipelineResources defaultResources, IBLFilterGGX iblFilter, int atlasResolution, GraphicsFormat probeFormat, bool isMipmaped)
|
|
{
|
|
m_ConvertTextureMaterial = CoreUtils.CreateEngineMaterial(defaultResources.shaders.blitCubeTextureFacePS);
|
|
m_ConvertTextureMPB = new MaterialPropertyBlock();
|
|
|
|
m_ProbeSize = atlasResolution;
|
|
m_TextureAtlas = new PowerOfTwoTextureAtlas(atlasResolution, 0, probeFormat, useMipMap: isMipmaped, name: "PlanarReflectionProbe Atlas");
|
|
m_IBLFilterGGX = iblFilter;
|
|
|
|
m_ProbeFormat = probeFormat;
|
|
}
|
|
|
|
void Initialize()
|
|
{
|
|
if (m_ConvolutionTargetTexture == null)
|
|
{
|
|
m_ConvolutionTargetTexture = new RenderTexture(m_ProbeSize, m_ProbeSize, 0, m_ProbeFormat);
|
|
m_ConvolutionTargetTexture.hideFlags = HideFlags.HideAndDontSave;
|
|
m_ConvolutionTargetTexture.dimension = TextureDimension.Tex2D;
|
|
m_ConvolutionTargetTexture.useMipMap = true;
|
|
m_ConvolutionTargetTexture.autoGenerateMips = false;
|
|
m_ConvolutionTargetTexture.filterMode = FilterMode.Point;
|
|
m_ConvolutionTargetTexture.name = CoreUtils.GetRenderTargetAutoName(m_ProbeSize, m_ProbeSize, 0, m_ProbeFormat, "PlanarReflectionConvolution", mips: true);
|
|
m_ConvolutionTargetTexture.enableRandomWrite = true;
|
|
m_ConvolutionTargetTexture.Create();
|
|
|
|
// Clear to avoid garbage in the convolution texture.
|
|
int mipCount = Mathf.FloorToInt(Mathf.Log(m_ProbeSize, 2)) + 1;
|
|
for (int mipIdx = 0; mipIdx < mipCount; ++mipIdx)
|
|
{
|
|
Graphics.SetRenderTarget(m_ConvolutionTargetTexture, mipIdx, CubemapFace.Unknown);
|
|
GL.Clear(false, true, Color.clear);
|
|
}
|
|
}
|
|
|
|
m_FrameProbeIndex = 0;
|
|
}
|
|
|
|
public void Release()
|
|
{
|
|
m_TextureAtlas.Release();
|
|
CoreUtils.Destroy(m_TempRenderTexture);
|
|
CoreUtils.Destroy(m_ConvolutionTargetTexture);
|
|
|
|
m_ProbeBakingState = null;
|
|
|
|
CoreUtils.Destroy(m_ConvertTextureMaterial);
|
|
}
|
|
|
|
public void NewFrame()
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
// This method is used to convert inputs that are either compressed or not of the right size.
|
|
// We can't use Graphics.ConvertTexture here because it does not work with a RenderTexture as destination.
|
|
void ConvertTexture(CommandBuffer cmd, Texture input, RenderTexture target)
|
|
{
|
|
m_ConvertTextureMPB.SetTexture(s_InputTexID, input);
|
|
m_ConvertTextureMPB.SetFloat(s_LoDID, 0.0f); // We want to convert mip 0 to whatever the size of the destination cache is.
|
|
CoreUtils.SetRenderTarget(cmd, target, ClearFlag.None, Color.black, 0, 0);
|
|
CoreUtils.DrawFullScreen(cmd, m_ConvertTextureMaterial, m_ConvertTextureMPB);
|
|
}
|
|
|
|
Texture ConvolveProbeTexture(CommandBuffer cmd, Texture texture, ref IBLFilterBSDF.PlanarTextureFilteringParameters planarTextureFilteringParameters, out Vector4 sourceScaleOffset)
|
|
{
|
|
Texture2D texture2D = texture as Texture2D;
|
|
RenderTexture renderTexture = texture as RenderTexture;
|
|
|
|
if (renderTexture.dimension != TextureDimension.Tex2D)
|
|
{
|
|
Debug.LogError("Planar Realtime reflection probe should always be a 2D RenderTexture.");
|
|
sourceScaleOffset = Vector4.zero;
|
|
return null;
|
|
}
|
|
|
|
float scaleX = (float)texture.width / m_ConvolutionTargetTexture.width;
|
|
float scaleY = (float)texture.height / m_ConvolutionTargetTexture.height;
|
|
sourceScaleOffset = new Vector4(scaleX, scaleY, 0, 0);
|
|
m_IBLFilterGGX.FilterPlanarTexture(cmd, renderTexture, ref planarTextureFilteringParameters, m_ConvolutionTargetTexture);
|
|
|
|
return m_ConvolutionTargetTexture;
|
|
}
|
|
|
|
public Vector4 FetchSlice(CommandBuffer cmd, Texture texture, ref IBLFilterBSDF.PlanarTextureFilteringParameters planarTextureFilteringParameters, out int fetchIndex)
|
|
{
|
|
Vector4 scaleOffset = Vector4.zero;
|
|
fetchIndex = m_FrameProbeIndex++;
|
|
|
|
if (m_TextureAtlas.IsCached(out scaleOffset, texture))
|
|
{
|
|
// If the texture is already in the atlas, we update it only if needed
|
|
if (NeedsUpdate(texture) || m_ProbeBakingState[scaleOffset] != ProbeFilteringState.Ready)
|
|
if (!UpdatePlanarTexture(cmd, texture, ref planarTextureFilteringParameters, ref scaleOffset))
|
|
Debug.LogError("Can't convolve or update the planar reflection render target");
|
|
}
|
|
else // Either we add it to the atlas
|
|
if (!UpdatePlanarTexture(cmd, texture, ref planarTextureFilteringParameters, ref scaleOffset))
|
|
Debug.LogError("No more space in the planar reflection probe atlas. To solve this issue, increase the size of the Planar Reflection Probe Atlas in the HDRP settings.");
|
|
|
|
return scaleOffset;
|
|
}
|
|
|
|
bool UpdatePlanarTexture(CommandBuffer cmd, Texture texture, ref IBLFilterBSDF.PlanarTextureFilteringParameters planarTextureFilteringParameters, ref Vector4 scaleOffset)
|
|
{
|
|
bool success = false;
|
|
|
|
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.ConvolvePlanarReflectionProbe)))
|
|
{
|
|
m_ProbeBakingState[scaleOffset] = ProbeFilteringState.Convolving;
|
|
|
|
Vector4 sourceScaleOffset;
|
|
Texture convolvedTexture = ConvolveProbeTexture(cmd, texture, ref planarTextureFilteringParameters, out sourceScaleOffset);
|
|
if (convolvedTexture == null)
|
|
return false;
|
|
|
|
if (m_TextureAtlas.IsCached(out scaleOffset, texture))
|
|
{
|
|
success = m_TextureAtlas.UpdateTexture(cmd, texture, convolvedTexture, ref scaleOffset, sourceScaleOffset);
|
|
}
|
|
else
|
|
{
|
|
// Reserve space for the rendertarget and then blit the result of the convolution at this
|
|
// location, we don't use the UpdateTexture because it will keep the reference to the
|
|
// temporary target used to convolve the result of the probe rendering.
|
|
if (!m_TextureAtlas.AllocateTextureWithoutBlit(texture, texture.width, texture.height, ref scaleOffset))
|
|
return false;
|
|
m_TextureAtlas.BlitTexture(cmd, scaleOffset, convolvedTexture, sourceScaleOffset);
|
|
success = true;
|
|
}
|
|
|
|
m_ProbeBakingState[scaleOffset] = ProbeFilteringState.Ready;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
public uint GetTextureHash(Texture texture)
|
|
{
|
|
uint textureHash = 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.
|
|
#if UNITY_EDITOR
|
|
textureHash += (uint)texture.imageContentsHash.GetHashCode();
|
|
#endif
|
|
return textureHash;
|
|
}
|
|
|
|
bool NeedsUpdate(Texture texture)
|
|
{
|
|
uint savedTextureHash;
|
|
uint currentTextureHash = GetTextureHash(texture);
|
|
int instanceId = texture.GetInstanceID();
|
|
bool needsUpdate = false;
|
|
|
|
if (!m_TextureHashes.TryGetValue(instanceId, out savedTextureHash) || savedTextureHash != currentTextureHash)
|
|
{
|
|
m_TextureHashes[instanceId] = currentTextureHash;
|
|
needsUpdate = true;
|
|
}
|
|
|
|
return needsUpdate;
|
|
}
|
|
|
|
public Texture GetTexCache() => m_TextureAtlas.AtlasTexture;
|
|
|
|
|
|
public void Clear(CommandBuffer cmd)
|
|
{
|
|
m_TextureAtlas.ResetAllocator();
|
|
m_TextureAtlas.ClearTarget(cmd);
|
|
}
|
|
|
|
public void ClearAtlasAllocator() => m_TextureAtlas.ResetAllocator();
|
|
|
|
internal static long GetApproxCacheSizeInByte(int nbElement, int atlasResolution, GraphicsFormat format)
|
|
=> PowerOfTwoTextureAtlas.GetApproxCacheSizeInByte(nbElement, atlasResolution, true, format);
|
|
|
|
internal static int GetMaxCacheSizeForWeightInByte(int weight, GraphicsFormat format)
|
|
=> PowerOfTwoTextureAtlas.GetMaxCacheSizeForWeightInByte(weight, true, format);
|
|
|
|
internal Vector4 GetAtlasDatas()
|
|
{
|
|
float padding = Mathf.Pow(2.0f, m_TextureAtlas.mipPadding) * 2.0f;
|
|
|
|
return new Vector4(
|
|
m_TextureAtlas.AtlasTexture.rt.width,
|
|
padding / (float)m_TextureAtlas.AtlasTexture.rt.width,
|
|
0,
|
|
0
|
|
);
|
|
}
|
|
}
|
|
}
|