Search Unity

Bug Texture2D ReadPixels artifacts in Vulkan + URP (Quest 2)

Discussion in 'Universal Render Pipeline' started by weiner_monkey, Aug 1, 2022.

  1. weiner_monkey

    weiner_monkey

    Joined:
    Aug 1, 2022
    Posts:
    49
    Hey guys, I'm pulling my hair on this problem. Had some legacy code running fine on Quest 2 + OpenGL + BRP, but getting strange artifacts when I try to ReadPixel and save an image.
    Unity 2021.3 LTS, URP 12.1.7

    Basically, theres a secondary camera whos output texture is set to a rendertexture (settings below). And I have a RawTexture in the scene to show me what the camera is rendering.


    This is attached to the cameras output texture.

    Before I explain further, here are what the artifacts look like


    I can't share the actual screenshot due to workplace privacy stuff. But this is what the problem looks like. The "section A" labelled above changes in size each time I capture a frame, but it is always a black rectangle. Section B is the rest of the image shown normally.

    Some things I've tried but didn't work:
    - Retrieve swapchain late
    - Changed swapchain buffers to 2
    - Tried AsyncGPUReadback
    - Tried changing RenderTexture format

    The code essentially looks like this:
    Code (CSharp):
    1. //rt is the RenderTexture attached to the camera
    2.  
    3. var texture = new Texture2D(rt.width, rt.height, rt.graphicsFormat, TextureCreationFlags.None);
    4.  
    5. //This is a new RT created for debug purposes on the GPU side
    6. var screenshot = new RenderTexture(rt.descriptor);
    7.  
    8. RenderTexture.active = rt;
    9. texture.ReadPixels(new Rect(0,0,rt.width,rt.height), 0, 0);
    10. texture.Apply(); //Shown in their documentation for some reason?
    11. RenderTexture.active = null;
    12.  
    13. //Saving the frame where we read pixels, the camera is drawing to a RawImage
    14. Graphics.CopyTexture(rt, screenshot);
    15. _camera.targetTexture = screenshot
    16.  
    17. var bytes = texture.EncodeToJPG();
    18. File.WriteAllBytes(_path, bytes);
    The frame is shown correctly on the debug target texture, which leads me to believe that the problem lies when reading pixels from the GPU. Any insight or fixes for this problem?
     
  2. funkyCoty

    funkyCoty

    Joined:
    May 22, 2018
    Posts:
    727
    Stalling the gpu to read pixels to cpu on Quest? You're a brave man..

    I'm guessing there's a bug with either Graphics.CopyTexture() or teture.ReadPixels() somewhere around the vr single pass instanced rendering workflow. You should try with multi pass and see if you have the same bug. If not, maybe consider making a bug report.
     
  3. weiner_monkey

    weiner_monkey

    Joined:
    Aug 1, 2022
    Posts:
    49
    Haha, this code is much improved from the legacy code which had 2 manual camera renders and a ReadPixel. I'm not sure if its related to single pass instanced, as it was single pass instanced prior to upgrading and working fine and the result from Graphics.CopyTexture is also correct right now. I've been trying to dig up how exactly ReadPixel works, if is synchronous/asynchronous, if it uses AsyncGPUReadback internally or not; Knowing that would probably help in some way.
     
  4. funkyCoty

    funkyCoty

    Joined:
    May 22, 2018
    Posts:
    727
    As far as I know, ReadPixels will stall the gpu pipeline, as it flushes the command buffers, waits for them to finish, then dispatches the command, waits for the result, and then finally returns it on the cpu. It's probably the slowest way you could get pixels from the GPU, so I'd avoid doing it on quest at all if you can.
     
  5. PelvisParsley

    PelvisParsley

    Joined:
    Aug 9, 2016
    Posts:
    89
    Are there any other options to get data to the CPU and ultimately save it out?
     
  6. funkyCoty

    funkyCoty

    Joined:
    May 22, 2018
    Posts:
    727
    Your best bet is probably AsyncGPUReadback. I have not tried this, but I believe you could simply get the raw native array from a Texture2D, then use AsyncGPUReadback to read a gpu texture into that native array. You mentioned this doesn't work for you, any reason why?
     
  7. weiner_monkey

    weiner_monkey

    Joined:
    Aug 1, 2022
    Posts:
    49
    It has the same result as ReadPixels() unfortunately. The varying black box when reading data
     
  8. funkyCoty

    funkyCoty

    Joined:
    May 22, 2018
    Posts:
    727
    Sounds like a bug, you should make a bug report and post the id of it here.
     
  9. weiner_monkey

    weiner_monkey

    Joined:
    Aug 1, 2022
    Posts:
    49
    I have made and sent a bug report, didn't receive an ID yet.
    Also to add: this doesn't seem to be URP related, but Vulkan related. It is always reproducible on an empty project on the Quest 2, but not on PC
     
  10. kancsa

    kancsa

    Joined:
    Feb 2, 2022
    Posts:
    9
    Hey, did you get any response yet? I'm having the same problem on Quest 2
     
  11. kancsa

    kancsa

    Joined:
    Feb 2, 2022
    Posts:
    9
    Just fixed mine, it came down to making the color format / graphics format the same in every texture operation. An incorrect color format of a Render Texture in a Graphics.Blit caused it in my case.
     
    Last edited: Aug 12, 2022
    funkyCoty likes this.
  12. weiner_monkey

    weiner_monkey

    Joined:
    Aug 1, 2022
    Posts:
    49
    I don't think thats related to the issue I'm having, operations on the GPU side are already correct for me, as is the Blit you mentioned. The problem is arising when that data is retrieved from GPU -> CPU, or when you call ReadPixels()
     
  13. jacobian

    jacobian

    Joined:
    Jul 20, 2015
    Posts:
    17
    Hitting the same issue, Any workaround you found.
     
  14. jacobian

    jacobian

    Joined:
    Jul 20, 2015
    Posts:
    17
    Oh I see its fixed on the new 2021 versions
     
    N7RX_Y likes this.