Search
lxdream.org :: lxdream/src/pvr2/glrender.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/pvr2/glrender.c
changeset 687:6bdc2b7032ea
prev684:95f2068235ef
next736:a02d1475ccfd
author nkeynes
date Sat Jun 14 11:54:15 2008 +0000 (13 years ago)
permissions -rw-r--r--
last change Change colour params to float
Convert background processing over to scene structure (fixes some depth issues as well)
Add color unclamp when supported
file annotate diff log raw
nkeynes@653
     1
/**
nkeynes@653
     2
 * $Id$
nkeynes@653
     3
 *
nkeynes@653
     4
 * Standard OpenGL rendering engine. 
nkeynes@653
     5
 *
nkeynes@653
     6
 * Copyright (c) 2005 Nathan Keynes.
nkeynes@653
     7
 *
nkeynes@653
     8
 * This program is free software; you can redistribute it and/or modify
nkeynes@653
     9
 * it under the terms of the GNU General Public License as published by
nkeynes@653
    10
 * the Free Software Foundation; either version 2 of the License, or
nkeynes@653
    11
 * (at your option) any later version.
nkeynes@653
    12
 *
nkeynes@653
    13
 * This program is distributed in the hope that it will be useful,
nkeynes@653
    14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
nkeynes@653
    15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
nkeynes@653
    16
 * GNU General Public License for more details.
nkeynes@653
    17
 */
nkeynes@653
    18
nkeynes@653
    19
#include <assert.h>
nkeynes@669
    20
#include <sys/time.h>
nkeynes@653
    21
#include "display.h"
nkeynes@653
    22
#include "pvr2/pvr2.h"
nkeynes@677
    23
#include "pvr2/pvr2mmio.h"
nkeynes@653
    24
#include "pvr2/scene.h"
nkeynes@665
    25
#include "pvr2/glutil.h"
nkeynes@653
    26
nkeynes@653
    27
int pvr2_poly_depthmode[8] = { GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL,
nkeynes@653
    28
				      GL_GREATER, GL_NOTEQUAL, GL_GEQUAL, 
nkeynes@653
    29
				      GL_ALWAYS };
nkeynes@653
    30
int pvr2_poly_srcblend[8] = { 
nkeynes@653
    31
    GL_ZERO, GL_ONE, GL_DST_COLOR, GL_ONE_MINUS_DST_COLOR,
nkeynes@653
    32
    GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, 
nkeynes@653
    33
    GL_ONE_MINUS_DST_ALPHA };
nkeynes@653
    34
int pvr2_poly_dstblend[8] = {
nkeynes@653
    35
    GL_ZERO, GL_ONE, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR,
nkeynes@653
    36
    GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA,
nkeynes@653
    37
    GL_ONE_MINUS_DST_ALPHA };
nkeynes@653
    38
int pvr2_poly_texblend[4] = {
nkeynes@653
    39
    GL_REPLACE, 
nkeynes@653
    40
    GL_MODULATE,  
nkeynes@653
    41
    GL_DECAL, 
nkeynes@653
    42
    GL_MODULATE 
nkeynes@653
    43
};
nkeynes@653
    44
int pvr2_render_colour_format[8] = {
nkeynes@653
    45
    COLFMT_BGRA1555, COLFMT_RGB565, COLFMT_BGRA4444, COLFMT_BGRA1555,
nkeynes@653
    46
    COLFMT_BGR888, COLFMT_BGRA8888, COLFMT_BGRA8888, COLFMT_BGRA4444 };
nkeynes@653
    47
nkeynes@653
    48
nkeynes@653
    49
/**
nkeynes@653
    50
 * Clip the tile bounds to the clipping plane. 
nkeynes@653
    51
 * @return TRUE if the tile was not clipped completely.
nkeynes@653
    52
 */
nkeynes@653
    53
