Search
lxdream.org :: lxdream/src/pvr2/glrender.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/pvr2/glrender.c
changeset 1207:f7ca985659c6
prev1205:a486ac64f34b
next1219:3966d3e55351
author nkeynes
date Tue Feb 07 11:20:00 2012 +1000 (8 years ago)
permissions -rw-r--r--
last change Gen helper functions for uniform + attribute variables, along with the main
program use, to provide a more usable interface to the shaders
view annotate diff log raw
     1 /**
     2  * $Id$
     3  *
     4  * Standard OpenGL rendering engine. 
     5  *
     6  * Copyright (c) 2005 Nathan Keynes.
     7  *
     8  * This program is free software; you can redistribute it and/or modify
     9  * it under the terms of the GNU General Public License as published by
    10  * the Free Software Foundation; either version 2 of the License, or
    11  * (at your option) any later version.
    12  *
    13  * This program is distributed in the hope that it will be useful,
    14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    16  * GNU General Public License for more details.
    17  */
    19 #include <assert.h>
    20 #include <sys/time.h>
    21 #include "display.h"
    22 #include "pvr2/pvr2.h"
    23 #include "pvr2/pvr2mmio.h"
    24 #include "pvr2/glutil.h"
    25 #include "pvr2/scene.h"
    26 #include "pvr2/tileiter.h"
    27 #include "pvr2/shaders.h"
    29 #ifdef APPLE_BUILD
    30 #include "OpenGL/CGLCurrent.h"
    31 #include "OpenGL/CGLMacro.h"
    33 static CGLContextObj CGL_MACRO_CONTEXT;
    34 #endif
    36 #define IS_EMPTY_TILE_LIST(p) ((*((uint32_t *)(pvr2_main_ram+(p))) >> 28) == 0x0F)
    38 int pvr2_poly_depthmode[8] = { GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL,
    39         GL_GREATER, GL_NOTEQUAL, GL_GEQUAL, 
    40         GL_ALWAYS };
    41 int pvr2_poly_srcblend[8] = { 
    42         GL_ZERO, GL_ONE, GL_DST_COLOR, GL_ONE_MINUS_DST_COLOR,
    43         GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, 
    44         GL_ONE_MINUS_DST_ALPHA };
    45 int pvr2_poly_dstblend[8] = {
    46         GL_ZERO, GL_ONE, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR,
    47         GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA,
    48         GL_ONE_MINUS_DST_ALPHA };
    49 int pvr2_poly_texblend[4] = {
    50         GL_REPLACE,
    51         GL_MODULATE,
    52         GL_DECAL,
    53         GL_MODULATE
    54 };
    56 static gboolean have_shaders = FALSE;
    57 static int currentTexId = -1;
    59 static inline void bind_texture(int texid)
    60 {
    61     if( currentTexId != texid ) {
    62         currentTexId = texid;
    63         glBindTexture(GL_TEXTURE_2D, texid);
    64     }
    65 }
    67 /**
    68  * Clip the tile bounds to the clipping plane. 
    69  * @return TRUE if the tile was not clipped completely.
    70  */
    71 static gboolean clip_tile_bounds( uint32_t *tile, float *clip )
    72 {
    73     if( tile[0] < clip[0] ) tile[0] = clip[0];
    74     if( tile[1] > clip[1] ) tile[1] = clip[1];
    75     if( tile[2] < clip[2] ) tile[2] = clip[2];
    76     if( tile[3] > clip[3] ) tile[3] = clip[3];
    77     return tile[0] < tile[1] && tile[2] < tile[3];
    78 }
    80 static void drawrect2d( uint32_t tile_bounds[], float z )
    81 {
    82     glBegin( GL_QUADS );
    83     glVertex3f( tile_bounds[0], tile_bounds[2], z );
    84     glVertex3f( tile_bounds[1], tile_bounds[2], z );
    85     glVertex3f( tile_bounds[1], tile_bounds[3], z );
    86     glVertex3f( tile_bounds[0], tile_bounds[3], z );
    87     glEnd();
    88 }
    90 static void pvr2_scene_load_textures()
    91 {
    92     int i;
    94     texcache_begin_scene( MMIO_READ( PVR2, RENDER_PALETTE ) & 0x03,
    95                          (MMIO_READ( PVR2, RENDER_TEXSIZE ) & 0x003F) << 5 );
    97     for( i=0; i < pvr2_scene.poly_count; i++ ) {
    98         struct polygon_struct *poly = &pvr2_scene.poly_array[i];
    99         if( POLY1_TEXTURED(poly->context[0]) ) {
   100             poly->tex_id = texcache_get_texture( poly->context[1], poly->context[2] );
   101             if( poly->mod_vertex_index != -1 ) {
   102                 if( pvr2_scene.shadow_mode == SHADOW_FULL ) {
   103                     poly->mod_tex_id = texcache_get_texture( poly->context[3], poly->context[4] );
   104                 } else {
   105                     poly->mod_tex_id = poly->tex_id;
   106                 }
   107             }
   108         } else {
   109             poly->tex_id = 0;
   110             poly->mod_tex_id = 0;
   111         }
   112     }
   113 }
   116 /**
   117  * Once-off call to setup the OpenGL context.
   118  */
   119 void pvr2_setup_gl_context()
   120 {
   122     if( glsl_is_supported() && isGLMultitextureSupported() ) {
   123         if( !glsl_load_shaders( ) ) {
   124             WARN( "Unable to load GL shaders" );
   125         } else {
   126             have_shaders = TRUE;
   127         }
   128     }
   130 #ifdef APPLE_BUILD
   131     CGL_MACRO_CONTEXT = CGLGetCurrentContext();
   132 #endif
   133     texcache_gl_init(); // Allocate texture IDs
   134     glDisable( GL_CULL_FACE );
   135     glEnable( GL_BLEND );
   136     glEnable( GL_DEPTH_TEST );
   137     glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
   138     glMatrixMode(GL_MODELVIEW);
   139     glLoadIdentity();
   141 #ifdef HAVE_OPENGL_CLAMP_COLOR
   142     if( isGLExtensionSupported("GL_ARB_color_buffer_float") ) {
   143         glClampColorARB(GL_CLAMP_VERTEX_COLOR_ARB, GL_FALSE );
   144         glClampColorARB(GL_CLAMP_FRAGMENT_COLOR_ARB, GL_FALSE );
   145     }
   146 #endif
   148     glEnableClientState( GL_COLOR_ARRAY );
   149     glEnableClientState( GL_VERTEX_ARRAY );
   150     glEnableClientState( GL_TEXTURE_COORD_ARRAY );
   151     glEnableClientState( GL_SECONDARY_COLOR_ARRAY );
   152     glEnableClientState( GL_FOG_COORDINATE_ARRAY_EXT );
   154     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
   155     glClearDepth(0);
   156     glClearStencil(0);
   158     glFogi(GL_FOG_COORDINATE_SOURCE_EXT, GL_FOG_COORDINATE_EXT);
   159     glFogi(GL_FOG_MODE, GL_LINEAR);
   160     glFogf(GL_FOG_START, 0.0);
   161     glFogf(GL_FOG_END, 1.0);
   163     if( have_shaders ) {
   164         glsl_use_pvr2_shader();
   165         glsl_set_pvr2_shader_primary_texture(0);
   166         glsl_set_pvr2_shader_palette_texture(1);
   167         glsl_clear_shader();
   168     }
   169 }
   171 /**
   172  * Setup the basic context that's shared between normal and modified modes -
   173  * depth, culling
   174  */
   175 static void render_set_base_context( uint32_t poly1, gboolean set_depth )
   176 {
   177     if( set_depth ) {
   178         glDepthFunc( POLY1_DEPTH_MODE(poly1) );
   179     }
   181     glDepthMask( POLY1_DEPTH_WRITE(poly1) ? GL_TRUE : GL_FALSE );
   182 }
   184 /**
   185  * Setup the texture/shading settings (TSP) which vary between mod/unmod modes.
   186  */
   187 static void render_set_tsp_context( uint32_t poly1, uint32_t poly2 )
   188 {
   189     glShadeModel( POLY1_SHADE_MODEL(poly1) );
   191     if( POLY1_TEXTURED(poly1) && !have_shaders ) {
   192         if( POLY2_TEX_BLEND(poly2) == 2 )
   193             glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL );
   194         else
   195             glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
   197      }
   199      switch( POLY2_FOG_MODE(poly2) ) {
   200      case PVR2_POLY_FOG_LOOKUP:
   201          glFogfv( GL_FOG_COLOR, pvr2_scene.fog_lut_colour );
   202          break;
   203      case PVR2_POLY_FOG_VERTEX:
   204          glFogfv( GL_FOG_COLOR, pvr2_scene.fog_vert_colour );
   205          break;
   206      }
   208      int srcblend = POLY2_SRC_BLEND(poly2);
   209      int destblend = POLY2_DEST_BLEND(poly2);
   210      glBlendFunc( srcblend, destblend );
   212      if( POLY2_SRC_BLEND_TARGET(poly2) || POLY2_DEST_BLEND_TARGET(poly2) ) {
   213          WARN( "Accumulation buffer not supported" );
   214      }   
   215 }
   217 /**
   218  * Setup the GL context for the supplied polygon context.
   219  * @param context pointer to 3 or 5 words of polygon context
   220  * @param depth_mode force depth mode, or 0 to use the polygon's
   221  * depth mode.
   222  */
   223 static void render_set_context( uint32_t *context, gboolean set_depth )
   224 {
   225     render_set_base_context(context[0], set_depth);
   226     render_set_tsp_context(context[0],context[1]);
   227 }
   229 static inline void gl_draw_vertexes( struct polygon_struct *poly )
   230 {
   231     do {
   232         glDrawArrays(GL_TRIANGLE_STRIP, poly->vertex_index, poly->vertex_count);
   233         poly = poly->sub_next;
   234     } while( poly != NULL );
   235 }
   237 static inline void gl_draw_mod_vertexes( struct polygon_struct *poly )
   238 {
   239     do {
   240         glDrawArrays(GL_TRIANGLE_STRIP, poly->mod_vertex_index, poly->vertex_count);
   241         poly = poly->sub_next;
   242     } while( poly != NULL );
   243 }
   245 static void gl_render_poly( struct polygon_struct *poly, gboolean set_depth)
   246 {
   247     if( poly->vertex_count == 0 )
   248         return; /* Culled */
   250     bind_texture(poly->tex_id);
   251     if( poly->mod_vertex_index == -1 ) {
   252         render_set_context( poly->context, set_depth );
   253         gl_draw_vertexes(poly);
   254     }  else {
   255         glEnable( GL_STENCIL_TEST );
   256         render_set_base_context( poly->context[0], set_depth );
   257         render_set_tsp_context( poly->context[0], poly->context[1] );
   258         glStencilFunc(GL_EQUAL, 0, 2);
   259         gl_draw_vertexes(poly);
   261         if( pvr2_scene.shadow_mode == SHADOW_FULL ) {
   262             bind_texture(poly->mod_tex_id);
   263             render_set_tsp_context( poly->context[0], poly->context[3] );
   264         }
   265         glStencilFunc(GL_EQUAL, 2, 2);
   266         gl_draw_mod_vertexes(poly);
   267         glDisable( GL_STENCIL_TEST );
   268     }
   269 }
   272 static void gl_render_modifier_polygon( struct polygon_struct *poly, uint32_t tile_bounds[] )
   273 {
   274     /* A bit of explanation:
   275      * In theory it works like this: generate a 1-bit stencil for each polygon
   276      * volume, and then AND or OR it against the overall 1-bit tile stencil at 
   277      * the end of the volume. 
   278      * 
   279      * The implementation here uses a 2-bit stencil buffer, where each volume
   280      * is drawn using only stencil bit 0, and then a 'flush' polygon is drawn
   281      * to update bit 1 accordingly and clear bit 0.
   282      * 
   283      * This could probably be more efficient, but at least it works correctly 
   284      * now :)
   285      */
   287     if( poly->vertex_count == 0 )
   288         return; /* Culled */
   290     gl_draw_vertexes(poly);
   294     int poly_type = POLY1_VOLUME_MODE(poly->context[0]);
   295     if( poly_type == PVR2_VOLUME_REGION0 ) {
   296         /* 00 => 00
   297          * 01 => 00
   298          * 10 => 10
   299          * 11 => 00
   300          */
   301         glStencilMask( 0x03 );
   302         glStencilFunc(GL_EQUAL, 0x02, 0x03);
   303         glStencilOp(GL_ZERO, GL_KEEP, GL_KEEP);
   304         glDisable( GL_DEPTH_TEST );
   306         drawrect2d( tile_bounds, pvr2_scene.bounds[4] );
   308         glEnable( GL_DEPTH_TEST );
   309         glStencilMask( 0x01 );
   310         glStencilFunc( GL_ALWAYS, 0, 1 );
   311         glStencilOp( GL_KEEP,GL_INVERT, GL_KEEP ); 
   312     } else if( poly_type == PVR2_VOLUME_REGION1 ) {
   313         /* This is harder with the standard stencil ops - do it in two passes
   314          * 00 => 00 | 00 => 10
   315          * 01 => 10 | 01 => 10
   316          * 10 => 10 | 10 => 00
   317          * 11 => 10 | 11 => 10
   318          */
   319         glStencilMask( 0x02 );
   320         glStencilOp( GL_INVERT, GL_INVERT, GL_INVERT );
   321         glDisable( GL_DEPTH_TEST );
   323         drawrect2d( tile_bounds, pvr2_scene.bounds[4] );
   325         glStencilMask( 0x03 );
   326         glStencilFunc( GL_NOTEQUAL,0x02, 0x03);
   327         glStencilOp( GL_ZERO, GL_REPLACE, GL_REPLACE );
   329         drawrect2d( tile_bounds, pvr2_scene.bounds[4] );
   331         glEnable( GL_DEPTH_TEST );
   332         glStencilMask( 0x01 );
   333         glStencilFunc( GL_ALWAYS, 0, 1 );
   334         glStencilOp( GL_KEEP,GL_INVERT, GL_KEEP );         
   335     }
   336 }
   338 static void gl_render_bkgnd( struct polygon_struct *poly )
   339 {
   340     bind_texture(poly->tex_id);
   341     render_set_tsp_context( poly->context[0], poly->context[1] );
   342     glDisable( GL_DEPTH_TEST );
   343     glBlendFunc( GL_ONE, GL_ZERO );
   344     gl_draw_vertexes(poly);
   345     glEnable( GL_DEPTH_TEST );
   346 }
   348 void gl_render_triangle( struct polygon_struct *poly, int index )
   349 {
   350     bind_texture(poly->tex_id);
   351     render_set_tsp_context( poly->context[0], poly->context[1] );
   352     glDrawArrays(GL_TRIANGLE_STRIP, poly->vertex_index + index, 3 );
   354 }
   356 void gl_render_tilelist( pvraddr_t tile_entry, gboolean set_depth )
   357 {
   358     tileentryiter list;
   360     FOREACH_TILEENTRY(list, tile_entry) {
   361         struct polygon_struct *poly = pvr2_scene.buf_to_poly_map[TILEENTRYITER_POLYADDR(list)];
   362         if( poly != NULL ) {
   363             do {
   364                 gl_render_poly(poly, set_depth);
   365                 poly = poly->next;
   366             } while( list.strip_count-- > 0 );
   367         }
   368     }
   369 }
   371 /**
   372  * Render the tilelist with depthbuffer updates only.
   373  */
   374 static void gl_render_tilelist_depthonly( pvraddr_t tile_entry )
   375 {
   376     tileentryiter list;
   378     FOREACH_TILEENTRY(list, tile_entry) {
   379         struct polygon_struct *poly = pvr2_scene.buf_to_poly_map[TILEENTRYITER_POLYADDR(list)];
   380         if( poly != NULL ) {
   381             do {
   382                 render_set_base_context(poly->context[0],TRUE);
   383                 gl_draw_vertexes(poly);
   384                 poly = poly->next;
   385             } while( list.strip_count-- > 0 );
   386         }
   387     }
   388 }
   390 static void gl_render_modifier_tilelist( pvraddr_t tile_entry, uint32_t tile_bounds[] )
   391 {
   392     tileentryiter list;
   394     if( !IS_TILE_PTR(tile_entry) )
   395         return;
   397     glEnable( GL_STENCIL_TEST );
   398     glEnable( GL_DEPTH_TEST );
   399     glStencilFunc( GL_ALWAYS, 0, 1 );
   400     glStencilOp( GL_KEEP,GL_INVERT, GL_KEEP ); 
   401     glStencilMask( 0x01 );
   402     glDepthFunc( GL_LEQUAL );
   403     glDepthMask( GL_FALSE );
   405     FOREACH_TILEENTRY(list, tile_entry ) {
   406         struct polygon_struct *poly = pvr2_scene.buf_to_poly_map[TILEENTRYITER_POLYADDR(list)];
   407         if( poly != NULL ) {
   408             do {
   409                 gl_render_modifier_polygon( poly, tile_bounds );
   410                 poly = poly->next;
   411             } while( list.strip_count-- > 0 );
   412         }
   413     }
   414     glDepthMask( GL_TRUE );
   415     glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
   416     glDisable( GL_STENCIL_TEST );
   417 }
   419 /**
   420  * Render the currently defined scene in pvr2_scene
   421  */
   422 void pvr2_scene_render( render_buffer_t buffer )
   423 {
   424     /* Scene setup */
   425     struct timeval start_tv, tex_tv, end_tv;
   427     gettimeofday(&start_tv, NULL);
   428     display_driver->set_render_target(buffer);
   429     pvr2_check_palette_changed();
   430     pvr2_scene_load_textures();
   431     currentTexId = -1;
   433     gettimeofday( &tex_tv, NULL );
   434     uint32_t ms = (tex_tv.tv_sec - start_tv.tv_sec) * 1000 +
   435     (tex_tv.tv_usec - start_tv.tv_usec)/1000;
   436     DEBUG( "Scene setup in %dms", ms );
   438     /* Setup view projection matrix */
   439     glMatrixMode(GL_PROJECTION);
   440     glLoadIdentity();
   441     float nearz = pvr2_scene.bounds[4];
   442     float farz = pvr2_scene.bounds[5];
   443     if( nearz == farz ) {
   444         farz*= 4.0;
   445     }
   446     glOrtho( 0, pvr2_scene.buffer_width, pvr2_scene.buffer_height, 0, 
   447              -farz, -nearz );
   448     float alphaRef = ((float)(MMIO_READ(PVR2, RENDER_ALPHA_REF)&0xFF)+1)/256.0;
   449     glAlphaFunc( GL_GEQUAL, alphaRef );
   451     /* Clear the buffer (FIXME: May not want always want to do this) */
   452     glDisable( GL_SCISSOR_TEST );
   453     glDepthMask( GL_TRUE );
   454     glStencilMask( 0x03 );
   455     glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
   457     /* Setup vertex array pointers */
   458     glVertexPointer(3, GL_FLOAT, sizeof(struct vertex_struct), &pvr2_scene.vertex_array[0].x);
   459     glColorPointer(4, GL_FLOAT, sizeof(struct vertex_struct), &pvr2_scene.vertex_array[0].rgba[0]);
   460     glTexCoordPointer(4, GL_FLOAT, sizeof(struct vertex_struct), &pvr2_scene.vertex_array[0].u);
   461     glSecondaryColorPointerEXT(3, GL_FLOAT, sizeof(struct vertex_struct), pvr2_scene.vertex_array[0].offset_rgba );
   462     glFogCoordPointerEXT(GL_FLOAT, sizeof(struct vertex_struct), &pvr2_scene.vertex_array[0].offset_rgba[3] );
   463     /* Turn on the shaders (if available) */
   464     glsl_use_pvr2_shader();
   466     /* Render the background */
   467     gl_render_bkgnd( pvr2_scene.bkgnd_poly );
   469     glEnable( GL_SCISSOR_TEST );
   470     glEnable( GL_COLOR_SUM );
   471     glEnable( GL_FOG );
   472     glEnable( GL_TEXTURE_2D );
   474     /* Process the segment list */
   475     struct tile_segment *segment = pvr2_scene.segment_list;
   476     do {
   477         int tilex = SEGMENT_X(segment->control);
   478         int tiley = SEGMENT_Y(segment->control);
   480         uint32_t tile_bounds[4] = { tilex << 5, (tilex+1)<<5, tiley<<5, (tiley+1)<<5 };
   481         if( !clip_tile_bounds(tile_bounds, pvr2_scene.bounds) ) {
   482             continue; // fully clipped, skip tile
   483         }
   485         /* Clip to the visible part of the tile */
   486         glScissor( tile_bounds[0], pvr2_scene.buffer_height-tile_bounds[3], 
   487                    tile_bounds[1]-tile_bounds[0], tile_bounds[3] - tile_bounds[2] );
   488         if( display_driver->capabilities.stencil_bits >= 2 && 
   489                 IS_TILE_PTR(segment->opaquemod_ptr) &&
   490                 !IS_EMPTY_TILE_LIST(segment->opaquemod_ptr) ) {
   491             /* Don't do this unless there's actually some shadow polygons */
   493             /* Use colormask instead of drawbuffer for simplicity */
   494             glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE );
   495             gl_render_tilelist_depthonly(segment->opaque_ptr);
   496             gl_render_modifier_tilelist(segment->opaquemod_ptr, tile_bounds);
   497             glClear( GL_DEPTH_BUFFER_BIT );
   498             glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
   499         }
   500         gl_render_tilelist(segment->opaque_ptr,TRUE);
   501         if( IS_TILE_PTR(segment->punchout_ptr) ) {
   502             glEnable(GL_ALPHA_TEST );
   503             glDepthFunc(GL_GEQUAL);
   504             gl_render_tilelist(segment->punchout_ptr, FALSE );
   505             glDisable(GL_ALPHA_TEST );
   506         }
   508         if( IS_TILE_PTR(segment->trans_ptr) ) {
   509             if( pvr2_scene.sort_mode == SORT_NEVER || 
   510                     (pvr2_scene.sort_mode == SORT_TILEFLAG && (segment->control&SEGMENT_SORT_TRANS))) {
   511                 gl_render_tilelist(segment->trans_ptr, TRUE);
   512             } else {
   513                 render_autosort_tile(segment->trans_ptr, RENDER_NORMAL );
   514             }
   515         }
   516     } while( !IS_LAST_SEGMENT(segment++) );
   517     glDisable( GL_SCISSOR_TEST );
   518     glDisable( GL_COLOR_SUM );
   519     glDisable( GL_FOG );
   520     glsl_clear_shader();
   522     pvr2_scene_finished();
   524     gettimeofday( &end_tv, NULL );
   525     ms = (end_tv.tv_sec - tex_tv.tv_sec) * 1000 +
   526     (end_tv.tv_usec - tex_tv.tv_usec)/1000;
   527     DEBUG( "Scene render in %dms", ms );
   528 }
.