- Home /
Saving Depth buffer for VR Game on Unity - DROP IN FPS
I am building a VR game in Unity and I want to save the depth buffer on to the disk, ideally, I want every 10th frame and maintain the game FPS as 90FPS. So for every 90 frames, I want to get 10 frames of depth. I have tried everything like AsyncGPUReadbackRequest, writing raw Texture 2D files, write raw byte files but nothing seems to work. My game FPS drops to 30-40.
I have one main camera, and one which is a duplicate of the main camera, I don't want to render the depth buffer to the user's eye hence I create another camera. I am not sure if this is even required?
Right now, I am doing : camera.depthTextureMode = DepthTextureMode.Depth;
I have a shader and follow this tutorial: https://www.ronja-tutorials.com/2018/07/01/postprocessing-depth.html
Then: Graphics.Blit(source, destination, material);
Any suggestions on how can I remove the second camera and/or increase faster storing of the depth maps on to the disk?
Update: Removed second camera, still low FPS
Removed the second camera and removed the EncodeToPNG() but still, the FPS is dropping.
Answer by Namey5 · Aug 24, 2020 at 11:59 AM
You don't need a second camera, you can just copy the depth texture of your current camera to a render texture and write that to a file. This example uses a coroutine to spread the operations out over multiple frames for performance;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
public class StoreDepthTexture : MonoBehaviour
{
private Camera m_Camera;
private bool m_ShouldSaveDepth = false;
private bool m_SavingDepth = false;
private void OnEnable ()
{
//Make sure our camera writes to depth
m_Camera = GetComponent<Camera> ();
m_Camera.depthTextureMode |= DepthTextureMode.Depth;
}
private void Update ()
{
//If we aren't already saving a depth texture, save a new one when the space key is pressed
if (!m_SavingDepth)
if (Input.GetKeyDown (KeyCode.Space))
m_ShouldSaveDepth = true;
}
//This is called after the attached camera is finished rendering, meaning the depth texture should be available
private void OnPostRender ()
{
if (m_ShouldSaveDepth)
{
//Start saving the depth texture
StartCoroutine (CaptureDepth());
m_ShouldSaveDepth = false;
}
}
private IEnumerator CaptureDepth ()
{
m_SavingDepth = true;
//The depth texture is set as a shader global under the variable name '_CameraDepthTexture' - we can just get a reference to that global
RenderTexture depth = Shader.GetGlobalTexture ("_CameraDepthTexture") as RenderTexture;
//The depth is technically stored in the depth buffer which we can't access directly, however if we blit that texture into another texture it will copy the depth into the red colour channel
RenderTexture tmp = RenderTexture.GetTemporary (depth.width, depth.height, 16, RenderTextureFormat.ARGB32);
Graphics.Blit (depth, tmp);
//Wait until the next frame
yield return null;
//Store the last active render texture and set our depth copy as active
RenderTexture lastActive = RenderTexture.active;
RenderTexture.active = tmp;
//Copy the active render texture into a normal Texture2D
//Unfortunately readpixels doesn't work with single channel formats, so ARGB32 will have to do
Texture2D tex = new Texture2D (depth.width, depth.height, TextureFormat.ARGB32, false);
tex.ReadPixels (new Rect (0, 0, depth.width, depth.height), 0, 0);
tex.Apply ();
//Restore the active render texture and release our temporary tex
RenderTexture.active = lastActive;
RenderTexture.ReleaseTemporary (tmp);
//Wait another frame
yield return null;
//Encode the texture data into .png formatted bytes
byte[] data = tex.EncodeToPNG();
//Wait another frame
yield return null;
//Write the texture to a file
File.WriteAllBytes (Application.dataPath + "/depthTexture.png", data);
m_SavingDepth = false;
}
}
Thank you for such a great explanation. But this is dropping the FPS down to ~30, I got rid of the tex.EncodeToPNG() and now saving raw texture files (I'll convert them later using post-processing script) but still can't see any visible improvements. Since it is a Virtual reality game, 30 FPS is a problem. Any suggestions on how can I speed it up?
Reading data from the GPU back onto the CPU is not cheap, so I can only suggest potentially reading back chunks of the texture at a time rather than the whole thing.
Thank you, I tried downsampling the texture but did not really help. Appreciate your help though!
Can you identify which part that is the bottleneck? $$anonymous$$easure the time taken for each step.
float t = Time.realtimeSinceStartup;
//code to measure
Debug.Log("Time: " + (Time.realtimeSinceStartup - t) * 1000.0f);
If it's tex.ReadPixels it should be easy to spread it out over more frames by doing it in chunks as Namey5 says.
Beyond that you can also do things like saving to disk in another Thread if the CPU is not at 100% load, but since most unity calls must be done from the main thread that is not always possible.
Sure, I can try and identify the bottleneck. For saving to disk, I created a RamDISK and was running the application from there but the FPS is still struggling.
Your answer
Follow this Question
Related Questions
Unity 5: How to display bird eye view layout of a 3D scene with UI component overlay on screen? 1 Answer
Snapchat's lenses skin smoothing effects in unity3d? achievable by shaders? 1 Answer
Draw call difference in Unity 5.3.4 and Unity 5.0. 1 Answer
Trouble with culling mask in Google cardboard. 1 Answer
How to correct deprecated GvrReticle and GazeInputModule 0 Answers