Search
lxdream.org :: lxdream/src/pvr2/render.c :: diff
lxdream 0.9.1
released Jun 29
Download Now
filename src/pvr2/render.c
changeset 103:9b9cfc5855e0
prev100:995e42e96cc9
next108:565de331ccec
author nkeynes
date Tue Mar 14 11:44:29 2006 +0000 (18 years ago)
permissions -rw-r--r--
last change Add elf loader suppt
file annotate diff log raw
1.1 --- a/src/pvr2/render.c Wed Feb 15 13:11:50 2006 +0000
1.2 +++ b/src/pvr2/render.c Tue Mar 14 11:44:29 2006 +0000
1.3 @@ -1,5 +1,5 @@
1.4 /**
1.5 - * $Id: render.c,v 1.1 2006-02-15 13:11:46 nkeynes Exp $
1.6 + * $Id: render.c,v 1.2 2006-03-13 12:39:07 nkeynes Exp $
1.7 *
1.8 * PVR2 Renderer support. This is where the real work happens.
1.9 *
1.10 @@ -18,20 +18,422 @@
1.11
1.12 #include "pvr2/pvr2.h"
1.13 #include "asic.h"
1.14 -#include "dream.h"
1.15 +
1.16 +
1.17 +#define POLY_COLOUR_ARGB8888 0x00000000
1.18 +#define POLY_COLOUR_ARGBFLOAT 0x00000010
1.19 +
1.20 +static int pvr2_poly_vertexes[4] = { 3, 4, 6, 8 };
1.21 +static int pvr2_poly_type[4] = { GL_TRIANGLES, GL_QUADS, GL_TRIANGLE_STRIP, GL_TRIANGLE_STRIP };
1.22 +static int pvr2_poly_depthmode[8] = { GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL,
1.23 + GL_GREATER, GL_NOTEQUAL, GL_GEQUAL,
1.24 + GL_ALWAYS };
1.25 +static int pvr2_poly_srcblend[8] = {
1.26 + GL_ZERO, GL_ONE, GL_DST_COLOR, GL_ONE_MINUS_DST_COLOR,
1.27 + GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA,
1.28 + GL_ONE_MINUS_DST_ALPHA };
1.29 +static int pvr2_poly_dstblend[8] = {
1.30 + GL_ZERO, GL_ONE, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR,
1.31 + GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA,
1.32 + GL_ONE_MINUS_DST_ALPHA };
1.33 +static int pvr2_render_colour_format[8] = {
1.34 + COLFMT_ARGB1555, COLFMT_RGB565, COLFMT_ARGB4444, COLFMT_ARGB1555,
1.35 + COLFMT_RGB888, COLFMT_ARGB8888, COLFMT_ARGB8888, COLFMT_ARGB4444 };
1.36 +
1.37 +#define POLY_STRIP_TYPE(poly) ( pvr2_poly_type[((poly->command)>>18)&0x03] )
1.38 +#define POLY_STRIP_VERTEXES(poly) ( pvr2_poly_vertexes[((poly->command)>>18)&0x03] )
1.39 +#define POLY_DEPTH_MODE(poly) ( pvr2_poly_depthmode[poly->poly_cfg>>29] )
1.40 +#define POLY_DEPTH_WRITE(poly) (poly->poly_cfg&0x04000000)
1.41 +#define POLY_TEX_WIDTH(poly) ( 1<< (((poly->poly_mode >> 3) & 0x07 ) + 3) )
1.42 +#define POLY_TEX_HEIGHT(poly) ( 1<< (((poly->poly_mode) & 0x07 ) + 3) )
1.43 +#define POLY_BLEND_SRC(poly) ( pvr2_poly_srcblend[(poly->poly_mode) >> 29] )
1.44 +#define POLY_BLEND_DEST(poly) ( pvr2_poly_dstblend[((poly->poly_mode)>>26)&0x07] )
1.45
1.46 extern uint32_t pvr2_frame_counter;
1.47
1.48 /**
1.49 - * Render a complete scene into an OpenGL buffer.
1.50 - * Note: this may need to be broken up eventually once timings are
1.51 + * Describes a rendering buffer that's actually held in GL, for when we need
1.52 + * to fetch the bits back to vram.
1.53 + */
1.54 +typedef struct pvr2_render_buffer {
1.55 + uint32_t render_addr; /* The actual address rendered to in pvr ram */
1.56 + int width, height;
1.57 + int colour_format;
1.58 +} *pvr2_render_buffer_t;
1.59 +
1.60 +struct pvr2_render_buffer front_buffer;
1.61 +struct pvr2_render_buffer back_buffer;
1.62 +
1.63 +struct tile_descriptor {
1.64 + uint32_t header[6];
1.65 + struct tile_pointers {
1.66 + uint32_t tile_id;
1.67 + uint32_t opaque_ptr;
1.68 + uint32_t opaque_mod_ptr;
1.69 + uint32_t trans_ptr;
1.70 + uint32_t trans_mod_ptr;
1.71 + uint32_t punchout_ptr;
1.72 + } tile[0];
1.73 +};
1.74 +
1.75 +/* Textured polygon */
1.76 +struct pvr2_poly {
1.77 + uint32_t command;
1.78 + uint32_t poly_cfg; /* Bitmask */
1.79 + uint32_t poly_mode; /* texture/blending mask */
1.80 + uint32_t texture; /* texture data */
1.81 + float alpha;
1.82 + float red;
1.83 + float green;
1.84 + float blue;
1.85 +};
1.86 +
1.87 +struct pvr2_specular_highlight {
1.88 + float base_alpha;
1.89 + float base_red;
1.90 + float base_green;
1.91 + float base_blue;
1.92 + float offset_alpha;
1.93 + float offset_red;
1.94 + float offset_green;
1.95 + float offset_blue;
1.96 +};
1.97 +
1.98 +
1.99 +struct pvr2_vertex_basic {
1.100 + uint32_t command;
1.101 + float x, y, z;
1.102 + float s,t;
1.103 + uint32_t col;
1.104 + float f;
1.105 +};
1.106 +
1.107 +
1.108 +void pvr2_render_copy_to_sh4( pvr2_render_buffer_t buffer,
1.109 + gboolean backBuffer );
1.110 +
1.111 +
1.112 +gboolean pvr2_render_init( void )
1.113 +{
1.114 + front_buffer.render_addr = -1;
1.115 + back_buffer.render_addr = -1;
1.116 +}
1.117 +
1.118 +/**
1.119 + * Display a rendered frame if one is available.
1.120 + * @param address An address in PVR ram (0500000 range).
1.121 + * @return TRUE if a frame was available to be displayed, otherwise false.
1.122 + */
1.123 +gboolean pvr2_render_display_frame( uint32_t address )
1.124 +{
1.125 + if( front_buffer.render_addr == address ) {
1.126 + /* Current front buffer is already displayed, so do nothing
1.127 + * and tell the caller that all is well.
1.128 + */
1.129 + return TRUE;
1.130 + }
1.131 + if( back_buffer.render_addr == address ) {
1.132 + /* The more useful case - back buffer is to be displayed. Swap
1.133 + * the buffers
1.134 + */
1.135 + video_driver->display_back_buffer();
1.136 + front_buffer = back_buffer;
1.137 + back_buffer.render_addr = -1;
1.138 + return TRUE;
1.139 + }
1.140 + return FALSE;
1.141 +}
1.142 +
1.143 +/**
1.144 + * Prepare the OpenGL context to receive instructions for a new frame.
1.145 + */
1.146 +static void pvr2_render_prepare_context( sh4addr_t render_addr,
1.147 + uint32_t width, uint32_t height,
1.148 + uint32_t colour_format,
1.149 + gboolean texture_target )
1.150 +{
1.151 + /* Select and initialize the render context */
1.152 + video_driver->set_render_format( width, height, colour_format, texture_target );
1.153 +
1.154 + if( back_buffer.render_addr != -1 &&
1.155 + back_buffer.render_addr != render_addr ) {
1.156 + /* There's a current back buffer, and we're rendering somewhere else -
1.157 + * flush the back buffer back to vram and start a new back buffer
1.158 + */
1.159 + pvr2_render_copy_to_sh4( &back_buffer, TRUE );
1.160 + }
1.161 +
1.162 + if( front_buffer.render_addr == render_addr ) {
1.163 + /* In case we've been asked to render to the current front buffer -
1.164 + * invalidate the front buffer and render to the back buffer, ensuring
1.165 + * we swap at the next frame display.
1.166 + */
1.167 + front_buffer.render_addr = -1;
1.168 + }
1.169 + back_buffer.render_addr = render_addr;
1.170 + back_buffer.width = width;
1.171 + back_buffer.height = height;
1.172 + back_buffer.colour_format = colour_format;
1.173 +
1.174 + /* Setup the display model */
1.175 + glDrawBuffer(GL_BACK);
1.176 + glShadeModel(GL_SMOOTH);
1.177 + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
1.178 + glViewport( 0, 0, width, height );
1.179 + glMatrixMode(GL_PROJECTION);
1.180 + glLoadIdentity();
1.181 + glOrtho( 0, width, height, 0, 0, 65535 );
1.182 + glMatrixMode(GL_MODELVIEW);
1.183 + glLoadIdentity();
1.184 +
1.185 + /* Clear out the buffers */
1.186 + glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
1.187 + glClearDepth(1.0f);
1.188 + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
1.189 +}
1.190 +
1.191 +static void pvr2_render_display_list( uint32_t *display_list, uint32_t length )
1.192 +{
1.193 + uint32_t *cmd_ptr = display_list;
1.194 + int expect_vertexes = 0;
1.195 + gboolean textured = FALSE;
1.196 + struct pvr2_poly *poly;
1.197 + while( cmd_ptr < display_list+length ) {
1.198 + switch( *cmd_ptr >> 24 ) {
1.199 + case PVR2_CMD_POLY_OPAQUE:
1.200 + poly = (struct pvr2_poly *)cmd_ptr;
1.201 +
1.202 + if( poly->command & PVR2_POLY_TEXTURED ) {
1.203 + uint32_t addr = PVR2_TEX_ADDR(poly->texture);
1.204 + int width = POLY_TEX_WIDTH(poly);
1.205 + int height = POLY_TEX_HEIGHT(poly);
1.206 + texcache_get_texture( addr, width, height, poly->texture );
1.207 + textured = TRUE;
1.208 + glEnable( GL_TEXTURE_2D );
1.209 + } else {
1.210 + textured = FALSE;
1.211 + glDisable( GL_TEXTURE_2D );
1.212 + }
1.213 + glBlendFunc( POLY_BLEND_SRC(poly), POLY_BLEND_DEST(poly) );
1.214 + if( poly->command & PVR2_POLY_SPECULAR ) {
1.215 + /* Second block expected */
1.216 + }
1.217 + if( POLY_DEPTH_WRITE(poly) ) {
1.218 + glEnable( GL_DEPTH_TEST );
1.219 + glDepthFunc( POLY_DEPTH_MODE(poly) );
1.220 + } else {
1.221 + glDisable( GL_DEPTH_TEST );
1.222 + }
1.223 +
1.224 + expect_vertexes = POLY_STRIP_VERTEXES( poly );
1.225 + if( expect_vertexes == 3 )
1.226 + glBegin( GL_TRIANGLES );
1.227 + else if( expect_vertexes == 4 )
1.228 + glBegin( GL_QUADS );
1.229 + else
1.230 + glBegin( GL_TRIANGLE_STRIP );
1.231 + break;
1.232 + case PVR2_CMD_VERTEX_LAST:
1.233 + case PVR2_CMD_VERTEX:
1.234 + if( expect_vertexes == 0 ) {
1.235 + ERROR( "Unexpected vertex!" );
1.236 + return;
1.237 + }
1.238 + expect_vertexes--;
1.239 + struct pvr2_vertex_basic *vertex = (struct pvr2_vertex_basic *)cmd_ptr;
1.240 + if( textured ) {
1.241 + glTexCoord2f( vertex->s, vertex->t );
1.242 + }
1.243 + glVertex3f( vertex->x, vertex->y, vertex->z );
1.244 +
1.245 + if( expect_vertexes == 0 )
1.246 + glEnd();
1.247 + break;
1.248 + }
1.249 + cmd_ptr += 8; /* Next record */
1.250 + }
1.251 +}
1.252 +
1.253 +/**
1.254 + * Render a complete scene into the OpenGL back buffer.
1.255 + * Note: this will probably need to be broken up eventually once timings are
1.256 * determined.
1.257 */
1.258 -void pvr2_render_scene( void )
1.259 +void pvr2_render_scene( )
1.260 {
1.261 - /* Actual rendering goes here :) */
1.262 + struct tile_descriptor *tile_desc =
1.263 + (struct tile_descriptor *)mem_get_region(PVR2_RAM_BASE + MMIO_READ( PVR2, TILEBASE ));
1.264
1.265 - /* End of render event */
1.266 + uint32_t render_addr = MMIO_READ( PVR2, RENDADDR1 );
1.267 + gboolean render_to_tex;
1.268 + if( render_addr & 0x01000000 ) {
1.269 + render_addr = (render_addr & 0x00FFFFFF) + PVR2_RAM_BASE_INT;
1.270 + /* Heuristic - if we're rendering to the interlaced region we're
1.271 + * probably creating a texture rather than rendering actual output.
1.272 + * We can optimise for this case a little
1.273 + */
1.274 + render_to_tex = TRUE;
1.275 + } else {
1.276 + render_addr = (render_addr & 0x00FFFFFF) + PVR2_RAM_BASE;
1.277 + render_to_tex = FALSE;
1.278 + }
1.279 + uint32_t render_mode = MMIO_READ( PVR2, RENDMODE );
1.280 + int width = 640; /* FIXME - get this from the tile buffer */
1.281 + int height = 480;
1.282 + int colour_format = pvr2_render_colour_format[render_mode&0x07];
1.283 + pvr2_render_prepare_context( render_addr, width, height, colour_format,
1.284 + render_to_tex );
1.285 +
1.286 + uint32_t *display_list =
1.287 + (uint32_t *)mem_get_region(PVR2_RAM_BASE + MMIO_READ( PVR2, OBJBASE ));
1.288 + uint32_t display_length = *display_list++;
1.289 +
1.290 + int clip_x = MMIO_READ( PVR2, HCLIP ) & 0x03FF;
1.291 + int clip_y = MMIO_READ( PVR2, VCLIP ) & 0x03FF;
1.292 + int clip_width = ((MMIO_READ( PVR2, HCLIP ) >> 16) & 0x03FF) - clip_x + 1;
1.293 + int clip_height= ((MMIO_READ( PVR2, VCLIP ) >> 16) & 0x03FF) - clip_y + 1;
1.294 +
1.295 + if( clip_x == 0 && clip_y == 0 && clip_width == width && clip_height == height ) {
1.296 + glDisable( GL_SCISSOR_TEST );
1.297 + } else {
1.298 + glEnable( GL_SCISSOR_TEST );
1.299 + glScissor( clip_x, clip_y, clip_width, clip_height );
1.300 + }
1.301 +
1.302 + /* Fog setup goes here */
1.303 +
1.304 + /* Render the display list */
1.305 + pvr2_render_display_list( display_list, display_length );
1.306 +
1.307 + /* Post-render cleanup and update */
1.308 +
1.309 +
1.310 + /* Generate end of render event */
1.311 asic_event( EVENT_PVR_RENDER_DONE );
1.312 DEBUG( "Rendered frame %d", pvr2_frame_counter );
1.313 }
1.314 +
1.315 +
1.316 +/**
1.317 + * Flush the indicated render buffer back to PVR. Caller is responsible for
1.318 + * tracking whether there is actually anything in the buffer.
1.319 + *
1.320 + * @param buffer A render buffer indicating the address to store to, and the
1.321 + * format the data needs to be in.
1.322 + * @param backBuffer TRUE to flush the back buffer, FALSE for
1.323 + * the front buffer.
1.324 + */
1.325 +void pvr2_render_copy_to_sh4( pvr2_render_buffer_t buffer,
1.326 + gboolean backBuffer )
1.327 +{
1.328 + if( buffer->render_addr == -1 )
1.329 + return;
1.330 + GLenum type, format = GL_RGBA;
1.331 + int size = buffer->width * buffer->height;
1.332 +
1.333 + switch( buffer->colour_format ) {
1.334 + case COLFMT_RGB565:
1.335 + type = GL_UNSIGNED_SHORT_5_6_5;
1.336 + format = GL_RGB;
1.337 + size <<= 1;
1.338 + break;
1.339 + case COLFMT_RGB888:
1.340 + type = GL_UNSIGNED_INT;
1.341 + format = GL_RGB;
1.342 + size = (size<<1)+size;
1.343 + break;
1.344 + case COLFMT_ARGB1555:
1.345 + type = GL_UNSIGNED_SHORT_5_5_5_1;
1.346 + size <<= 1;
1.347 + break;
1.348 + case COLFMT_ARGB4444:
1.349 + type = GL_UNSIGNED_SHORT_4_4_4_4;
1.350 + size <<= 1;
1.351 + break;
1.352 + case COLFMT_ARGB8888:
1.353 + type = GL_UNSIGNED_INT_8_8_8_8;
1.354 + size <<= 2;
1.355 + break;
1.356 + }
1.357 +
1.358 + if( backBuffer ) {
1.359 + glFinish();
1.360 + glReadBuffer( GL_BACK );
1.361 + } else {
1.362 + glReadBuffer( GL_FRONT );
1.363 + }
1.364 +
1.365 + if( buffer->render_addr & 0xFF000000 == 0x04000000 ) {
1.366 + /* Interlaced buffer. Go the double copy... :( */
1.367 + char target[size];
1.368 + glReadPixels( 0, 0, buffer->width, buffer->height, format, type, target );
1.369 + pvr2_vram64_write( buffer->render_addr, target, size );
1.370 + } else {
1.371 + /* Regular buffer - go direct */
1.372 + char *target = mem_get_region( buffer->render_addr );
1.373 + glReadPixels( 0, 0, buffer->width, buffer->height, format, type, target );
1.374 + }
1.375 +}
1.376 +
1.377 +
1.378 +/**
1.379 + * Copy data from PVR ram into the GL render buffer.
1.380 + *
1.381 + * @param buffer A render buffer indicating the address to read from, and the
1.382 + * format the data is in.
1.383 + * @param backBuffer TRUE to write the back buffer, FALSE for
1.384 + * the front buffer.
1.385 + */
1.386 +void pvr2_render_copy_from_sh4( pvr2_render_buffer_t buffer,
1.387 + gboolean backBuffer )
1.388 +{
1.389 + if( buffer->render_addr == -1 )
1.390 + return;
1.391 + GLenum type, format = GL_RGBA;
1.392 + int size = buffer->width * buffer->height;
1.393 +
1.394 + switch( buffer->colour_format ) {
1.395 + case COLFMT_RGB565:
1.396 + type = GL_UNSIGNED_SHORT_5_6_5;
1.397 + format = GL_RGB;
1.398 + size <<= 1;
1.399 + break;
1.400 + case COLFMT_RGB888:
1.401 + type = GL_UNSIGNED_INT;
1.402 + format = GL_RGB;
1.403 + size = (size<<1)+size;
1.404 + break;
1.405 + case COLFMT_ARGB1555:
1.406 + type = GL_UNSIGNED_SHORT_5_5_5_1;
1.407 + size <<= 1;
1.408 + break;
1.409 + case COLFMT_ARGB4444:
1.410 + type = GL_UNSIGNED_SHORT_4_4_4_4;
1.411 + size <<= 1;
1.412 + break;
1.413 + case COLFMT_ARGB8888:
1.414 + type = GL_UNSIGNED_INT_8_8_8_8;
1.415 + size <<= 2;
1.416 + break;
1.417 + }
1.418 +
1.419 + if( backBuffer ) {
1.420 + glDrawBuffer( GL_BACK );
1.421 + } else {
1.422 + glDrawBuffer( GL_FRONT );
1.423 + }
1.424 +
1.425 + glRasterPos2i( 0, 0 );
1.426 + if( buffer->render_addr & 0xFF000000 == 0x04000000 ) {
1.427 + /* Interlaced buffer. Go the double copy... :( */
1.428 + char target[size];
1.429 + pvr2_vram64_read( target, buffer->render_addr, size );
1.430 + glDrawPixels( buffer->width, buffer->height,
1.431 + format, type, target );
1.432 + } else {
1.433 + /* Regular buffer - go direct */
1.434 + char *target = mem_get_region( buffer->render_addr );
1.435 + glDrawPixels( buffer->width, buffer->height,
1.436 + format, type, target );
1.437 + }
1.438 +}
.