GPUFramebuffer: Add recursive downsampling function.

This special case function enables rendering to a miplevel while using the miplevels above as texture input.
This is needed for some algorithm (i.e. creating a min-max depth pyramid texture).
This commit is contained in:
Clément Foucault 2017-06-22 02:01:58 +02:00
parent fe2ff3fc89
commit ed59d03bfc
4 changed files with 70 additions and 18 deletions

View File

@ -202,6 +202,9 @@ void DRW_framebuffer_texture_layer_attach(struct GPUFrameBuffer *fb, struct GPUT
void DRW_framebuffer_cubeface_attach(struct GPUFrameBuffer *fb, struct GPUTexture *tex, int slot, int face, int mip);
void DRW_framebuffer_texture_detach(struct GPUTexture *tex);
void DRW_framebuffer_blit(struct GPUFrameBuffer *fb_read, struct GPUFrameBuffer *fb_write, bool depth);
void DRW_framebuffer_recursive_downsample(
struct GPUFrameBuffer *fb, struct GPUTexture *tex, int num_iter,
void (*callback)(void *userData, int level), void *userData);
void DRW_framebuffer_viewport_size(struct GPUFrameBuffer *fb_read, int x, int y, int w, int h);
void DRW_framebuffer_free(struct GPUFrameBuffer *fb);
#define DRW_FRAMEBUFFER_FREE_SAFE(fb) do { \

View File

@ -2137,6 +2137,12 @@ void DRW_framebuffer_blit(struct GPUFrameBuffer *fb_read, struct GPUFrameBuffer
GPU_framebuffer_blit(fb_read, 0, fb_write, 0, depth);
}
void DRW_framebuffer_recursive_downsample(
struct GPUFrameBuffer *fb, struct GPUTexture *tex, int num_iter,
void (*callback)(void *userData, int level), void *userData)
{
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);

View File

@ -75,6 +75,10 @@ void GPU_framebuffer_blit(
GPUFrameBuffer *fb_read, int read_slot,
GPUFrameBuffer *fb_write, int write_slot, bool use_depth);
void GPU_framebuffer_recursive_downsample(
GPUFrameBuffer *fb, struct GPUTexture *tex, int num_iter,
void (*callback)(void *userData, int level), void *userData);
/* GPU OffScreen
* - wrapper around framebuffer and texture for simple offscreen drawing
* - changes size if graphics card can't support it */

View File

@ -142,16 +142,7 @@ bool GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slo
else
attachment = GL_COLOR_ATTACHMENT0 + slot;
#if defined(WITH_GL_PROFILE_COMPAT)
/* Workaround for Mac & Mesa compatibility profile, remove after we switch to core profile */
/* glFramebufferTexture was introduced in 3.2. It is *not* available in the ARB FBO extension */
if (GLEW_VERSION_3_2)
glFramebufferTexture(GL_FRAMEBUFFER, attachment, GPU_texture_opengl_bindcode(tex), mip); /* normal core call, same as below */
else
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GPU_texture_target(tex), GPU_texture_opengl_bindcode(tex), mip);
#else
glFramebufferTexture(GL_FRAMEBUFFER, attachment, GPU_texture_opengl_bindcode(tex), mip);
#endif
if (GPU_texture_depth(tex))
fb->depthtex = tex;
@ -250,16 +241,7 @@ void GPU_framebuffer_texture_detach(GPUTexture *tex)
attachment = GL_COLOR_ATTACHMENT0 + fb_attachment;
}
#if defined(WITH_GL_PROFILE_COMPAT)
/* Workaround for Mac & Mesa compatibility profile, remove after we switch to core profile */
/* glFramebufferTexture was introduced in 3.2. It is *not* available in the ARB FBO extension */
if (GLEW_VERSION_3_2)
glFramebufferTexture(GL_FRAMEBUFFER, attachment, 0, 0); /* normal core call, same as below */
else
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GPU_texture_target(tex), 0, 0);
#else
glFramebufferTexture(GL_FRAMEBUFFER, attachment, 0, 0);
#endif
GPU_texture_framebuffer_set(tex, NULL, -1);
}
@ -554,6 +536,63 @@ void GPU_framebuffer_blit(GPUFrameBuffer *fb_read, int read_slot, GPUFrameBuffer
glDrawBuffer(GL_COLOR_ATTACHMENT0);
}
/**
* Use this if you need to custom downsample your texture and use the previous mip level as input.
* This function only takes care of the correct texture handling. It execute the callback for each texture level.
**/
void GPU_framebuffer_recursive_downsample(
GPUFrameBuffer *fb, GPUTexture *tex, int num_iter, void (*callback)(void *userData, int level), void *userData)
{
int current_dim[2] = {GPU_texture_width(tex), GPU_texture_height(tex)};
GLenum attachment;
/* Manually setup framebuffer to not use GPU_texture_framebuffer_set() */
glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
GG.currentfb = fb->object;
if (GPU_texture_stencil(tex) && GPU_texture_depth(tex))
attachment = GL_DEPTH_STENCIL_ATTACHMENT;
else if (GPU_texture_depth(tex))
attachment = GL_DEPTH_ATTACHMENT;
else
attachment = GL_COLOR_ATTACHMENT0;
/* last bound prevails here, better allow explicit control here too */
glDrawBuffer(GL_COLOR_ATTACHMENT0);
glReadBuffer(GL_COLOR_ATTACHMENT0);
for (int i=1; i < num_iter+1 && (current_dim[0] > 1 && current_dim[1] > 1); i++) {
/* calculate next viewport size */
current_dim[0] /= 2;
current_dim[1] /= 2;
/* ensure that the viewport size is always at least 1x1 */
CLAMP_MIN(current_dim[0], 1);
CLAMP_MIN(current_dim[1], 1);
glViewport(0, 0, current_dim[0], current_dim[1]);
/* bind next level for rendering but first restrict fetches only to previous level */
GPU_texture_bind(tex, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, i-1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, i-1);
GPU_texture_unbind(tex);
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, GPU_texture_opengl_bindcode(tex), i);
callback(userData, i);
}
glFramebufferTexture(GL_FRAMEBUFFER, attachment, 0, 0);
/* reset mipmap level range for the depth image */
GPU_texture_bind(tex, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, num_iter - 1);
GPU_texture_unbind(tex);
}
/* GPUOffScreen */
struct GPUOffScreen {