Search
lxdream.org :: lxdream/src/pvr2/render.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/pvr2/render.c
changeset 108:565de331ccec
prev103:9b9cfc5855e0
next113:ce4eb7959d56
author nkeynes
date Wed Mar 15 13:16:50 2006 +0000 (14 years ago)
permissions -rw-r--r--
last change More video WIP - displays _something_ now, at least...
view annotate diff log raw
     1 /**
     2  * $Id: render.c,v 1.3 2006-03-15 13:16:50 nkeynes Exp $
     3  *
     4  * PVR2 Renderer support. This is where the real work happens.
     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 "pvr2/pvr2.h"
    20 #include "asic.h"
    23 #define POLY_COLOUR_ARGB8888 0x00000000
    24 #define POLY_COLOUR_ARGBFLOAT 0x00000010
    26 static int pvr2_poly_vertexes[4] = { 3, 4, 6, 8 };
    27 static int pvr2_poly_type[4] = { GL_TRIANGLES, GL_QUADS, GL_TRIANGLE_STRIP, GL_TRIANGLE_STRIP };
    28 static int pvr2_poly_depthmode[8] = { GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL,
    29 				      GL_GREATER, GL_NOTEQUAL, GL_GEQUAL, 
    30 				      GL_ALWAYS };
    31 static int pvr2_poly_srcblend[8] = { 
    32     GL_ZERO, GL_ONE, GL_DST_COLOR, GL_ONE_MINUS_DST_COLOR,
    33     GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, 
    34     GL_ONE_MINUS_DST_ALPHA };
    35 static int pvr2_poly_dstblend[8] = {
    36     GL_ZERO, GL_ONE, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR,
    37     GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA,
    38     GL_ONE_MINUS_DST_ALPHA };
    39 static int pvr2_poly_texblend[4] = {
    40     GL_REPLACE, GL_BLEND, GL_DECAL, GL_MODULATE };
    41 static int pvr2_render_colour_format[8] = {
    42     COLFMT_ARGB1555, COLFMT_RGB565, COLFMT_ARGB4444, COLFMT_ARGB1555,
    43     COLFMT_RGB888, COLFMT_ARGB8888, COLFMT_ARGB8888, COLFMT_ARGB4444 };
    45 #define POLY_STRIP_TYPE(poly) ( pvr2_poly_type[((poly->command)>>18)&0x03] )
    46 #define POLY_STRIP_VERTEXES(poly) ( pvr2_poly_vertexes[((poly->command)>>18)&0x03] )
    47 #define POLY_DEPTH_MODE(poly) ( pvr2_poly_depthmode[poly->poly_cfg>>29] )
    48 #define POLY_DEPTH_WRITE(poly) (poly->poly_cfg&0x04000000)
    49 #define POLY_TEX_WIDTH(poly) ( 1<< (((poly->poly_mode >> 3) & 0x07 ) + 3) )
    50 #define POLY_TEX_HEIGHT(poly) ( 1<< (((poly->poly_mode) & 0x07 ) + 3) )
    51 #define POLY_BLEND_SRC(poly) ( pvr2_poly_srcblend[(poly->poly_mode) >> 29] )
    52 #define POLY_BLEND_DEST(poly) ( pvr2_poly_dstblend[((poly->poly_mode)>>26)&0x07] )
    53 #define POLY_TEX_BLEND(poly) ( pvr2_poly_texblend[((poly->poly_mode) >> 6)&0x03] )
    55 extern uint32_t pvr2_frame_counter;
    57 /**
    58  * Describes a rendering buffer that's actually held in GL, for when we need
    59  * to fetch the bits back to vram.
    60  */
    61 typedef struct pvr2_render_buffer {
    62     uint32_t render_addr; /* The actual address rendered to in pvr ram */
    63     int width, height;
    64     int colour_format;
    65 } *pvr2_render_buffer_t;
    67 struct pvr2_render_buffer front_buffer;
    68 struct pvr2_render_buffer back_buffer;
    70 struct tile_descriptor {
    71     uint32_t header[6];
    72     struct tile_pointers {
    73 	uint32_t tile_id;
    74 	uint32_t opaque_ptr;
    75 	uint32_t opaque_mod_ptr;
    76 	uint32_t trans_ptr;
    77 	uint32_t trans_mod_ptr;
    78 	uint32_t punchout_ptr;
    79     } tile[0];
    80 };
    82 /* Textured polygon */
    83 struct pvr2_poly {
    84     uint32_t command;
    85     uint32_t poly_cfg; /* Bitmask */
    86     uint32_t poly_mode; /* texture/blending mask */
    87     uint32_t texture; /* texture data */
    88     float alpha;
    89     float red;
    90     float green;
    91     float blue;
    92 };
    94 struct pvr2_specular_highlight {
    95     float base_alpha;
    96     float base_red;
    97     float base_green;
    98     float base_blue;
    99     float offset_alpha;
   100     float offset_red;
   101     float offset_green;
   102     float offset_blue;
   103 };
   106 struct pvr2_vertex_basic {
   107     uint32_t command;
   108     float x, y, z;
   109     float s,t;
   110     uint32_t col;
   111     float f;
   112 };
   115 void pvr2_render_copy_to_sh4( pvr2_render_buffer_t buffer, 
   116 			      gboolean backBuffer );
   118 int pvr2_render_font_list = -1;
   120 int glPrintf( const char *fmt, ... )
   121 {
   122     va_list ap;     /* our argument pointer */
   123     char buf[256];
   124     int len;
   125     if (fmt == NULL)    /* if there is no string to draw do nothing */
   126         return;
   127     va_start(ap, fmt); 
   128     len = vsnprintf(buf, sizeof(buf), fmt, ap);
   129     va_end(ap);
   131     if( pvr2_render_font_list == -1 ) {
   132 	pvr2_render_font_list = video_glx_load_font( "-*-helvetica-*-r-normal--16-*-*-*-p-*-iso8859-1");
   133     }
   135     glPushAttrib(GL_LIST_BIT);
   136     glListBase(pvr2_render_font_list - 32);
   137     glCallLists(len, GL_UNSIGNED_BYTE, buf);
   138     glPopAttrib();
   140     return len;
   141 }
   144 gboolean pvr2_render_init( void )
   145 {
   146     front_buffer.render_addr = -1;
   147     back_buffer.render_addr = -1;
   148 }
   150 /**
   151  * Display a rendered frame if one is available.
   152  * @param address An address in PVR ram (0500000 range).
   153  * @return TRUE if a frame was available to be displayed, otherwise false.
   154  */
   155 gboolean pvr2_render_display_frame( uint32_t address )
   156 {
   157     if( front_buffer.render_addr == address ) {
   158 	/* Current front buffer is already displayed, so do nothing
   159 	 * and tell the caller that all is well.
   160 	 */
   161 	return TRUE;
   162     }
   163     if( back_buffer.render_addr == address ) {
   164 	/* The more useful case - back buffer is to be displayed. Swap
   165 	 * the buffers 
   166 	 */
   167 	video_driver->display_back_buffer();
   168 	front_buffer = back_buffer;
   169 	back_buffer.render_addr = -1;
   170 	return TRUE;
   171     }
   172     return FALSE;
   173 }	
   175 /**
   176  * Prepare the OpenGL context to receive instructions for a new frame.
   177  */
   178 static void pvr2_render_prepare_context( sh4addr_t render_addr, 
   179 					 uint32_t width, uint32_t height,
   180 					 uint32_t colour_format, 
   181 					 gboolean texture_target )
   182 {
   183     /* Select and initialize the render context */
   184     video_driver->set_render_format( width, height, colour_format, texture_target );
   186     if( back_buffer.render_addr != -1 && 
   187 	back_buffer.render_addr != render_addr ) {
   188 	/* There's a current back buffer, and we're rendering somewhere else -
   189 	 * flush the back buffer back to vram and start a new back buffer
   190 	 */
   191 	pvr2_render_copy_to_sh4( &back_buffer, TRUE );
   192     }
   194     if( front_buffer.render_addr == render_addr ) {
   195 	/* In case we've been asked to render to the current front buffer -
   196 	 * invalidate the front buffer and render to the back buffer, ensuring
   197 	 * we swap at the next frame display.
   198 	 */
   199 	front_buffer.render_addr = -1;
   200     }
   201     back_buffer.render_addr = render_addr;
   202     back_buffer.width = width;
   203     back_buffer.height = height;
   204     back_buffer.colour_format = colour_format;
   206     /* Setup the display model */
   207     glDrawBuffer(GL_BACK);
   208     glShadeModel(GL_SMOOTH);
   209     glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
   210     glViewport( 0, 0, width, height );
   211     glMatrixMode(GL_PROJECTION);
   212     glLoadIdentity();
   213     glOrtho( 0, width, height, 0, 0, -65535 );
   214     glMatrixMode(GL_MODELVIEW);
   215     glLoadIdentity();
   216     glCullFace( GL_BACK );
   218     /* Clear out the buffers */
   219     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
   220     glClearDepth(1.0f);
   221     glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
   222 }
   224 static void pvr2_render_display_list( uint32_t *display_list, uint32_t length )
   225 {
   226     uint32_t *cmd_ptr = display_list;
   227     int expect_vertexes = 0;
   228     gboolean textured = FALSE;
   229     struct pvr2_poly *poly;
   230     while( cmd_ptr < display_list+length ) {
   231 	switch( *cmd_ptr >> 24 ) {
   232 	case PVR2_CMD_POLY_OPAQUE:
   233 	    poly = (struct pvr2_poly *)cmd_ptr;
   234 	    if( poly->command & PVR2_POLY_TEXTURED ) {
   235 		uint32_t addr = PVR2_TEX_ADDR(poly->texture);
   236 		int width = POLY_TEX_WIDTH(poly);
   237 		int height = POLY_TEX_HEIGHT(poly);
   238 		glEnable( GL_TEXTURE_2D );
   239 		texcache_get_texture( addr, width, height, poly->texture );
   240 		textured = TRUE;
   241 		glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, POLY_TEX_BLEND(poly) );
   242 	    } else {
   243 		textured = FALSE;
   244 		glDisable( GL_TEXTURE_2D );
   245 	    }
   246 	    glBlendFunc( POLY_BLEND_SRC(poly), POLY_BLEND_DEST(poly) );
   247 	    if( poly->command & PVR2_POLY_SPECULAR ) {
   248 		/* Second block expected */
   249 	    }
   250 	    if( POLY_DEPTH_WRITE(poly) ) {
   251 		glEnable( GL_DEPTH_TEST );
   252 		glDepthFunc( POLY_DEPTH_MODE(poly) );
   253 	    } else {
   254 		glDisable( GL_DEPTH_TEST );
   255 	    }
   257 	    switch( (poly->poly_cfg >> 27) & 0x03 ) {
   258 	    case 0:
   259 	    case 1:
   260 		glDisable( GL_CULL_FACE );
   261 		break;
   262 	    case 2:
   263 		glEnable( GL_CULL_FACE );
   264 		glFrontFace( GL_CW );
   265 		break;
   266 	    case 3:
   267 		glEnable( GL_CULL_FACE );
   268 		glFrontFace( GL_CCW );
   269 	    }
   270 	    expect_vertexes = POLY_STRIP_VERTEXES( poly );
   271 	    if( expect_vertexes == 3 )
   272 		glBegin( GL_TRIANGLES );
   273 	    else 
   274 		glBegin( GL_TRIANGLE_STRIP );
   275 	    fprintf( stderr, "Begin %d\n", expect_vertexes );
   276 	    break;
   277 	case PVR2_CMD_VERTEX_LAST:
   278 	case PVR2_CMD_VERTEX:
   279 	    if( expect_vertexes == 0 ) {
   280 		ERROR( "Unexpected vertex!" );
   281 		return;
   282 	    }
   283 	    expect_vertexes--;
   284 	    struct pvr2_vertex_basic *vertex = (struct pvr2_vertex_basic *)cmd_ptr;
   285 	    fprintf( stderr, "(%f,%f,%f)", vertex->x, vertex->y, vertex->z );
   286 	    if( textured ) {
   287 		fprintf( stderr, "{%f,%f}", vertex->s, vertex->t );
   288 		glTexCoord2f( vertex->s, vertex->t );
   289 	    }
   290 	    fprintf( stderr, "\n" );
   291 	    glVertex3f( vertex->x, vertex->y, vertex->z );
   293 	    if( expect_vertexes == 0 ) {
   294 		glEnd();
   295 		fprintf( stderr, "End" );
   296 	    }
   297 	    break;
   298 	}
   299 	cmd_ptr += 8; /* Next record */
   300     }
   301 }
   303 /**
   304  * Render a complete scene into the OpenGL back buffer.
   305  * Note: this will probably need to be broken up eventually once timings are
   306  * determined.
   307  */
   308 void pvr2_render_scene( )
   309 {
   310     struct tile_descriptor *tile_desc =
   311 	(struct tile_descriptor *)mem_get_region(PVR2_RAM_BASE + MMIO_READ( PVR2, TILEBASE ));
   313     uint32_t render_addr = MMIO_READ( PVR2, RENDADDR1 );
   314     gboolean render_to_tex;
   315     if( render_addr & 0x01000000 ) {
   316 	render_addr = (render_addr & 0x00FFFFFF) + PVR2_RAM_BASE_INT;
   317 	/* Heuristic - if we're rendering to the interlaced region we're
   318 	 * probably creating a texture rather than rendering actual output.
   319 	 * We can optimise for this case a little
   320 	 */
   321 	render_to_tex = TRUE;
   322     } else {
   323 	render_addr = (render_addr & 0x00FFFFFF) + PVR2_RAM_BASE;
   324 	render_to_tex = FALSE;
   325     }
   326     uint32_t render_mode = MMIO_READ( PVR2, RENDMODE );
   327     int width = 640; /* FIXME - get this from the tile buffer */
   328     int height = 480;
   329     int colour_format = pvr2_render_colour_format[render_mode&0x07];
   330     pvr2_render_prepare_context( render_addr, width, height, colour_format, 
   331 				 render_to_tex );
   333     uint32_t *display_list = 
   334 	(uint32_t *)mem_get_region(PVR2_RAM_BASE + MMIO_READ( PVR2, OBJBASE ));
   335     uint32_t display_length = *display_list++;
   337     int clip_x = MMIO_READ( PVR2, HCLIP ) & 0x03FF;
   338     int clip_y = MMIO_READ( PVR2, VCLIP ) & 0x03FF;
   339     int clip_width = ((MMIO_READ( PVR2, HCLIP ) >> 16) & 0x03FF) - clip_x + 1;
   340     int clip_height= ((MMIO_READ( PVR2, VCLIP ) >> 16) & 0x03FF) - clip_y + 1;
   342     if( clip_x == 0 && clip_y == 0 && clip_width == width && clip_height == height ) {
   343 	glDisable( GL_SCISSOR_TEST );
   344     } else {
   345 	glEnable( GL_SCISSOR_TEST );
   346 	glScissor( clip_x, clip_y, clip_width, clip_height );
   347     }
   349     /* Fog setup goes here */
   351     /* Render the display list */
   352     pvr2_render_display_list( display_list, display_length );
   354     /* Post-render cleanup and update */
   356     /* Add frame, fps, etc data */
   357     glRasterPos2i( 4, 16 );
   358     //    glColor3f( 0.0f, 0.0f, 1.0f );
   359     glPrintf( "Frame %d", pvr2_frame_counter );
   361     /* Generate end of render event */
   362     asic_event( EVENT_PVR_RENDER_DONE );
   363     DEBUG( "Rendered frame %d", pvr2_frame_counter );
   364 }
   367 /**
   368  * Flush the indicated render buffer back to PVR. Caller is responsible for
   369  * tracking whether there is actually anything in the buffer.
   370  *
   371  * @param buffer A render buffer indicating the address to store to, and the
   372  * format the data needs to be in.
   373  * @param backBuffer TRUE to flush the back buffer, FALSE for 
   374  * the front buffer.
   375  */
   376 void pvr2_render_copy_to_sh4( pvr2_render_buffer_t buffer, 
   377 			      gboolean backBuffer )
   378 {
   379     if( buffer->render_addr == -1 )
   380 	return;
   381     GLenum type, format = GL_RGBA;
   382     int size = buffer->width * buffer->height;
   384     switch( buffer->colour_format ) {
   385     case COLFMT_RGB565: 
   386 	type = GL_UNSIGNED_SHORT_5_6_5; 
   387 	format = GL_RGB; 
   388 	size <<= 1;
   389 	break;
   390     case COLFMT_RGB888: 
   391 	type = GL_UNSIGNED_INT; 
   392 	format = GL_RGB;
   393 	size = (size<<1)+size;
   394 	break;
   395     case COLFMT_ARGB1555: 
   396 	type = GL_UNSIGNED_SHORT_5_5_5_1; 
   397 	size <<= 1;
   398 	break;
   399     case COLFMT_ARGB4444: 
   400 	type = GL_UNSIGNED_SHORT_4_4_4_4; 
   401 	size <<= 1;
   402 	break;
   403     case COLFMT_ARGB8888: 
   404 	type = GL_UNSIGNED_INT_8_8_8_8; 
   405 	size <<= 2;
   406 	break;
   407     }
   409     if( backBuffer ) {
   410 	glFinish();
   411 	glReadBuffer( GL_BACK );
   412     } else {
   413 	glReadBuffer( GL_FRONT );
   414     }
   416     if( buffer->render_addr & 0xFF000000 == 0x04000000 ) {
   417 	/* Interlaced buffer. Go the double copy... :( */
   418 	char target[size];
   419 	glReadPixels( 0, 0, buffer->width, buffer->height, format, type, target );
   420 	pvr2_vram64_write( buffer->render_addr, target, size );
   421     } else {
   422 	/* Regular buffer - go direct */
   423 	char *target = mem_get_region( buffer->render_addr );
   424 	glReadPixels( 0, 0, buffer->width, buffer->height, format, type, target );
   425     }
   426 }
   429 /**
   430  * Copy data from PVR ram into the GL render buffer. 
   431  *
   432  * @param buffer A render buffer indicating the address to read from, and the
   433  * format the data is in.
   434  * @param backBuffer TRUE to write the back buffer, FALSE for 
   435  * the front buffer.
   436  */
   437 void pvr2_render_copy_from_sh4( pvr2_render_buffer_t buffer, 
   438 				gboolean backBuffer )
   439 {
   440     if( buffer->render_addr == -1 )
   441 	return;
   442     GLenum type, format = GL_RGBA;
   443     int size = buffer->width * buffer->height;
   445     switch( buffer->colour_format ) {
   446     case COLFMT_RGB565: 
   447 	type = GL_UNSIGNED_SHORT_5_6_5; 
   448 	format = GL_RGB; 
   449 	size <<= 1;
   450 	break;
   451     case COLFMT_RGB888: 
   452 	type = GL_UNSIGNED_INT; 
   453 	format = GL_RGB;
   454 	size = (size<<1)+size;
   455 	break;
   456     case COLFMT_ARGB1555: 
   457 	type = GL_UNSIGNED_SHORT_5_5_5_1; 
   458 	size <<= 1;
   459 	break;
   460     case COLFMT_ARGB4444: 
   461 	type = GL_UNSIGNED_SHORT_4_4_4_4; 
   462 	size <<= 1;
   463 	break;
   464     case COLFMT_ARGB8888: 
   465 	type = GL_UNSIGNED_INT_8_8_8_8; 
   466 	size <<= 2;
   467 	break;
   468     }
   470     if( backBuffer ) {
   471 	glDrawBuffer( GL_BACK );
   472     } else {
   473 	glDrawBuffer( GL_FRONT );
   474     }
   476     glRasterPos2i( 0, 0 );
   477     if( buffer->render_addr & 0xFF000000 == 0x04000000 ) {
   478 	/* Interlaced buffer. Go the double copy... :( */
   479 	char target[size];
   480 	pvr2_vram64_read( target, buffer->render_addr, size );
   481 	glDrawPixels( buffer->width, buffer->height, 
   482 		      format, type, target );
   483     } else {
   484 	/* Regular buffer - go direct */
   485 	char *target = mem_get_region( buffer->render_addr );
   486 	glDrawPixels( buffer->width, buffer->height, 
   487 		      format, type, target );
   488     }
   489 }
.