Search
lxdream.org :: lxdream/src/pvr2/scene.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/pvr2/scene.c
changeset 635:76c63aac3590
next636:2ccf94f966fc
author nkeynes
date Thu Feb 14 13:54:11 2008 +0000 (15 years ago)
branchlxdream-render
permissions -rw-r--r--
last change Commit render work in progress. Main changes:
* Preliminary OSMesa support
* Move the generic gl code out to pvr2/
* Implement scene data structure + reader
* Remove the 1/z adjustments
view annotate diff log raw
     1 /**
     2  * $Id: gl_common.c 602 2008-01-15 20:50:23Z nkeynes $
     3  *
     4  * Manage the internal vertex/polygon buffers and scene data structure. 
     5  * Where possible this uses VBOs for the vertex + index data.
     6  *
     7  * Copyright (c) 2005 Nathan Keynes.
     8  *
     9  * This program is free software; you can redistribute it and/or modify
    10  * it under the terms of the GNU General Public License as published by
    11  * the Free Software Foundation; either version 2 of the License, or
    12  * (at your option) any later version.
    13  *
    14  * This program is distributed in the hope that it will be useful,
    15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    17  * GNU General Public License for more details.
    18  */
    20 #include <assert.h>
    21 #include <string.h>
    22 #include "lxdream.h"
    23 #include "display.h"
    24 #include "pvr2/pvr2.h"
    25 #include "pvr2/glutil.h"
    26 #include "pvr2/scene.h"
    28 #define VBO_EXT_STRING "GL_ARB_vertex_buffer_object"
    29 #define PBO_EXT_STRING "GL_ARB_pixel_buffer_object"
    31 struct pvr2_scene_struct pvr2_scene;
    33 static gboolean vbo_init = FALSE;
    34 static gboolean vbo_supported = FALSE;
    36 /**
    37  * Test for VBO support, and allocate all the system memory needed for the
    38  * temporary structures. GL context must have been initialized before this
    39  * point.
    40  */
    41 void pvr2_scene_init()
    42 {
    43     if( !vbo_init ) {
    44 	if( isGLExtensionSupported(VBO_EXT_STRING) ) {
    45 	    vbo_supported = TRUE;
    46 	    pvr2_scene.vbo_id = 1;
    47 	}
    48 	pvr2_scene.vertex_array = NULL;
    49 	pvr2_scene.vertex_array_size = 0;
    50 	pvr2_scene.poly_array = g_malloc( MAX_POLY_BUFFER_SIZE );
    51 	pvr2_scene.buf_to_poly_map = g_malloc0( BUF_POLY_MAP_SIZE );
    52 	vbo_init = TRUE;
    53     }
    54 }
    56 void pvr2_scene_shutdown()
    57 {
    58     if( vbo_supported ) {
    59 	glBindBufferARB( GL_ARRAY_BUFFER_ARB, 0 );
    60 	glDeleteBuffersARB( 1, &pvr2_scene.vbo_id );
    61 	pvr2_scene.vbo_id = 0;
    62     } else {
    63 	g_free( pvr2_scene.vertex_array );
    64 	pvr2_scene.vertex_array = NULL;
    65     }
    66     g_free( pvr2_scene.poly_array );
    67     g_free( pvr2_scene.buf_to_poly_map );
    68     vbo_init = FALSE;
    69 }
    71 void *vertex_buffer_map()
    72 {
    73     uint32_t size = pvr2_scene.vertex_count * sizeof(struct vertex_struct);
    74     if( vbo_supported ) {
    75 	glBindBufferARB( GL_ARRAY_BUFFER_ARB, pvr2_scene.vbo_id );
    77 	if( size > pvr2_scene.vertex_array_size ) {
    78 	    glBufferDataARB( GL_ARRAY_BUFFER_ARB, size, NULL, GL_DYNAMIC_DRAW_ARB );
    79 	    assert( glGetError() == 0 );
    80 	}
    81 	pvr2_scene.vertex_array = glMapBufferARB( GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB );
    82 	assert(pvr2_scene.vertex_array != NULL );
    83     } else {
    84 	if( size > pvr2_scene.vertex_array_size ) {
    85 	    pvr2_scene.vertex_array = g_realloc( pvr2_scene.vertex_array, size );
    86 	}
    87     }
    88     return pvr2_scene.vertex_array;
    89 }
    91 gboolean vertex_buffer_unmap()
    92 {
    93     if( vbo_supported ) {
    94 	pvr2_scene.vertex_array = NULL;
    95 	return glUnmapBufferARB( GL_ARRAY_BUFFER_ARB );
    96     } else {
    97 	return TRUE;
    98     }
    99 }
   101 static struct polygon_struct *vertex_buffer_add_polygon( pvraddr_t poly_idx, int vertex_count,
   102 							 gboolean is_modified ) 
   103 {
   104     int vert_mul = is_modified ? 2 : 1;
   106     if( pvr2_scene.buf_to_poly_map[poly_idx] != NULL ) {
   107 	if( vertex_count > pvr2_scene.buf_to_poly_map[poly_idx]->vertex_count ) {
   108 	    pvr2_scene.vertex_count += (vertex_count - pvr2_scene.buf_to_poly_map[poly_idx]->vertex_count) * vert_mul;
   109 	    pvr2_scene.buf_to_poly_map[poly_idx]->vertex_count = vertex_count;
   110 	}
   111 	return pvr2_scene.buf_to_poly_map[poly_idx];
   112     } else {
   113 	struct polygon_struct *poly = &pvr2_scene.poly_array[pvr2_scene.poly_count++];
   114 	poly->context = (uint32_t *)(video_base + MMIO_READ(PVR2,RENDER_POLYBASE) + (poly_idx<<2));
   115 	poly->vertex_count = vertex_count;
   116 	poly->vertex_index = -1;
   117 	pvr2_scene.buf_to_poly_map[poly_idx] = poly;
   118 	pvr2_scene.vertex_count += (vertex_count * vert_mul);
   119 	return poly;
   120     }
   121 }
   123 /**
   124  * Decode a single PVR2 renderable vertex (opaque/trans/punch-out, but not shadow
   125  * volume)
   126  * @param vert Pointer to output vertex structure
   127  * @param poly1 First word of polygon context (needed to understand vertex)
   128  * @param pvr2_data Pointer to raw pvr2 vertex data (in VRAM)
   129  * @param modify_offset Offset in 32-bit words to the tex/color data. 0 for
   130  *        the normal vertex, half the vertex length for the modified vertex.
   131  */
   132 static void pvr2_decode_render_vertex( struct vertex_struct *vert, uint32_t poly1, 
   133 				       uint32_t *pvr2_data, int modify_offset )
   134 {
   135     union pvr2_data_type {
   136 	uint32_t *ival;
   137 	float *fval;
   138     } data;
   140     data.ival = pvr2_data;
   142     vert->x = *data.fval++;
   143     vert->y = *data.fval++;
   145     float z = *data.fval++;
   146     if( z > pvr2_scene.bounds[5] ) {
   147 	pvr2_scene.bounds[5] = z;
   148     } else if( z < pvr2_scene.bounds[4] && z != 0 ) {
   149 	pvr2_scene.bounds[4] = z;
   150     }
   151     vert->z = z;
   152     data.ival += modify_offset;
   155     if( POLY1_TEXTURED(poly1) ) {
   156 	if( POLY1_UV16(poly1) ) {
   157 	    vert->u = halftofloat( *data.ival>>16 );
   158 	    vert->v = halftofloat( *data.ival );
   159 	    data.ival++;
   160 	} else {
   161 	    vert->u = *data.fval++;
   162 	    vert->v = *data.fval++;
   163 	}
   164     }
   165     vert->rgba = *data.ival++;
   166     if( POLY1_SPECULAR(poly1) ) {
   167 	vert->offset_rgba = *data.ival++;
   168     }
   169 }
   171 /**
   172  * Compute texture, colour, and z values for a result point by interpolating from
   173  * a set of 3 input points. The result point must define its x,y.
   174  */
   175 static void vertex_buffer_compute_vertex( struct vertex_struct *result, 
   176 					  struct vertex_struct *input,
   177 					  gboolean is_solid_shaded )
   178 {
   179     int i;
   180     float sx = input[2].x - input[1].x;
   181     float sy = input[2].y - input[1].y;
   182     float tx = input[0].x - input[1].x;
   183     float ty = input[0].y - input[1].y;
   185     float detxy = ((sy) * (tx)) - ((ty) * (sx));
   186     if( detxy == 0 ) {
   187 	result->z = input[2].z;
   188 	result->u = input[2].u;
   189 	result->v = input[2].v;
   190 	result->rgba = input[2].rgba;
   191 	result->offset_rgba = input[2].offset_rgba;
   192 	return;
   193     }
   194     float t = ((result->x - input[1].x) * sy -
   195 	       (result->y - input[1].y) * sx) / detxy;
   196     float s = ((result->y - input[1].y) * tx -
   197 	       (result->x - input[1].x) * ty) / detxy;
   199     float sz = input[2].z - input[1].z;
   200     float tz = input[0].z - input[1].z;
   201     float su = input[2].u - input[1].u;
   202     float tu = input[0].u - input[1].u;
   203     float sv = input[2].v - input[1].v;
   204     float tv = input[0].v - input[1].v;
   206     float rz = input[1].z + (t*tz) + (s*sz);
   207     if( rz > pvr2_scene.bounds[5] ) {
   208 	pvr2_scene.bounds[5] = rz;
   209     } else if( rz < pvr2_scene.bounds[4] ) {
   210 	pvr2_scene.bounds[4] = rz; 
   211     }
   212     result->z = rz;
   213     result->u = input[1].u + (t*tu) + (s*su);
   214     result->v = input[1].v + (t*tv) + (s*sv);
   216     if( is_solid_shaded ) {
   217 	result->rgba = input[2].rgba;
   218 	result->offset_rgba = input[2].offset_rgba;
   219     } else {
   220 	uint8_t *rgba0 = (uint8_t *)&input[0].rgba;
   221 	uint8_t *rgba1 = (uint8_t *)&input[1].rgba;
   222 	uint8_t *rgba2 = (uint8_t *)&input[2].rgba;
   223 	uint8_t *rgba3 = (uint8_t *)&result->rgba;
   224 	for( i=0; i<8; i++ ) { // note: depends on rgba & offset_rgba being adjacent
   225 	    float tc = *rgba0++ - *rgba1;
   226 	    float sc = *rgba2++ - *rgba1;
   227 	    float rc = *rgba1++ + (t*tc) + (s*sc);
   228 	    if( rc < 0 ) {
   229 		rc = 0;
   230 	    } else if( rc > 255 ) {
   231 		rc = 255;
   232 	    }
   233 	    *rgba3++ = rc;
   234 	}
   235     }    
   237 }
   239 static void vertex_buffer_add_vertexes( pvraddr_t poly_idx, int vertex_length,
   240 					gboolean is_modified )
   241 {
   242     struct polygon_struct *poly = pvr2_scene.buf_to_poly_map[poly_idx];
   243     uint32_t *ptr = &pvr2_scene.pvr2_pbuf[poly_idx];
   244     uint32_t *context = ptr;
   245     unsigned int i;
   247     assert( poly != NULL );
   248     if( poly->vertex_index == -1 ) {
   249 	ptr += (is_modified ? 5 : 3 );
   250 	poly->vertex_index = pvr2_scene.vertex_index;
   251 	assert( pvr2_scene.vertex_index + poly->vertex_count <= pvr2_scene.vertex_count );
   252 	for( i=0; i<poly->vertex_count; i++ ) {
   253 	    pvr2_decode_render_vertex( &pvr2_scene.vertex_array[pvr2_scene.vertex_index++], context[0], ptr, 0 );
   254 	    ptr += vertex_length;
   255 	}
   256 	if( is_modified ) {
   257 	    int mod_offset = (vertex_length - 3)>>1;
   258 	    ptr = &pvr2_scene.pvr2_pbuf[poly_idx] + 5;
   259 	    poly->mod_vertex_index = pvr2_scene.vertex_index;
   260 	    for( i=0; i<poly->vertex_count; i++ ) {
   261 		pvr2_decode_render_vertex( &pvr2_scene.vertex_array[pvr2_scene.vertex_index++], context[0], ptr, mod_offset );
   262 		ptr += vertex_length;
   263 	    }
   264 	}
   265     }
   266 }
   268 static void vertex_buffer_add_quad_vertexes( pvraddr_t poly_idx, int vertex_length, 
   269 					     gboolean is_modified )
   270 {
   271     struct polygon_struct *poly = pvr2_scene.buf_to_poly_map[poly_idx];
   272     uint32_t *ptr = &pvr2_scene.pvr2_pbuf[poly_idx];
   273     uint32_t *context = ptr;
   274     unsigned int i;
   276     if( poly->vertex_index == -1 ) {
   277 	// Construct it locally and copy to the vertex buffer, as the VBO is 
   278 	// allowed to be horribly slow for reads (ie it could be direct-mapped
   279 	// vram).
   280 	struct vertex_struct quad[4];
   282 	assert( poly != NULL );
   283 	ptr += (is_modified ? 5 : 3 );
   284 	poly->vertex_index = pvr2_scene.vertex_index;
   285 	for( i=0; i<4; i++ ) {
   286 	    pvr2_decode_render_vertex( &quad[i], context[0], ptr, 0 );
   287 	    ptr += vertex_length;
   288 	}
   289 	vertex_buffer_compute_vertex( &quad[3], &quad[0], !POLY1_GOURAUD_SHADED(context[0]) );
   290 	memcpy( &pvr2_scene.vertex_array[pvr2_scene.vertex_index], quad, sizeof(quad) );
   291 	pvr2_scene.vertex_index += 4;
   293 	if( is_modified ) {
   294 	    int mod_offset = (vertex_length - 3)>>1;
   295 	    ptr = &pvr2_scene.pvr2_pbuf[poly_idx] + 5;
   296 	    poly->mod_vertex_index = pvr2_scene.vertex_index;
   297 	    for( i=0; i<4; i++ ) {
   298 		pvr2_decode_render_vertex( &quad[4], context[0], ptr, mod_offset );
   299 		ptr += vertex_length;
   300 	    }
   301 	    vertex_buffer_compute_vertex( &quad[3], &quad[0], !POLY1_GOURAUD_SHADED(context[0]) );
   302 	    memcpy( &pvr2_scene.vertex_array[pvr2_scene.vertex_index], quad, sizeof(quad) );
   303 	    pvr2_scene.vertex_index += 4;
   304 	}
   305     }
   306 }
   308 static void vertex_buffer_extract_polygons( pvraddr_t tile_entry )
   309 {
   310     uint32_t *tile_list = (uint32_t *)(video_base+tile_entry);
   311     do {
   312 	uint32_t entry = *tile_list++;
   313 	if( entry >> 28 == 0x0F ) {
   314 	    break;
   315 	} else if( entry >> 28 == 0x0E ) {
   316 	    tile_list = (uint32_t *)(video_base + (entry&0x007FFFFF));
   317 	} else {
   318 	    pvraddr_t polyaddr = entry&0x000FFFFF;
   319 	    int is_modified = (entry & 0x01000000) && pvr2_scene.full_shadow;
   320 	    int vertex_length = (entry >> 21) & 0x07;
   321 	    int context_length = 3;
   322 	    if( is_modified ) {
   323 		context_length = 5;
   324 		vertex_length <<= 1 ;
   325 	    }
   326 	    vertex_length += 3;
   328 	    if( (entry & 0xE0000000) == 0x80000000 ) {
   329 		/* Triangle(s) */
   330 		int strip_count = ((entry >> 25) & 0x0F)+1;
   331 		int polygon_length = 3 * vertex_length + context_length;
   332 		int i;
   333 		struct polygon_struct *last_poly = NULL;
   334 		for( i=0; i<strip_count; i++ ) {
   335 		    struct polygon_struct *poly = vertex_buffer_add_polygon( polyaddr, 3, is_modified );
   336 		    polyaddr += polygon_length;
   337 		    if( last_poly != NULL && last_poly->next == NULL ) {
   338 			last_poly->next = poly;
   339 		    }
   340 		    last_poly = poly;
   341 		}
   342 	    } else if( (entry & 0xE0000000) == 0xA0000000 ) {
   343 		/* Sprite(s) */
   344 		int strip_count = ((entry >> 25) & 0x0F)+1;
   345 		int polygon_length = 4 * vertex_length + context_length;
   346 		int i;
   347 		struct polygon_struct *last_poly = NULL;
   348 		for( i=0; i<strip_count; i++ ) {
   349 		    struct polygon_struct *poly = vertex_buffer_add_polygon( polyaddr, 4, is_modified );
   350 		    polyaddr += polygon_length;
   351 		    if( last_poly != NULL && last_poly->next == NULL ) {
   352 			last_poly->next = poly;
   353 		    }
   354 		    last_poly = poly;
   355 		}
   356 	    } else {
   357 		/* Polygon */
   358 		int i, last = -1;
   359 		for( i=5; i>=0; i-- ) {
   360 		    if( entry & (0x40000000>>i) ) {
   361 			last = i;
   362 			break;
   363 		    }
   364 		}
   365 		if( last != -1 ) {
   366 		    vertex_buffer_add_polygon( polyaddr, last+3, is_modified );
   367 		}
   368 	    }
   369 	}
   370     } while( 1 );
   371 }
   373 static void vertex_buffer_extract_vertexes( pvraddr_t tile_entry )
   374 {
   375     uint32_t *tile_list = (uint32_t *)(video_base+tile_entry);
   376     do {
   377 	uint32_t entry = *tile_list++;
   378 	if( entry >> 28 == 0x0F ) {
   379 	    break;
   380 	} else if( entry >> 28 == 0x0E ) {
   381 	    tile_list = (uint32_t *)(video_base + (entry&0x007FFFFF));
   382 	} else {
   383 	    pvraddr_t polyaddr = entry&0x000FFFFF;
   384 	    int is_modified = (entry & 0x01000000) && pvr2_scene.full_shadow;
   385 	    int vertex_length = (entry >> 21) & 0x07;
   386 	    int context_length = 3;
   387 	    if( is_modified ) {
   388 		context_length = 5;
   389 		vertex_length <<=1 ;
   390 	    }
   391 	    vertex_length += 3;
   393 	    if( (entry & 0xE0000000) == 0x80000000 ) {
   394 		/* Triangle(s) */
   395 		int strip_count = ((entry >> 25) & 0x0F)+1;
   396 		int polygon_length = 3 * vertex_length + context_length;
   397 		int i;
   398 		for( i=0; i<strip_count; i++ ) {
   399 		    vertex_buffer_add_vertexes( polyaddr, vertex_length, is_modified );
   400 		    polyaddr += polygon_length;
   401 		}
   402 	    } else if( (entry & 0xE0000000) == 0xA0000000 ) {
   403 		/* Sprite(s) */
   404 		int strip_count = ((entry >> 25) & 0x0F)+1;
   405 		int polygon_length = 4 * vertex_length + context_length;
   406 		int i;
   407 		for( i=0; i<strip_count; i++ ) {
   408 		    vertex_buffer_add_quad_vertexes( polyaddr, vertex_length, is_modified );
   409 		    polyaddr += polygon_length;
   410 		}
   411 	    } else {
   412 		/* Polygon */
   413 		int i, last = -1;
   414 		for( i=5; i>=0; i-- ) {
   415 		    if( entry & (0x40000000>>i) ) {
   416 			last = i;
   417 			break;
   418 		    }
   419 		}
   420 		if( last != -1 ) {
   421 		    vertex_buffer_add_vertexes( polyaddr, vertex_length, is_modified );
   422 		}
   423 	    }
   424 	}
   425     } while( 1 );    
   426 }
   428 /**
   429  * Extract the current scene into the rendering structures. We run two passes
   430  * - first pass extracts the polygons into pvr2_scene.poly_array (finding vertex counts), 
   431  * second pass extracts the vertex data into the VBO/vertex array.
   432  *
   433  * Difficult to do in single pass as we don't generally know the size of a 
   434  * polygon for certain until we've seen all tiles containing it. It also means we
   435  * can count the vertexes and allocate the appropriate size VBO.
   436  *
   437  * FIXME: accesses into VRAM need to be bounds-checked properly
   438  */
   439 void pvr2_scene_read( void )
   440 {
   441     pvr2_scene_init();
   443     pvr2_scene.poly_count = 0;
   444     pvr2_scene.vertex_count = 0;
   445     pvr2_scene.bounds[0] = MMIO_READ( PVR2, RENDER_HCLIP ) & 0x03FF;
   446     pvr2_scene.bounds[1] = ((MMIO_READ( PVR2, RENDER_HCLIP ) >> 16) & 0x03FF) + 1;
   447     pvr2_scene.bounds[2] = MMIO_READ( PVR2, RENDER_VCLIP ) & 0x03FF;
   448     pvr2_scene.bounds[3] = ((MMIO_READ( PVR2, RENDER_VCLIP ) >> 16) & 0x03FF) + 1;
   449     pvr2_scene.bounds[4] = pvr2_scene.bounds[5] = MMIO_READF( PVR2, RENDER_FARCLIP );
   451     uint32_t *tilebuffer = (uint32_t *)(video_base + MMIO_READ( PVR2, RENDER_TILEBASE ));
   452     uint32_t *segment = tilebuffer;
   453     pvr2_scene.segment_list = (struct tile_segment *)tilebuffer;
   454     pvr2_scene.pvr2_pbuf = (uint32_t *)(video_base + MMIO_READ(PVR2,RENDER_POLYBASE));
   455     pvr2_scene.full_shadow = MMIO_READ( PVR2, RENDER_SHADOW ) & 0x100 ? FALSE : TRUE;
   457     int max_tile_x = 0;
   458     int max_tile_y = 0;
   459     int obj_config = MMIO_READ( PVR2, RENDER_OBJCFG );
   460     int isp_config = MMIO_READ( PVR2, RENDER_ISPCFG );
   462     if( (obj_config & 0x00200000) == 0 ) {
   463 	if( isp_config & 1 ) {
   464 	    pvr2_scene.sort_mode = SORT_NEVER;
   465 	} else {
   466 	    pvr2_scene.sort_mode = SORT_ALWAYS;
   467 	}
   468     } else {
   469 	pvr2_scene.sort_mode = SORT_BYFLAG;
   470     }
   472     // Pass 1: Extract polygon list 
   473     uint32_t control;
   474     int i;
   475     do {
   476 	control = *segment++;
   477 	int tile_x = SEGMENT_X(control);
   478 	int tile_y = SEGMENT_Y(control);
   479 	if( tile_x > max_tile_x ) {
   480 	    max_tile_x = tile_x;
   481 	} 
   482 	if( tile_y > max_tile_y ) {
   483 	    max_tile_y = tile_y;
   484 	}
   485 	for( i=0; i<5; i++ ) {
   486 	    if( (*segment & NO_POINTER) == 0 ) {
   487 		vertex_buffer_extract_polygons( *segment );
   488 	    }
   489 	    segment++;
   490 	}
   491     } while( (control & SEGMENT_END) == 0 );
   493     pvr2_scene.buffer_width = (max_tile_x+1)<<5;
   494     pvr2_scene.buffer_height = (max_tile_y+1)<<5;
   496     if( pvr2_scene.vertex_count > 0 ) {
   497 	// Pass 2: Extract vertex data
   498 	vertex_buffer_map();
   499 	pvr2_scene.vertex_index = 0;
   500 	segment = tilebuffer;
   501 	do {
   502 	    control = *segment++;
   503 	    for( i=0; i<5; i++ ) {
   504 		if( (*segment & NO_POINTER) == 0 ) {
   505 		    vertex_buffer_extract_vertexes( *segment );
   506 		}
   507 		segment++;
   508 	    }
   509 	} while( (control & SEGMENT_END) == 0 );
   511 	vertex_buffer_unmap();
   512     }
   513 }
   515 /**
   516  * Render the data in the scene structure. The GL target should already be setup.
   517  * Note: thar be GL code here.
   518  */
   519 void vertex_buffer_render()
   520 {
   521     /* Scene setup */
   523     glEnable( GL_SCISSOR_TEST );
   525     /* Scene render */
   526     struct tile_segment *segment = pvr2_scene.segment_list;
   527     do {
   530     } while( !IS_LAST_SEGMENT(segment) );
   531     glDisable( GL_SCISSOR_TEST );
   532 }
.