Eevee: Minmax Depth Pyramid.

This commit introduce the computation of a depth pyramid containing min and max depth values of the original depth buffer.
This is useful for Clustered Light Culling but also for raytracing on the depth buffer (SSR).
It's also usefull to have to fetch higher mips in order to improve texture cache usage.

As of now, 1st mip (highest res) is half the resolution of the depth buffer, but everything is already done to be able to make a fullres copy of the depth buffer in the 1st mip instead of downsampling.
Also, the texture used is RG_32F which is a too much but enough to cover the 24bits of the depth buffer. Reducing the texture size would make things quite faster.
This commit is contained in:
Clément Foucault 2017-06-22 02:28:49 +02:00
parent ed59d03bfc
commit 2c7f6db8d1
6 changed files with 151 additions and 10 deletions

View File

@ -118,6 +118,7 @@ data_to_c_simple(engines/clay/shaders/ssao_groundtruth.glsl SRC)
data_to_c_simple(engines/eevee/shaders/default_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/default_world_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/background_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_minmaxz_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_geom.glsl SRC)

View File

@ -51,6 +51,11 @@ typedef struct EEVEE_LightProbeData {
#define ENABLE_EFFECT_DOF 1
static struct {
/* Downsample Depth */
struct GPUShader *minmaxz_downlevel_sh;
struct GPUShader *minmaxz_downdepth_sh;
struct GPUShader *minmaxz_copydepth_sh;
/* Motion Blur */
struct GPUShader *motion_blur_sh;
@ -66,6 +71,7 @@ static struct {
struct GPUShader *dof_resolve_sh;
} e_data = {NULL}; /* Engine data */
extern char datatoc_effect_minmaxz_frag_glsl[];
extern char datatoc_effect_motion_blur_frag_glsl[];
extern char datatoc_effect_bloom_frag_glsl[];
extern char datatoc_effect_dof_vert_glsl[];
@ -141,20 +147,22 @@ void EEVEE_effects_init(EEVEE_Data *vedata)
const float *viewport_size = DRW_viewport_size_get();
/* Shaders */
if (!e_data.motion_blur_sh) {
e_data.motion_blur_sh = DRW_shader_create_fullscreen(datatoc_effect_motion_blur_frag_glsl, NULL);
}
e_data.minmaxz_downlevel_sh = DRW_shader_create_fullscreen(datatoc_effect_minmaxz_frag_glsl, NULL);
e_data.minmaxz_downdepth_sh = DRW_shader_create_fullscreen(datatoc_effect_minmaxz_frag_glsl, "#define INPUT_DEPTH\n");
e_data.minmaxz_copydepth_sh = DRW_shader_create_fullscreen(datatoc_effect_minmaxz_frag_glsl, "#define INPUT_DEPTH\n"
"#define COPY_DEPTH\n");
e_data.motion_blur_sh = DRW_shader_create_fullscreen(datatoc_effect_motion_blur_frag_glsl, NULL);
if (!e_data.dof_downsample_sh) {
e_data.dof_downsample_sh = DRW_shader_create(datatoc_effect_dof_vert_glsl, NULL,
datatoc_effect_dof_frag_glsl, "#define STEP_DOWNSAMPLE\n");
e_data.dof_scatter_sh = DRW_shader_create(datatoc_effect_dof_vert_glsl, NULL,
datatoc_effect_dof_frag_glsl, "#define STEP_SCATTER\n");
e_data.dof_resolve_sh = DRW_shader_create(datatoc_effect_dof_vert_glsl, NULL,
datatoc_effect_dof_frag_glsl, "#define STEP_RESOLVE\n");
}
if (!e_data.bloom_blit_sh[0]) {
e_data.bloom_blit_sh[0] = DRW_shader_create_fullscreen(datatoc_effect_bloom_frag_glsl, "#define STEP_BLIT\n");
e_data.bloom_blit_sh[1] = DRW_shader_create_fullscreen(datatoc_effect_bloom_frag_glsl, "#define STEP_BLIT\n"
"#define HIGH_QUALITY\n");
@ -378,6 +386,14 @@ void EEVEE_effects_init(EEVEE_Data *vedata)
(int)viewport_size[0], (int)viewport_size[1],
&tex, 1);
}
/* MinMax Pyramid */
/* TODO reduce precision */
DRWFboTexture tex = {&stl->g_data->minmaxz, DRW_TEX_RG_32, DRW_TEX_MIPMAP | DRW_TEX_TEMP};
DRW_framebuffer_init(&fbl->minmaxz_fb, &draw_engine_eevee_type,
(int)viewport_size[0] / 2, (int)viewport_size[1] / 2,
&tex, 1);
}
static DRWShadingGroup *eevee_create_bloom_pass(const char *name, EEVEE_EffectsInfo *effects, struct GPUShader *sh, DRWPass **pass, bool upsample)
@ -408,6 +424,23 @@ void EEVEE_effects_cache_init(EEVEE_Data *vedata)
struct Gwn_Batch *quad = DRW_cache_fullscreen_quad_get();
{
psl->minmaxz_downlevel = DRW_pass_create("HiZ Down Level", DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(e_data.minmaxz_downlevel_sh, psl->minmaxz_downlevel);
DRW_shgroup_uniform_buffer(grp, "depthBuffer", &stl->g_data->minmaxz);
DRW_shgroup_call_add(grp, quad, NULL);
psl->minmaxz_downdepth = DRW_pass_create("HiZ Down Depth", DRW_STATE_WRITE_COLOR);
grp = DRW_shgroup_create(e_data.minmaxz_downdepth_sh, psl->minmaxz_downdepth);
DRW_shgroup_uniform_buffer(grp, "depthBuffer", &dtxl->depth);
DRW_shgroup_call_add(grp, quad, NULL);
psl->minmaxz_copydepth = DRW_pass_create("HiZ Copy Depth", DRW_STATE_WRITE_COLOR);
grp = DRW_shgroup_create(e_data.minmaxz_copydepth_sh, psl->minmaxz_copydepth);
DRW_shgroup_uniform_buffer(grp, "depthBuffer", &dtxl->depth);
DRW_shgroup_call_add(grp, quad, NULL);
}
{
psl->motion_blur = DRW_pass_create("Motion Blur", DRW_STATE_WRITE_COLOR);
@ -519,6 +552,28 @@ void EEVEE_effects_cache_init(EEVEE_Data *vedata)
} \
} ((void)0)
static void minmax_downsample_cb(void *vedata, int UNUSED(level))
{
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
DRW_draw_pass(psl->minmaxz_downlevel);
}
void EEVEE_create_minmax_buffer(EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_StorageList *stl = vedata->stl;
/* Copy depth buffer to minmax texture top level */
DRW_framebuffer_texture_attach(fbl->minmaxz_fb, stl->g_data->minmaxz, 0, 0);
DRW_framebuffer_bind(fbl->minmaxz_fb);
DRW_draw_pass(psl->minmaxz_downdepth);
DRW_framebuffer_texture_detach(stl->g_data->minmaxz);
/* Create lower levels */
DRW_framebuffer_recursive_downsample(fbl->minmaxz_fb, stl->g_data->minmaxz, 6, &minmax_downsample_cb, vedata);
}
void EEVEE_draw_effects(EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
@ -642,6 +697,10 @@ void EEVEE_draw_effects(EEVEE_Data *vedata)
void EEVEE_effects_free(void)
{
DRW_SHADER_FREE_SAFE(e_data.minmaxz_downlevel_sh);
DRW_SHADER_FREE_SAFE(e_data.minmaxz_downdepth_sh);
DRW_SHADER_FREE_SAFE(e_data.minmaxz_copydepth_sh);
DRW_SHADER_FREE_SAFE(e_data.motion_blur_sh);
DRW_SHADER_FREE_SAFE(e_data.dof_downsample_sh);
DRW_SHADER_FREE_SAFE(e_data.dof_scatter_sh);

View File

@ -46,6 +46,7 @@ static void EEVEE_engine_init(void *ved)
EEVEE_Data *vedata = (EEVEE_Data *)ved;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
EEVEE_SceneLayerData *sldata = EEVEE_scene_layer_data_get();
DRWFboTexture tex = {&txl->color, DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER};
@ -55,6 +56,11 @@ static void EEVEE_engine_init(void *ved)
(int)viewport_size[0], (int)viewport_size[1],
&tex, 1);
if (!stl->g_data) {
/* Alloc transient pointers */
stl->g_data = MEM_mallocN(sizeof(*stl->g_data), __func__);
}
EEVEE_materials_init();
EEVEE_lights_init(sldata);
EEVEE_lightprobes_init(sldata, vedata);
@ -67,11 +73,6 @@ static void EEVEE_cache_init(void *vedata)
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
EEVEE_SceneLayerData *sldata = EEVEE_scene_layer_data_get();
if (!stl->g_data) {
/* Alloc transient pointers */
stl->g_data = MEM_mallocN(sizeof(*stl->g_data), __func__);
}
EEVEE_materials_cache_init(vedata);
EEVEE_lights_cache_init(sldata, psl);
EEVEE_lightprobes_cache_init(sldata, psl, stl);
@ -148,6 +149,12 @@ static void EEVEE_draw_scene(void *vedata)
DRW_draw_pass(psl->depth_pass);
DRW_draw_pass(psl->depth_pass_cull);
/* Create minmax texture */
EEVEE_create_minmax_buffer(vedata);
/* Restore main FB */
DRW_framebuffer_bind(fbl->main);
/* Shading pass */
DRW_draw_pass(psl->probe_display);
DRW_draw_pass(psl->default_pass);

View File

@ -69,6 +69,9 @@ typedef struct EEVEE_PassList {
struct DRWPass *dof_down;
struct DRWPass *dof_scatter;
struct DRWPass *dof_resolve;
struct DRWPass *minmaxz_downlevel;
struct DRWPass *minmaxz_downdepth;
struct DRWPass *minmaxz_copydepth;
struct DRWPass *depth_pass;
struct DRWPass *depth_pass_cull;
@ -83,6 +86,7 @@ typedef struct EEVEE_PassList {
typedef struct EEVEE_FramebufferList {
/* Effects */
struct GPUFrameBuffer *minmaxz_fb;
struct GPUFrameBuffer *effect_fb; /* HDR */
struct GPUFrameBuffer *bloom_blit_fb; /* HDR */
struct GPUFrameBuffer *bloom_down_fb[MAX_BLOOM_STEP]; /* HDR */
@ -371,6 +375,7 @@ typedef struct EEVEE_PrivateData {
struct DRWShadingGroup *cube_display_shgrp;
struct GHash *material_hash;
struct GHash *hair_material_hash;
struct GPUTexture *minmaxz;
} EEVEE_PrivateData; /* Transient data */
/* eevee_data.c */
@ -413,6 +418,7 @@ void EEVEE_lightprobes_free(void);
/* eevee_effects.c */
void EEVEE_effects_init(EEVEE_Data *vedata);
void EEVEE_effects_cache_init(EEVEE_Data *vedata);
void EEVEE_create_minmax_buffer(EEVEE_Data *vedata);
void EEVEE_draw_effects(EEVEE_Data *vedata);
void EEVEE_effects_free(void);

View File

@ -0,0 +1,66 @@
/**
* Shader that downsample depth buffer,
* saving min and max value of each texel in the above mipmaps.
* Adapted from http://rastergrid.com/blog/2010/10/hierarchical-z-map-based-occlusion-culling/
**/
uniform sampler2D depthBuffer;
out vec4 FragMinMax;
vec2 sampleLowerMip(ivec2 texel)
{
#ifdef INPUT_DEPTH
return texelFetch(depthBuffer, texel, 0).rr;
#else
return texelFetch(depthBuffer, texel, 0).rg;
#endif
}
void minmax(inout vec2 val[2])
{
val[0].x = min(val[0].x, val[1].x);
val[0].y = max(val[0].y, val[1].y);
}
void main()
{
vec2 val[2];
ivec2 texelPos = ivec2(gl_FragCoord.xy);
ivec2 mipsize = textureSize(depthBuffer, 0);
#ifndef COPY_DEPTH
texelPos *= 2;
#endif
val[0] = sampleLowerMip(texelPos);
#ifndef COPY_DEPTH
val[1] = sampleLowerMip(texelPos + ivec2(1, 0));
minmax(val);
val[1] = sampleLowerMip(texelPos + ivec2(1, 1));
minmax(val);
val[1] = sampleLowerMip(texelPos + ivec2(0, 1));
minmax(val);
/* if we are reducing an odd-width texture then fetch the edge texels */
if (((mipsize.x & 1) != 0) && (int(gl_FragCoord.x) == mipsize.x-3)) {
/* if both edges are odd, fetch the top-left corner texel */
if (((mipsize.y & 1) != 0) && (int(gl_FragCoord.y) == mipsize.y-3)) {
val[1] = sampleLowerMip(texelPos + ivec2(-1, -1));
minmax(val);
}
val[1] = sampleLowerMip(texelPos + ivec2(0, -1));
minmax(val);
val[1] = sampleLowerMip(texelPos + ivec2(1, -1));
minmax(val);
}
/* if we are reducing an odd-height texture then fetch the edge texels */
else if (((mipsize.y & 1) != 0) && (int(gl_FragCoord.y) == mipsize.y-3)) {
val[1] = sampleLowerMip(texelPos + ivec2(0, -1));
minmax(val);
val[1] = sampleLowerMip(texelPos + ivec2(1, -1));
minmax(val);
}
#endif
FragMinMax = vec4(val[0], 0.0, 1.0);
}

View File

@ -1997,6 +1997,7 @@ static GPUTextureFormat convert_tex_format(int fbo_format, int *channels, bool *
case DRW_TEX_R_16: *channels = 1; return GPU_R16F;
case DRW_TEX_R_32: *channels = 1; return GPU_R32F;
case DRW_TEX_RG_16: *channels = 2; return GPU_RG16F;
case DRW_TEX_RG_32: *channels = 2; return GPU_RG32F;
case DRW_TEX_RGBA_8: *channels = 4; return GPU_RGBA8;
case DRW_TEX_RGBA_16: *channels = 4; return GPU_RGBA16F;
case DRW_TEX_RGBA_32: *channels = 4; return GPU_RGBA32F;
@ -2143,6 +2144,7 @@ void DRW_framebuffer_recursive_downsample(
{
GPU_framebuffer_recursive_downsample(fb, tex, num_iter, callback, userData);
}
void DRW_framebuffer_viewport_size(struct GPUFrameBuffer *UNUSED(fb_read), int x, int y, int w, int h)
{
glViewport(x, y, w, h);