Search Unity

CommandBuffer rendering scene flipped upside down in Forward rendering

Discussion in 'General Graphics' started by jimmikaelkael, Jul 14, 2016.

  1. jimmikaelkael

    jimmikaelkael

    Joined:
    Apr 27, 2015
    Posts:
    796
    Hi,

    I'm writing some image effects using Command Buffers and there's quite some time I have a problem either scene flipped upside down in Forward rendering either an empty _MainTex.
    The result can be different if MSAA is enabled AND there is one Post Effect using "OnRenderImage".

    I really don't understand what's happening, and I'm not sure it's me doing something wrong or a bug in Unity...

    The shader is quite simple, it only have a single pass to copy _Maintex to output:

    Code (csharp):
    1.  
    2. Shader "Hidden/TestImageEffect"
    3. {
    4.    Properties {
    5.      _MainTex ("", 2D) = "" {}
    6.      _ScreenTex("", 2D) = "" {}
    7.      _OutTex ("", 2D) = "" {}
    8.    }
    9.  
    10.    CGINCLUDE
    11.      #pragma target 3.0
    12.  
    13.      #include "UnityCG.cginc"
    14.  
    15.      sampler2D _MainTex;
    16.      float4 _MainTex_TexelSize;
    17.  
    18.      struct v2f {
    19.        float2 uv : TEXCOORD0;
    20.        float2 uv2 : TEXCOORD1;
    21.      };
    22.  
    23.      v2f vert(appdata_img v, out float4 outpos : SV_POSITION) {
    24.        v2f o;
    25.        o.uv = v.texcoord.xy;
    26.        o.uv2 = v.texcoord.xy;
    27.        #if UNITY_UV_STARTS_AT_TOP
    28.        if (_MainTex_TexelSize.y < 0)
    29.          o.uv2.y = 1 - o.uv2.y;
    30.        #endif
    31.        outpos = mul(UNITY_MATRIX_MVP, v.vertex);
    32.        return o;
    33.      }
    34.  
    35.    ENDCG
    36.  
    37.    SubShader {
    38.      ZTest Always Cull Off ZWrite Off
    39.  
    40.      Pass { // 0
    41.        CGPROGRAM
    42.  
    43.          #pragma vertex vert
    44.          #pragma fragment frag
    45.          
    46.          half4 frag (v2f i) : SV_Target {
    47.            return tex2D(_MainTex, i.uv2);
    48.          }
    49.          
    50.        ENDCG
    51.      }
    52.  
    53.    }
    54.  
    55.    FallBack off
    56. }
    57.  

    The script provides 3 test cases:

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnityEngine.Rendering;
    4. using System.Linq;
    5.  
    6. #if UNITY_5_4_OR_NEWER
    7. [ImageEffectAllowedInSceneView]
    8. #endif
    9. [ExecuteInEditMode, AddComponentMenu("Image Effects/TestImageEffect")]
    10. [RequireComponent(typeof(Camera))]
    11.  
    12. public class TestImageEffect : MonoBehaviour
    13. {
    14.     public enum TestCase
    15.     {
    16.         Case1,
    17.         Case2,
    18.         Case3
    19.     }
    20.  
    21.     public bool MSAA2x;
    22.     public TestCase testCase;
    23.     public Shader shader = null;
    24.  
    25.     private Material m_Material;
    26.     private Camera m_Camera;
    27.     private CommandBuffer m_CommandBuffer;
    28.     private const string CB_NAME = "CB Test";
    29.     private int m_Width;
    30.     private int m_Height;
    31.     private RenderingPath m_RenderingPath;
    32.     private TestCase m_TestCase;
    33.  
    34.     void OnEnable()
    35.     {
    36.         if (!SystemInfo.supportsImageEffects || !SystemInfo.supportsRenderTextures)
    37.         {
    38.             Debug.LogWarning("Test shader is not supported on this platform.");
    39.             enabled = false;
    40.             return;
    41.         }
    42.  
    43.         if (shader != null && !shader.isSupported)
    44.         {
    45.             Debug.LogWarning("Test shader is not supported on this platform.");
    46.             enabled = false;
    47.             return;
    48.         }
    49.  
    50.         if (shader == null)
    51.         {
    52.             return;
    53.         }
    54.  
    55.         if (m_Material == null)
    56.         {
    57.             m_Material = new Material(shader);
    58.             m_Material.hideFlags = HideFlags.HideAndDontSave;
    59.         }
    60.  
    61.         if (m_Camera == null)
    62.         {
    63.             m_Camera = GetComponent<Camera>();
    64.         }
    65.  
    66.         if (m_CommandBuffer == null)
    67.         {
    68.             m_CommandBuffer = new CommandBuffer();
    69.             m_CommandBuffer.name = CB_NAME;
    70.         }
    71.     }
    72.  
    73.     void OnDisable()
    74.     {
    75.         ClearCommandBuffer();
    76.  
    77.         if (m_Material != null)
    78.             DestroyImmediate(m_Material);
    79.     }
    80.  
    81.     /*
    82.     void OnRenderImage(RenderTexture source, RenderTexture destination)
    83.     {
    84.         Graphics.Blit(source, destination);
    85.     }
    86.     */
    87.  
    88.     void OnPreRender()
    89.     {
    90.         if (shader == null || m_Camera == null)
    91.         {
    92.             return;
    93.         }
    94.  
    95.         QualitySettings.antiAliasing = MSAA2x ? 2 : 0;
    96.  
    97.         bool shouldPrepareCommandBuffer = false;
    98.         if (m_Width != m_Camera.pixelWidth || m_Height != m_Camera.pixelHeight || m_RenderingPath != m_Camera.renderingPath || m_TestCase != testCase)
    99.         {
    100.             m_Width = m_Camera.pixelWidth;
    101.             m_Height = m_Camera.pixelHeight;
    102.             m_RenderingPath = m_Camera.renderingPath;
    103.             m_TestCase = testCase;
    104.  
    105.             shouldPrepareCommandBuffer = true;
    106.         }
    107.  
    108.         var cameraEvent = CameraEvent.AfterImageEffects;
    109.         if (shouldPrepareCommandBuffer || m_Camera.GetCommandBuffers(cameraEvent).Where(x => x.name == CB_NAME).Count() == 0)
    110.         {
    111.             ClearCommandBuffer();
    112.  
    113.             switch (m_TestCase)
    114.             {
    115.                 case TestCase.Case1:
    116.                     PrepareCommandBuffer_ForwardFlippedUpsideDown(cameraEvent);
    117.                     break;
    118.  
    119.                 case TestCase.Case2:
    120.                     PrepareCommandBuffer_ForwardMainTexNotSet(cameraEvent);
    121.                     break;
    122.  
    123.                 case TestCase.Case3:
    124.                     PrepareCommandBuffer_ForwardMainTexForced(cameraEvent);
    125.                     break;
    126.             }
    127.  
    128.             m_Camera.AddCommandBuffer(cameraEvent, m_CommandBuffer);
    129.         }
    130.     }
    131.  
    132.     private void ClearCommandBuffer()
    133.     {
    134.         if (m_CommandBuffer != null)
    135.         {
    136.             if (m_Camera != null)
    137.                 m_Camera.RemoveCommandBuffer(CameraEvent.AfterImageEffects, m_CommandBuffer);
    138.             m_CommandBuffer.Clear();
    139.         }
    140.     }
    141.  
    142.     private void PrepareCommandBuffer_ForwardFlippedUpsideDown(CameraEvent cameraEvent)
    143.     {
    144.         var outTexId = new RenderTargetIdentifier(Shader.PropertyToID("_OutTex"));
    145.  
    146.         m_CommandBuffer.GetTemporaryRT(Shader.PropertyToID("_OutTex"), m_Width, m_Height);
    147.         m_CommandBuffer.Blit(BuiltinRenderTextureType.CameraTarget, outTexId);
    148.         m_CommandBuffer.Blit(outTexId, BuiltinRenderTextureType.CameraTarget);
    149.         m_CommandBuffer.ReleaseTemporaryRT(Shader.PropertyToID("_OutTex"));
    150.     }
    151.  
    152.     private void PrepareCommandBuffer_ForwardMainTexNotSet(CameraEvent cameraEvent)
    153.     {
    154.         var outTexId = new RenderTargetIdentifier(Shader.PropertyToID("_OutTex"));
    155.  
    156.         m_CommandBuffer.GetTemporaryRT(Shader.PropertyToID("_OutTex"), m_Width, m_Height);
    157.         m_CommandBuffer.Blit(BuiltinRenderTextureType.CameraTarget, outTexId, m_Material, 0);
    158.         m_CommandBuffer.Blit(outTexId, BuiltinRenderTextureType.CameraTarget);
    159.         m_CommandBuffer.ReleaseTemporaryRT(Shader.PropertyToID("_OutTex"));
    160.     }
    161.  
    162.     private void PrepareCommandBuffer_ForwardMainTexForced(CameraEvent cameraEvent)
    163.     {
    164.         var outTexId = new RenderTargetIdentifier(Shader.PropertyToID("_OutTex"));
    165.         var screenTexId = new RenderTargetIdentifier(Shader.PropertyToID("_ScreenTex"));
    166.  
    167.         if (!isDeferredShadingOrLighting())
    168.         {
    169.             m_CommandBuffer.GetTemporaryRT(Shader.PropertyToID("_ScreenTex"), m_Width, m_Height);
    170.             m_CommandBuffer.Blit(BuiltinRenderTextureType.CameraTarget, screenTexId);
    171.         }
    172.         m_CommandBuffer.GetTemporaryRT(Shader.PropertyToID("_OutTex"), m_Width, m_Height);
    173.         if (!isDeferredShadingOrLighting())
    174.             m_CommandBuffer.Blit(screenTexId, outTexId, m_Material, 0);
    175.         else
    176.             m_CommandBuffer.Blit(BuiltinRenderTextureType.CameraTarget, outTexId, m_Material, 0);
    177.         m_CommandBuffer.Blit(outTexId, BuiltinRenderTextureType.CameraTarget);
    178.         if (!isDeferredShadingOrLighting())
    179.             m_CommandBuffer.ReleaseTemporaryRT(Shader.PropertyToID("_ScreenTex"));
    180.         m_CommandBuffer.ReleaseTemporaryRT(Shader.PropertyToID("_OutTex"));
    181.     }
    182.  
    183.     private bool isDeferredShadingOrLighting()
    184.     {
    185. #if UNITY_EDITOR
    186.         return (!m_Camera.orthographic && (m_Camera.renderingPath == RenderingPath.DeferredShading || m_Camera.renderingPath == RenderingPath.DeferredLighting ||
    187.   (m_Camera.renderingPath == RenderingPath.UsePlayerSettings && (UnityEditor.PlayerSettings.renderingPath == RenderingPath.DeferredShading || UnityEditor.PlayerSettings.renderingPath == RenderingPath.DeferredLighting))));
    188. #else
    189.         return (!hbaoCamera.orthographic && (hbaoCamera.renderingPath == RenderingPath.DeferredShading || hbaoCamera.renderingPath == RenderingPath.DeferredLighting));
    190. #endif
    191.     }
    192.   }
    193.  

    The scene is stritcly minimal:



    Problem with case 1, where I'm only doing a blit from camera target to _OutTex, then I blit from _OutTex to camera target: the screen is rendered upside down despite the fact I don't use any shader specific pass to blit...


    Problem with case 2, almost like case 1 but I'm blitting camera target to _OutTex with my shader pass. _Maintex just remains gray, like if it was not set:


    Problem with case 3, I seem to be able to force camera target to render to an intermediate RT which I called _ScreenTex, however the screen remains flipped down (despite the specific compiler flag in my vertex this flip up down, using uv or uv2 doesn't even makes a difference...):



    Now, as you can see, I had a commented OnRenderImage method in the script, which just blit source to destination. I'm uncommenting it (MSAA is disabled) and for all 3 cases I get the good result, as in deferred:



    Finally, I 'm activating MSAA (OnRenderImage still uncommented), and this time I get wrong results for the 3 cases, exactly like the first I've shown for each case.

    Tested with Unity 5.3.5f1, but it dos seem to happen from Unity 5.0.
    Case repro package is attached.

    I'll be glad to get help with this this is a real show stopper for my HBAO package concerning the color bleeding :)
     

    Attached Files:

  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Try removing the UNITY_UV_STARTS_AT_TOP block and see what happens. There are a few different times and ways that Unity flips the screen (because DX11 likes to flip the screen and Unity tries to correct that, not because Unity is doing something dumb) and I've not yet figured out when you should and shouldn't do it.
     
  3. jimmikaelkael

    jimmikaelkael

    Joined:
    Apr 27, 2015
    Posts:
    796
    Thank you very much for the help, however this doesn't fix the problem: with further test I realized that depth texture is always fine (I will need both depth and scene color in the same shader pass).

    So there's a real inconsistency and adding a dummy post effect (just blit source to dest) to the camera it shows the problem very well.

    I think I have a good repro case to submit to Unity.
     
  4. jimmikaelkael

    jimmikaelkael

    Joined:
    Apr 27, 2015
    Posts:
    796

    Attached Files:

  5. jimmikaelkael

    jimmikaelkael

    Joined:
    Apr 27, 2015
    Posts:
    796
  6. arkano22

    arkano22

    Joined:
    Sep 20, 2012
    Posts:
    1,929
    I´m having issues with this also. One of my assets in the store is affected by it, only seems to happen on Windows. It is NOT caused by forgetting to use #if UNITY_UV_STARTS_AT_TOP. I´ve voted for this as it pretty much renders CommandBuffers unusable.
     
  7. jimmikaelkael

    jimmikaelkael

    Joined:
    Apr 27, 2015
    Posts:
    796
    I just hope it will be solved soon :)
     
  8. arkano22

    arkano22

    Joined:
    Sep 20, 2012
    Posts:
    1,929
    Hi all!

    Mild necro here, for a good reason. I got an answer from Unity that sheds a lot of light on this issue (which isn't a bug, though it sure does look a lot like one):

    So it seems that when OnRenderImage is being used, or when HDR rendering is on, or when anything that needs the camera to render to an intermediate buffer is enabled, the camera automatically renders to an intermediate buffer instead of the backbuffer (screen) directly. This is nice.

    However when Unity cannot predict that the camera is going to need an intermediate buffer, and your command buffer does a Blit(), things go wrong: since Unity cannot know in advance what you're doing in the CB, it grabs the image from the back buffer again and the Blit() (which does not know where the input is coming from) flips it.

    The issue can be fixed by enabling the camera's "forceIntoRenderTexture". This forces Unity to quit rendering to the back buffer directly and always render the camera to an intermediate buffer (regardless of HDR state, whether or not there are image effects present, etc.)
     
    Last edited: Jun 20, 2017
    ElliotB, _geo__, goo876 and 1 other person like this.
  9. jimmikaelkael

    jimmikaelkael

    Joined:
    Apr 27, 2015
    Posts:
    796
    Wow, thank you sooooo much for sharing this @arkano22. This will ultimately be very useful to me!
     
    arkano22 likes this.
  10. chingwa

    chingwa

    Joined:
    Dec 4, 2009
    Posts:
    3,790
    The recommended fix is not working for me. If I'm rendering my camera into a target texture the image is inevitably rendering upside down from the wrong projection. It doesn't matter if HDR is enabled, or if I use forceIntoRenderTexture. The only way I can get a proper camera perspective is to remove the targetTexture so the camera just render normally on a higher level than the original scene camera. So. Frustrating.
     
  11. arkano22

    arkano22

    Joined:
    Sep 20, 2012
    Posts:
    1,929
    Are you using UNITY_UV_STARTS_AT_TOP in your shader to check if you must flip the texture coords there? Note that forceIntoRenderTexture must be used in conjunction with correct shaders for this to work.
     
  12. chingwa

    chingwa

    Joined:
    Dec 4, 2009
    Posts:
    3,790
    Thanks @arkano22 I am not using the starts at top directive, but I manually tried flipping the UVs in y-space either direction and neither had any effect.
     
  13. arkano22

    arkano22

    Joined:
    Sep 20, 2012
    Posts:
    1,929
    How can it be possible that flipping the uv coords in your shader has no effect? Make sure that what you're getting on the screen is the actual output of your shader... you can use the frame debugger to tell what's going on, it's pretty handy in these cases.
     
  14. FranFndz

    FranFndz

    Joined:
    Sep 20, 2018
    Posts:
    178
    just start getting this error,

    only on Metal, Ios Build.
     
  15. iMagesBlues

    iMagesBlues

    Joined:
    Mar 18, 2013
    Posts:
    76
    Hey guys. So this thread has helped me with my inverted render textures as well.
    However I encountered another problem. When I capture a screenshot, the textures invert again.

    Any insights?
     
  16. CyRaid

    CyRaid

    Joined:
    Mar 31, 2015
    Posts:
    134
    It shows flipped for me too using URP. Even in the camera preview when rendering to render texture, and when showing up in the inspector for the render texture.

    Edit: Nvm, it was my mistake! Problem solved. :)
     
    Last edited: May 1, 2020