Multisampling

This commit is contained in:
Andrei Drexler 2011-03-15 03:10:20 +00:00
parent 14ffe063ea
commit 36bf6c2d9e
3 changed files with 231 additions and 47 deletions

View file

@ -4,22 +4,60 @@
# define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) # define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
#endif #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 { typedef struct fboRenderBuffer_s {
GLuint id; GLuint id;
int internalFormat; int internalFormat;
int width; int width;
int height; int height;
qboolean msaa;
} fboRenderBuffer_t; } 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 { typedef struct fbo_s {
GLuint id; GLuint id;
int numColorBufs; int numColorBufs;
image_t* color[MAX_FBO_COLOR_BUFFERS]; fboColorBuffer_t* color[MAX_FBO_COLOR_BUFFERS];
fboZBuffer_t* depth; fboZBuffer_t* depth;
fboStencilBuffer_t* stencil; fboStencilBuffer_t* stencil;
@ -32,7 +70,14 @@ void R_FBO_InitRenderBuffer(fboRenderBuffer_t* buf)
buf->id = 0; buf->id = 0;
qglGenRenderbuffersEXT(1, &buf->id); qglGenRenderbuffersEXT(1, &buf->id);
qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 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); qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
} }
@ -45,7 +90,7 @@ void R_FBO_ReleaseRenderBuffer(fboRenderBuffer_t* buf)
buf->id = 0; 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; fboRenderBuffer_t* ret = NULL;
if (tr.fbo.numRenderBuffers >= ARRAY_SIZE(tr.fbo.renderBuffers)) 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->width = width;
ret->height = height; ret->height = height;
ret->internalFormat = pixelformat; ret->internalFormat = pixelformat;
ret->msaa = (msaa && tr.fbo.maxSamples > 1);
R_FBO_InitRenderBuffer(ret); R_FBO_InitRenderBuffer(ret);
@ -68,38 +114,6 @@ fboRenderBuffer_t* R_FBO_CreateRenderBuffer(int width, int height, int pixelform
return ret; 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() qboolean R_FBO_Check()
{ {
GLenum status = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); GLenum status = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
@ -138,6 +152,10 @@ qboolean R_FBO_Check()
msg = "incomplete read buffer"; msg = "incomplete read buffer";
break; break;
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT:
msg = "mismatched multisample settings";
break;
case GL_FRAMEBUFFER_UNSUPPORTED_EXT: case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
msg = "unsupported"; msg = "unsupported";
break; break;
@ -147,11 +165,106 @@ qboolean R_FBO_Check()
break; 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; 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* R_FBO_Bind(fbo_t* fbo)
{ {
fbo_t* old = glState.currentFBO; fbo_t* old = glState.currentFBO;
@ -170,6 +283,13 @@ fbo_t* R_FBO_Bind(fbo_t* fbo)
if (fbo) if (fbo)
{ {
int i;
for (i=0; i<fbo->numColorBufs; ++i)
{
fboColorBuffer_t* color = fbo->color[i];
color->dirty = (color->buf != NULL);
}
if (1) if (1)
R_FBO_Check(); R_FBO_Check();
} }
@ -179,10 +299,35 @@ fbo_t* R_FBO_Bind(fbo_t* fbo)
void R_FBO_BindColorBuffer(fbo_t* fbo, int index) 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; fbo_t* fbo = NULL;
@ -221,45 +366,75 @@ fbo_t* R_FBO_CreateEx(const char* name, int numColorBuffers, image_t** colorBuff
return fbo; 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); return R_FBO_CreateEx(name, color != NULL, &color, depth, stencil);
} }
/*
===============
R_InitFBOs
===============
*/
void R_InitFBOs(void) void R_InitFBOs(void)
{ {
qboolean msaa;
if (!glRefConfig.framebufferObject) if (!glRefConfig.framebufferObject)
return; return;
ri.Printf(PRINT_ALL, "------- R_InitFBOs -------\n"); 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( tr.fbo.full[0] = R_FBO_CreateSimple(
"main", "main",
R_CreateImage("*framebuffer", NULL, glConfig.vidWidth, glConfig.vidHeight, qfalse, qfalse, GL_CLAMP_TO_EDGE), R_FBO_CreateColorBuffer("*framebuffer0", glConfig.vidWidth, glConfig.vidHeight, msaa, qfalse, GL_CLAMP_TO_EDGE),
R_FBO_CreateZBuffer(glConfig.vidWidth, glConfig.vidHeight), R_FBO_CreateZBuffer(glConfig.vidWidth, glConfig.vidHeight, msaa),
NULL); NULL);
tr.fbo.full[1] = R_FBO_CreateSimple( tr.fbo.full[1] = R_FBO_CreateSimple(
"postprocess", "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, tr.fbo.full[0]->depth,
NULL); NULL);
tr.fbo.quarter[0] = R_FBO_CreateSimple( tr.fbo.quarter[0] = R_FBO_CreateSimple(
"quarter0", "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,
NULL); NULL);
tr.fbo.quarter[1] = R_FBO_CreateSimple( tr.fbo.quarter[1] = R_FBO_CreateSimple(
"quarter1", "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,
NULL); NULL);
ri.Printf(PRINT_DEVELOPER, "Created %d FBOs\n", tr.fbo.numFBOs); ri.Printf(PRINT_DEVELOPER, "Created %d FBOs\n", tr.fbo.numFBOs);
} }
/*
===============
R_ShutDownFBOs
===============
*/
void R_ShutDownFBOs(void) void R_ShutDownFBOs(void)
{ {
if (!glRefConfig.framebufferObject) if (!glRefConfig.framebufferObject)
@ -272,6 +447,9 @@ void R_ShutDownFBOs(void)
while (tr.fbo.numRenderBuffers>0) while (tr.fbo.numRenderBuffers>0)
R_FBO_ReleaseRenderBuffer(tr.fbo.renderBuffers[--tr.fbo.numRenderBuffers]); 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) while (tr.fbo.numFBOs>0)
R_FBO_Release(tr.fbo.fbos[--tr.fbo.numFBOs]); R_FBO_Release(tr.fbo.fbos[--tr.fbo.numFBOs]);
} }

View file

@ -153,8 +153,7 @@ typedef struct IBO_s
} IBO_t; } IBO_t;
typedef struct fboRenderBuffer_s fboRenderBuffer_t; typedef struct fboRenderBuffer_s fboRenderBuffer_t;
typedef fboRenderBuffer_t fboZBuffer_t; typedef struct fboColorBuffer_s fboColorBuffer_t;
typedef fboRenderBuffer_t fboStencilBuffer_t;
typedef struct fbo_s fbo_t; typedef struct fbo_s fbo_t;
fbo_t *R_FBO_Bind(fbo_t* fbo); fbo_t *R_FBO_Bind(fbo_t* fbo);
@ -2224,6 +2223,8 @@ typedef struct {
} backEndState_t; } backEndState_t;
typedef struct { typedef struct {
int maxSamples;
fbo_t *full[2]; // full resolution, shared zbuffer fbo_t *full[2]; // full resolution, shared zbuffer
fbo_t *quarter[2]; // quarter resolution, no zbuffer fbo_t *quarter[2]; // quarter resolution, no zbuffer
@ -2232,6 +2233,9 @@ typedef struct {
int numRenderBuffers; int numRenderBuffers;
fboRenderBuffer_t *renderBuffers[1024]; fboRenderBuffer_t *renderBuffers[1024];
int numColorBuffers;
fboColorBuffer_t *colorBuffers[1024];
} fboState_t; } fboState_t;
/* /*

View file

@ -425,6 +425,8 @@ static int GLimp_SetMode(int mode, qboolean fullscreen, qboolean noborder)
depthbits = r_depthbits->value; depthbits = r_depthbits->value;
stencilbits = r_stencilbits->value; stencilbits = r_stencilbits->value;
samples = r_ext_multisample->value; samples = r_ext_multisample->value;
if (r_ext_framebuffer_object->integer)
samples = 0;
for (i = 0; i < 16; i++) for (i = 0; i < 16; i++)
{ {