filename | src/drivers/gl_fbo.c |
changeset | 352:f0df7a6d4703 |
next | 424:421d68e78c46 |
author | nkeynes |
date | Sun Feb 11 10:09:32 2007 +0000 (13 years ago) |
permissions | -rw-r--r-- |
last change | Bug 27: Implement opengl framebuffer objects Rewrite much of the final video output stage. Now uses generic "render buffers", implemented on GL using framebuffer objects + textures. |
file | annotate | diff | log | raw |
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +00001.2 +++ b/src/drivers/gl_fbo.c Sun Feb 11 10:09:32 2007 +00001.3 @@ -0,0 +1,299 @@1.4 +/**1.5 + * $Id: gl_fbo.c,v 1.1 2007-02-11 10:09:32 nkeynes Exp $1.6 + *1.7 + * GL framebuffer-based driver shell. This requires the EXT_framebuffer_object1.8 + * extension, but is much nicer/faster/etc than pbuffers when it's available.1.9 + * This is (optionally) used indirectly by the top-level GLX driver.1.10 + *1.11 + * Strategy-wise, we maintain 2 framebuffers with up to 4 target colour1.12 + * buffers a piece. Effectively this reserves one fb for display output,1.13 + * and the other for texture output (each fb has a single size).1.14 + *1.15 + * Copyright (c) 2005 Nathan Keynes.1.16 + *1.17 + * This program is free software; you can redistribute it and/or modify1.18 + * it under the terms of the GNU General Public License as published by1.19 + * the Free Software Foundation; either version 2 of the License, or1.20 + * (at your option) any later version.1.21 + *1.22 + * This program is distributed in the hope that it will be useful,1.23 + * but WITHOUT ANY WARRANTY; without even the implied warranty of1.24 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the1.25 + * GNU General Public License for more details.1.26 + */1.27 +1.28 +#include <GL/gl.h>1.29 +#include "display.h"1.30 +1.31 +#define MAX_FRAMEBUFFERS 21.32 +#define MAX_TEXTURES_PER_FB 41.33 +1.34 +static render_buffer_t gl_fbo_create_render_buffer( uint32_t width, uint32_t height );1.35 +static void gl_fbo_destroy_render_buffer( render_buffer_t buffer );1.36 +static gboolean gl_fbo_set_render_target( render_buffer_t buffer );1.37 +static gboolean gl_fbo_display_render_buffer( render_buffer_t buffer );1.38 +static gboolean gl_fbo_display_frame_buffer( frame_buffer_t buffer );1.39 +static gboolean gl_fbo_display_blank( uint32_t colour );1.40 +static gboolean gl_fbo_read_render_buffer( render_buffer_t buffer, char *target );1.41 +1.42 +extern uint32_t video_width, video_height;1.43 +1.44 +/**1.45 + * Framebuffer info structure1.46 + */1.47 +struct gl_fbo_info {1.48 + GLuint fb_id;1.49 + GLuint depth_id;1.50 + GLuint stencil_id;1.51 + GLuint tex_ids[MAX_TEXTURES_PER_FB];1.52 + int width, height;1.53 +};1.54 +1.55 +static struct gl_fbo_info fbo[MAX_FRAMEBUFFERS];1.56 +const static int ATTACHMENT_POINTS[MAX_TEXTURES_PER_FB] = {1.57 + GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT,1.58 + GL_COLOR_ATTACHMENT2_EXT, GL_COLOR_ATTACHMENT3_EXT };1.59 +static int last_used_fbo;1.60 +1.61 +gboolean gl_fbo_is_supported()1.62 +{1.63 + return isGLExtensionSupported("GL_EXT_framebuffer_object");1.64 +}1.65 +1.66 +/**1.67 + * Construct the initial frame buffers and allocate ids for everything.1.68 + * The render handling driver methods are set to the fbo versions.1.69 + */1.70 +void gl_fbo_init( display_driver_t driver )1.71 +{1.72 + int i,j;1.73 + int fbids[MAX_FRAMEBUFFERS];1.74 + int rbids[MAX_FRAMEBUFFERS*2]; /* depth buffer, stencil buffer per fb */1.75 +1.76 + glGenFramebuffersEXT( MAX_FRAMEBUFFERS, &fbids );1.77 + glGenRenderbuffersEXT( MAX_FRAMEBUFFERS*2, &rbids );1.78 + for( i=0; i<MAX_FRAMEBUFFERS; i++ ) {1.79 + fbo[i].fb_id = fbids[i];1.80 + fbo[i].depth_id = rbids[i*2];1.81 + fbo[i].stencil_id = rbids[i*2+1];1.82 + fbo[i].width = -1;1.83 + fbo[i].height = -1;1.84 + for( j=0; j<MAX_TEXTURES_PER_FB; j++ ) {1.85 + fbo[i].tex_ids[j] = -1;1.86 + }1.87 + }1.88 + last_used_fbo = 0;1.89 +1.90 + driver->create_render_buffer = gl_fbo_create_render_buffer;1.91 + driver->destroy_render_buffer = gl_fbo_destroy_render_buffer;1.92 + driver->set_render_target = gl_fbo_set_render_target;1.93 + driver->display_render_buffer = gl_fbo_display_render_buffer;1.94 + driver->display_frame_buffer = gl_fbo_display_frame_buffer;1.95 + driver->display_blank = gl_fbo_display_blank;1.96 + driver->read_render_buffer = gl_fbo_read_render_buffer;1.97 +}1.98 +1.99 +void gl_fbo_shutdown()1.100 +{1.101 + int i;1.102 + glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );1.103 + for( i=0; i<MAX_FRAMEBUFFERS; i++ ) {1.104 + glDeleteFramebuffersEXT( 1, &fbo[i].fb_id );1.105 + glDeleteRenderbuffersEXT( 2, &fbo[i].depth_id );1.106 + }1.107 +}1.108 +1.109 +void gl_fbo_setup_framebuffer( int bufno, int width, int height )1.110 +{1.111 + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo[bufno].fb_id);1.112 + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fbo[bufno].depth_id);1.113 + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, width, height);1.114 + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,1.115 + GL_RENDERBUFFER_EXT, fbo[bufno].depth_id);1.116 + /* Stencil doesn't work on ATI, and we're not using it at the moment anyway, so...1.117 + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fbo[bufno].stencil_id);1.118 + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX, width, height);1.119 + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,1.120 + GL_RENDERBUFFER_EXT, fbo[bufno].stencil_id);1.121 + */1.122 + fbo[bufno].width = width;1.123 + fbo[bufno].height = height;1.124 +}1.125 +1.126 +int gl_fbo_get_framebuffer( int width, int height )1.127 +{1.128 + int bufno = -1, i;1.129 + /* find a compatible framebuffer context */1.130 + for( i=0; i<MAX_FRAMEBUFFERS; i++ ) {1.131 + if( fbo[i].width == -1 && bufno == -1 ) {1.132 + bufno = i;1.133 + } else if( fbo[i].width == width && fbo[i].height == height ) {1.134 + bufno = i;1.135 + break;1.136 + }1.137 + }1.138 + if( bufno == -1 ) {1.139 + bufno = last_used_fbo + 1;1.140 + if( bufno > MAX_FRAMEBUFFERS ) {1.141 + bufno = 0;1.142 + }1.143 + last_used_fbo = bufno;1.144 + }1.145 + if( fbo[bufno].width == width && fbo[bufno].height == height ) {1.146 + glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, fbo[bufno].fb_id );1.147 + } else {1.148 + gl_fbo_setup_framebuffer( bufno, width, height );1.149 + }1.150 + return bufno;1.151 +}1.152 +1.153 +/**1.154 + * Attach a texture to the framebuffer. The texture must already be initialized1.155 + * to the correct dimensions etc.1.156 + */1.157 +static GLint gl_fbo_attach_texture( int fbo_no, GLint tex_id ) {1.158 + int attach = -1, i;1.159 + for( i=0; i<MAX_TEXTURES_PER_FB; i++ ) {1.160 + if( fbo[fbo_no].tex_ids[i] == tex_id ) {1.161 + glDrawBuffer(ATTACHMENT_POINTS[i]);1.162 + glReadBuffer(ATTACHMENT_POINTS[i]);1.163 + return ATTACHMENT_POINTS[i]; // already attached1.164 + } else if( fbo[fbo_no].tex_ids[i] == -1 && attach == -1 ) {1.165 + attach = i;1.166 + }1.167 + }1.168 + if( attach == -1 ) {1.169 + /* should never happen */1.170 + attach = 0;1.171 + }1.172 + fbo[fbo_no].tex_ids[attach] = tex_id;1.173 + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, 0 ); // Ensure the output texture is unbound1.174 + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, ATTACHMENT_POINTS[attach],1.175 + GL_TEXTURE_RECTANGLE_ARB, tex_id, 0 );1.176 + /* Set draw/read buffers by default */1.177 + glDrawBuffer(ATTACHMENT_POINTS[attach]);1.178 + glReadBuffer(ATTACHMENT_POINTS[attach]);1.179 +1.180 +1.181 + GLint status = glGetError();1.182 + if( status != GL_NO_ERROR ) {1.183 + ERROR( "GL error setting render target (%x)!", status );1.184 + }1.185 + status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);1.186 + if( status != GL_FRAMEBUFFER_COMPLETE_EXT ) {1.187 + ERROR( "Framebuffer failure: %x", status );1.188 + exit(1);1.189 + }1.190 +1.191 + return ATTACHMENT_POINTS[attach];1.192 +}1.193 +1.194 +static render_buffer_t gl_fbo_create_render_buffer( uint32_t width, uint32_t height )1.195 +{1.196 + render_buffer_t buffer = calloc( sizeof(struct render_buffer), 1 );1.197 + buffer->width = width;1.198 + buffer->height = height;1.199 + glGenTextures( 1, &buffer->buf_id );1.200 + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, buffer->buf_id );1.201 + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP );1.202 + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP );1.203 + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST );1.204 + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST );1.205 + glTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );1.206 + return buffer;1.207 +}1.208 +1.209 +static void gl_fbo_destroy_render_buffer( render_buffer_t buffer )1.210 +{1.211 + int i,j;1.212 + for( i=0; i<MAX_FRAMEBUFFERS; i++ ) {1.213 + for( j=0; j < MAX_TEXTURES_PER_FB; j++ ) {1.214 + if( fbo[i].tex_ids[j] == buffer->buf_id ) {1.215 + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, ATTACHMENT_POINTS[j],1.216 + GL_TEXTURE_RECTANGLE_ARB, GL_NONE, 0 );1.217 + fbo[i].tex_ids[j] = -1;1.218 + }1.219 + }1.220 + }1.221 +1.222 + glDeleteTextures( 1, &buffer->buf_id );1.223 + buffer->buf_id = 0;1.224 + free( buffer );1.225 +}1.226 +1.227 +static gboolean gl_fbo_set_render_target( render_buffer_t buffer )1.228 +{1.229 + glGetError();1.230 + int fb = gl_fbo_get_framebuffer( buffer->width, buffer->height );1.231 + GLint attach = gl_fbo_attach_texture( fb, buffer->buf_id );1.232 + /* setup the gl context */1.233 + glViewport( 0, 0, buffer->width, buffer->height );1.234 +1.235 + return TRUE;1.236 +}1.237 +1.238 +/**1.239 + * Render the texture holding the given buffer to the front window1.240 + * buffer.1.241 + */1.242 +static gboolean gl_fbo_display_render_buffer( render_buffer_t buffer )1.243 +{1.244 + glFinish();1.245 + glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); // real window1.246 + glViewport( 0, 0, video_width, video_height );1.247 + glEnable( GL_TEXTURE_RECTANGLE_ARB );1.248 + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, buffer->buf_id );1.249 + glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );1.250 + glDrawBuffer( GL_FRONT );1.251 + glReadBuffer( GL_FRONT );1.252 + glDisable( GL_ALPHA_TEST );1.253 + glDisable( GL_DEPTH_TEST );1.254 + glDisable( GL_SCISSOR_TEST );1.255 + glDisable( GL_CULL_FACE );1.256 +1.257 + glMatrixMode(GL_PROJECTION);1.258 + glLoadIdentity();1.259 + glOrtho( 0, buffer->width, buffer->height, 0, 0, -65535 );1.260 + glMatrixMode(GL_MODELVIEW);1.261 + glLoadIdentity();1.262 + glEnable( GL_BLEND );1.263 + glBlendFunc( GL_ONE, GL_ZERO );1.264 + glDisable( GL_DEPTH_TEST );1.265 + glBegin( GL_QUADS );1.266 + glTexCoord2i( 0, buffer->height );1.267 + glVertex2f( 0.0, 0.0 );1.268 + glTexCoord2i( buffer->width, buffer->height );1.269 + glVertex2f( buffer->width, 0.0 );1.270 + glTexCoord2i( buffer->width, 0 );1.271 + glVertex2f( buffer->width, buffer->height );1.272 + glTexCoord2i( 0, 0 );1.273 + glVertex2f( 0.0, buffer->height );1.274 + glEnd();1.275 + glDisable( GL_TEXTURE_RECTANGLE_ARB );1.276 + glFlush();1.277 + return TRUE;1.278 +}1.279 +1.280 +static gboolean gl_fbo_display_frame_buffer( frame_buffer_t buffer )1.281 +{1.282 + glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );1.283 + glDrawBuffer( GL_FRONT );1.284 + glReadBuffer( GL_FRONT );1.285 + return gl_display_frame_buffer( buffer );1.286 +}1.287 +1.288 +static gboolean gl_fbo_display_blank( uint32_t colour )1.289 +{1.290 + glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );1.291 + glDrawBuffer( GL_FRONT );1.292 + glReadBuffer( GL_FRONT );1.293 + return gl_display_blank( colour );1.294 +}1.295 +1.296 +static gboolean gl_fbo_read_render_buffer( render_buffer_t buffer, char *target )1.297 +{1.298 + int fb = gl_fbo_get_framebuffer( buffer->width, buffer->height );1.299 + GLint attach = gl_fbo_attach_texture( fb, buffer->buf_id );1.300 + return gl_read_render_buffer( buffer, target );1.301 +}1.302 +
.