static gboolean clip_tile_bounds( uint32_t *tile, float *clip )
nkeynes@653
    54
{
nkeynes@653
    55
    if( tile[0] < clip[0] ) tile[0] = clip[0];
nkeynes@653
    56
    if( tile[1] > clip[1] ) tile[1] = clip[1];
nkeynes@653
    57
    if( tile[2] < clip[2] ) tile[2] = clip[2];
nkeynes@653
    58
    if( tile[3] > clip[3] ) tile[3] = clip[3];
nkeynes@653
    59
    return tile[0] < tile[1] && tile[2] < tile[3];
nkeynes@653
    60
}
nkeynes@653
    61
nkeynes@653
    62
void pvr2_scene_load_textures()
nkeynes@653
    63
{
nkeynes@653
    64
    int i;
nkeynes@653
    65
    for( i=0; i < pvr2_scene.poly_count; i++ ) {
nkeynes@653
    66
	struct polygon_struct *poly = &pvr2_scene.poly_array[i];
nkeynes@653
    67
	if( POLY1_TEXTURED(poly->context[0]) ) {
nkeynes@653
    68
	    poly->tex_id = texcache_get_texture( poly->context[2],
nkeynes@653
    69
						 POLY2_TEX_WIDTH(poly->context[1]),
nkeynes@653
    70
						 POLY2_TEX_HEIGHT(poly->context[1]) );
nkeynes@653
    71
	    if( poly->mod_vertex_index != -1 ) {
nkeynes@653
    72
		poly->mod_tex_id = texcache_get_texture( poly->context[4],
nkeynes@653
    73
							 POLY2_TEX_WIDTH(poly->context[3]),
nkeynes@653
    74
							 POLY2_TEX_HEIGHT(poly->context[3]) );
nkeynes@653
    75
	    }
nkeynes@653
    76
	} else {
nkeynes@653
    77
	    poly->tex_id = -1;
nkeynes@653
    78
	    poly->mod_tex_id = -1;
nkeynes@653
    79
	}
nkeynes@653
    80
    }
nkeynes@653
    81
}
nkeynes@653
    82
nkeynes@653
    83
nkeynes@653
    84
nkeynes@653
    85
/**
nkeynes@653
    86
 * Once-off call to setup the OpenGL context.
nkeynes@653
    87
 */
nkeynes@653
    88
void pvr2_setup_gl_context()
nkeynes@653
    89
{
nkeynes@665
    90
nkeynes@665
    91
    if( glsl_is_supported() ) {
nkeynes@665
    92
    	if( !glsl_load_shaders( glsl_vertex_shader_src, NULL ) ) {
nkeynes@665
    93
            WARN( "Unable to load GL shaders" );
nkeynes@665
    94
        }
nkeynes@665
    95
    }
nkeynes@665
    96
    
nkeynes@653
    97
    texcache_gl_init(); // Allocate texture IDs
nkeynes@653
    98
    glCullFace( GL_BACK );
nkeynes@653
    99
    glEnable( GL_BLEND );
nkeynes@653
   100
    glEnable( GL_DEPTH_TEST );
nkeynes@653
   101
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
nkeynes@653
   102
    glMatrixMode(GL_MODELVIEW);
nkeynes@653
   103
    glLoadIdentity();
nkeynes@687
   104
    
nkeynes@687
   105
#ifdef HAVE_OPENGL_CLAMP_COLOR
nkeynes@687
   106
    if( isGLExtensionSupported("GL_ARB_color_buffer_float") ) {
nkeynes@687
   107
        glClampColorARB(GL_CLAMP_VERTEX_COLOR_ARB, GL_FALSE );
nkeynes@687
   108
        glClampColorARB(GL_CLAMP_FRAGMENT_COLOR_ARB, GL_FALSE );
nkeynes@687
   109
    }
nkeynes@687
   110
#endif
nkeynes@653
   111
nkeynes@653
   112
    glEnableClientState( GL_COLOR_ARRAY );
nkeynes@653
   113
    glEnableClientState( GL_VERTEX_ARRAY );
nkeynes@653
   114
    glEnableClientState( GL_TEXTURE_COORD_ARRAY );
nkeynes@653
   115
    glEnableClientState( GL_SECONDARY_COLOR_ARRAY );
nkeynes@653
   116
nkeynes@653
   117
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
nkeynes@653
   118
    glClearDepth(0);
nkeynes@653
   119
    glClearStencil(0);
nkeynes@653
   120
}
nkeynes@653
   121
