nkeynes@352: /** nkeynes@477: * $Id: gl_fbo.c,v 1.7 2007-10-31 09:10:23 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@477: #include nkeynes@477: #include "lxdream.h" nkeynes@352: #include "display.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@477: static void gl_fbo_load_frame_buffer( frame_buffer_t frame, render_buffer_t buffer ); nkeynes@352: static gboolean gl_fbo_display_blank( uint32_t colour ); nkeynes@477: static gboolean gl_fbo_read_render_buffer( unsigned char *target, render_buffer_t buffer, int rowstride, int format ); 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@477: driver->load_frame_buffer = gl_fbo_load_frame_buffer; nkeynes@352: driver->display_blank = gl_fbo_display_blank; nkeynes@352: driver->read_render_buffer = gl_fbo_read_render_buffer; nkeynes@445: nkeynes@445: glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); nkeynes@445: glDrawBuffer(GL_FRONT); nkeynes@445: glReadBuffer(GL_FRONT); 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@477: glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo[i].fb_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@477: glFinish(); 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@477: gl_fbo_detach(); nkeynes@477: gl_display_render_buffer( buffer ); nkeynes@352: return TRUE; nkeynes@352: } nkeynes@352: nkeynes@477: static void gl_fbo_load_frame_buffer( frame_buffer_t frame, render_buffer_t buffer ) nkeynes@477: { nkeynes@477: glFinish(); nkeynes@477: gl_fbo_detach(); nkeynes@545: gl_load_frame_buffer( frame, buffer->buf_id ); nkeynes@477: } nkeynes@477: nkeynes@477: static gboolean gl_fbo_display_blank( uint32_t colour ) nkeynes@477: { nkeynes@477: glFinish(); nkeynes@477: gl_fbo_detach(); nkeynes@477: return gl_display_blank( colour ); nkeynes@477: } nkeynes@477: nkeynes@477: void gl_fbo_detach() nkeynes@352: { nkeynes@352: glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); nkeynes@352: glDrawBuffer( GL_FRONT ); nkeynes@352: glReadBuffer( GL_FRONT ); nkeynes@477: } nkeynes@352: nkeynes@477: static gboolean gl_fbo_read_render_buffer( unsigned char *target, render_buffer_t buffer, nkeynes@477: int rowstride, int format ) 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@477: return gl_read_render_buffer( target, buffer, rowstride, format ); nkeynes@352: } nkeynes@352: