512 lines
27 KiB
C#
512 lines
27 KiB
C#
using UnityEngine.Rendering.RenderGraphModule;
|
|
using UnityEngine.Rendering.RenderGraphModule.Util;
|
|
using UnityEngine.Rendering.Universal;
|
|
|
|
namespace UnityEngine.Rendering
|
|
{
|
|
internal struct PackedMipChainInfo
|
|
{
|
|
public Vector2Int textureSize;
|
|
public int mipLevelCount; // mips contain min (closest) depth
|
|
public int mipLevelCountCheckerboard;
|
|
public Vector2Int[] mipLevelSizes;
|
|
public Vector2Int[] mipLevelOffsets; // mips contain min (closest) depth
|
|
public Vector2Int[] mipLevelOffsetsCheckerboard;
|
|
|
|
private Vector2 cachedTextureScale;
|
|
private Vector2Int cachedHardwareTextureSize;
|
|
private int cachedCheckerboardMipCount;
|
|
|
|
public void Allocate()
|
|
{
|
|
mipLevelOffsets = new Vector2Int[15];
|
|
mipLevelOffsetsCheckerboard = new Vector2Int[15];
|
|
mipLevelSizes = new Vector2Int[15];
|
|
}
|
|
|
|
enum PackDirection
|
|
{
|
|
Right,
|
|
Down,
|
|
}
|
|
|
|
static Vector2Int NextMipBegin(Vector2Int prevMipBegin, Vector2Int prevMipSize, PackDirection dir)
|
|
{
|
|
Vector2Int mipBegin = prevMipBegin;
|
|
if (dir == PackDirection.Right)
|
|
mipBegin.x += prevMipSize.x;
|
|
else
|
|
mipBegin.y += prevMipSize.y;
|
|
return mipBegin;
|
|
}
|
|
|
|
// We pack all MIP levels into the top MIP level to avoid the Pow2 MIP chain restriction.
|
|
// We compute the required size iteratively.
|
|
// This function is NOT fast, but it is illustrative, and can be optimized later.
|
|
public void ComputePackedMipChainInfo(Vector2Int viewportSize, int checkerboardMipCount)
|
|
{
|
|
// only support up to 2 mips of checkerboard data being created
|
|
checkerboardMipCount = Mathf.Clamp(checkerboardMipCount, 0, 2);
|
|
|
|
bool isHardwareDrsOn = DynamicResolutionHandler.instance.HardwareDynamicResIsEnabled();
|
|
Vector2Int hardwareTextureSize = isHardwareDrsOn ? DynamicResolutionHandler.instance.ApplyScalesOnSize(viewportSize) : viewportSize;
|
|
Vector2 textureScale = isHardwareDrsOn ? new Vector2((float)viewportSize.x / (float)hardwareTextureSize.x, (float)viewportSize.y / (float)hardwareTextureSize.y) : new Vector2(1.0f, 1.0f);
|
|
|
|
// No work needed.
|
|
if (cachedHardwareTextureSize == hardwareTextureSize && cachedTextureScale == textureScale && cachedCheckerboardMipCount == checkerboardMipCount)
|
|
return;
|
|
|
|
cachedHardwareTextureSize = hardwareTextureSize;
|
|
cachedTextureScale = textureScale;
|
|
cachedCheckerboardMipCount = checkerboardMipCount;
|
|
|
|
mipLevelSizes[0] = hardwareTextureSize;
|
|
mipLevelOffsets[0] = Vector2Int.zero;
|
|
mipLevelOffsetsCheckerboard[0] = mipLevelOffsets[0];
|
|
|
|
int mipLevel = 0;
|
|
Vector2Int mipSize = hardwareTextureSize;
|
|
bool hasCheckerboard = (checkerboardMipCount != 0);
|
|
do
|
|
{
|
|
mipLevel++;
|
|
|
|
// Round up.
|
|
mipSize.x = Mathf.Max(1, (mipSize.x + 1) >> 1);
|
|
mipSize.y = Mathf.Max(1, (mipSize.y + 1) >> 1);
|
|
|
|
mipLevelSizes[mipLevel] = mipSize;
|
|
|
|
Vector2Int prevMipSize = mipLevelSizes[mipLevel - 1];
|
|
Vector2Int prevMipBegin = mipLevelOffsets[mipLevel - 1];
|
|
Vector2Int prevMipBeginCheckerboard = mipLevelOffsetsCheckerboard[mipLevel - 1];
|
|
|
|
Vector2Int mipBegin = prevMipBegin;
|
|
Vector2Int mipBeginCheckerboard = prevMipBeginCheckerboard;
|
|
if (mipLevel == 1)
|
|
{
|
|
// first mip always below full resolution
|
|
mipBegin = NextMipBegin(prevMipBegin, prevMipSize, PackDirection.Down);
|
|
|
|
// pack checkerboard next to it if present
|
|
if (hasCheckerboard)
|
|
mipBeginCheckerboard = NextMipBegin(mipBegin, mipSize, PackDirection.Right);
|
|
else
|
|
mipBeginCheckerboard = mipBegin;
|
|
}
|
|
else
|
|
{
|
|
// alternate directions, mip 2 starts with down if checkerboard, right if not
|
|
bool isOdd = ((mipLevel & 1) != 0);
|
|
PackDirection dir = (isOdd ^ hasCheckerboard) ? PackDirection.Down : PackDirection.Right;
|
|
|
|
mipBegin = NextMipBegin(prevMipBegin, prevMipSize, dir);
|
|
mipBeginCheckerboard = NextMipBegin(prevMipBeginCheckerboard, prevMipSize, dir);
|
|
}
|
|
|
|
mipLevelOffsets[mipLevel] = mipBegin;
|
|
mipLevelOffsetsCheckerboard[mipLevel] = mipBeginCheckerboard;
|
|
|
|
hardwareTextureSize.x = Mathf.Max(hardwareTextureSize.x, mipBegin.x + mipSize.x);
|
|
hardwareTextureSize.y = Mathf.Max(hardwareTextureSize.y, mipBegin.y + mipSize.y);
|
|
hardwareTextureSize.x = Mathf.Max(hardwareTextureSize.x, mipBeginCheckerboard.x + mipSize.x);
|
|
hardwareTextureSize.y = Mathf.Max(hardwareTextureSize.y, mipBeginCheckerboard.y + mipSize.y);
|
|
}
|
|
while ((mipSize.x > 1) || (mipSize.y > 1));
|
|
|
|
textureSize = new Vector2Int(
|
|
(int)Mathf.Ceil((float)hardwareTextureSize.x * textureScale.x), (int)Mathf.Ceil((float)hardwareTextureSize.y * textureScale.y));
|
|
|
|
mipLevelCount = mipLevel + 1;
|
|
mipLevelCountCheckerboard = hasCheckerboard ? (1 + checkerboardMipCount) : 0;
|
|
}
|
|
}
|
|
|
|
internal class MipGenerator
|
|
{
|
|
ComputeShader m_ColorPyramidCS = null;
|
|
ComputeShader m_DepthPyramidCS = null;
|
|
Shader m_ColorPyramidPS;
|
|
Material m_ColorPyramidPSMat;
|
|
MaterialPropertyBlock m_PropertyBlockBlur;
|
|
|
|
int m_DepthDownsampleKernel = -1;
|
|
int m_ColorDownsampleKernel = -1;
|
|
int m_ColorGaussianKernel = -1;
|
|
|
|
bool m_PreferCompute;
|
|
bool m_SupportCompute;
|
|
|
|
LocalKeyword m_DisableTexture2DArrayColorKeyword;
|
|
LocalKeyword m_DisableTexture2DArrayColorPSKeyword;
|
|
LocalKeyword m_DisableTexture2DArrayDepthKeyword;
|
|
LocalKeyword m_EnableCheckerboardKeyword;
|
|
|
|
public static readonly int _DepthMipChain = Shader.PropertyToID("_DepthMipChain");
|
|
public static readonly int _DepthPyramidConstants = Shader.PropertyToID("DepthPyramidConstants");
|
|
|
|
public static readonly int _Size = Shader.PropertyToID("_Size");
|
|
public static readonly int _Source = Shader.PropertyToID("_Source");
|
|
public static readonly int _Destination = Shader.PropertyToID("_Destination");
|
|
|
|
public static readonly int _SourceMip = Shader.PropertyToID("_SourceMip");
|
|
public static readonly int _SrcScaleBias = Shader.PropertyToID("_SrcScaleBias");
|
|
public static readonly int _SrcUvLimits = Shader.PropertyToID("_SrcUvLimits");
|
|
|
|
public static readonly string k_EnableCheckerboard = "ENABLE_CHECKERBOARD";
|
|
|
|
public const int k_MinimumResolutionGaussian = 8;
|
|
|
|
public MipGenerator(bool preferCompute = true)
|
|
{
|
|
if (!GraphicsSettings.TryGetRenderPipelineSettings<MipGenRenderPipelineRuntimeResources>(out var runtimeShaders))
|
|
{
|
|
Debug.LogErrorFormat(
|
|
$"Couldn't find the required resources for the {nameof(MipGenerator)} .");
|
|
}
|
|
|
|
m_SupportCompute = SystemInfo.supportsComputeShaders
|
|
&& runtimeShaders.colorPyramidCS.HasKernel("ColorDownsample")
|
|
&& runtimeShaders.colorPyramidCS.HasKernel("ColorGaussian")
|
|
&& runtimeShaders.depthPyramidCS.HasKernel("DepthDownsample");
|
|
if (m_SupportCompute)
|
|
{
|
|
m_ColorPyramidCS = runtimeShaders.colorPyramidCS;
|
|
m_DepthPyramidCS = runtimeShaders.depthPyramidCS;
|
|
|
|
m_ColorDownsampleKernel = m_ColorPyramidCS.FindKernel("ColorDownsample");
|
|
m_ColorGaussianKernel = m_ColorPyramidCS.FindKernel("ColorGaussian");
|
|
m_DisableTexture2DArrayColorKeyword = new LocalKeyword(m_ColorPyramidCS, ShaderKeywordStrings.DisableTexture2DXArray);
|
|
|
|
m_DepthDownsampleKernel = m_DepthPyramidCS.FindKernel("DepthDownsample");
|
|
m_DisableTexture2DArrayDepthKeyword = new LocalKeyword(m_DepthPyramidCS, ShaderKeywordStrings.DisableTexture2DXArray);
|
|
m_EnableCheckerboardKeyword = new LocalKeyword(m_DepthPyramidCS, k_EnableCheckerboard);
|
|
}
|
|
|
|
m_ColorPyramidPS = runtimeShaders.colorPyramidPS;
|
|
m_DisableTexture2DArrayColorPSKeyword = new LocalKeyword(m_ColorPyramidPS, ShaderKeywordStrings.DisableTexture2DXArray);
|
|
m_ColorPyramidPSMat = CoreUtils.CreateEngineMaterial(m_ColorPyramidPS);
|
|
m_PropertyBlockBlur = new MaterialPropertyBlock();
|
|
|
|
m_PreferCompute = preferCompute;
|
|
}
|
|
|
|
public void Release()
|
|
{
|
|
CoreUtils.Destroy(m_ColorPyramidPSMat);
|
|
}
|
|
|
|
class DepthPyramidPassData
|
|
{
|
|
public PackedMipChainInfo info;
|
|
public ComputeShader cs;
|
|
public int kernel;
|
|
public TextureHandle depthTexture;
|
|
public LocalKeyword disableTexture2DArrayKeyword;
|
|
public LocalKeyword enableCheckerboardKeyword;
|
|
}
|
|
|
|
// Generates an in-place depth pyramid Compute only
|
|
// TODO: Mip-mapping depth is problematic for precision at lower mips, generate a packed atlas instead
|
|
public void RenderMinDepthPyramid(RenderGraph renderGraph, TextureHandle depthTexture, ref PackedMipChainInfo depthBufferMipChainInfo)
|
|
{
|
|
if (!m_SupportCompute)
|
|
{
|
|
Debug.LogError("MipGenerator: Can't render depth pyramid, platform doesn't support compute shaders.");
|
|
return;
|
|
}
|
|
|
|
using (var builder = renderGraph.AddComputePass<DepthPyramidPassData>("Generate Depth Buffer MIP Chain", out var passData))
|
|
{
|
|
passData.info = depthBufferMipChainInfo;
|
|
passData.cs = m_DepthPyramidCS;
|
|
passData.kernel = m_DepthDownsampleKernel;
|
|
passData.depthTexture = depthTexture;
|
|
passData.disableTexture2DArrayKeyword = m_DisableTexture2DArrayDepthKeyword;
|
|
passData.enableCheckerboardKeyword = m_EnableCheckerboardKeyword;
|
|
|
|
// Enable global state modification so we can set keywords.
|
|
builder.AllowGlobalStateModification(true);
|
|
builder.UseTexture(passData.depthTexture, AccessFlags.ReadWrite);
|
|
builder.SetRenderFunc<DepthPyramidPassData>(static (data, context) =>
|
|
{
|
|
var cmd = context.cmd;
|
|
var sourceIsArray = (((RenderTexture)data.depthTexture).dimension == TextureDimension.Tex2DArray);
|
|
|
|
cmd.SetKeyword(data.cs, data.disableTexture2DArrayKeyword, !sourceIsArray);
|
|
|
|
// Note: Gather() doesn't take a LOD parameter and we cannot bind an SRV of a MIP level,
|
|
// and we don't support Min samplers either. So we are forced to perform 4x loads.
|
|
for (int dstIndex0 = 1; dstIndex0 < data.info.mipLevelCount;)
|
|
{
|
|
int minCount = Mathf.Min(data.info.mipLevelCount - dstIndex0, 4);
|
|
int cbCount = 0;
|
|
if (dstIndex0 < data.info.mipLevelCountCheckerboard)
|
|
{
|
|
cbCount = data.info.mipLevelCountCheckerboard - dstIndex0;
|
|
Debug.Assert(dstIndex0 == 1, "expected to make checkerboard mips on the first pass");
|
|
Debug.Assert(cbCount <= minCount, "expected fewer checkerboard mips than min mips");
|
|
Debug.Assert(cbCount <= 2, "expected 2 or fewer checkerboard mips for now");
|
|
}
|
|
|
|
Vector2Int srcOffset = data.info.mipLevelOffsets[dstIndex0 - 1];
|
|
Vector2Int srcSize = data.info.mipLevelSizes[dstIndex0 - 1];
|
|
int dstIndex1 = Mathf.Min(dstIndex0 + 1, data.info.mipLevelCount - 1);
|
|
int dstIndex2 = Mathf.Min(dstIndex0 + 2, data.info.mipLevelCount - 1);
|
|
int dstIndex3 = Mathf.Min(dstIndex0 + 3, data.info.mipLevelCount - 1);
|
|
|
|
DepthPyramidConstants cb = new DepthPyramidConstants
|
|
{
|
|
_MinDstCount = (uint)minCount,
|
|
_CbDstCount = (uint)cbCount,
|
|
_SrcOffset = srcOffset,
|
|
_SrcLimit = srcSize - Vector2Int.one,
|
|
_DstSize0 = data.info.mipLevelSizes[dstIndex0],
|
|
_DstSize1 = data.info.mipLevelSizes[dstIndex1],
|
|
_DstSize2 = data.info.mipLevelSizes[dstIndex2],
|
|
_DstSize3 = data.info.mipLevelSizes[dstIndex3],
|
|
_MinDstOffset0 = data.info.mipLevelOffsets[dstIndex0],
|
|
_MinDstOffset1 = data.info.mipLevelOffsets[dstIndex1],
|
|
_MinDstOffset2 = data.info.mipLevelOffsets[dstIndex2],
|
|
_MinDstOffset3 = data.info.mipLevelOffsets[dstIndex3],
|
|
_CbDstOffset0 = data.info.mipLevelOffsetsCheckerboard[dstIndex0],
|
|
_CbDstOffset1 = data.info.mipLevelOffsetsCheckerboard[dstIndex1],
|
|
};
|
|
|
|
ConstantBuffer.Push(cmd, in cb, data.cs, _DepthPyramidConstants);
|
|
|
|
cmd.SetComputeTextureParam(data.cs, data.kernel, _DepthMipChain, data.depthTexture);
|
|
cmd.SetKeyword(data.cs, data.enableCheckerboardKeyword, cbCount != 0);
|
|
|
|
Vector2Int dstSize = data.info.mipLevelSizes[dstIndex0];
|
|
cmd.DispatchCompute(data.cs, data.kernel, CoreUtils.DivRoundUp(dstSize.x, 8), CoreUtils.DivRoundUp(dstSize.y, 8), ((RenderTexture)data.depthTexture).volumeDepth);
|
|
|
|
dstIndex0 += minCount;
|
|
}
|
|
|
|
});
|
|
}
|
|
}
|
|
|
|
private class PassDataMipChainRaster
|
|
{
|
|
public MaterialPropertyBlock propertyBlock;
|
|
public Material material;
|
|
public TextureHandle source;
|
|
public int dstMipWidth, dstMipHeight;
|
|
public int srcMipLevel;
|
|
public float scaleX, scaleY;
|
|
public float blurSourceTextureWidth, blurSourceTextureHeight;
|
|
public LocalKeyword disableTexture2DArrayKeyword;
|
|
public bool sourceIsArray;
|
|
}
|
|
|
|
private class PassDataMipChainCompute
|
|
{
|
|
public int numTheadGroupX, numTheadGroupY, numTheadGroupZ;
|
|
public int srcMipWidth, srcMipHeight;
|
|
public int dstMipWidth, dstMipHeight;
|
|
public int srcMipLevel;
|
|
public TextureHandle tempDownsamplePyramid, destination;
|
|
public ComputeShader cs;
|
|
public LocalKeyword disableTexture2DArrayKeyword;
|
|
public int downsampleKernel, gaussianKernel;
|
|
public bool sourceIsArray;
|
|
}
|
|
|
|
// Generates the gaussian pyramid of source into destination
|
|
// We can't do it in place as the color pyramid has to be read while writing to the color
|
|
// buffer in some cases (e.g. refraction, distortion)
|
|
// Returns the number of mips
|
|
public int RenderColorGaussianPyramid(RenderGraph renderGraph, Vector2Int size, TextureHandle source, TextureHandle destination)
|
|
{
|
|
TextureDesc descSrc = renderGraph.GetTextureDesc(source);
|
|
TextureDesc descDst = renderGraph.GetTextureDesc(destination);
|
|
|
|
// Select between Tex2D and Tex2DArray versions of the kernels
|
|
bool sourceIsArray = (descSrc.dimension == TextureDimension.Tex2DArray);
|
|
// Sanity check
|
|
if (sourceIsArray)
|
|
{
|
|
Debug.Assert(descSrc.dimension == descDst.dimension, "MipGenerator source texture does not match dimension of destination!");
|
|
}
|
|
|
|
int srcMipLevel = 0;
|
|
int srcMipWidth = size.x;
|
|
int srcMipHeight = size.y;
|
|
|
|
bool isHardwareDrsOn = DynamicResolutionHandler.instance.HardwareDynamicResIsEnabled();
|
|
var hardwareTextureSize = new Vector2Int(descSrc.width, descSrc.height);
|
|
if (isHardwareDrsOn)
|
|
hardwareTextureSize = DynamicResolutionHandler.instance.ApplyScalesOnSize(hardwareTextureSize);
|
|
|
|
renderGraph.AddCopyPass(source, destination, "Copy mip 0");
|
|
var finalTargetSize = new Vector2Int(descDst.width, descDst.height);
|
|
if (descDst.useDynamicScale && isHardwareDrsOn)
|
|
finalTargetSize = DynamicResolutionHandler.instance.ApplyScalesOnSize(finalTargetSize);
|
|
|
|
bool usePixelShader = !m_SupportCompute || !m_PreferCompute;
|
|
|
|
// Note: smaller mips are excluded as we don't need them and the gaussian compute works
|
|
// on 8x8 blocks
|
|
while (srcMipWidth >= k_MinimumResolutionGaussian || srcMipHeight >= k_MinimumResolutionGaussian)
|
|
{
|
|
int dstMipWidth = Mathf.Max(1, srcMipWidth >> 1);
|
|
int dstMipHeight = Mathf.Max(1, srcMipHeight >> 1);
|
|
|
|
var desc = new TextureDesc(dstMipWidth, dstMipHeight);
|
|
desc.name = "Temporary Downsampled Pyramid";
|
|
desc.scale = Vector2.one * 0.5f;
|
|
desc.dimension = descSrc.dimension;
|
|
desc.colorFormat = descDst.colorFormat;
|
|
desc.enableRandomWrite = !usePixelShader;
|
|
desc.useMipMap = false;
|
|
desc.slices = sourceIsArray ? TextureXR.slices : 1;
|
|
var tempDownsamplePyramid = renderGraph.CreateTexture(desc);
|
|
|
|
// Scale for downsample
|
|
float downsampleScaleX = ((float)srcMipWidth / finalTargetSize.x);
|
|
float downsampleScaleY = ((float)srcMipHeight / finalTargetSize.y);
|
|
|
|
if (usePixelShader)
|
|
{
|
|
// Downsample.
|
|
renderGraph.AddBlitPass(destination, tempDownsamplePyramid, new(downsampleScaleX, downsampleScaleY), Vector2.zero, sourceMip: srcMipLevel, passName: "Color Pyramid - Downsample");
|
|
|
|
// In this mip generation process, source viewport can be smaller than the source render target itself because of the RTHandle system
|
|
// We are not using the scale provided by the RTHandle system for two reasons:
|
|
// - Source might be a planar probe which will not be scaled by the system (since it's actually the final target of probe rendering at the exact size)
|
|
// - When computing mip size, depending on even/odd sizes, the scale computed for mip 0 might miss a texel at the border.
|
|
// This can result in a shift in the mip map downscale that depends on the render target size rather than the actual viewport
|
|
// (Two rendering at the same viewport size but with different RTHandle reference size would yield different results which can break automated testing)
|
|
// So in the end we compute a specific scale for downscale and blur passes at each mip level.
|
|
|
|
// Scales for Blur
|
|
// Same size as m_TempColorTargets which is the source for vertical blur
|
|
desc.name = "Temp Gaussian Pyramid Target";
|
|
desc.slices = sourceIsArray ? TextureXR.slices : 1;
|
|
desc.dimension = descSrc.dimension;
|
|
desc.filterMode = FilterMode.Bilinear;
|
|
desc.colorFormat = descDst.colorFormat;
|
|
desc.enableRandomWrite = false;
|
|
desc.useMipMap = false;
|
|
var tempColorTarget = renderGraph.CreateTexture(desc);
|
|
|
|
var hardwareBlurSourceTextureSize = new Vector2Int(dstMipWidth, dstMipHeight);
|
|
if (isHardwareDrsOn)
|
|
hardwareBlurSourceTextureSize = DynamicResolutionHandler.instance.ApplyScalesOnSize(hardwareBlurSourceTextureSize);
|
|
|
|
float blurSourceTextureWidth = (float)hardwareBlurSourceTextureSize.x;
|
|
float blurSourceTextureHeight = (float)hardwareBlurSourceTextureSize.y;
|
|
|
|
float scaleX = ((float)dstMipWidth / blurSourceTextureWidth);
|
|
float scaleY = ((float)dstMipHeight / blurSourceTextureHeight);
|
|
|
|
// Blur horizontal.
|
|
using (var builder = renderGraph.AddRasterRenderPass<PassDataMipChainRaster>("Color Pyramid - Horizontal Blur", out var passData))
|
|
{
|
|
passData.propertyBlock = m_PropertyBlockBlur;
|
|
passData.source = tempDownsamplePyramid;
|
|
passData.material = m_ColorPyramidPSMat;
|
|
passData.dstMipWidth = dstMipWidth;
|
|
passData.dstMipHeight = dstMipHeight;
|
|
passData.scaleX = scaleX;
|
|
passData.scaleY = scaleY;
|
|
passData.blurSourceTextureWidth = blurSourceTextureWidth;
|
|
passData.blurSourceTextureHeight = blurSourceTextureHeight;
|
|
passData.disableTexture2DArrayKeyword = m_DisableTexture2DArrayColorPSKeyword;
|
|
passData.sourceIsArray = sourceIsArray;
|
|
|
|
builder.UseTexture(passData.source, AccessFlags.Read);
|
|
builder.SetRenderAttachment(tempColorTarget, 0);
|
|
builder.SetRenderFunc<PassDataMipChainRaster>(static (data, context) => MipChainRasterBlurExecutePass(data, context, false));
|
|
}
|
|
|
|
// Blur vertical.
|
|
using (var builder = renderGraph.AddRasterRenderPass<PassDataMipChainRaster>("Color Pyramid - Vertical Blur", out var passData))
|
|
{
|
|
passData.propertyBlock = m_PropertyBlockBlur;
|
|
passData.source = tempColorTarget;
|
|
passData.material = m_ColorPyramidPSMat;
|
|
passData.dstMipWidth = dstMipWidth;
|
|
passData.dstMipHeight = dstMipHeight;
|
|
passData.scaleX = scaleX;
|
|
passData.scaleY = scaleY;
|
|
passData.blurSourceTextureWidth = blurSourceTextureWidth;
|
|
passData.blurSourceTextureHeight = blurSourceTextureHeight;
|
|
passData.disableTexture2DArrayKeyword = m_DisableTexture2DArrayColorPSKeyword;
|
|
passData.sourceIsArray = sourceIsArray;
|
|
|
|
builder.UseTexture(passData.source, AccessFlags.Read);
|
|
builder.SetRenderAttachment(destination, 0, AccessFlags.Write, srcMipLevel + 1, -1);
|
|
builder.SetRenderFunc<PassDataMipChainRaster>(static (data, context) => MipChainRasterBlurExecutePass(data, context, true));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
using (var builder = renderGraph.AddComputePass<PassDataMipChainCompute>("Color Pyramid - MIP Chain (Compute)", out var passData))
|
|
{
|
|
passData.numTheadGroupX = (dstMipWidth + 7) / 8;
|
|
passData.numTheadGroupY = (dstMipHeight + 7) / 8;
|
|
passData.numTheadGroupZ = TextureXR.slices;
|
|
passData.srcMipWidth = srcMipWidth;
|
|
passData.srcMipHeight = srcMipHeight;
|
|
passData.dstMipWidth = dstMipWidth;
|
|
passData.dstMipHeight = dstMipHeight;
|
|
passData.srcMipLevel = srcMipLevel;
|
|
passData.tempDownsamplePyramid = tempDownsamplePyramid;
|
|
passData.destination = destination;
|
|
passData.cs = m_ColorPyramidCS;
|
|
passData.disableTexture2DArrayKeyword = m_DisableTexture2DArrayColorKeyword;
|
|
passData.downsampleKernel = m_ColorDownsampleKernel;
|
|
passData.gaussianKernel = m_ColorGaussianKernel;
|
|
passData.sourceIsArray = sourceIsArray;
|
|
|
|
// Enable global state modification so we can set keywords.
|
|
builder.AllowGlobalStateModification(true);
|
|
builder.UseTexture(passData.tempDownsamplePyramid, AccessFlags.ReadWrite);
|
|
builder.UseTexture(destination, AccessFlags.ReadWrite);
|
|
|
|
builder.SetRenderFunc(static (PassDataMipChainCompute data, ComputeGraphContext context) =>
|
|
{
|
|
context.cmd.SetKeyword(data.cs, data.disableTexture2DArrayKeyword, !data.sourceIsArray);
|
|
|
|
// Downsample.
|
|
context.cmd.SetComputeVectorParam(data.cs, _Size, new Vector4(data.srcMipWidth, data.srcMipHeight, 0f, 0f));
|
|
context.cmd.SetComputeTextureParam(data.cs, data.downsampleKernel, _Source, data.destination, data.srcMipLevel);
|
|
context.cmd.SetComputeTextureParam(data.cs, data.downsampleKernel, _Destination, data.tempDownsamplePyramid);
|
|
context.cmd.DispatchCompute(data.cs, data.downsampleKernel, data.numTheadGroupX, data.numTheadGroupY, data.numTheadGroupZ);
|
|
|
|
// Single pass blur
|
|
context.cmd.SetComputeVectorParam(data.cs, _Size, new Vector4(data.dstMipWidth, data.dstMipHeight, 0f, 0f));
|
|
context.cmd.SetComputeTextureParam(data.cs, data.gaussianKernel, _Source, data.tempDownsamplePyramid);
|
|
context.cmd.SetComputeTextureParam(data.cs, data.gaussianKernel, _Destination, data.destination, data.srcMipLevel + 1);
|
|
context.cmd.DispatchCompute(data.cs, data.gaussianKernel, data.numTheadGroupX, data.numTheadGroupY, data.numTheadGroupZ);
|
|
});
|
|
}
|
|
}
|
|
|
|
srcMipLevel++;
|
|
srcMipWidth = dstMipWidth;
|
|
srcMipHeight = dstMipHeight;
|
|
|
|
finalTargetSize.x >>= 1;
|
|
finalTargetSize.y >>= 1;
|
|
}
|
|
|
|
return srcMipLevel + 1;
|
|
}
|
|
|
|
static void MipChainRasterBlurExecutePass(PassDataMipChainRaster data, RasterGraphContext context, bool isVertical)
|
|
{
|
|
data.propertyBlock.SetTexture(_Source, data.source);
|
|
data.propertyBlock.SetVector(_SrcScaleBias, new Vector4(data.scaleX, data.scaleY, 0f, 0f));
|
|
data.propertyBlock.SetVector(_SrcUvLimits, new Vector4((data.dstMipWidth - 0.5f) / data.blurSourceTextureWidth, (data.dstMipHeight - 0.5f) / data.blurSourceTextureHeight, !isVertical ? 1.0f / data.blurSourceTextureWidth : 0f, isVertical ? 1.0f / data.blurSourceTextureHeight : 0));
|
|
data.propertyBlock.SetFloat(_SourceMip, 0);
|
|
data.material.SetKeyword(data.disableTexture2DArrayKeyword, !data.sourceIsArray);
|
|
context.cmd.SetViewport(new Rect(0, 0, data.dstMipWidth, data.dstMipHeight));
|
|
context.cmd.DrawProcedural(Matrix4x4.identity, data.material, 0, MeshTopology.Triangles, 3, 1, data.propertyBlock);
|
|
}
|
|
}
|
|
}
|