nkeynes@653
   122
/**
nkeynes@653
   123
 * Setup the GL context for the supplied polygon context.
nkeynes@653
   124
 * @param context pointer to 3 or 5 words of polygon context
nkeynes@653
   125
 * @param modified boolean flag indicating that the modified
nkeynes@653
   126
 *  version should be used, rather than the normal version.
nkeynes@653
   127
 */
nkeynes@653
   128
void render_set_context( uint32_t *context, int render_mode )
nkeynes@653
   129
{
nkeynes@653
   130
    uint32_t poly1 = context[0], poly2, texture;
nkeynes@653
   131
    if( render_mode == RENDER_FULLMOD ) {
nkeynes@653
   132
	poly2 = context[3];
nkeynes@653
   133
	texture = context[4];
nkeynes@653
   134
    } else {
nkeynes@653
   135
	poly2 = context[1];
nkeynes@653
   136
	texture = context[2];
nkeynes@653
   137
    }
nkeynes@653
   138
    
nkeynes@653
   139
    glDepthFunc( POLY1_DEPTH_MODE(poly1) );
nkeynes@653
   140
    glDepthMask( POLY1_DEPTH_WRITE(poly1) ? GL_TRUE : GL_FALSE );
nkeynes@653
   141
    
nkeynes@653
   142
    switch( POLY1_CULL_MODE(poly1) ) {
nkeynes@653
   143
    case CULL_NONE:
nkeynes@653
   144
    case CULL_SMALL:
nkeynes@653
   145
	glDisable( GL_CULL_FACE );
nkeynes@653
   146
	break;
nkeynes@653
   147
    case CULL_CCW:
nkeynes@653
   148
	glEnable( GL_CULL_FACE );
nkeynes@653
   149
	glFrontFace( GL_CW );
nkeynes@653
   150
	break;
nkeynes@653
   151
    case CULL_CW:
nkeynes@653
   152
	glEnable( GL_CULL_FACE );
nkeynes@653
   153
	glFrontFace( GL_CCW );
nkeynes@653
   154
	break;
nkeynes@653
   155
    }
nkeynes@653
   156
nkeynes@653
   157
    if( POLY1_SPECULAR(poly1) ) {
nkeynes@653
   158
	glEnable(GL_COLOR_SUM);
nkeynes@653
   159
    } else {
nkeynes@653
   160
	glDisable(GL_COLOR_SUM);
nkeynes@653
   161
    }
nkeynes@653
   162
nkeynes@653
   163
nkeynes@653
   164
    if( POLY1_TEXTURED(poly1) ) {
nkeynes@653
   165
	glEnable(GL_TEXTURE_2D);
nkeynes@653
   166
	glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, pvr2_poly_texblend[POLY2_TEX_BLEND(poly2)] );
nkeynes@653
   167
	if( POLY2_TEX_CLAMP_U(poly2) ) {
nkeynes@653
   168
	    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
nkeynes@655
   169
	} else if( POLY2_TEX_MIRROR_U(poly2) ) {
nkeynes@655
   170
	    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT_ARB );
nkeynes@653
   171
	} else {
nkeynes@653
   172
	    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
nkeynes@653
   173
	}	    
nkeynes@653
   174
	if( POLY2_TEX_CLAMP_V(poly2) ) {
nkeynes@653
   175
	    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
nkeynes@655
   176
	} else if( POLY2_TEX_MIRROR_V(poly2) ) {
nkeynes@655
   177
	    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT_ARB );
