--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/drivers/gl_fbo.c Sun Feb 11 10:09:32 2007 +0000 @@ -0,0 +1,299 @@ +/** + * $Id: gl_fbo.c,v 1.1 2007-02-11 10:09:32 nkeynes Exp $ + * + * GL framebuffer-based driver shell. This requires the EXT_framebuffer_object + * extension, but is much nicer/faster/etc than pbuffers when it's available. + * This is (optionally) used indirectly by the top-level GLX driver. + * + * Strategy-wise, we maintain 2 framebuffers with up to 4 target colour + * buffers a piece. Effectively this reserves one fb for display output, + * and the other for texture output (each fb has a single size). + * + * Copyright (c) 2005 Nathan Keynes. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include "display.h" + +#define MAX_FRAMEBUFFERS 2 +#define MAX_TEXTURES_PER_FB 4 + +static render_buffer_t gl_fbo_create_render_buffer( uint32_t width, uint32_t height ); +static void gl_fbo_destroy_render_buffer( render_buffer_t buffer ); +static gboolean gl_fbo_set_render_target( render_buffer_t buffer ); +static gboolean gl_fbo_display_render_buffer( render_buffer_t buffer ); +static gboolean gl_fbo_display_frame_buffer( frame_buffer_t buffer ); +static gboolean gl_fbo_display_blank( uint32_t colour ); +static gboolean gl_fbo_read_render_buffer( render_buffer_t buffer, char *target ); + +extern uint32_t video_width, video_height; + +/** + * Framebuffer info structure + */ +struct gl_fbo_info { + GLuint fb_id; + GLuint depth_id; + GLuint stencil_id; + GLuint tex_ids[MAX_TEXTURES_PER_FB]; + int width, height; +}; + +static struct gl_fbo_info fbo[MAX_FRAMEBUFFERS]; +const static int ATTACHMENT_POINTS[MAX_TEXTURES_PER_FB] = { + GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT, + GL_COLOR_ATTACHMENT2_EXT, GL_COLOR_ATTACHMENT3_EXT }; +static int last_used_fbo; + +gboolean gl_fbo_is_supported() +{ + return isGLExtensionSupported("GL_EXT_framebuffer_object"); +} + +/** + * Construct the initial frame buffers and allocate ids for everything. + * The render handling driver methods are set to the fbo versions. + */ +void gl_fbo_init( display_driver_t driver ) +{ + int i,j; + int fbids[MAX_FRAMEBUFFERS]; + int rbids[MAX_FRAMEBUFFERS*2]; /* depth buffer, stencil buffer per fb */ + + glGenFramebuffersEXT( MAX_FRAMEBUFFERS, &fbids ); + glGenRenderbuffersEXT( MAX_FRAMEBUFFERS*2, &rbids ); + for( i=0; icreate_render_buffer = gl_fbo_create_render_buffer; + driver->destroy_render_buffer = gl_fbo_destroy_render_buffer; + driver->set_render_target = gl_fbo_set_render_target; + driver->display_render_buffer = gl_fbo_display_render_buffer; + driver->display_frame_buffer = gl_fbo_display_frame_buffer; + driver->display_blank = gl_fbo_display_blank; + driver->read_render_buffer = gl_fbo_read_render_buffer; +} + +void gl_fbo_shutdown() +{ + int i; + glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); + for( i=0; i MAX_FRAMEBUFFERS ) { + bufno = 0; + } + last_used_fbo = bufno; + } + if( fbo[bufno].width == width && fbo[bufno].height == height ) { + glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, fbo[bufno].fb_id ); + } else { + gl_fbo_setup_framebuffer( bufno, width, height ); + } + return bufno; +} + +/** + * Attach a texture to the framebuffer. The texture must already be initialized + * to the correct dimensions etc. + */ +static GLint gl_fbo_attach_texture( int fbo_no, GLint tex_id ) { + int attach = -1, i; + for( i=0; iwidth = width; + buffer->height = height; + glGenTextures( 1, &buffer->buf_id ); + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, buffer->buf_id ); + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP ); + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP ); + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL ); + return buffer; +} + +static void gl_fbo_destroy_render_buffer( render_buffer_t buffer ) +{ + int i,j; + for( i=0; ibuf_id ) { + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, ATTACHMENT_POINTS[j], + GL_TEXTURE_RECTANGLE_ARB, GL_NONE, 0 ); + fbo[i].tex_ids[j] = -1; + } + } + } + + glDeleteTextures( 1, &buffer->buf_id ); + buffer->buf_id = 0; + free( buffer ); +} + +static gboolean gl_fbo_set_render_target( render_buffer_t buffer ) +{ + glGetError(); + int fb = gl_fbo_get_framebuffer( buffer->width, buffer->height ); + GLint attach = gl_fbo_attach_texture( fb, buffer->buf_id ); + /* setup the gl context */ + glViewport( 0, 0, buffer->width, buffer->height ); + + return TRUE; +} + +/** + * Render the texture holding the given buffer to the front window + * buffer. + */ +static gboolean gl_fbo_display_render_buffer( render_buffer_t buffer ) +{ + glFinish(); + glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); // real window + glViewport( 0, 0, video_width, video_height ); + glEnable( GL_TEXTURE_RECTANGLE_ARB ); + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, buffer->buf_id ); + glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); + glDrawBuffer( GL_FRONT ); + glReadBuffer( GL_FRONT ); + glDisable( GL_ALPHA_TEST ); + glDisable( GL_DEPTH_TEST ); + glDisable( GL_SCISSOR_TEST ); + glDisable( GL_CULL_FACE ); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho( 0, buffer->width, buffer->height, 0, 0, -65535 ); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glEnable( GL_BLEND ); + glBlendFunc( GL_ONE, GL_ZERO ); + glDisable( GL_DEPTH_TEST ); + glBegin( GL_QUADS ); + glTexCoord2i( 0, buffer->height ); + glVertex2f( 0.0, 0.0 ); + glTexCoord2i( buffer->width, buffer->height ); + glVertex2f( buffer->width, 0.0 ); + glTexCoord2i( buffer->width, 0 ); + glVertex2f( buffer->width, buffer->height ); + glTexCoord2i( 0, 0 ); + glVertex2f( 0.0, buffer->height ); + glEnd(); + glDisable( GL_TEXTURE_RECTANGLE_ARB ); + glFlush(); + return TRUE; +} + +static gboolean gl_fbo_display_frame_buffer( frame_buffer_t buffer ) +{ + glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); + glDrawBuffer( GL_FRONT ); + glReadBuffer( GL_FRONT ); + return gl_display_frame_buffer( buffer ); +} + +static gboolean gl_fbo_display_blank( uint32_t colour ) +{ + glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); + glDrawBuffer( GL_FRONT ); + glReadBuffer( GL_FRONT ); + return gl_display_blank( colour ); +} + +static gboolean gl_fbo_read_render_buffer( render_buffer_t buffer, char *target ) +{ + int fb = gl_fbo_get_framebuffer( buffer->width, buffer->height ); + GLint attach = gl_fbo_attach_texture( fb, buffer->buf_id ); + return gl_read_render_buffer( buffer, target ); +} +