Handmade Hero » Forums » Code » [Day 387 Bug] Handmade resolve framebuffer
Mox
30 posts
#12110 [Day 387 Bug] Handmade resolve framebuffer
3 weeks, 1 day ago Edited by Mox on June 4, 2017, 3:32 p.m. Reason: only glClear or glDepthFunc is needed

Hi,

I believe a major problem with the attempt to do the handmade resolve is that the depth buffer is disabled with glDisable(GL_DEPTH_TEST). But this also disables the depth write and therefore messes up all the following peels. Before using the handmade resolve the blit framebuffer would fill the depth buffer, but now this should be done manually.
A good way to demonstrate the bug is to only use Peel1 or higher for the FinalStretch shader. This will not display anything.
So a better way is this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
internal void
ResolveMultisample(opengl_framebuffer *From, opengl_framebuffer *To,
                   u32 Width, u32 Height)
{
    glBindFramebuffer(GL_FRAMEBUFFER, To->FramebufferHandle);
    glViewport(0, 0, Width, Height);
    glScissor(0, 0, Width, Height);
    //Realized after posting only one of glDepthFunc and glClear is needed.
    //glClear(GL_DEPTH_BUFFER_BIT);
    glDepthFunc(GL_ALWAYS);

...

    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    glDepthFunc(GL_LEQUAL);
}


I found this out while trying to improve the manual resolve shader. I believed the DepthMin/Max range would give good results. But ran into the depth issue while debugging it. After that the DepthMin/Max worked ok if I also used the UsedSampleCount/SampleCount fraction to set the alpha value of the CombinedColor (line 44 below) and a #define DepthThreshold 0.001f:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
    f32 DepthMin = texelFetch(DepthSampler, ivec2(gl_FragCoord.xy), 0).r;
    f32 DepthMax = DepthMin;
    for(s32 SampleIndex = 1;
    SampleIndex < SampleCount;
    ++SampleIndex)
    {
    	f32 Depth = texelFetch(DepthSampler, ivec2(gl_FragCoord.xy), SampleIndex).r;
    	if (Depth < DepthMin) {
    		f32 Diff = (DepthMin - Depth);
    		if (Diff >= DepthThreshold) {
    			DepthMax = Depth;
    		}
    		DepthMin = Depth;
    	} else if (Depth > DepthMax) {
    		f32 Diff = (Depth - DepthMax);
    		if (Diff < DepthThreshold) {
    			DepthMax = Depth;
    		}
    	}
    }
    
    gl_FragDepth = DepthMax;
    
    v4 CombinedColor = V4(0, 0, 0, 0);
    s32 UsedSampleCount = 0;
    for(s32 SampleIndex = 0;
    SampleIndex < SampleCount;
    ++SampleIndex)
    {
    f32 Depth = texelFetch(DepthSampler, ivec2(gl_FragCoord.xy), SampleIndex).r;
    if(Depth >= DepthMin && Depth <= DepthMax)
    {
    v4 Color = texelFetch(ColorSampler, ivec2(gl_FragCoord.xy), SampleIndex);
    #if ShaderSimTexReadSRGB
    Color.rgb *= Color.rgb;
    #endif
    CombinedColor += Color;
    UsedSampleCount += 1;
    }
    }
    
    f32 InvSampleCount = 1.0f / f32(UsedSampleCount);
    ResultColor = InvSampleCount*CombinedColor;
    ResultColor *= f32(UsedSampleCount)/f32(SampleCount);
    #if ShaderSimTexWriteSRGB
    ResultColor.rgb = sqrt(ResultColor.rgb);
    #endif


I hope to point these problems out in the prestream tonight, but being in Europe I don't know if I can make it ;)

-----
Some more tweaking:
-----
After a bit more fidgeting, and actually watching the rest of the episode I saw that you where also using centroid to get better depth per resolved pixel. With the centroid still used, some edges won't get smoothed (the 'connection' of a lower block against a higher block).

But disabling the centroid gave weird results along other edges. So I also tried collecting all the samples without respect for the depth they have and only setting the glFragDepth to the DepthMax of the tested range by commenting line 31 to disable the range test. I think this emulates the normal resolve as far as selecting colors the best, but still has the upside of controlling the depth buffer output.

That combined with disabling the centroid and a DepthThreshold of 0.0025f gave the best result. A bit hacky, but it seems to give nice results :)

Happy coding,
Mox
ratchetfreak
267 posts
#12111 [Day 387 Bug] Handmade resolve framebuffer
3 weeks, 1 day ago

depth writes and depth tests are controlled separately using glDepthMask and glEnable(GL_DEPTH_TEST) resp.

So that's not an issue. What is an issue with that is that the last fragment stays in the depth buffer. It doesn't give you a min or max.
Mox
30 posts
#12112 [Day 387 Bug] Handmade resolve framebuffer
3 weeks, 1 day ago

ratchetfreak:
depth writes and depth tests are controlled separately using glDepthMask and glEnable(GL_DEPTH_TEST) resp.

So that's not an issue. What is an issue with that is that the last fragment stays in the depth buffer. It doesn't give you a min or max.

I don't think that is true. From http://docs.gl/gl3/glEnable:

GL_DEPTH_TEST
If enabled, do depth comparisons and update the depth buffer. Note that even if the depth buffer exists and the depth mask is non-zero, the depth buffer is not updated if the depth test is disabled. See glDepthFunc and glDepthRange.