nkeynes@653
   178
	} else {
nkeynes@653
   179
	    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
nkeynes@653
   180
	}
nkeynes@653
   181
    } else {
nkeynes@653
   182
	glDisable( GL_TEXTURE_2D );
nkeynes@653
   183
    }
nkeynes@653
   184
nkeynes@653
   185
    glShadeModel( POLY1_SHADE_MODEL(poly1) );
nkeynes@653
   186
nkeynes@653
   187
    int srcblend = POLY2_SRC_BLEND(poly2);
nkeynes@653
   188
    int destblend = POLY2_DEST_BLEND(poly2);
nkeynes@653
   189
    glBlendFunc( srcblend, destblend );
nkeynes@653
   190
nkeynes@653
   191
    if( POLY2_SRC_BLEND_TARGET(poly2) || POLY2_DEST_BLEND_TARGET(poly2) ) {
nkeynes@653
   192
	ERROR( "Accumulation buffer not supported" );
nkeynes@653
   193
    }
nkeynes@653
   194
nkeynes@653
   195
}
nkeynes@653
   196
nkeynes@653
   197
nkeynes@653
   198
static void gl_render_poly( struct polygon_struct *poly )
nkeynes@653
   199
{
nkeynes@653
   200
    if( poly->tex_id != -1 ) {
nkeynes@653
   201
	glBindTexture(GL_TEXTURE_2D, poly->tex_id);
nkeynes@653
   202
    }
nkeynes@653
   203
    render_set_context( poly->context, RENDER_NORMAL );
nkeynes@653
   204
    glDrawArrays(GL_TRIANGLE_STRIP, poly->vertex_index, poly->vertex_count );
nkeynes@653
   205
}
nkeynes@653
   206
nkeynes@687
   207
nkeynes@687
   208
static void gl_render_bkgnd( struct polygon_struct *poly )
nkeynes@687
   209
{
nkeynes@687
   210
    if( poly->tex_id != -1 ) {
nkeynes@687
   211
        glBindTexture(GL_TEXTURE_2D, poly->tex_id);
nkeynes@687
   212
    }
nkeynes@687
   213
    render_set_context( poly->context, RENDER_NORMAL );
nkeynes@687
   214
    glDisable( GL_DEPTH_TEST );
nkeynes@687
   215
    glDisable( GL_CULL_FACE );
nkeynes@687
   216
    glBlendFunc( GL_ONE, GL_ZERO );
nkeynes@687
   217
    glDrawArrays(GL_TRIANGLE_STRIP, poly->vertex_index, poly->vertex_count );
nkeynes@687
   218
    glEnable( GL_CULL_FACE );
nkeynes@687
   219
    glEnable( GL_DEPTH_TEST );
nkeynes@687
   220
}
nkeynes@687
   221
nkeynes@687
   222
nkeynes@687
   223
nkeynes@653
   224
void gl_render_tilelist( pvraddr_t tile_entry )
nkeynes@653
   225
{
nkeynes@653
   226
    uint32_t *tile_list = (uint32_t *)(video_base+tile_entry);
nkeynes@653
   227
    int strip_count;
nkeynes@653
   228
    struct polygon_struct *poly;
nkeynes@653
   229
nkeynes@653
   230
    while(1) {
nkeynes@653
   231
	uint32_t entry = *tile_list++;
nkeynes@653
   232
	switch( entry >> 28 ) {
nkeynes@653
   233
	case 0x0F:
nkeynes@653
   234
	    return; // End-of-list
nkeynes@653
   235
	case 0x0E:
nkeynes@653
   236
	    tile_list = (uint32_t *)(video_base + (entry&0x007FFFFF));
nkeynes@653
   237
	    break;
nkeynes@653
   238
	case 0x08: case 0x09: case 0x0A: case 0x0B:
nkeynes@653
   239
	    strip_count = ((entry >> 25) & 0x0F)+1;
nkeynes@653
   240
	    poly = pvr2_scene.buf_to_poly_map[entry&0x000FFFFF];
nkeynes@653
   241
	    while( strip_count > 0 ) {
nkeynes@653
   242
		assert( poly != NULL );
nkeynes@653
   243
		gl_render_poly( poly );
nkeynes@653
   244
		poly = poly->next;
nkeynes@653
   245
		strip_count--;
nkeynes@653
   246
	    }
nkeynes@653
   247
	    break;
nkeynes@653
   248
	default:
nkeynes@653
   249
	    if( entry & 0x7E000000 ) {
nkeynes@653
   250
		poly = pvr2_scene.buf_to_poly_map[entry&0x000FFFFF];
nkeynes@653
   251
		gl_render_poly( poly );
nkeynes@653
   252
	    }
nkeynes@653
   253
	}
nkeynes@653
   254
    }	    
nkeynes@653
   255
}
nkeynes@653
   256
