Search
lxdream.org :: lxdream/src/pvr2/render.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/pvr2/render.c
changeset 113:ce4eb7959d56
prev108:565de331ccec
next118:fcec54454d10
author nkeynes
date Mon Mar 20 11:58:37 2006 +0000 (18 years ago)
permissions -rw-r--r--
last change Add an fwrite_dump() method to do a binary block dump
view annotate diff log raw
     1 /**
     2  * $Id: render.c,v 1.4 2006-03-16 12:42:39 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 	    break;
   276 	case PVR2_CMD_VERTEX_LAST:
   277 	case PVR2_CMD_VERTEX:
   278 	    if( expect_vertexes == 0 ) {
   279 		ERROR( "Unexpected vertex!" );
   280 		return;
   281 	    }
   282 	    expect_vertexes--;
   283 	    struct pvr2_vertex_basic *vertex = (struct pvr2_vertex_basic *)cmd_ptr;
   284 	    if( textured ) {
   285 		glTexCoord2f( vertex->s, vertex->t );
   286 	    }
   287 	    glVertex3f( vertex->x, vertex->y, vertex->z );
   289 	    if( expect_vertexes == 0 ) {
   290 		glEnd();
   291 	    }
   292 	    break;
   293 	}
   294 	cmd_ptr += 8; /* Next record */
   295     }
   296 }
   298 /**
   299  * Render a complete scene into the OpenGL back buffer.
   300  * Note: this will probably need to be broken up eventually once timings are
   301  * determined.
   302  */
   303 void pvr2_render_scene( )
   304 {
   305     struct tile_descriptor *tile_desc =
   306 	(struct tile_descriptor *)mem_get_region(PVR2_RAM_BASE + MMIO_READ( PVR2, TILEBASE ));
   308     uint32_t render_addr = MMIO_READ( PVR2, RENDADDR1 );
   309     gboolean render_to_tex;
   310     if( render_addr & 0x01000000 ) {
   311 	render_addr = (render_addr & 0x00FFFFFF) + PVR2_RAM_BASE_INT;
   312 	/* Heuristic - if we're rendering to the interlaced region we're
   313 	 * probably creating a texture rather than rendering actual output.
   314 	 * We can optimise for this case a little
   315 	 */
   316 	render_to_tex = TRUE;
   317     } else {
   318 	render_addr = (render_addr & 0x00FFFFFF) + PVR2_RAM_BASE;
   319 	render_to_tex = FALSE;
   320     }
   321     uint32_t render_mode = MMIO_READ( PVR2, RENDMODE );
   322     int width = 640; /* FIXME - get this from the tile buffer */
   323     int height = 480;
   324     int colour_format = pvr2_render_colour_format[render_mode&0x07];
   325     pvr2_render_prepare_context( render_addr, width, height, colour_format, 
   326 				 render_to_tex );
   328     uint32_t *display_list = 
   329 	(uint32_t *)mem_get_region(PVR2_RAM_BASE + MMIO_READ( PVR2, OBJBASE ));
   330     uint32_t display_length = *display_list++;
   332     int clip_x = MMIO_READ( PVR2, HCLIP ) & 0x03FF;
   333     int clip_y = MMIO_READ( PVR2, VCLIP ) & 0x03FF;
   334     int clip_width = ((MMIO_READ( PVR2, HCLIP ) >> 16) & 0x03FF) - clip_x + 1;
   335     int clip_height= ((MMIO_READ( PVR2, VCLIP ) >> 16) & 0x03FF) - clip_y + 1;
   337     if( clip_x == 0 && clip_y == 0 && clip_width == width && clip_height == height ) {
   338 	glDisable( GL_SCISSOR_TEST );
   339     } else {
   340 	glEnable( GL_SCISSOR_TEST );
   341 	glScissor( clip_x, clip_y, clip_width, clip_height );
   342     }
   344     /* Fog setup goes here */
   346     /* Render the display list */
   347     pvr2_render_display_list( display_list, display_length );
   349     /* Post-render cleanup and update */
   351     /* Add frame, fps, etc data */
   352     glRasterPos2i( 4, 16 );
   353     //    glColor3f( 0.0f, 0.0f, 1.0f );
   354     glPrintf( "Frame %d", pvr2_frame_counter );
   356     /* Generate end of render event */
   357     asic_event( EVENT_PVR_RENDER_DONE );
   358     DEBUG( "Rendered frame %d", pvr2_frame_counter );
   359 }
   362 /**
   363  * Flush the indicated render buffer back to PVR. Caller is responsible for
   364  * tracking whether there is actually anything in the buffer.
   365  *
   366  * @param buffer A render buffer indicating the address to store to, and the
   367  * format the data needs to be in.
   368  * @param backBuffer TRUE to flush the back buffer, FALSE for 
   369  * the front buffer.
   370  */
   371 void pvr2_render_copy_to_sh4( pvr2_render_buffer_t buffer, 
   372 			      gboolean backBuffer )
   373 {
   374     if( buffer->render_addr == -1 )
   375 	return;
   376     GLenum type, format = GL_RGBA;
   377     int size = buffer->width * buffer->height;
   379     switch( buffer->colour_format ) {
   380     case COLFMT_RGB565: 
   381 	type = GL_UNSIGNED_SHORT_5_6_5; 
   382 	format = GL_RGB; 
   383 	size <<= 1;
   384 	break;
   385     case COLFMT_RGB888: 
   386 	type = GL_UNSIGNED_INT; 
   387 	format = GL_RGB;
   388 	size = (size<<1)+size;
   389 	break;
   390     case COLFMT_ARGB1555: 
   391 	type = GL_UNSIGNED_SHORT_5_5_5_1; 
   392 	size <<= 1;
   393 	break;
   394     case COLFMT_ARGB4444: 
   395 	type = GL_UNSIGNED_SHORT_4_4_4_4; 
   396 	size <<= 1;
   397 	break;
   398     case COLFMT_ARGB8888: 
   399 	type = GL_UNSIGNED_INT_8_8_8_8; 
   400 	size <<= 2;
   401 	break;
   402     }
   404     if( backBuffer ) {
   405 	glFinish();
   406 	glReadBuffer( GL_BACK );
   407     } else {
   408 	glReadBuffer( GL_FRONT );
   409     }
   411     if( buffer->render_addr & 0xFF000000 == 0x04000000 ) {
   412 	/* Interlaced buffer. Go the double copy... :( */
   413 	char target[size];
   414 	glReadPixels( 0, 0, buffer->width, buffer->height, format, type, target );
   415 	pvr2_vram64_write( buffer->render_addr, target, size );
   416     } else {
   417 	/* Regular buffer - go direct */
   418 	char *target = mem_get_region( buffer->render_addr );
   419 	glReadPixels( 0, 0, buffer->width, buffer->height, format, type, target );
   420     }
   421 }
   424 /**
   425  * Copy data from PVR ram into the GL render buffer. 
   426  *
   427  * @param buffer A render buffer indicating the address to read from, and the
   428  * format the data is in.
   429  * @param backBuffer TRUE to write the back buffer, FALSE for 
   430  * the front buffer.
   431  */
   432 void pvr2_render_copy_from_sh4( pvr2_render_buffer_t buffer, 
   433 				gboolean backBuffer )
   434 {
   435     if( buffer->render_addr == -1 )
   436 	return;
   437     GLenum type, format = GL_RGBA;
   438     int size = buffer->width * buffer->height;
   440     switch( buffer->colour_format ) {
   441     case COLFMT_RGB565: 
   442 	type = GL_UNSIGNED_SHORT_5_6_5; 
   443 	format = GL_RGB; 
   444 	size <<= 1;
   445 	break;
   446     case COLFMT_RGB888: 
   447 	type = GL_UNSIGNED_INT; 
   448 	format = GL_RGB;
   449 	size = (size<<1)+size;
   450 	break;
   451     case COLFMT_ARGB1555: 
   452 	type = GL_UNSIGNED_SHORT_5_5_5_1; 
   453 	size <<= 1;
   454 	break;
   455     case COLFMT_ARGB4444: 
   456 	type = GL_UNSIGNED_SHORT_4_4_4_4; 
   457 	size <<= 1;
   458 	break;
   459     case COLFMT_ARGB8888: 
   460 	type = GL_UNSIGNED_INT_8_8_8_8; 
   461 	size <<= 2;
   462 	break;
   463     }
   465     if( backBuffer ) {
   466 	glDrawBuffer( GL_BACK );
   467     } else {
   468 	glDrawBuffer( GL_FRONT );
   469     }
   471     glRasterPos2i( 0, 0 );
   472     if( buffer->render_addr & 0xFF000000 == 0x04000000 ) {
   473 	/* Interlaced buffer. Go the double copy... :( */
   474 	char target[size];
   475 	pvr2_vram64_read( target, buffer->render_addr, size );
   476 	glDrawPixels( buffer->width, buffer->height, 
   477 		      format, type, target );
   478     } else {
   479 	/* Regular buffer - go direct */
   480 	char *target = mem_get_region( buffer->render_addr );
   481 	glDrawPixels( buffer->width, buffer->height, 
   482 		      format, type, target );
   483     }
   484 }
.