Search Unity

How to read a certain level of mipmaps from a RenderTexture? And Blit() performance...

Discussion in 'General Graphics' started by zyjisdog, Nov 5, 2019.

  1. zyjisdog

    zyjisdog

    Joined:
    Oct 16, 2019
    Posts:
    7
    I wrote a shader to collect some statistics and saved them to the alpha channel of the output texture.
    Now, I need to get an average value of them, so I tried to read the last level of the RenderTexture (which means a 1x1 Texture) in OnRenderImage(RenderTexture src, RenderTexture dest):

    Code (CSharp):
    1. RenderTexture tempBuffer = RenderTexture.GetTemporary(src.width, src.height, 0, src.format, RenderTextureReadWrite.Linear);
    2. tempBuffer.useMipMap = true;
    3. mipTex = new Texture2D(1, 1, src.graphicsFormat, TextureCreationFlags.None);
    4. Graphics.Blit(src, tempBuffer, _Material, 0);
    5. Graphics.CopyTexture(tempBuffer, 0, tempBuffer.mipmapCount - 1, mipTex, 0, 0);
    6. Color c = mipTex.GetPixel(0, 0);
    7. Graphics.Blit(tempBuffer, dest, _Material, 1);
    However, I got a negative value of c.a in Unity Editor, which is obviously wrong.


    Update:

    It seems to work on my Hololens 1, but the fps dropped heavily from ~50 to ~25, and the memory usage increased from 300+MB to 800+MB. It's confusing, I just read a fullscreen texture and return its colors in pass 1 like this:
    Code (CSharp):
    1. fixed4 frag (v2f i) : SV_Target
    2. {
    3.         UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
    4.         fixed4 renderTex = UNITY_SAMPLE_SCREENSPACE_TEXTURE(_MainTex, i.uv);
    5.         return renderTex;
    6. }
    How can it be so costly?

    Update 2:
    It did not work on Hololens. c.a is always 0.804. Besides, if I remove tempBuffer and blit from src to dest twice like this:
    Code (CSharp):
    1. Graphics.Blit(src, dest, _Material, 0);
    2. Graphics.Blit(src, dest, _Material, 1);
    the fps keeps ~50. I'm extremely confused about this problem...
     
    Last edited: Nov 6, 2019
  2. Olmi

    Olmi

    Joined:
    Nov 29, 2012
    Posts:
    1,553
    Hi @zyjisdog

    You need to use SAMPLE_TEXTURE2D_LOD if you want to sample a certain mipmap level:

    Code (HLSL):
    1. SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod)
    Should work ok if you got a HLSL shader. And on Unity 2019 you have to use SampleLevel if I remember correctly.
     
  3. zyjisdog

    zyjisdog

    Joined:
    Oct 16, 2019
    Posts:
    7
    Dear @Olmi

    Thanks anyway, but I need to read the mipmap pixels to CPU.
     
  4. Olmi

    Olmi

    Joined:
    Nov 29, 2012
    Posts:
    1,553
    Oh, oops. How did I miss that. Sorry :)
    Wouldn't GetPixels work then, I thought it supports reading at specified mip level?

    public Color[] GetPixels(int x, int y, int blockWidth, int blockHeight, int miplevel);
    https://docs.unity3d.com/ScriptReference/Texture2D.GetPixels.html

    But you do need to the texture back from GPU first.

    And looking at your edit, did you get it working?
     
  5. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
    Did he generated the mip map first from the rendertexture?
     
  6. zyjisdog

    zyjisdog

    Joined:
    Oct 16, 2019
    Posts:
    7
    No, finally it did not work. I found a similar issue at https://forum.unity.com/threads/graphics-copytexture-then-getpixels.482601/, but Texture.ReadPixels cannot access to mipmaps.
     
    Last edited: Nov 6, 2019
  7. zyjisdog

    zyjisdog

    Joined:
    Oct 16, 2019
    Posts:
    7
    According to the documents of Unity, the mipmap is generated automatically if I set the 'useMipMap' flag to True.
    I also set 'autoGenerateMips' to true to ensure that Unity creates the mipmaps. When debugging, the tempBuffer.mipmapCount is greater than 1. I believe the mipmaps are generated successfully.
     
    Last edited: Nov 6, 2019
  8. zyjisdog

    zyjisdog

    Joined:
    Oct 16, 2019
    Posts:
    7
  9. benthroop

    benthroop

    Joined:
    Jan 5, 2007
    Posts:
    263
    Anything change with this?
     
  10. alexis_morin

    alexis_morin

    Joined:
    Jan 13, 2017
    Posts:
    6
    I struggled with this for a good while before finding a workaround, decided to relay what I did here for posterity. In a roundabout way, Olmi was right!

    - Start with your large render texture, make sure that it has mipmaps enabled (duh).
    - Create a second render texture that is the resolution of your desired mip level; this is what you'll actually end up running ReadPixels() on!
    - Create a shader/material that does nothing but sample _MainTex at your desired mip level and pipe it back out.
    - Graphics.Blit your first texture into your second texture using your newly made material. This feels like a dirty hack but actually works! ymmv on the performance of this, but for my uses it ran great.
    - ReadPixels on your small texture and enjoy the newfound performance gains.