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