void FBO_CreateBuffer(FBO_t *fbo, int format, int index, int multisample) { uint32_t *pRenderBuffer; GLenum attachment; qboolean absent; switch(format) { case GL_RGB: case GL_RGBA: case GL_RGB8: case GL_RGBA8: case GL_RGB16F_ARB: case GL_RGBA16F_ARB: case GL_RGB32F_ARB: case GL_RGBA32F_ARB: fbo->colorFormat = format; pRenderBuffer = &fbo->colorBuffers[index]; attachment = GL_COLOR_ATTACHMENT0_EXT + index; break; case GL_DEPTH_COMPONENT: case GL_DEPTH_COMPONENT16_ARB: case GL_DEPTH_COMPONENT24_ARB: case GL_DEPTH_COMPONENT32_ARB: fbo->depthFormat = format; pRenderBuffer = &fbo->depthBuffer; attachment = GL_DEPTH_ATTACHMENT_EXT; break; case GL_STENCIL_INDEX: case GL_STENCIL_INDEX1_EXT: case GL_STENCIL_INDEX4_EXT: case GL_STENCIL_INDEX8_EXT: case GL_STENCIL_INDEX16_EXT: fbo->stencilFormat = format; pRenderBuffer = &fbo->stencilBuffer; attachment = GL_STENCIL_ATTACHMENT_EXT; break; case GL_DEPTH_STENCIL_EXT: case GL_DEPTH24_STENCIL8_EXT: fbo->packedDepthStencilFormat = format; pRenderBuffer = &fbo->packedDepthStencilBuffer; attachment = 0; // special for stencil and depth break; default: ri.Printf(PRINT_WARNING, "FBO_CreateBuffer: invalid format %d\n", format); return; } absent = *pRenderBuffer == 0; if (absent) qglGenRenderbuffersEXT(1, pRenderBuffer); qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, *pRenderBuffer); if (multisample && glRefConfig.framebufferMultisample) { qglRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, multisample, format, fbo->width, fbo->height); } else { qglRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, format, fbo->width, fbo->height); } if(absent) { if (attachment == 0) { qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, *pRenderBuffer); qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, *pRenderBuffer); } else qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, attachment, GL_RENDERBUFFER_EXT, *pRenderBuffer); } }
/* * RFB_RegisterObject */ int RFB_RegisterObject( int width, int height, bool builtin, bool depthRB, bool stencilRB, bool colorRB, int samples, bool useFloat, bool sRGB ) { int i; int format; GLuint fbID; GLuint rbID = 0; r_fbo_t *fbo = NULL; if( !r_frambuffer_objects_initialized ) { return 0; } #ifdef GL_ES_VERSION_2_0 if( samples ) { return 0; } #else if( samples && !glConfig.ext.framebuffer_multisample ) { return 0; } #endif for( i = 0, fbo = r_framebuffer_objects; i < r_num_framebuffer_objects; i++, fbo++ ) { if( !fbo->objectID ) { // free slot goto found; } } if( i == MAX_FRAMEBUFFER_OBJECTS ) { Com_Printf( S_COLOR_YELLOW "RFB_RegisterObject: framebuffer objects limit exceeded\n" ); return 0; } clamp_high( samples, glConfig.maxFramebufferSamples ); i = r_num_framebuffer_objects++; fbo = r_framebuffer_objects + i; found: qglGenFramebuffersEXT( 1, &fbID ); memset( fbo, 0, sizeof( *fbo ) ); fbo->objectID = fbID; if( builtin ) { fbo->registrationSequence = -1; } else { fbo->registrationSequence = rsh.registrationSequence; } fbo->width = width; fbo->height = height; fbo->samples = samples; fbo->sRGB = sRGB; qglBindFramebufferEXT( GL_FRAMEBUFFER_EXT, fbo->objectID ); if( colorRB ) { format = glConfig.forceRGBAFramebuffers ? GL_RGBA : GL_RGB; if( useFloat ) { format = glConfig.forceRGBAFramebuffers ? GL_RGBA16F_ARB : GL_RGB16F_ARB; } qglGenRenderbuffersEXT( 1, &rbID ); fbo->colorRenderBuffer = rbID; qglBindRenderbufferEXT( GL_RENDERBUFFER_EXT, rbID ); #ifndef GL_ES_VERSION_2_0 if( samples ) { qglRenderbufferStorageMultisampleEXT( GL_RENDERBUFFER_EXT, samples, format, width, height ); } else #endif qglRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, format, width, height ); } #ifndef GL_ES_VERSION_2_0 else { // until a color texture is attached, don't enable drawing to the buffer qglDrawBuffer( GL_NONE ); qglReadBuffer( GL_NONE ); } #endif if( depthRB ) { qglGenRenderbuffersEXT( 1, &rbID ); fbo->depthRenderBuffer = rbID; qglBindRenderbufferEXT( GL_RENDERBUFFER_EXT, rbID ); if( stencilRB ) { format = GL_DEPTH24_STENCIL8_EXT; } else if( glConfig.ext.depth24 ) { format = GL_DEPTH_COMPONENT24; } else if( glConfig.ext.depth_nonlinear ) { format = GL_DEPTH_COMPONENT16_NONLINEAR_NV; } else { format = GL_DEPTH_COMPONENT16; } #ifndef GL_ES_VERSION_2_0 if( samples ) { qglRenderbufferStorageMultisampleEXT( GL_RENDERBUFFER_EXT, samples, format, width, height ); } else #endif qglRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, format, width, height ); if( stencilRB ) { fbo->stencilRenderBuffer = rbID; } } if( rbID ) { qglBindRenderbufferEXT( GL_RENDERBUFFER_EXT, 0 ); } if( fbo->colorRenderBuffer ) { qglFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, fbo->colorRenderBuffer ); } if( fbo->depthRenderBuffer ) { qglFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbo->depthRenderBuffer ); } if( fbo->stencilRenderBuffer ) { qglFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbo->stencilRenderBuffer ); } if( colorRB && depthRB ) { if( !RFB_CheckObjectStatus() ) { goto fail; } } if( r_bound_framebuffer_objectID ) { qglBindFramebufferEXT( GL_FRAMEBUFFER_EXT, r_bound_framebuffer_object->objectID ); } else { qglBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); } return i + 1; fail: RFB_DeleteObject( fbo ); qglBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); return 0; }
/** * @brief create a new framebuffer object * @param[in] width The width of the framebuffer * @param[in] height The height of the framebuffer * @param[in] ntextures The amount of textures for this framebuffer. See also the filters array. * @param[in] depth Also generate a depth buffer * @param[in] halfFloat Use half float pixel format * @param[in] filters Filters for the textures. Must have @c ntextures entries */ r_framebuffer_t * R_CreateFramebuffer (int width, int height, int ntextures, bool depth, bool halfFloat, unsigned int *filters) { r_framebuffer_t *buf; int i; if (!r_state.frameBufferObjectsInitialized) { Com_Printf("Warning: framebuffer creation failed; framebuffers not initialized!\n"); return NULL; } if (frameBufferObjectCount >= lengthof(frameBufferObjects)) { Com_Printf("Warning: framebuffer creation failed; already created too many framebuffers!\n"); return NULL; } buf = &frameBufferObjects[frameBufferObjectCount++]; OBJZERO(*buf); if (ntextures > r_config.maxDrawBuffers) { Com_Printf("Couldn't allocate requested number of drawBuffers in R_SetupFramebuffer!\n"); ntextures = r_config.maxDrawBuffers; } Vector4Clear(buf->clearColor); buf->width = width; buf->height = height; R_SetupViewport(buf, 0, 0, width, height); buf->nTextures = ntextures; buf->textures = Mem_AllocTypeN(unsigned int, ntextures); buf->pixelFormat = halfFloat ? GL_RGBA16F_ARB : GL_RGBA8; buf->byteFormat = halfFloat ? GL_HALF_FLOAT_ARB : GL_UNSIGNED_BYTE; /* Presence of depth buffer indicates render target that could use antialiasing*/ if (depth) { /** @todo also check if we are running on older (SM2.0) hardware, which doesn't support antialiased MRT */ if (qglRenderbufferStorageMultisampleEXT && qglBlitFramebuffer) { int samples = std::min(4, std::max(0, r_multisample->integer)); if (samples>1) buf->samples = samples; } } for (i = 0; i < buf->nTextures; i++) { buf->textures[i] = R_GetFreeFBOTexture(); glBindTexture(GL_TEXTURE_2D, buf->textures[i]); glTexImage2D(GL_TEXTURE_2D, 0, buf->pixelFormat, buf->width, buf->height, 0, GL_RGBA, buf->byteFormat, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filters[i]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); qglGenerateMipmapEXT(GL_TEXTURE_2D); if (r_config.anisotropic) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, r_config.maxAnisotropic); R_CheckError(); } glBindTexture(GL_TEXTURE_2D, 0); /* create FBO itself */ qglGenFramebuffersEXT(1, &buf->fbo); R_CheckError(); qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, buf->fbo); /* create&attach depth renderbuffer */ if (depth) { qglGenRenderbuffersEXT(1, &buf->depth); qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, buf->depth); if (buf->samples) qglRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, buf->samples, GL_DEPTH_COMPONENT, buf->width, buf->height); else qglRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, buf->width, buf->height); qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, buf->depth); } else { buf->depth = 0; } /* create multisample color buffers if needed */ if (buf->samples) { /* generate color buffers */ for (i = 0; i < buf->nTextures; i++) { unsigned colorbuffer; qglGenRenderbuffersEXT(1, &colorbuffer); qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, colorbuffer); qglRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, buf->samples, buf->pixelFormat, buf->width, buf->height); qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, colorAttachments[i], GL_RENDERBUFFER_EXT, colorbuffer); R_CheckError(); } /* proxy framebuffer object for resolving MSAA */ qglGenFramebuffersEXT(1, &buf->proxyFBO); R_CheckError(); qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, buf->proxyFBO); } /* Whether multisampling was enabled or not, current FBO should be populated with render-to-texture bindings */ for (i = 0; i < buf->nTextures; i++) { qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, colorAttachments[i], GL_TEXTURE_2D, buf->textures[i], 0); R_CheckError(); } R_CheckError(); /* unbind the framebuffer and return to default state */ qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); return buf; }
/* * RFB_RegisterObject */ int RFB_RegisterObject( int width, int height, bool builtin, bool depthRB, bool stencilRB ) { int i; GLuint fbID; GLuint rbID; r_fbo_t *fbo; if( !r_frambuffer_objects_initialized ) return 0; for( i = 0, fbo = r_framebuffer_objects; i < r_num_framebuffer_objects; i++, fbo++ ) { if( !fbo->objectID ) { // free slot goto found; } } if( i == MAX_FRAMEBUFFER_OBJECTS ) { Com_Printf( S_COLOR_YELLOW "RFB_RegisterObject: framebuffer objects limit exceeded\n" ); return 0; } i = r_num_framebuffer_objects++; fbo = r_framebuffer_objects + i; found: qglGenFramebuffersEXT( 1, &fbID ); memset( fbo, 0, sizeof( *fbo ) ); fbo->objectID = fbID; if( builtin ) fbo->registrationSequence = -1; else fbo->registrationSequence = rsh.registrationSequence; fbo->width = width; fbo->height = height; qglBindFramebufferEXT( GL_FRAMEBUFFER_EXT, fbo->objectID ); #ifndef GL_ES_VERSION_2_0 // until a color texture is attached, don't enable drawing to the buffer qglDrawBuffer( GL_NONE ); qglReadBuffer( GL_NONE ); #endif if( depthRB ) { int format; qglGenRenderbuffersEXT( 1, &rbID ); fbo->depthRenderBuffer = rbID; qglBindRenderbufferEXT( GL_RENDERBUFFER_EXT, rbID ); if( stencilRB ) format = GL_DEPTH24_STENCIL8_EXT; else if( glConfig.ext.depth24 ) format = GL_DEPTH_COMPONENT24; else if( glConfig.ext.depth_nonlinear ) format = GL_DEPTH_COMPONENT16_NONLINEAR_NV; else format = GL_DEPTH_COMPONENT16; qglRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, format, width, height ); qglFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, rbID ); if( stencilRB ) qglFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, rbID ); qglBindRenderbufferEXT( GL_RENDERBUFFER_EXT, 0 ); } if( r_bound_framebuffer_objectID ) qglBindFramebufferEXT( GL_FRAMEBUFFER_EXT, r_bound_framebuffer_object->objectID ); else qglBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); return i+1; }