Search
lxdream.org :: lxdream/src/pvr2/render.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/pvr2/render.c
changeset 103:9b9cfc5855e0
prev100:995e42e96cc9
next108:565de331ccec
author nkeynes
date Mon Mar 13 12:39:07 2006 +0000 (14 years ago)
permissions -rw-r--r--
last change More rendering work in progress. Almost there now...
view annotate diff log raw
     1 /**
     2  * $Id: render.c,v 1.2 2006-03-13 12:39:07 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_render_colour_format[8] = {
    40     COLFMT_ARGB1555, COLFMT_RGB565, COLFMT_ARGB4444, COLFMT_ARGB1555,
    41     COLFMT_RGB888, COLFMT_ARGB8888, COLFMT_ARGB8888, COLFMT_ARGB4444 };
    43 #define POLY_STRIP_TYPE(poly) ( pvr2_poly_type[((poly->command)>>18)&0x03] )
    44 #define POLY_STRIP_VERTEXES(poly) ( pvr2_poly_vertexes[((poly->command)>>18)&0x03] )
    45 #define POLY_DEPTH_MODE(poly) ( pvr2_poly_depthmode[poly->poly_cfg>>29] )
    46 #define POLY_DEPTH_WRITE(poly) (poly->poly_cfg&0x04000000)
    47 #define POLY_TEX_WIDTH(poly) ( 1<< (((poly->poly_mode >> 3) & 0x07 ) + 3) )
    48 #define POLY_TEX_HEIGHT(poly) ( 1<< (((poly->poly_mode) & 0x07 ) + 3) )
    49 #define POLY_BLEND_SRC(poly) ( pvr2_poly_srcblend[(poly->poly_mode) >> 29] )
    50 #define POLY_BLEND_DEST(poly) ( pvr2_poly_dstblend[((poly->poly_mode)>>26)&0x07] )
    52 extern uint32_t pvr2_frame_counter;
    54 /**
    55  * Describes a rendering buffer that's actually held in GL, for when we need
    56  * to fetch the bits back to vram.
    57  */
    58 typedef struct pvr2_render_buffer {
    59     uint32_t render_addr; /* The actual address rendered to in pvr ram */
    60     int width, height;
    61     int colour_format;
    62 } *pvr2_render_buffer_t;
    64 struct pvr2_render_buffer front_buffer;
    65 struct pvr2_render_buffer back_buffer;
    67 struct tile_descriptor {
    68     uint32_t header[6];
    69     struct tile_pointers {
    70 	uint32_t tile_id;
    71 	uint32_t opaque_ptr;
    72 	uint32_t opaque_mod_ptr;
    73 	uint32_t trans_ptr;
    74 	uint32_t trans_mod_ptr;
    75 	uint32_t punchout_ptr;
    76     } tile[0];
    77 };
    79 /* Textured polygon */
    80 struct pvr2_poly {
    81     uint32_t command;
    82     uint32_t poly_cfg; /* Bitmask */
    83     uint32_t poly_mode; /* texture/blending mask */
    84     uint32_t texture; /* texture data */
    85     float alpha;
    86     float red;
    87     float green;
    88     float blue;
    89 };
    91 struct pvr2_specular_highlight {
    92     float base_alpha;
    93     float base_red;
    94     float base_green;
    95     float base_blue;
    96     float offset_alpha;
    97     float offset_red;
    98     float offset_green;
    99     float offset_blue;
   100 };
   103 struct pvr2_vertex_basic {
   104     uint32_t command;
   105     float x, y, z;
   106     float s,t;
   107     uint32_t col;
   108     float f;
   109 };
   112 void pvr2_render_copy_to_sh4( pvr2_render_buffer_t buffer, 
   113 			      gboolean backBuffer );
   116 gboolean pvr2_render_init( void )
   117 {
   118     front_buffer.render_addr = -1;
   119     back_buffer.render_addr = -1;
   120 }
   122 /**
   123  * Display a rendered frame if one is available.
   124  * @param address An address in PVR ram (0500000 range).
   125  * @return TRUE if a frame was available to be displayed, otherwise false.
   126  */
   127 gboolean pvr2_render_display_frame( uint32_t address )
   128 {
   129     if( front_buffer.render_addr == address ) {
   130 	/* Current front buffer is already displayed, so do nothing
   131 	 * and tell the caller that all is well.
   132 	 */
   133 	return TRUE;
   134     }
   135     if( back_buffer.render_addr == address ) {
   136 	/* The more useful case - back buffer is to be displayed. Swap
   137 	 * the buffers 
   138 	 */
   139 	video_driver->display_back_buffer();
   140 	front_buffer = back_buffer;
   141 	back_buffer.render_addr = -1;
   142 	return TRUE;
   143     }
   144     return FALSE;
   145 }
   147 /**
   148  * Prepare the OpenGL context to receive instructions for a new frame.
   149  */
   150 static void pvr2_render_prepare_context( sh4addr_t render_addr, 
   151 					 uint32_t width, uint32_t height,
   152 					 uint32_t colour_format, 
   153 					 gboolean texture_target )
   154 {
   155     /* Select and initialize the render context */
   156     video_driver->set_render_format( width, height, colour_format, texture_target );
   158     if( back_buffer.render_addr != -1 && 
   159 	back_buffer.render_addr != render_addr ) {
   160 	/* There's a current back buffer, and we're rendering somewhere else -
   161 	 * flush the back buffer back to vram and start a new back buffer
   162 	 */
   163 	pvr2_render_copy_to_sh4( &back_buffer, TRUE );
   164     }
   166     if( front_buffer.render_addr == render_addr ) {
   167 	/* In case we've been asked to render to the current front buffer -
   168 	 * invalidate the front buffer and render to the back buffer, ensuring
   169 	 * we swap at the next frame display.
   170 	 */
   171 	front_buffer.render_addr = -1;
   172     }
   173     back_buffer.render_addr = render_addr;
   174     back_buffer.width = width;
   175     back_buffer.height = height;
   176     back_buffer.colour_format = colour_format;
   178     /* Setup the display model */
   179     glDrawBuffer(GL_BACK);
   180     glShadeModel(GL_SMOOTH);
   181     glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
   182     glViewport( 0, 0, width, height );
   183     glMatrixMode(GL_PROJECTION);
   184     glLoadIdentity();
   185     glOrtho( 0, width, height, 0, 0, 65535 );
   186     glMatrixMode(GL_MODELVIEW);
   187     glLoadIdentity();
   189     /* Clear out the buffers */
   190     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
   191     glClearDepth(1.0f);
   192     glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
   193 }
   195 static void pvr2_render_display_list( uint32_t *display_list, uint32_t length )
   196 {
   197     uint32_t *cmd_ptr = display_list;
   198     int expect_vertexes = 0;
   199     gboolean textured = FALSE;
   200     struct pvr2_poly *poly;
   201     while( cmd_ptr < display_list+length ) {
   202 	switch( *cmd_ptr >> 24 ) {
   203 	case PVR2_CMD_POLY_OPAQUE:
   204 	    poly = (struct pvr2_poly *)cmd_ptr;
   206 	    if( poly->command & PVR2_POLY_TEXTURED ) {
   207 		uint32_t addr = PVR2_TEX_ADDR(poly->texture);
   208 		int width = POLY_TEX_WIDTH(poly);
   209 		int height = POLY_TEX_HEIGHT(poly);
   210 		texcache_get_texture( addr, width, height, poly->texture );
   211 		textured = TRUE;
   212 		glEnable( GL_TEXTURE_2D );
   213 	    } else {
   214 		textured = FALSE;
   215 		glDisable( GL_TEXTURE_2D );
   216 	    }
   217 	    glBlendFunc( POLY_BLEND_SRC(poly), POLY_BLEND_DEST(poly) );
   218 	    if( poly->command & PVR2_POLY_SPECULAR ) {
   219 		/* Second block expected */
   220 	    }
   221 	    if( POLY_DEPTH_WRITE(poly) ) {
   222 		glEnable( GL_DEPTH_TEST );
   223 		glDepthFunc( POLY_DEPTH_MODE(poly) );
   224 	    } else {
   225 		glDisable( GL_DEPTH_TEST );
   226 	    }
   228 	    expect_vertexes = POLY_STRIP_VERTEXES( poly );
   229 	    if( expect_vertexes == 3 )
   230 		glBegin( GL_TRIANGLES );
   231 	    else if( expect_vertexes == 4 )
   232 		glBegin( GL_QUADS );
   233 	    else 
   234 		glBegin( GL_TRIANGLE_STRIP );
   235 	    break;
   236 	case PVR2_CMD_VERTEX_LAST:
   237 	case PVR2_CMD_VERTEX:
   238 	    if( expect_vertexes == 0 ) {
   239 		ERROR( "Unexpected vertex!" );
   240 		return;
   241 	    }
   242 	    expect_vertexes--;
   243 	    struct pvr2_vertex_basic *vertex = (struct pvr2_vertex_basic *)cmd_ptr;
   244 	    if( textured ) {
   245 		glTexCoord2f( vertex->s, vertex->t );
   246 	    }
   247 	    glVertex3f( vertex->x, vertex->y, vertex->z );
   249 	    if( expect_vertexes == 0 )
   250 		glEnd();
   251 	    break;
   252 	}
   253 	cmd_ptr += 8; /* Next record */
   254     }
   255 }
   257 /**
   258  * Render a complete scene into the OpenGL back buffer.
   259  * Note: this will probably need to be broken up eventually once timings are
   260  * determined.
   261  */
   262 void pvr2_render_scene( )
   263 {
   264     struct tile_descriptor *tile_desc =
   265 	(struct tile_descriptor *)mem_get_region(PVR2_RAM_BASE + MMIO_READ( PVR2, TILEBASE ));
   267     uint32_t render_addr = MMIO_READ( PVR2, RENDADDR1 );
   268     gboolean render_to_tex;
   269     if( render_addr & 0x01000000 ) {
   270 	render_addr = (render_addr & 0x00FFFFFF) + PVR2_RAM_BASE_INT;
   271 	/* Heuristic - if we're rendering to the interlaced region we're
   272 	 * probably creating a texture rather than rendering actual output.
   273 	 * We can optimise for this case a little
   274 	 */
   275 	render_to_tex = TRUE;
   276     } else {
   277 	render_addr = (render_addr & 0x00FFFFFF) + PVR2_RAM_BASE;
   278 	render_to_tex = FALSE;
   279     }
   280     uint32_t render_mode = MMIO_READ( PVR2, RENDMODE );
   281     int width = 640; /* FIXME - get this from the tile buffer */
   282     int height = 480;
   283     int colour_format = pvr2_render_colour_format[render_mode&0x07];
   284     pvr2_render_prepare_context( render_addr, width, height, colour_format, 
   285 				 render_to_tex );
   287     uint32_t *display_list = 
   288 	(uint32_t *)mem_get_region(PVR2_RAM_BASE + MMIO_READ( PVR2, OBJBASE ));
   289     uint32_t display_length = *display_list++;
   291     int clip_x = MMIO_READ( PVR2, HCLIP ) & 0x03FF;
   292     int clip_y = MMIO_READ( PVR2, VCLIP ) & 0x03FF;
   293     int clip_width = ((MMIO_READ( PVR2, HCLIP ) >> 16) & 0x03FF) - clip_x + 1;
   294     int clip_height= ((MMIO_READ( PVR2, VCLIP ) >> 16) & 0x03FF) - clip_y + 1;
   296     if( clip_x == 0 && clip_y == 0 && clip_width == width && clip_height == height ) {
   297 	glDisable( GL_SCISSOR_TEST );
   298     } else {
   299 	glEnable( GL_SCISSOR_TEST );
   300 	glScissor( clip_x, clip_y, clip_width, clip_height );
   301     }
   303     /* Fog setup goes here */
   305     /* Render the display list */
   306     pvr2_render_display_list( display_list, display_length );
   308     /* Post-render cleanup and update */
   311     /* Generate end of render event */
   312     asic_event( EVENT_PVR_RENDER_DONE );
   313     DEBUG( "Rendered frame %d", pvr2_frame_counter );
   314 }
   317 /**
   318  * Flush the indicated render buffer back to PVR. Caller is responsible for
   319  * tracking whether there is actually anything in the buffer.
   320  *
   321  * @param buffer A render buffer indicating the address to store to, and the
   322  * format the data needs to be in.
   323  * @param backBuffer TRUE to flush the back buffer, FALSE for 
   324  * the front buffer.
   325  */
   326 void pvr2_render_copy_to_sh4( pvr2_render_buffer_t buffer, 
   327 			      gboolean backBuffer )
   328 {
   329     if( buffer->render_addr == -1 )
   330 	return;
   331     GLenum type, format = GL_RGBA;
   332     int size = buffer->width * buffer->height;
   334     switch( buffer->colour_format ) {
   335     case COLFMT_RGB565: 
   336 	type = GL_UNSIGNED_SHORT_5_6_5; 
   337 	format = GL_RGB; 
   338 	size <<= 1;
   339 	break;
   340     case COLFMT_RGB888: 
   341 	type = GL_UNSIGNED_INT; 
   342 	format = GL_RGB;
   343 	size = (size<<1)+size;
   344 	break;
   345     case COLFMT_ARGB1555: 
   346 	type = GL_UNSIGNED_SHORT_5_5_5_1; 
   347 	size <<= 1;
   348 	break;
   349     case COLFMT_ARGB4444: 
   350 	type = GL_UNSIGNED_SHORT_4_4_4_4; 
   351 	size <<= 1;
   352 	break;
   353     case COLFMT_ARGB8888: 
   354 	type = GL_UNSIGNED_INT_8_8_8_8; 
   355 	size <<= 2;
   356 	break;
   357     }
   359     if( backBuffer ) {
   360 	glFinish();
   361 	glReadBuffer( GL_BACK );
   362     } else {
   363 	glReadBuffer( GL_FRONT );
   364     }
   366     if( buffer->render_addr & 0xFF000000 == 0x04000000 ) {
   367 	/* Interlaced buffer. Go the double copy... :( */
   368 	char target[size];
   369 	glReadPixels( 0, 0, buffer->width, buffer->height, format, type, target );
   370 	pvr2_vram64_write( buffer->render_addr, target, size );
   371     } else {
   372 	/* Regular buffer - go direct */
   373 	char *target = mem_get_region( buffer->render_addr );
   374 	glReadPixels( 0, 0, buffer->width, buffer->height, format, type, target );
   375     }
   376 }
   379 /**
   380  * Copy data from PVR ram into the GL render buffer. 
   381  *
   382  * @param buffer A render buffer indicating the address to read from, and the
   383  * format the data is in.
   384  * @param backBuffer TRUE to write the back buffer, FALSE for 
   385  * the front buffer.
   386  */
   387 void pvr2_render_copy_from_sh4( pvr2_render_buffer_t buffer, 
   388 				gboolean backBuffer )
   389 {
   390     if( buffer->render_addr == -1 )
   391 	return;
   392     GLenum type, format = GL_RGBA;
   393     int size = buffer->width * buffer->height;
   395     switch( buffer->colour_format ) {
   396     case COLFMT_RGB565: 
   397 	type = GL_UNSIGNED_SHORT_5_6_5; 
   398 	format = GL_RGB; 
   399 	size <<= 1;
   400 	break;
   401     case COLFMT_RGB888: 
   402 	type = GL_UNSIGNED_INT; 
   403 	format = GL_RGB;
   404 	size = (size<<1)+size;
   405 	break;
   406     case COLFMT_ARGB1555: 
   407 	type = GL_UNSIGNED_SHORT_5_5_5_1; 
   408 	size <<= 1;
   409 	break;
   410     case COLFMT_ARGB4444: 
   411 	type = GL_UNSIGNED_SHORT_4_4_4_4; 
   412 	size <<= 1;
   413 	break;
   414     case COLFMT_ARGB8888: 
   415 	type = GL_UNSIGNED_INT_8_8_8_8; 
   416 	size <<= 2;
   417 	break;
   418     }
   420     if( backBuffer ) {
   421 	glDrawBuffer( GL_BACK );
   422     } else {
   423 	glDrawBuffer( GL_FRONT );
   424     }
   426     glRasterPos2i( 0, 0 );
   427     if( buffer->render_addr & 0xFF000000 == 0x04000000 ) {
   428 	/* Interlaced buffer. Go the double copy... :( */
   429 	char target[size];
   430 	pvr2_vram64_read( target, buffer->render_addr, size );
   431 	glDrawPixels( buffer->width, buffer->height, 
   432 		      format, type, target );
   433     } else {
   434 	/* Regular buffer - go direct */
   435 	char *target = mem_get_region( buffer->render_addr );
   436 	glDrawPixels( buffer->width, buffer->height, 
   437 		      format, type, target );
   438     }
   439 }
.