nkeynes@635: /** nkeynes@635: * $Id$ nkeynes@635: * nkeynes@635: * Common GL code that doesn't depend on a specific implementation nkeynes@635: * nkeynes@635: * Copyright (c) 2005 Nathan Keynes. nkeynes@635: * nkeynes@635: * This program is free software; you can redistribute it and/or modify nkeynes@635: * it under the terms of the GNU General Public License as published by nkeynes@635: * the Free Software Foundation; either version 2 of the License, or nkeynes@635: * (at your option) any later version. nkeynes@635: * nkeynes@635: * This program is distributed in the hope that it will be useful, nkeynes@635: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@635: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@635: * GNU General Public License for more details. nkeynes@635: */ nkeynes@635: nkeynes@635: #include nkeynes@635: nkeynes@635: #include "display.h" nkeynes@635: #include "pvr2/pvr2.h" nkeynes@1236: #include "pvr2/glutil.h" nkeynes@1240: #include "pvr2/shaders.h" nkeynes@635: #include "drivers/video_gl.h" nkeynes@635: nkeynes@1239: /* FIXME: Need to actually handle this case */ nkeynes@1239: #ifndef GL_PACK_ROW_LENGTH nkeynes@1239: #define glPixelStorei(key,val) nkeynes@1239: #endif nkeynes@1239: nkeynes@1236: uint32_t video_width, video_height; nkeynes@1236: struct video_vertex { nkeynes@1236: float x,y; nkeynes@1236: float u,v; nkeynes@1240: float r,g,b,a; nkeynes@1236: }; nkeynes@1236: nkeynes@1236: static struct video_box_t { nkeynes@1236: float viewMatrix[16]; nkeynes@1236: struct video_vertex gap1[4]; nkeynes@1236: struct video_vertex gap2[4]; nkeynes@1236: struct video_vertex video_view[4]; nkeynes@1236: struct video_vertex invert_view[4]; nkeynes@1236: } video_box; nkeynes@1236: nkeynes@1251: void gl_set_video_size( uint32_t width, uint32_t height, int flipped ) nkeynes@1236: { nkeynes@1236: video_width = width; nkeynes@1236: video_height = height; nkeynes@1236: nkeynes@1236: int x1=0,y1=0,x2=video_width,y2=video_height; nkeynes@1251: int top = 0, bottom = 1; nkeynes@1251: nkeynes@1251: if( flipped ) { nkeynes@1251: top = 1; nkeynes@1251: bottom = 0; nkeynes@1251: } nkeynes@1236: nkeynes@1236: int ah = video_width * 0.75; nkeynes@1236: nkeynes@1236: if( ah > video_height ) { nkeynes@1236: int w = (video_height/0.75); nkeynes@1236: x1 = (video_width - w)/2; nkeynes@1236: x2 -= x1; nkeynes@1236: video_box.gap1[0].x = 0; video_box.gap1[0].y = 0; nkeynes@1236: video_box.gap1[1].x = x1; video_box.gap1[1].y = 0; nkeynes@1262: video_box.gap1[2].x = x1; video_box.gap1[2].y = video_height; nkeynes@1259: video_box.gap1[3].x = 0; video_box.gap1[3].y = video_height; nkeynes@1236: video_box.gap2[0].x = x2; video_box.gap2[0].y = 0; nkeynes@1236: video_box.gap2[1].x = video_width; video_box.gap2[1].y = 0; nkeynes@1259: video_box.gap2[2].x = video_width; video_box.gap2[2].y = video_height; nkeynes@1259: video_box.gap2[3].x = x2; video_box.gap2[3].y = video_height; nkeynes@1236: } else if( ah < video_height ) { nkeynes@1236: y1 = (video_height - ah)/2; nkeynes@1236: y2 -= y1; nkeynes@1236: nkeynes@1236: video_box.gap1[0].x = 0; video_box.gap1[0].y = 0; nkeynes@1236: video_box.gap1[1].x = video_width; video_box.gap1[1].y = 0; nkeynes@1259: video_box.gap1[2].x = video_width; video_box.gap1[2].y = y1; nkeynes@1259: video_box.gap1[3].x = 0; video_box.gap1[3].y = y1; nkeynes@1236: video_box.gap2[0].x = 0; video_box.gap2[0].y = y2; nkeynes@1236: video_box.gap2[1].x = video_width; video_box.gap2[1].y = y2; nkeynes@1259: video_box.gap2[2].x = video_width; video_box.gap2[2].y = video_height; nkeynes@1259: video_box.gap2[3].x = 0; video_box.gap2[3].y = video_height; nkeynes@1236: } nkeynes@1236: nkeynes@1236: video_box.video_view[0].x = x1; video_box.video_view[0].y = y1; nkeynes@1259: video_box.video_view[0].u = 0; video_box.video_view[0].v = top; nkeynes@1236: video_box.video_view[1].x = x2; video_box.video_view[1].y = y1; nkeynes@1259: video_box.video_view[1].u = 1; video_box.video_view[1].v = top; nkeynes@1259: video_box.video_view[2].x = x2; video_box.video_view[2].y = y2; nkeynes@1259: video_box.video_view[2].u = 1; video_box.video_view[2].v = bottom; nkeynes@1259: video_box.video_view[3].x = x1; video_box.video_view[3].y = y2; nkeynes@1259: video_box.video_view[3].u = 0; video_box.video_view[3].v = bottom; nkeynes@1236: nkeynes@1236: memcpy( &video_box.invert_view, &video_box.video_view, sizeof(video_box.video_view) ); nkeynes@1251: video_box.invert_view[0].v = bottom; video_box.invert_view[1].v = bottom; nkeynes@1251: video_box.invert_view[2].v = top; video_box.invert_view[3].v = top; nkeynes@1236: nkeynes@1236: defineOrthoMatrix(video_box.viewMatrix, video_width, video_height, 0, 65535); nkeynes@1236: } nkeynes@635: nkeynes@1239: #ifdef HAVE_OPENGL_FIXEDFUNC nkeynes@635: /** nkeynes@1236: * Setup the gl context for writes to the display output. nkeynes@635: */ nkeynes@1246: void gl_framebuffer_setup() nkeynes@635: { nkeynes@1239: glViewport( 0, 0, video_width, video_height ); nkeynes@1236: glLoadMatrixf(video_box.viewMatrix); nkeynes@1236: glBlendFunc( GL_ONE, GL_ZERO ); nkeynes@1236: glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); nkeynes@1236: glVertexPointer(2, GL_FLOAT, sizeof(struct video_vertex), &video_box.gap1[0].x); nkeynes@1236: glColorPointer(3, GL_FLOAT, sizeof(struct video_vertex), &video_box.gap1[0].r); nkeynes@1236: glTexCoordPointer(2, GL_FLOAT, sizeof(struct video_vertex), &video_box.gap1[0].u); nkeynes@1236: glEnableClientState( GL_VERTEX_ARRAY ); nkeynes@1236: glEnableClientState( GL_COLOR_ARRAY ); nkeynes@1236: glEnableClientState( GL_TEXTURE_COORD_ARRAY ); nkeynes@635: } nkeynes@635: nkeynes@1246: void gl_framebuffer_cleanup() nkeynes@1240: { nkeynes@1240: glDisableClientState( GL_VERTEX_ARRAY ); nkeynes@1240: glDisableClientState( GL_COLOR_ARRAY ); nkeynes@1240: glDisableClientState( GL_TEXTURE_COORD_ARRAY ); nkeynes@1240: } nkeynes@1239: #else nkeynes@1246: void gl_framebuffer_setup() nkeynes@1239: { nkeynes@1240: glViewport( 0, 0, video_width, video_height ); nkeynes@1240: glBlendFunc( GL_ONE, GL_ZERO ); nkeynes@1240: glsl_use_basic_shader(); nkeynes@1240: glsl_set_basic_shader_view_matrix(video_box.viewMatrix); nkeynes@1240: glsl_set_basic_shader_in_vertex_pointer(&video_box.gap1[0].x, sizeof(struct video_vertex)); nkeynes@1240: glsl_set_basic_shader_in_colour_pointer(&video_box.gap1[0].r, sizeof(struct video_vertex)); nkeynes@1240: glsl_set_basic_shader_in_texcoord_pointer(&video_box.gap1[0].u, sizeof(struct video_vertex)); nkeynes@1240: glsl_set_basic_shader_primary_texture(0); nkeynes@1240: } nkeynes@1240: nkeynes@1246: void gl_framebuffer_cleanup() nkeynes@1240: { nkeynes@1240: glsl_clear_shader(); nkeynes@1239: } nkeynes@1239: #endif nkeynes@1239: nkeynes@635: void gl_display_render_buffer( render_buffer_t buffer ) nkeynes@635: { nkeynes@635: gl_texture_window( buffer->width, buffer->height, buffer->buf_id, buffer->inverted ); nkeynes@635: } nkeynes@635: nkeynes@854: /** nkeynes@854: * Convert window coordinates to dreamcast device coords (640x480) using the nkeynes@854: * same viewable area as gl_texture_window. nkeynes@854: * If the coordinates are outside the viewable area, the result is -1,-1. nkeynes@854: */ nkeynes@854: void gl_window_to_system_coords( int *x, int *y ) nkeynes@854: { nkeynes@854: int x1=0,y1=0,x2=video_width,y2=video_height; nkeynes@854: nkeynes@854: int ah = video_width * 0.75; nkeynes@854: nkeynes@854: if( ah > video_height ) { nkeynes@854: int w = (video_height/0.75); nkeynes@854: x1 = (video_width - w)/2; nkeynes@854: x2 -= x1; nkeynes@854: } else if( ah < video_height ) { nkeynes@854: y1 = (video_height - ah)/2; nkeynes@854: y2 -= y1; nkeynes@854: } nkeynes@854: if( *x < x1 || *x >= x2 || *y < y1 || *y >= y2 ) { nkeynes@854: *x = -1; nkeynes@854: *y = -1; nkeynes@854: } else { nkeynes@854: *x = (*x - x1) * DISPLAY_WIDTH / (x2-x1); nkeynes@854: *y = (*y - y1) * DISPLAY_HEIGHT / (y2-y1); nkeynes@854: } nkeynes@854: } nkeynes@854: nkeynes@1259: /** nkeynes@1259: * Use quads if we have them, otherwise tri-fans. nkeynes@1259: */ nkeynes@1259: #ifndef GL_QUADS nkeynes@1260: #define GL_QUADS GL_TRIANGLE_FAN nkeynes@1259: #endif nkeynes@1259: nkeynes@635: void gl_texture_window( int width, int height, int tex_id, gboolean inverted ) nkeynes@635: { nkeynes@1240: /* Set video box tex alpha to 1 */ nkeynes@1240: video_box.video_view[0].a = video_box.video_view[1].a = video_box.video_view[2].a = video_box.video_view[3].a = 1; nkeynes@1251: video_box.invert_view[0].a = video_box.invert_view[1].a = video_box.invert_view[2].a = video_box.invert_view[3].a = 1; nkeynes@1240: nkeynes@635: /* Reset display parameters */ nkeynes@1236: gl_framebuffer_setup(); nkeynes@1259: glDrawArrays(GL_QUADS, 0, 4); nkeynes@1259: glDrawArrays(GL_QUADS, 4, 4); nkeynes@1236: glEnable(GL_TEXTURE_2D); nkeynes@1236: glBindTexture(GL_TEXTURE_2D,tex_id); nkeynes@1222: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); nkeynes@1222: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); nkeynes@1259: glDrawArrays(GL_QUADS, inverted ? 12 : 8, 4); nkeynes@1236: glDisable(GL_TEXTURE_2D); nkeynes@1251: gl_framebuffer_cleanup(); nkeynes@635: glFlush(); nkeynes@635: } nkeynes@635: nkeynes@635: gboolean gl_load_frame_buffer( frame_buffer_t frame, int tex_id ) nkeynes@635: { nkeynes@635: GLenum type = colour_formats[frame->colour_format].type; nkeynes@635: GLenum format = colour_formats[frame->colour_format].format; nkeynes@635: int bpp = colour_formats[frame->colour_format].bpp; nkeynes@635: int rowstride = (frame->rowstride / bpp) - frame->width; nkeynes@736: nkeynes@635: glPixelStorei( GL_UNPACK_ROW_LENGTH, rowstride ); nkeynes@1222: glBindTexture( GL_TEXTURE_2D, tex_id ); nkeynes@1222: glTexSubImage2D( GL_TEXTURE_2D, 0, 0,0, nkeynes@736: frame->width, frame->height, format, type, frame->data ); nkeynes@1236: glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 ); nkeynes@635: return TRUE; nkeynes@635: } nkeynes@635: nkeynes@669: void gl_display_blank( uint32_t colour ) nkeynes@635: { nkeynes@1236: /* Set the video_box background colour */ nkeynes@1236: video_box.video_view[0].r = ((float)(((colour >> 16) & 0xFF) + 1)) / 256.0; nkeynes@1236: video_box.video_view[0].g = ((float)(((colour >> 8) & 0xFF) + 1)) / 256.0; nkeynes@1236: video_box.video_view[0].b = ((float)((colour & 0xFF) + 1)) / 256.0; nkeynes@1240: video_box.video_view[0].a = 0; nkeynes@1236: memcpy( &video_box.video_view[1].r, &video_box.video_view[0].r, sizeof(float)*3 ); nkeynes@1236: memcpy( &video_box.video_view[2].r, &video_box.video_view[0].r, sizeof(float)*3 ); nkeynes@1236: memcpy( &video_box.video_view[3].r, &video_box.video_view[0].r, sizeof(float)*3 ); nkeynes@1236: nkeynes@1236: /* And render */ nkeynes@1236: gl_framebuffer_setup(); nkeynes@1259: glDrawArrays(GL_QUADS, 0, 4); nkeynes@1259: glDrawArrays(GL_QUADS, 4, 4); nkeynes@1259: glDrawArrays(GL_QUADS, 8, 4); nkeynes@1251: gl_framebuffer_cleanup(); nkeynes@635: glFlush(); nkeynes@635: } nkeynes@635: nkeynes@635: /** nkeynes@635: * Generic GL read_render_buffer. This function assumes that the caller nkeynes@635: * has already set the appropriate glReadBuffer(); in other words, unless nkeynes@635: * there's only one buffer this needs to be wrapped. nkeynes@635: */ nkeynes@635: gboolean gl_read_render_buffer( unsigned char *target, render_buffer_t buffer, nkeynes@736: int rowstride, int colour_format ) nkeynes@635: { nkeynes@635: glFinish(); nkeynes@635: GLenum type = colour_formats[colour_format].type; nkeynes@635: GLenum format = colour_formats[colour_format].format; nkeynes@635: // int line_size = buffer->width * colour_formats[colour_format].bpp; nkeynes@635: // int size = line_size * buffer->height; nkeynes@635: int glrowstride = (rowstride / colour_formats[colour_format].bpp) - buffer->width; nkeynes@635: glPixelStorei( GL_PACK_ROW_LENGTH, glrowstride ); nkeynes@635: glReadPixels( 0, 0, buffer->width, buffer->height, format, type, target ); nkeynes@1236: glPixelStorei( GL_PACK_ROW_LENGTH, 0 ); nkeynes@635: return TRUE; nkeynes@635: } nkeynes@1244: nkeynes@1258: gboolean gl_init_driver( display_driver_t driver, gboolean need_fbo ) nkeynes@1258: { nkeynes@1258: /* Use framebuffer objects if available */ nkeynes@1258: if( gl_fbo_is_supported() ) { nkeynes@1258: gl_fbo_init(driver); nkeynes@1258: } else if( need_fbo ) { nkeynes@1258: ERROR( "Framebuffer objects not supported - unable to construct an off-screen buffer" ); nkeynes@1258: return FALSE; nkeynes@1258: } nkeynes@1258: nkeynes@1258: /* Use SL shaders if available */ nkeynes@1258: gboolean have_shaders = glsl_init(driver); nkeynes@1258: #ifndef HAVE_OPENGL_FIXEDFUNC nkeynes@1258: if( !have_shaders ) { /* Shaders are required if we don't have fixed-functionality */ nkeynes@1258: gl_fbo_shutdown(); nkeynes@1258: return FALSE; nkeynes@1258: } nkeynes@1258: #endif nkeynes@1258: nkeynes@1258: /* Use vertex arrays, VBOs, etc, if we have them */ nkeynes@1258: gl_vbo_init(driver); nkeynes@1258: nkeynes@1258: driver->capabilities.has_gl = TRUE; nkeynes@1258: return TRUE; nkeynes@1258: } nkeynes@1258: nkeynes@1244: static gboolean video_gl_init(); nkeynes@1244: nkeynes@1244: /** nkeynes@1244: * Minimal GL driver (assuming that the GL context is already set up externally) nkeynes@1244: * This requires FBO support (since otherwise we have no way to get a render buffer) nkeynes@1244: */ nkeynes@1244: struct display_driver display_gl_driver = { nkeynes@1244: "gl", N_("OpenGL driver"), video_gl_init, NULL, nkeynes@1244: NULL, NULL, NULL, nkeynes@1244: NULL, NULL, NULL, NULL, nkeynes@1244: gl_load_frame_buffer, gl_display_render_buffer, gl_display_blank, nkeynes@1244: NULL, gl_read_render_buffer, NULL, NULL nkeynes@1244: }; nkeynes@1244: nkeynes@1244: static gboolean video_gl_init() nkeynes@1244: { nkeynes@1258: return gl_init_driver(&display_gl_driver, TRUE); nkeynes@1244: }