nkeynes@653
   257
nkeynes@653
   258
/**
nkeynes@653
   259
 * Render the currently defined scene in pvr2_scene
nkeynes@653
   260
 */
nkeynes@653
   261
void pvr2_scene_render( render_buffer_t buffer )
nkeynes@653
   262
{
nkeynes@653
   263
    /* Scene setup */
nkeynes@653
   264
    struct timeval start_tv, tex_tv, end_tv;
nkeynes@653
   265
nkeynes@653
   266
    gettimeofday(&start_tv, NULL);
nkeynes@653
   267
    display_driver->set_render_target(buffer);
nkeynes@653
   268
    pvr2_check_palette_changed();
nkeynes@653
   269
    pvr2_scene_load_textures();
nkeynes@653
   270
    
nkeynes@653
   271
    gettimeofday( &tex_tv, NULL );
nkeynes@653
   272
    uint32_t ms = (tex_tv.tv_sec - start_tv.tv_sec) * 1000 +
nkeynes@653
   273
        (tex_tv.tv_usec - start_tv.tv_usec)/1000;
nkeynes@653
   274
    DEBUG( "Scene setup in %dms", ms );
nkeynes@653
   275
nkeynes@653
   276
    /* Setup view projection matrix */
nkeynes@653
   277
    glMatrixMode(GL_PROJECTION);
nkeynes@653
   278
    glLoadIdentity();
nkeynes@653
   279
    float nearz = pvr2_scene.bounds[4];
nkeynes@653
   280
    float farz = pvr2_scene.bounds[5];
nkeynes@653
   281
    if( nearz == farz ) {
nkeynes@687
   282
        farz*= 4.0;
nkeynes@653
   283
    }
nkeynes@653
   284
    glOrtho( 0, pvr2_scene.buffer_width, pvr2_scene.buffer_height, 0, 
nkeynes@687
   285
    	     -farz, -nearz );
nkeynes@653
   286
    float alphaRef = ((float)(MMIO_READ(PVR2, RENDER_ALPHA_REF)&0xFF)+1)/256.0;
nkeynes@653
   287
    glAlphaFunc( GL_GEQUAL, alphaRef );
nkeynes@653
   288
nkeynes@653
   289
    /* Clear the buffer (FIXME: May not want always want to do this) */
nkeynes@653
   290
    glDisable( GL_SCISSOR_TEST );
nkeynes@653
   291
    glDepthMask( GL_TRUE );
nkeynes@653
   292
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
nkeynes@653
   293
nkeynes@653
   294
    /* Setup vertex array pointers */
nkeynes@687
   295
    glVertexPointer(3, GL_FLOAT, sizeof(struct vertex_struct), &pvr2_scene.vertex_array[0].x);
nkeynes@687
   296
    glColorPointer(4, GL_FLOAT, sizeof(struct vertex_struct), &pvr2_scene.vertex_array[0].rgba[0]);
nkeynes@687
   297
    glTexCoordPointer(2, GL_FLOAT, sizeof(struct vertex_struct), &pvr2_scene.vertex_array[0].u);
nkeynes@687
   298
    glSecondaryColorPointerEXT(3, GL_FLOAT, sizeof(struct vertex_struct), pvr2_scene.vertex_array[0].offset_rgba );
nkeynes@653
   299
nkeynes@669
   300
    /* Turn on the shaders (if available) */
nkeynes@669
   301
    glsl_enable_shaders(TRUE);
nkeynes@669
   302
nkeynes@687
   303
    /* Render the background */
nkeynes@687
   304
    gl_render_bkgnd( pvr2_scene.bkgnd_poly );
nkeynes@653
   305
    
nkeynes@653
   306
    glEnable( GL_SCISSOR_TEST );
nkeynes@653
   307
nkeynes@653
   308
    /* Process the segment list */
nkeynes@653
   309
    struct tile_segment *segment = pvr2_scene.segment_list;
nkeynes@653
   310
    do {
nkeynes@653
   311
	int tilex = SEGMENT_X(segment->control);
nkeynes@653
   312
	int tiley = SEGMENT_Y(segment->control);
nkeynes@653
   313
	
nkeynes@669
   314
	uint32_t tile_bounds[4] = { tilex << 5, (tilex+1)<<5, tiley<<5, (tiley+1)<<5 };
nkeynes@653
   315
	if( !clip_tile_bounds(tile_bounds, pvr2_scene.bounds) ) {
nkeynes@653
   316
	    continue; // fully clipped, skip tile
nkeynes@653
   317
	}
nkeynes@653
   318
nkeynes@653
   319
	/* Clip to the visible part of the tile */
nkeynes@653
   320
	glScissor( tile_bounds[0], pvr2_scene.buffer_height-tile_bounds[3], 
nkeynes@653
   321
		   tile_bounds[1]-tile_bounds[0], tile_bounds[3] - tile_bounds[2] );
nkeynes@653
   322
	if( IS_TILE_PTR(segment->opaque_ptr) ) {
nkeynes@653
   323
	    gl_render_tilelist(segment->opaque_ptr);
nkeynes@653
   324
	}
nkeynes@653
   325
	if( IS_TILE_PTR(segment->trans_ptr) ) {
nkeynes@653
   326
	    if( pvr2_scene.sort_mode == SORT_NEVER || 
nkeynes@653
   327
		(pvr2_scene.sort_mode == SORT_TILEFLAG && (segment->control&SEGMENT_SORT_TRANS))) {
nkeynes@653
   328
		gl_render_tilelist(segment->trans_ptr);
nkeynes@653
   329
	    } else {
nkeynes@653
   330
		render_autosort_tile(segment->trans_ptr, RENDER_NORMAL );
nkeynes@653
   331
	    }
nkeynes@653
   332
	}
nkeynes@653
   333
	if( IS_TILE_PTR(segment->punchout_ptr) ) {
nkeynes@653
   334
	    glEnable(GL_ALPHA_TEST );
nkeynes@653
   335
	    render_autosort_tile(segment->punchout_ptr, RENDER_NORMAL );
nkeynes@653
   336
	    glDisable(GL_ALPHA_TEST );
nkeynes@653
   337
	}
nkeynes@653
   338
    } while( !IS_LAST_SEGMENT(segment++) );
nkeynes@653
   339
    glDisable( GL_SCISSOR_TEST );
nkeynes@653
   340
nkeynes@669
   341
    glsl_enable_shaders(FALSE);
nkeynes@669
   342
nkeynes@653
   343
    gettimeofday( &end_tv, NULL );
nkeynes@653
   344
    ms = (end_tv.tv_sec - tex_tv.tv_sec) * 1000 +
nkeynes@653
   345
        (end_tv.tv_usec - tex_tv.tv_usec)/1000;
nkeynes@653
   346
    DEBUG( "Scene render in %dms", ms );
nkeynes@653
   347
}
.