Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question CopyTexture in CustomPass with only Stencil element

Discussion in 'High Definition Render Pipeline' started by ignarmezh, Feb 10, 2023.

  1. ignarmezh

    ignarmezh

    Joined:
    Mar 23, 2017
    Posts:
    56
    Hi!
    I want to copy Stencil buffer from native Stancil buffer into my texture.
    I created a texture like this:
    Code (CSharp):
    1. RTHandle MyStencilBuffer = RTHandles.Alloc(Vector2.one, TextureXR.slices, DepthBits.Depth32, dimension: TextureXR.dimension,
    2.                 colorFormat: GraphicsFormat.R32_SFloat, name: "_MyStencilBuffer ");
    And trying to copy only Stencil buffer with this method:
    Code (CSharp):
    1. ctx.cmd.CopyTexture(ctx.cameraDepthBuffer,(int)RenderTextureSubElement.Stencil,
    2.     MyStencilBuffer,(int)RenderTextureSubElement.Stencil);
    3. ctx.cmd.SetGlobalTexture("_MyStencilBuffer",MyStencilBuffer, RenderTextureSubElement.Stencil);
    And I got an error:
    Code (CSharp):
    1. Graphics.CopyTexture called with invalid source element index (got 2, have 1 elements/layers)
    2. UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)
    But if I remove from CopyTexture the SubElements, everything will be work, but I think because when I set it as global texture with RenderTextureSubElement.Stencil.
    I suggest that if I copy texture without SubElements, it will call unnecessary copying of color (ctx.cameraDepthBuffer does not contain it) and depth buffer.
    Am I wrong with it, or it is a bug with CopyTexture function when we try to paste these custom RenderTextureSubElement?
     
  2. antoinel_unity

    antoinel_unity

    Unity Technologies

    Joined:
    Jan 7, 2019
    Posts:
    257
    Hello,

    CopyTexture can't copy the components of a RenderTexture like you're trying to do. Casting `RenderTextureSubElement` as int will call this override of the CopyTexture function:
    Code (CSharp):
    1. CopyTexture(RenderTargetIdentifier src, int srcElement, RenderTargetIdentifier dst, int dstElement);
    Where srcElement and dstElement is the slice index of the Texture2DArray which is invalid in your case as the texture array we use in HDRP non VR have only one slice.

    To copy the stencil you need to use a shader with an RW texture and the stencil texture bound (like the SetGlobal you're doing)
     
    AurochChris likes this.
  3. AurochChris

    AurochChris

    Joined:
    Jan 7, 2014
    Posts:
    24
    Hi @antoinel_unity, apologies for tagging you (and slightly hijackling ignarmezh's thread, but hopefully this adds useful context) but I am working on something similar to ignarmezh, but instead of copying the Stencil texture from the DepthBuffer, I'd like to do it from a custom depthStencil RenderTexture.

    I wasn't sure what you meant by this:

    If you have a moment, would you be able to provide some details?

    In my current project I set up the RTHandles for a custom depth pass as such:

    Code (CSharp):
    1.         public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
    2.         {
    3.             var desc = renderingData.cameraData.cameraTargetDescriptor;
    4.          
    5.             desc.colorFormat = RenderTextureFormat.RFloat;
    6.             desc.depthStencilFormat = GraphicsFormat.D32_SFloat;
    7.             desc.depthBufferBits = 32;
    8.             desc.stencilFormat = GraphicsFormat.R8_UInt;
    9.             desc.msaaSamples = 1;
    10.          
    11.             RenderingUtils.ReAllocateIfNeeded(ref m_CustomDepth, desc, FilterMode.Point, TextureWrapMode.Clamp, name: "_CustomDepth");
    12.          
    13.             ConfigureTarget(m_CustomDepth);
    14.             ConfigureClear(ClearFlag.All, Color.black);
    15.         }
    16.        
    and then in Execute() I try to do this (I need both the CustomDepth and CustomStencil as shader resources):

    Code (CSharp):
    1.         public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    2.         {
    3.             DrawingSettings drawingSettings = CreateDrawingSettings(
    4.                 _shaderTags,
    5.                 ref renderingData,
    6.                 m_settings.SortingCriteria
    7.             );
    8.             drawingSettings.overrideMaterial = m_settings.m_CustomDepthMaterial;
    9.             drawingSettings.overrideMaterialPassIndex = 0;
    10.  
    11.             CommandBuffer cmd = CommandBufferPool.Get();
    12.             using (new ProfilingScope(cmd, profilingSampler))
    13.             {
    14.                 context.ExecuteCommandBuffer(cmd);
    15.                 cmd.Clear();
    16.  
    17.                 // Write renderers on RenderLayer 1 to bit 128, and renderers on RenderLayer 2 to bit 64
    18.              
    19.                 RenderStateBlock renderState_1 = (// configure stencil state for 1st stencil bit layer)
    20.                 context.DrawRenderers(renderingData.cullResults, ref drawingSettings, ref m_filterSettingsLayer1, ref renderState_1);
    21.              
    22.                 RenderStateBlock renderState_2 = (// configure stencil state for 2nd stencil bit layer)
    23.                     context.DrawRenderers(renderingData.cullResults, ref drawingSettings, ref m_filterSettingsLayer2, ref renderState_2);
    24.  
    25.                 cmd.SetGlobalTexture("_CustomDepth", m_CustomDepth.nameID);
    26.                 cmd.SetGlobalTexture("_CustomStencil", m_CustomDepth.nameID, RenderTextureSubElement.Stencil);
    27.             }
    28.          
    29.             context.ExecuteCommandBuffer(cmd);
    30.             CommandBufferPool.Release(cmd);
    31.         }
    The shader then simply tries to access the "_CustomStencil" resource in the Fragment Shader, but I only get a default Unity White texture at this stage. Copying depth works just fine.

    Any help would be greatly appreciated!
     
  4. antoinel_unity

    antoinel_unity

    Unity Technologies

    Joined:
    Jan 7, 2019
    Posts:
    257
    Hello,

    This looks like URP render pass, but technically it should work similarly to HDRP custom passes in terms of graphics API.

    You should be able to bind both `_CustomDepth` and `_CustomStencil` simultaneously because depth and stencil are different elements, just make sure that you're using the correct dimension and format for the stencil texture on the shader side.

    To retrieve the stencil value in the shader, you can use the GetStencilValue function in Common.hlsl: https://github.com/Unity-Technologi...ipelines.core/ShaderLibrary/Common.hlsl#L1534

    As you can see the function takes an uint2 in parameter, so you need to declare the texture accordingly like so:
    Code (CSharp):
    1.  
    2. TEXTURE2D_X_UINT2(_CustomStencil); // XR compatible syntax
    3. Texture2D<uint2> _CustomStencil; // hlsl syntax
    4.  
     
    AurochChris likes this.
  5. AurochChris

    AurochChris

    Joined:
    Jan 7, 2014
    Posts:
    24
    Ah yes, sorry, I realized I ended up on HDRP through searching for RenderTextureSubElement! Wasn't trying to confuse people, but good to know there's crossover.

    Thanks so much for your help - will try this out right now!