Search
lxdream.org :: lxdream/src/drivers/gl_fbo.c :: diff
lxdream 0.9.1
released Jun 29
Download Now
filename src/drivers/gl_fbo.c
changeset 352:f0df7a6d4703
next424:421d68e78c46
author nkeynes
date Sun Feb 11 10:09:32 2007 +0000 (12 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 +0000
1.2 +++ b/src/drivers/gl_fbo.c Sun Feb 11 10:09:32 2007 +0000
1.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_object
1.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 colour
1.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 modify
1.18 + * it under the terms of the GNU General Public License as published by
1.19 + * the Free Software Foundation; either version 2 of the License, or
1.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 of
1.24 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.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 2
1.32 +#define MAX_TEXTURES_PER_FB 4
1.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 structure
1.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 initialized
1.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 attached
1.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 unbound
1.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 window
1.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 window
1.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 +
.