Hello! I’m trying to do, well, what the title says. I want to edit the depth buffer before rendering gets underway, as the camera I’m rendering with only has to render an extremely tiny amount of the screen. Culling off this extra area on the screen would save me like 90% rendertime per camera (which is very important in VR!) but no matter what I do I can’t seem to figure out how to get it to function.
That is the code that I’m using to attempt to clear the depth buffer of the camera to 0 after it is rendered, with this being the depthClear material shader:
Does anybody have any idea how this can be made to work? I wouldn’t think that setting the depth buffer would be this much of a pain… Hell, if I could even just set the camera to do a SIMPLE stencil test this would be much easier but Unity doesn’t let you reject fragments at rendertime unless you’re using a surface shader. Shouldn’t this shader be zeroing out the depth buffer?
The more I work on this, the worse it seems to get. No matter what is written to the depth buffer (in deferred OR forward), the camera continues on rendering. This is extremely disappointing, since I’m not sure what the depth buffer is even there for if writing to it doesn’t seem to cause any pixel rejection. Shouldn’t writing 1 to the entire depth buffer (before OR after the depthtexture is generated) cause it to reject every single fragment it tries to render?
I’ve been able to successfully write to the depth buffer (1ing and zeroing it out) but neither of them actually resulted in fragments being rejected. Everything still is “rendered”, even if the screen is clearing to skybox.
Ah, I realized you’re rendering during the BeforeDepthTexture event, and the depth texture isn’t the zbuffer used during rendering. In forward that’s a separate texture renderered prior to the forward passes and set as a global texture, and in deferred it’s rendered after the deferred pass as a combination of the depth from the deferred and some additionally injected forward passes, though I don’t even know if that event gets fired during deferred.
Unfortunately, no matter when I render the commandbuffer, even if it displays a pure white/skybox screen due to depth clearing, the performance does not increase at all and the graphics card is still rendering everything “behind” the rect.
Truly annoying, I wonder what on earth could be causing this. Every step in the frame debugger after the clear is just… nothing, or white, but it still takes up just as much rendering time.
That’s not something specific to openVR, that’s just where Unity does its clear of the depth texture. And again the depth texture has nothing to do with the zbuffer used when rendering the forward or deferred passes, it’s completely separate or at best a copy of the depth buffer after the fact. Also if you’re expecting to see things disappear from the frame debugger, that’s just showing the CPU side calls. Unity’s culling doesn’t use gpu occlusion so they’ll still show up in the frame debugger even if the first thing that renders covers the entire view.
The only way to see if your pixels are actually getting rejected is if the GPU time decreases, or using RenderDoc to see what’s happening on the GPU. If you’re not fillrate bound, it’s unlikely what you’re doing will actually noticeably increase framerate.
My images are unfortunately inaccurate- they were from before I realized that the depth texture wasn’t what I was looking to modify (I was confused by the fact that OVR drew its stencil during it), but even though I’ve since moved my depth drawing to beforeforwardopaque, it is not actually rejecting the fragments.
Also, you say that the results won’t show up in the frame debugger, but that isn’t true- the mask from OVR flat-out rejects fragments all the way through the entire frame debug, and the mask is visible for the entire render.
I’m a little confused as to why you say that this wouldn’t increase performance. How would rendering 1/10th of a camera not be significantly faster?
Breakdown:
If I’m barking up the wrong tree, then what should I be doing instead? Unity must support some way to do away with parts of the frame that won’t be rendered (like a scissor test) for performance reasons.
Those are still showing you drawing during the depth texture creation, which is producing correct results in blanking out the depth texture, but will have no effect on the final render (apart from possibly messing with shadows or other effects that rely on the depth texture).
Just ignore everything under the UpdateDepthTexture right now, and move your depth pass to run during CameraEvent.BeforeForwardOpaque. If you look for Camera.Render > Drawing > Render.OpaqueGeometry > RenderForwardOpaque.Render > RenderForward.RenderLoopJob there’s another clear just before that which is probably another Draw GL like what you’re seeing prior to the UpdateDepthTexture > DepthPass.Job. If you use CameraEvent.BeforeForwardOpaque you should get a CommandBuffer.BeforeForwardOpaque event right after that to blank out the zbuffer with.
Right, that’s what I’m doing (I mentioned that the picture wasn’t my current method in that post, but I don’t blame you for not seeing it). I am drawing to the depth buffer before the forward opaque, but it is still not actually rejecting the fragments. With the depth buffer having a solid 1 written to it to trigger the near clip early rejection, It renders a pure black screen- however, my GPU performance doesn’t actually increase whatsoever (In any sense- framerates remain constant, GPU load doesn’t drop, nothing). RenderDoc reports the same as the frame debugger- the depth buffer gets written to a solid 1, and then it renders everything else as per normal at the exact same speed as it does when rendering normally.
I took a couple of minutes to setup a test case. A stupid expensive shader, a shader that writes depth, and a script to blit that to the camera. Worked fine for me.
using UnityEngine;
using UnityEngine.Rendering;
using System.Collections;
[RequireComponent(typeof(Camera))]
public class DebugDepthWrite : MonoBehaviour {
public Material depthMat;
private CommandBuffer cmdBuffer;
void OnEnable() {
Camera cam = GetComponent<Camera>();
cmdBuffer = new CommandBuffer();
cmdBuffer.name = "Depth Write";
cmdBuffer.Blit(BuiltinRenderTextureType.CurrentActive, BuiltinRenderTextureType.CurrentActive, depthMat);
cam.AddCommandBuffer(CameraEvent.BeforeForwardOpaque, cmdBuffer);
}
void OnDisable() {
Camera cam = GetComponent<Camera>();
cam.RemoveCommandBuffer(CameraEvent.BeforeForwardOpaque, cmdBuffer);
cmdBuffer = null;
}
}
I put the “StupidExpensive” shader on a sphere, pointed a camera at it, added the component to the camera and assigned a material with the depth write shader to the component. Toggle it on and off I saw the FPS go from <20 to >2000.
The depth write shader defaults to writing to zero since I’m using an older version of Unity atm, but if you set it to 1 on the material or use your own shader it should work just as well.
Thanks for the writeup, it works perfectly! It turns out that I, in my infinite wisdom, had a second point light sun from an older revision of my code sitting beneath the terrain which was casting dynamic shadows. This was resulting in my depth buffer writing not really mattering that much when it came to the final rendering cost, since shadows are rendered whether or not they are occluded… I feel like a pretty big chump right now.
Thanks!
I guess I’m off to try and figure out how OVR shares shadow maps between eyes so I can use that on my portals as well.