From 36bf6c2d9e1764b85944b603c2fa40bbc5f51685 Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Tue, 15 Mar 2011 03:10:20 +0000 Subject: [PATCH] Multisampling --- reaction/code/renderer/tr_fbo.c | 268 +++++++++++++++++++++++++----- reaction/code/renderer/tr_local.h | 8 +- reaction/code/sdl/sdl_glimp.c | 2 + 3 files changed, 231 insertions(+), 47 deletions(-) diff --git a/reaction/code/renderer/tr_fbo.c b/reaction/code/renderer/tr_fbo.c index 212654a9..a659cb01 100644 --- a/reaction/code/renderer/tr_fbo.c +++ b/reaction/code/renderer/tr_fbo.c @@ -4,22 +4,60 @@ # define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) #endif +#define MAX_FBO_COLOR_BUFFERS 8 + +/* +=============== +Render buffer +Can be either a zbuffer, a stencil buffer or a multisampled color buffer +=============== +*/ + typedef struct fboRenderBuffer_s { GLuint id; int internalFormat; int width; int height; + qboolean msaa; } fboRenderBuffer_t; -#define MAX_FBO_COLOR_BUFFERS 8 +typedef fboRenderBuffer_t fboZBuffer_t; +typedef fboRenderBuffer_t fboStencilBuffer_t; + +/* +=============== +Color buffer + +If anti-aliased we'll have a texture, a render buffer +and two frame buffers needed for the MSAA resolve. + +Otherwise, only the texture is initialized. +=============== +*/ + +typedef struct fboColorBuffer_s { + fboRenderBuffer_t *buf; + image_t *tex; + GLuint fboResolve[2]; + qboolean dirty; +} fboColorBuffer_t; + +/* +=============== +Frame buffer + +Has one or more color buffers, an optional zbuffer +and an optional stencil buffer +=============== +*/ typedef struct fbo_s { GLuint id; int numColorBufs; - image_t* color[MAX_FBO_COLOR_BUFFERS]; + fboColorBuffer_t* color[MAX_FBO_COLOR_BUFFERS]; fboZBuffer_t* depth; fboStencilBuffer_t* stencil; @@ -32,7 +70,14 @@ void R_FBO_InitRenderBuffer(fboRenderBuffer_t* buf) buf->id = 0; qglGenRenderbuffersEXT(1, &buf->id); qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, buf->id); - qglRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, buf->internalFormat, buf->width, buf->height); + if (buf->msaa) + { + qglRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, tr.fbo.maxSamples, buf->internalFormat, buf->width, buf->height); + } + else + { + qglRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, buf->internalFormat, buf->width, buf->height); + } qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); } @@ -45,7 +90,7 @@ void R_FBO_ReleaseRenderBuffer(fboRenderBuffer_t* buf) buf->id = 0; } -fboRenderBuffer_t* R_FBO_CreateRenderBuffer(int width, int height, int pixelformat) +fboRenderBuffer_t *R_FBO_CreateRenderBuffer(int width, int height, int pixelformat, qboolean msaa) { fboRenderBuffer_t* ret = NULL; if (tr.fbo.numRenderBuffers >= ARRAY_SIZE(tr.fbo.renderBuffers)) @@ -60,6 +105,7 @@ fboRenderBuffer_t* R_FBO_CreateRenderBuffer(int width, int height, int pixelform ret->width = width; ret->height = height; ret->internalFormat = pixelformat; + ret->msaa = (msaa && tr.fbo.maxSamples > 1); R_FBO_InitRenderBuffer(ret); @@ -68,38 +114,6 @@ fboRenderBuffer_t* R_FBO_CreateRenderBuffer(int width, int height, int pixelform return ret; } -fboZBuffer_t* R_FBO_CreateZBuffer(int width, int height) -{ - return R_FBO_CreateRenderBuffer(width, height, GL_DEPTH_COMPONENT24); -} - -void R_FBO_Init(fbo_t* fbo) -{ - memset(fbo, 0, sizeof(*fbo)); - qglGenFramebuffersEXT(1, &fbo->id); -} - -void R_FBO_Release(fbo_t* fbo) -{ - if (fbo->id == 0) - return; - - qglDeleteFramebuffersEXT(1, &fbo->id); - fbo->id = 0; -} - -void R_FBO_AddColorBuffer(fbo_t* fbo, image_t* image) -{ - if (fbo->numColorBufs >= ARRAY_SIZE(fbo->color)) - { - ri.Error(ERR_FATAL, "Max FBO color buffers exceeded.\n"); - return; - } - fbo->color[fbo->numColorBufs++] = image; - qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo->id); - qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, (GLenum)(GL_COLOR_ATTACHMENT0_EXT + fbo->numColorBufs - 1), GL_TEXTURE_2D, image->texnum, 0); -} - qboolean R_FBO_Check() { GLenum status = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); @@ -137,6 +151,10 @@ qboolean R_FBO_Check() case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: msg = "incomplete read buffer"; break; + + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT: + msg = "mismatched multisample settings"; + break; case GL_FRAMEBUFFER_UNSUPPORTED_EXT: msg = "unsupported"; @@ -147,11 +165,106 @@ qboolean R_FBO_Check() break; } - ri.Error(ERR_FATAL, "Bad framebuffer setup for '%s': %s\n", glState.currentFBO->name, msg); + ri.Error(ERR_FATAL, "Framebuffer setup error: %s\n", msg); return qfalse; } +fboColorBuffer_t *R_FBO_CreateColorBuffer(const char* name, int width, int height, qboolean msaa, qboolean mipmap, int clamp) +{ + if (tr.fbo.numColorBuffers>= ARRAY_SIZE(tr.fbo.colorBuffers)) + { + ri.Error(ERR_FATAL, "Too many FBO color buffers\n"); + return NULL; + } + else + { + qboolean realmsaa = msaa && tr.fbo.maxSamples > 1; + image_t *texture = R_CreateImage(name, NULL, width, height, mipmap, qfalse, clamp); + fboRenderBuffer_t *buf = realmsaa ? R_FBO_CreateRenderBuffer(width, height, texture->internalFormat, qtrue) : NULL; + fboColorBuffer_t *ret = ri.Hunk_Alloc(sizeof(*ret), h_low); + + Com_Memset(ret, 0, sizeof(*ret)); + + ret->buf = buf; + ret->tex = texture; + ret->dirty = qfalse; + + if (realmsaa) + { + qglGenFramebuffersEXT(2, ret->fboResolve); + + qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, ret->fboResolve[0]); + qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, ret->buf->id); + R_FBO_Check(); + + qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, ret->fboResolve[1]); + qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, ret->tex->texnum, 0); + R_FBO_Check(); + + qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + } + else + { + ret->fboResolve[0] = ret->fboResolve[1] = 0; + } + + tr.fbo.colorBuffers[tr.fbo.numColorBuffers++] = ret; + + return ret; + } +} + +void R_FBO_ReleaseColorBuffer(fboColorBuffer_t* color) +{ + if (color->buf) + { + qglDeleteFramebuffersEXT(2, color->fboResolve); + } +} + +fboZBuffer_t *R_FBO_CreateZBuffer(int width, int height, qboolean msaa) +{ + return R_FBO_CreateRenderBuffer(width, height, GL_DEPTH_COMPONENT24, msaa); +} + +void R_FBO_Init(fbo_t* fbo) +{ + memset(fbo, 0, sizeof(*fbo)); + qglGenFramebuffersEXT(1, &fbo->id); +} + +void R_FBO_Release(fbo_t* fbo) +{ + if (fbo->id == 0) + return; + + qglDeleteFramebuffersEXT(1, &fbo->id); + fbo->id = 0; +} + +void R_FBO_AddColorBuffer(fbo_t* fbo, fboColorBuffer_t* color) +{ + GLenum attach; + if (fbo->numColorBufs >= ARRAY_SIZE(fbo->color)) + { + ri.Error(ERR_FATAL, "Max FBO color buffers exceeded.\n"); + return; + } + fbo->color[fbo->numColorBufs++] = color; + + qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo->id); + attach = GL_COLOR_ATTACHMENT0_EXT + fbo->numColorBufs - 1; + if (color->buf) + { + qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, attach, GL_RENDERBUFFER_EXT, color->buf->id); + } + else + { + qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attach, GL_TEXTURE_2D, color->tex->texnum, 0); + } +} + fbo_t* R_FBO_Bind(fbo_t* fbo) { fbo_t* old = glState.currentFBO; @@ -170,6 +283,13 @@ fbo_t* R_FBO_Bind(fbo_t* fbo) if (fbo) { + int i; + for (i=0; inumColorBufs; ++i) + { + fboColorBuffer_t* color = fbo->color[i]; + color->dirty = (color->buf != NULL); + } + if (1) R_FBO_Check(); } @@ -179,10 +299,35 @@ fbo_t* R_FBO_Bind(fbo_t* fbo) void R_FBO_BindColorBuffer(fbo_t* fbo, int index) { - GL_Bind(fbo->color[index]); + fboColorBuffer_t* color = fbo->color[index]; + if (color->dirty) + { + color->dirty = qfalse; + + // resolve + if (color->buf) + { + int id; + int width = color->buf->width; + int height = color->buf->height; + + qglBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, color->fboResolve[0]); + qglBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, color->fboResolve[1]); + qglBlitFramebufferEXT(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_LINEAR); + + if (glState.currentFBO) + id = glState.currentFBO->id; + else + id = 0; + + qglBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, id); + qglBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, id); + } + } + GL_Bind(color->tex); } -fbo_t* R_FBO_CreateEx(const char* name, int numColorBuffers, image_t** colorBuffers, fboZBuffer_t* depth, fboStencilBuffer_t* stencil) +fbo_t* R_FBO_CreateEx(const char* name, int numColorBuffers, fboColorBuffer_t** colorBuffers, fboZBuffer_t* depth, fboStencilBuffer_t* stencil) { fbo_t* fbo = NULL; @@ -221,45 +366,75 @@ fbo_t* R_FBO_CreateEx(const char* name, int numColorBuffers, image_t** colorBuff return fbo; } -fbo_t* R_FBO_CreateSimple(const char* name, image_t* color, fboZBuffer_t* depth, fboStencilBuffer_t* stencil) +fbo_t* R_FBO_CreateSimple(const char* name, fboColorBuffer_t* color, fboZBuffer_t* depth, fboStencilBuffer_t* stencil) { return R_FBO_CreateEx(name, color != NULL, &color, depth, stencil); } +/* +=============== +R_InitFBOs +=============== +*/ + void R_InitFBOs(void) { + qboolean msaa; if (!glRefConfig.framebufferObject) return; ri.Printf(PRINT_ALL, "------- R_InitFBOs -------\n"); + if (glRefConfig.framebufferMultisample && glRefConfig.framebufferBlit) + { + GLint maxSupported = 0; + qglGetIntegerv(GL_MAX_SAMPLES_EXT, &maxSupported); + tr.fbo.maxSamples = maxSupported < r_ext_multisample->integer ? maxSupported : r_ext_multisample->integer; + if (tr.fbo.maxSamples < 2) + tr.fbo.maxSamples = 0; + } + else + { + tr.fbo.maxSamples = 0; + } + + Com_Printf("Samples: %d\n", tr.fbo.maxSamples); + msaa = tr.fbo.maxSamples > 1; + tr.fbo.full[0] = R_FBO_CreateSimple( "main", - R_CreateImage("*framebuffer", NULL, glConfig.vidWidth, glConfig.vidHeight, qfalse, qfalse, GL_CLAMP_TO_EDGE), - R_FBO_CreateZBuffer(glConfig.vidWidth, glConfig.vidHeight), + R_FBO_CreateColorBuffer("*framebuffer0", glConfig.vidWidth, glConfig.vidHeight, msaa, qfalse, GL_CLAMP_TO_EDGE), + R_FBO_CreateZBuffer(glConfig.vidWidth, glConfig.vidHeight, msaa), NULL); tr.fbo.full[1] = R_FBO_CreateSimple( "postprocess", - R_CreateImage("*framebuffer", NULL, glConfig.vidWidth, glConfig.vidHeight, qfalse, qfalse, GL_CLAMP_TO_EDGE), + R_FBO_CreateColorBuffer("*framebuffer1", glConfig.vidWidth, glConfig.vidHeight, msaa, qfalse, GL_CLAMP_TO_EDGE), tr.fbo.full[0]->depth, NULL); tr.fbo.quarter[0] = R_FBO_CreateSimple( "quarter0", - R_CreateImage("*quarterBuffer0", NULL, glConfig.vidWidth/2, glConfig.vidHeight/2, qfalse, qfalse, GL_CLAMP_TO_EDGE), + R_FBO_CreateColorBuffer("*quarterBuffer0", glConfig.vidWidth/2, glConfig.vidHeight/2, qfalse, qfalse, GL_CLAMP_TO_EDGE), NULL, NULL); tr.fbo.quarter[1] = R_FBO_CreateSimple( "quarter1", - R_CreateImage("*quarterBuffer1", NULL, glConfig.vidWidth/2, glConfig.vidHeight/2, qfalse, qfalse, GL_CLAMP_TO_EDGE), + R_FBO_CreateColorBuffer("*quarterBuffer1", glConfig.vidWidth/2, glConfig.vidHeight/2, qfalse, qfalse, GL_CLAMP_TO_EDGE), NULL, NULL); ri.Printf(PRINT_DEVELOPER, "Created %d FBOs\n", tr.fbo.numFBOs); } + +/* +=============== +R_ShutDownFBOs +=============== +*/ + void R_ShutDownFBOs(void) { if (!glRefConfig.framebufferObject) @@ -272,6 +447,9 @@ void R_ShutDownFBOs(void) while (tr.fbo.numRenderBuffers>0) R_FBO_ReleaseRenderBuffer(tr.fbo.renderBuffers[--tr.fbo.numRenderBuffers]); + while (tr.fbo.numColorBuffers>0) + R_FBO_ReleaseColorBuffer(tr.fbo.colorBuffers[--tr.fbo.numColorBuffers]); + while (tr.fbo.numFBOs>0) R_FBO_Release(tr.fbo.fbos[--tr.fbo.numFBOs]); } diff --git a/reaction/code/renderer/tr_local.h b/reaction/code/renderer/tr_local.h index 4f984a17..821b9079 100644 --- a/reaction/code/renderer/tr_local.h +++ b/reaction/code/renderer/tr_local.h @@ -153,8 +153,7 @@ typedef struct IBO_s } IBO_t; typedef struct fboRenderBuffer_s fboRenderBuffer_t; -typedef fboRenderBuffer_t fboZBuffer_t; -typedef fboRenderBuffer_t fboStencilBuffer_t; +typedef struct fboColorBuffer_s fboColorBuffer_t; typedef struct fbo_s fbo_t; fbo_t *R_FBO_Bind(fbo_t* fbo); @@ -2224,6 +2223,8 @@ typedef struct { } backEndState_t; typedef struct { + int maxSamples; + fbo_t *full[2]; // full resolution, shared zbuffer fbo_t *quarter[2]; // quarter resolution, no zbuffer @@ -2232,6 +2233,9 @@ typedef struct { int numRenderBuffers; fboRenderBuffer_t *renderBuffers[1024]; + + int numColorBuffers; + fboColorBuffer_t *colorBuffers[1024]; } fboState_t; /* diff --git a/reaction/code/sdl/sdl_glimp.c b/reaction/code/sdl/sdl_glimp.c index 1cdbc9f8..7719743f 100644 --- a/reaction/code/sdl/sdl_glimp.c +++ b/reaction/code/sdl/sdl_glimp.c @@ -425,6 +425,8 @@ static int GLimp_SetMode(int mode, qboolean fullscreen, qboolean noborder) depthbits = r_depthbits->value; stencilbits = r_stencilbits->value; samples = r_ext_multisample->value; + if (r_ext_framebuffer_object->integer) + samples = 0; for (i = 0; i < 16; i++) {