using UnityEngine.Rendering ;
using System.Collections.Generic ;
using UnityEngine.Rendering ;
namespace UnityEngine.Experimental.Rendering.HDPipeline
{
GPUCopy m_GPUCopy ;
ComputeShader m_ColorPyramidCS ;
RenderTextureDescriptor m_ColorRenderTextureDescriptor ;
int [ ] m_ColorPyramidMips = new int [ 0 ] ;
RTHandle m_ColorPyramidBuffer ;
List < RTHandle > m_ColorPyramidMips = new List < RTHandle > ( ) ;
RenderTextureDescriptor m_DepthRenderTextureDescriptor ;
int [ ] m_DepthPyramidMips = new int [ 0 ] ;
RTHandle m_DepthPyramidBuffer ;
List < RTHandle > m_DepthPyramidMips = new List < RTHandle > ( ) ;
public RenderTextureDescriptor colorRenderTextureDescriptor { get { return m_ColorRenderTextureDescriptor ; } }
public int colorUsedMipMapCount { get { return Mathf . Min ( colorBufferMipMapCount , m_ColorPyramidMips . Length ) ; } }
public int colorBufferMipMapCount
{
get
{
var minSize = Mathf . Min ( colorRenderTextureDescriptor . width , colorRenderTextureDescriptor . height ) ;
return Mathf . FloorToInt ( Mathf . Log ( minSize , 2f ) ) ;
}
}
public RenderTextureDescriptor depthRenderTextureDescriptor { get { return m_DepthRenderTextureDescriptor ; } }
public int depthUsedMipMapCount { get { return Mathf . Min ( depthBufferMipMapCount , m_DepthPyramidMips . Length ) ; } }
public int depthBufferMipMapCount
{
get
{
var minSize = Mathf . Min ( depthRenderTextureDescriptor . width , depthRenderTextureDescriptor . height ) ;
return Mathf . FloorToInt ( Mathf . Log ( minSize , 2f ) ) ;
}
}
public RTHandle colorPyramid { get { return m_ColorPyramidBuffer ; } }
public RTHandle depthPyramid { get { return m_DepthPyramidBuffer ; } }
ComputeShader colorPyramidCS , int [ ] colorMipIds ,
ComputeShader depthPyramidCS , GPUCopy gpuCopy , int [ ] depthMipIds )
ComputeShader colorPyramidCS ,
ComputeShader depthPyramidCS , GPUCopy gpuCopy )
m_ColorPyramidMips = colorMipIds ;
m_DepthPyramidMips = depthMipIds ;
public void RenderDepthPyramid (
HDCamera hdCamera ,
CommandBuffer cmd ,
ScriptableRenderContext renderContext ,
RenderTargetIdentifier depthTexture ,
RenderTargetIdentifier targetTexture )
public int GetPyramidLodCount ( HDCamera camera )
var depthPyramidDesc = m_DepthRenderTextureDescriptor ;
var minSize = Mathf . Min ( camera . actualWidth , camera . actualHeight ) ;
return Mathf . FloorToInt ( Mathf . Log ( minSize , 2f ) ) ;
}
var lodCount = depthBufferMipMapCount ;
if ( lodCount > m_DepthPyramidMips . Length )
Vector2Int CalculatePyramidMipSize ( Vector2Int baseMipSize , int mipIndex )
{
float scale = GetXRscale ( ) ;
return new Vector2Int ( ( int ) ( baseMipSize . x * scale ) > > mipIndex , baseMipSize . y > > mipIndex ) ;
}
void UpdatePyramidMips ( HDCamera camera , RenderTextureFormat format , List < RTHandle > mipList , int lodCount )
{
int currentLodCount = mipList . Count ;
if ( lodCount > currentLodCount )
Debug . LogWarningFormat ( "Cannot compute all mipmaps of the depth pyramid, max texture size supported: {0}" , ( 2 < < m_DepthPyramidMips . Length ) . ToString ( ) ) ;
lodCount = m_DepthPyramidMips . Length ;
for ( int i = currentLodCount ; i < lodCount ; + + i )
{
int mipIndexCopy = i + 1 ; // Don't remove this copy! It's important for the value to be correctly captured by the lambda.
RTHandle newMip = RTHandle . Alloc ( size = > CalculatePyramidMipSize ( size , mipIndexCopy ) , colorFormat : format , sRGB : false , enableRandomWrite : true , useMipMap : false , filterMode : FilterMode . Bilinear ) ;
mipList . Add ( newMip ) ;
}
}
cmd . ReleaseTemporaryRT ( m_DepthPyramidMips [ 0 ] ) ;
public void RenderDepthPyramid (
HDCamera hdCamera ,
CommandBuffer cmd ,
ScriptableRenderContext renderContext ,
RTHandle depthTexture )
{
int lodCount = GetPyramidLodCount ( hdCamera ) ;
UpdatePyramidMips ( hdCamera , m_DepthPyramidBuffer . rt . format , m_DepthPyramidMips , lodCount ) ;
depthPyramidDesc . sRGB = false ;
depthPyramidDesc . enableRandomWrite = true ;
depthPyramidDesc . useMipMap = false ;
cmd . SetGlobalVector ( HDShaderIDs . _DepthPyramidMipSize , new Vector4 ( hdCamera . actualWidth , hdCamera . actualHeight , lodCount , 0.0f ) ) ;
cmd . GetTemporaryRT ( m_DepthPyramidMips [ 0 ] , depthPyramidDesc , FilterMode . Bilinear ) ;
m_GPUCopy . SampleCopyChannel_xyzw2x ( cmd , depthTexture , m_DepthPyramidMips [ 0 ] , new Vector2 ( depthPyramidDesc . width , depthPyramidDesc . height ) ) ;
cmd . CopyTexture ( m_DepthPyramidMips [ 0 ] , 0 , 0 , targetTexture , 0 , 0 ) ;
m_GPUCopy . SampleCopyChannel_xyzw2x ( cmd , depthTexture , m_DepthPyramidBuffer , new Vector2 ( hdCamera . actualWidth , hdCamera . actualHeight ) ) ;
RTHandle src = m_DepthPyramidBuffer ;
var srcMipWidth = depthPyramidDesc . width ;
var srcMipHeight = depthPyramidDesc . height ;
depthPyramidDesc . width = srcMipWidth > > 1 ;
depthPyramidDesc . height = srcMipHeight > > 1 ;
RTHandle dest = m_DepthPyramidMips [ i ] ;
var srcMipWidth = hdCamera . actualWidth > > i ;
var srcMipHeight = hdCamera . actualHeight > > i ;
var dstMipWidth = srcMipWidth > > 1 ;
var dstMipHeight = srcMipHeight > > 1 ;
if ( depthPyramidDesc . width < 4 * k_DepthBlockSize
| | depthPyramidDesc . height < 4 * k_DepthBlockSize )
if ( dstMipWidth < 4 * k_DepthBlockSize
| | dstMipHeight < 4 * k_DepthBlockSize )
cmd . ReleaseTemporaryRT ( m_DepthPyramidMips [ i + 1 ] ) ;
cmd . GetTemporaryRT ( m_DepthPyramidMips [ i + 1 ] , depthPyramidDesc , FilterMode . Bilinear ) ;
cmd . SetComputeTextureParam ( m_DepthPyramidCS , kernel , _Source , m_DepthPyramidMips [ i ] ) ;
cmd . SetComputeTextureParam ( m_DepthPyramidCS , kernel , _Result , m_DepthPyramidMips [ i + 1 ] ) ;
cmd . SetComputeVectorParam ( m_DepthPyramidCS , _SrcSize , new Vector4 ( srcMipWidth , srcMipHeight , 1f / srcMipWidth , 1f / srcMipHeight ) ) ;
cmd . SetComputeTextureParam ( m_DepthPyramidCS , kernel , _Source , src ) ;
cmd . SetComputeTextureParam ( m_DepthPyramidCS , kernel , _Result , dest ) ;
cmd . SetComputeVectorParam ( m_DepthPyramidCS , _SrcSize , new Vector4 ( srcMipWidth , srcMipHeight , hdCamera . scaleBias . x / srcMipWidth , hdCamera . scaleBias . y / srcMipHeight ) ) ;
Mathf . CeilToInt ( depthPyramidDesc . width / kernelBlockSize ) ,
Mathf . CeilToInt ( depthPyramidDesc . height / kernelBlockSize ) ,
Mathf . CeilToInt ( dstMipWidth / kernelBlockSize ) ,
Mathf . CeilToInt ( dstMipHeight / kernelBlockSize ) ,
cmd . CopyTexture ( m_DepthPyramidMips [ i + 1 ] , 0 , 0 , targetTexture , 0 , i + 1 ) ;
// If we could bind texture mips as UAV we could avoid this copy...(which moreover copies more than the needed viewport if not fullscreen)
cmd . CopyTexture ( m_DepthPyramidMips [ i ] , 0 , 0 , m_DepthPyramidBuffer , 0 , i + 1 ) ;
src = dest ;
for ( int i = 0 ; i < lodCount + 1 ; i + + )
cmd . ReleaseTemporaryRT ( m_DepthPyramidMips [ i ] ) ;
cmd . SetGlobalTexture ( HDShaderIDs . _PyramidDepthTexture , m_DepthPyramidBuffer ) ;
}
public void RenderColorPyramid (
RenderTargetIdentifier colorTexture ,
RenderTargetIdentifier targetTexture )
RTHandle colorTexture )
var colorPyramidDesc = colorRenderTextureDescriptor ;
int lodCount = GetPyramidLodCount ( hdCamera ) ;
UpdatePyramidMips ( hdCamera , m_ColorPyramidBuffer . rt . format , m_ColorPyramidMips , lodCount ) ;
var lodCount = colorBufferMipMapCount ;
if ( lodCount > m_ColorPyramidMips . Length )
{
Debug . LogWarningFormat ( "Cannot compute all mipmaps of the color pyramid, max texture size supported: {0}" , ( 2 < < m_ColorPyramidMips . Length ) . ToString ( ) ) ;
lodCount = m_ColorPyramidMips . Length ;
}
cmd . SetGlobalVector ( HDShaderIDs . _GaussianPyramidColorMipSize , new Vector4 ( hdCamera . actualWidth , hdCamera . actualHeight , lodCount , 0.0f ) ) ;
cmd . CopyTexture ( colorTexture , 0 , 0 , targetTexture , 0 , 0 ) ;
var last = colorTexture ;
HDUtils . BlitCameraTexture ( cmd , hdCamera , colorTexture , m_ColorPyramidBuffer ) ; // true : bilinear
colorPyramidDesc . sRGB = false ;
colorPyramidDesc . enableRandomWrite = true ;
colorPyramidDesc . useMipMap = false ;
RTHandle src = m_ColorPyramidBuffer ;
colorPyramidDesc . width = colorPyramidDesc . width > > 1 ;
colorPyramidDesc . height = colorPyramidDesc . height > > 1 ;
RTHandle dest = m_ColorPyramidMips [ i ] ;
var srcMipWidth = hdCamera . actualWidth > > i ;
var srcMipHeight = hdCamera . actualHeight > > i ;
var dstMipWidth = srcMipWidth > > 1 ;
var dstMipHeight = srcMipHeight > > 1 ;
cmd . ReleaseTemporaryRT ( m_ColorPyramidMips [ i + 1 ] ) ;
cmd . GetTemporaryRT ( m_ColorPyramidMips [ i + 1 ] , colorPyramidDesc , FilterMode . Bilinear ) ;
cmd . SetComputeTextureParam ( m_ColorPyramidCS , m_ColorPyramidKernel , _Source , last ) ;
cmd . SetComputeTextureParam ( m_ColorPyramidCS , m_ColorPyramidKernel , _Result , m_ColorPyramidMips [ i + 1 ] ) ;
cmd . SetComputeVectorParam ( m_ColorPyramidCS , _Size , new Vector4 ( colorPyramidDesc . width , colorPyramidDesc . height , 1f / colorPyramidDesc . width , 1f / colorPyramidDesc . height ) ) ;
cmd . SetComputeTextureParam ( m_ColorPyramidCS , m_ColorPyramidKernel , _Source , src ) ;
cmd . SetComputeTextureParam ( m_ColorPyramidCS , m_ColorPyramidKernel , _Result , dest ) ;
// _Size is used as a scale inside the whole render target so here we need to keep the full size (and not the scaled size depending on the current camera)
cmd . SetComputeVectorParam ( m_ColorPyramidCS , _Size , new Vector4 ( dest . rt . width , dest . rt . height , 1f / dest . rt . width , 1f / dest . rt . height ) ) ;
Mathf . CeilToInt ( colorPyramidDesc . width / 8f ) ,
Mathf . CeilToInt ( colorPyramidDesc . height / 8f ) ,
Mathf . CeilToInt ( dstMipWidth / 8f ) ,
Mathf . CeilToInt ( dstMipHeight / 8f ) ,
cmd . CopyTexture ( m_ColorPyramidMips [ i + 1 ] , 0 , 0 , targetTexture , 0 , i + 1 ) ;
// If we could bind texture mips as UAV we could avoid this copy...(which moreover copies more than the needed viewport if not fullscreen)
cmd . CopyTexture ( m_ColorPyramidMips [ i ] , 0 , 0 , m_ColorPyramidBuffer , 0 , i + 1 ) ;
last = m_ColorPyramidMips [ i + 1 ] ;
src = dest ;
for ( int i = 0 ; i < lodCount ; i + + )
cmd . ReleaseTemporaryRT ( m_ColorPyramidMips [ i + 1 ] ) ;
cmd . SetGlobalTexture ( HDShaderIDs . _GaussianPyramidColorTexture , m_ColorPyramidBuffer ) ;
public void Initialize ( HDCamera hdCamera , bool enableStereo )
float GetXRscale ( )
var colorDesc = CalculateRenderTextureDescriptor ( hdCamera , enableStereo ) ;
colorDesc . colorFormat = RenderTextureFormat . ARGBHalf ;
m_ColorRenderTextureDescriptor = colorDesc ;
var depthDesc = CalculateRenderTextureDescriptor ( hdCamera , enableStereo ) ;
depthDesc . colorFormat = RenderTextureFormat . RFloat ;
m_DepthRenderTextureDescriptor = depthDesc ;
float scale = 1.0f ;
//if (m_Asset.renderPipelineSettings.supportsStereo && (desc.dimension != TextureDimension.Tex2DArray))
// scale = 2.0f; // double-wide
return scale ;
public static RenderTextureDescriptor CalculateRenderTextureDescriptor ( HDCamera hdCamera , bool enableStereo )
public void CreateBuffers ( )
var desc = hdCamera . renderTextureDesc ;
desc . depthBufferBits = 0 ;
desc . useMipMap = true ;
desc . autoGenerateMips = false ;
Vector2 sizeScale = Vector2 . one ;
sizeScale . x * = GetXRscale ( ) ;
desc . msaaSamples = 1 ; // These are approximation textures, they don't need MSAA
m_ColorPyramidBuffer = RTHandle . Alloc ( sizeScale , filterMode : FilterMode . Trilinear , colorFormat : RenderTextureFormat . ARGBHalf , sRGB : true , useMipMap : true , autoGenerateMips : false ) ;
m_DepthPyramidBuffer = RTHandle . Alloc ( sizeScale , filterMode : FilterMode . Trilinear , colorFormat : RenderTextureFormat . RFloat , sRGB : false , useMipMap : true , autoGenerateMips : false , enableRandomWrite : true ) ; // Need randomReadWrite because we downsample the first mip with a compute shader.
}
// for stereo double-wide, each half of the texture will represent a single eye's pyramid
//var widthModifier = 1;
//if (stereoEnabled && (desc.dimension != TextureDimension.Tex2DArray))
// widthModifier = 2; // double-wide
public void DestroyBuffers ( )
{
RTHandle . Release ( m_ColorPyramidBuffer ) ;
RTHandle . Release ( m_DepthPyramidBuffer ) ;
//desc.width = pyramidSize * widthModifier;
desc . width = ( int ) hdCamera . screenSize . x ;
desc . height = ( int ) hdCamera . screenSize . y ;
foreach ( var rth in m_ColorPyramidMips )
{
RTHandle . Release ( rth ) ;
}
return desc ;
foreach ( var rth in m_DepthPyramidMips )
{
RTHandle . Release ( rth ) ;
}
}
}
}