filename | src/pvr2/pvr2.c |
changeset | 352:f0df7a6d4703 |
prev | 337:cdd757aa8e8c |
next | 373:0ac2ac96a4c5 |
author | nkeynes |
date | Sun Feb 11 10:09:32 2007 +0000 (13 years ago) |
permissions | -rw-r--r-- |
last change | Bug 27: Implement opengl framebuffer objects Rewrite much of the final video output stage. Now uses generic "render buffers", implemented on GL using framebuffer objects + textures. |
file | annotate | diff | log | raw |
1.1 --- a/src/pvr2/pvr2.c Sun Jan 28 11:36:00 2007 +00001.2 +++ b/src/pvr2/pvr2.c Sun Feb 11 10:09:32 2007 +00001.3 @@ -1,5 +1,5 @@1.4 /**1.5 - * $Id: pvr2.c,v 1.43 2007-01-28 11:36:00 nkeynes Exp $1.6 + * $Id: pvr2.c,v 1.44 2007-02-11 10:09:32 nkeynes Exp $1.7 *1.8 * PVR2 (Video) Core module implementation and MMIO registers.1.9 *1.10 @@ -30,6 +30,8 @@1.12 char *video_base;1.14 +#define MAX_RENDER_BUFFERS 41.15 +1.16 #define HPOS_PER_FRAME 01.17 #define HPOS_PER_LINECOUNT 11.19 @@ -40,11 +42,13 @@1.20 static int pvr2_load_state( FILE *f );1.21 static void pvr2_update_raster_posn( uint32_t nanosecs );1.22 static void pvr2_schedule_scanline_event( int eventid, int line, int minimum_lines, int line_time_ns );1.23 +static render_buffer_t pvr2_get_render_buffer( frame_buffer_t frame );1.24 +static render_buffer_t pvr2_next_render_buffer( );1.25 uint32_t pvr2_get_sync_status();1.27 void pvr2_display_frame( void );1.29 -int colour_format_bytes[] = { 2, 2, 2, 1, 3, 4, 1, 1 };1.30 +static int output_colour_formats[] = { COLFMT_ARGB1555, COLFMT_RGB565, COLFMT_RGB888, COLFMT_ARGB8888 };1.32 struct dreamcast_module pvr2_module = { "PVR2", pvr2_init, pvr2_reset, NULL,1.33 pvr2_run_slice, NULL,1.34 @@ -53,16 +57,6 @@1.36 display_driver_t display_driver = NULL;1.38 -struct video_timing {1.39 - int fields_per_second;1.40 - int total_lines;1.41 - int retrace_lines;1.42 - int line_time_ns;1.43 -};1.44 -1.45 -struct video_timing pal_timing = { 50, 625, 65, 31945 };1.46 -struct video_timing ntsc_timing= { 60, 525, 65, 31746 };1.47 -1.48 struct pvr2_state {1.49 uint32_t frame_count;1.50 uint32_t line_count;1.51 @@ -89,11 +83,10 @@1.52 uint32_t retrace_start_line;1.53 uint32_t retrace_end_line;1.54 gboolean interlaced;1.55 - struct video_timing timing;1.56 } pvr2_state;1.58 -struct video_buffer video_buffer[2];1.59 -int video_buffer_idx = 0;1.60 +render_buffer_t render_buffers[MAX_RENDER_BUFFERS];1.61 +int render_buffer_count = 0;1.63 /**1.64 * Event handler for the hpos callback1.65 @@ -127,6 +120,7 @@1.67 static void pvr2_init( void )1.68 {1.69 + int i;1.70 register_io_region( &mmio_region_PVR2 );1.71 register_io_region( &mmio_region_PVR2PAL );1.72 register_io_region( &mmio_region_PVR2TA );1.73 @@ -138,6 +132,10 @@1.74 pvr2_reset();1.75 pvr2_ta_reset();1.76 pvr2_state.save_next_render_filename = NULL;1.77 + for( i=0; i<MAX_RENDER_BUFFERS; i++ ) {1.78 + render_buffers[i] = NULL;1.79 + }1.80 + render_buffer_count = 0;1.81 }1.83 static void pvr2_reset( void )1.84 @@ -147,7 +145,6 @@1.85 pvr2_state.cycles_run = 0;1.86 pvr2_state.irq_vpos1 = 0;1.87 pvr2_state.irq_vpos2 = 0;1.88 - pvr2_state.timing = ntsc_timing;1.89 pvr2_state.dot_clock = PVR2_DOT_CLOCK;1.90 pvr2_state.back_porch_ns = 4000;1.91 pvr2_state.palette_changed = FALSE;1.92 @@ -155,10 +152,8 @@1.93 mmio_region_PVR2_write( DISP_SYNCTIME, 0x07D6A53F );1.94 mmio_region_PVR2_write( YUV_ADDR, 0 );1.95 mmio_region_PVR2_write( YUV_CFG, 0 );1.96 - video_buffer_idx = 0;1.98 pvr2_ta_init();1.99 - pvr2_render_init();1.100 texcache_flush();1.101 }1.103 @@ -241,82 +236,64 @@1.104 */1.105 void pvr2_display_frame( void )1.106 {1.107 - uint32_t display_addr;1.108 - int dispsize = MMIO_READ( PVR2, DISP_SIZE );1.109 int dispmode = MMIO_READ( PVR2, DISP_MODE );1.110 int vidcfg = MMIO_READ( PVR2, DISP_SYNCCFG );1.111 - int vid_stride = (((dispsize & DISPSIZE_MODULO) >> 20) - 1);1.112 - int vid_lpf = ((dispsize & DISPSIZE_LPF) >> 10) + 1;1.113 - int vid_ppl = ((dispsize & DISPSIZE_PPL)) + 1;1.114 gboolean bEnabled = (dispmode & DISPMODE_ENABLE) && (vidcfg & DISPCFG_VO ) ? TRUE : FALSE;1.115 - gboolean interlaced = (vidcfg & DISPCFG_I ? TRUE : FALSE);1.116 - video_buffer_t buffer = &video_buffer[video_buffer_idx];1.117 - video_buffer_idx = !video_buffer_idx;1.118 - video_buffer_t last = &video_buffer[video_buffer_idx];1.119 - buffer->rowstride = (vid_ppl + vid_stride) << 2;1.120 - buffer->data = video_base + MMIO_READ( PVR2, DISP_ADDR1 );1.121 - buffer->line_double = (dispmode & DISPMODE_LINEDOUBLE) ? TRUE : FALSE;1.122 - buffer->vres = vid_lpf;1.123 - if( interlaced ) {1.124 - if( vid_ppl == vid_stride ) { /* Magic deinterlace */1.125 - buffer->vres <<= 1;1.126 - buffer->rowstride = vid_ppl << 2;1.127 - display_addr = MMIO_READ( PVR2, DISP_ADDR1 );1.128 - } else {1.129 - /* Just display the field as is, folks. This is slightly tricky -1.130 - * we pick the field based on which frame is about to come through,1.131 - * which may not be the same as the odd_even_field.1.132 - */1.133 - gboolean oddfield = pvr2_state.odd_even_field;1.134 - if( pvr2_state.line_count >= pvr2_state.retrace_start_line ) {1.135 - oddfield = !oddfield;1.136 +1.137 + if( display_driver == NULL ) {1.138 + return; /* can't really do anything much */1.139 + } else if( !bEnabled ) {1.140 + /* Output disabled == black */1.141 + display_driver->display_blank( 0 );1.142 + } else if( MMIO_READ( PVR2, DISP_CFG2 ) & 0x08 ) {1.143 + /* Enabled but blanked - border colour */1.144 + uint32_t colour = MMIO_READ( PVR2, DISP_BORDER );1.145 + display_driver->display_blank( colour );1.146 + } else {1.147 + /* Real output - determine dimensions etc */1.148 + struct frame_buffer fbuf;1.149 + uint32_t dispsize = MMIO_READ( PVR2, DISP_SIZE );1.150 + int vid_stride = (((dispsize & DISPSIZE_MODULO) >> 20) - 1);1.151 + int vid_ppl = ((dispsize & DISPSIZE_PPL)) + 1;1.152 +1.153 + fbuf.colour_format = output_colour_formats[(dispmode & DISPMODE_COLFMT) >> 2];1.154 + fbuf.width = vid_ppl << 2 / colour_formats[fbuf.colour_format].bpp;1.155 + fbuf.height = ((dispsize & DISPSIZE_LPF) >> 10) + 1;1.156 + fbuf.size = vid_ppl << 2 * fbuf.height;1.157 + fbuf.rowstride = (vid_ppl + vid_stride) << 2;1.158 +1.159 + /* Determine the field to display, and deinterlace if possible */1.160 + if( pvr2_state.interlaced ) {1.161 + if( vid_ppl == vid_stride ) { /* Magic deinterlace */1.162 + fbuf.height = fbuf.height << 1;1.163 + fbuf.rowstride = vid_ppl << 2;1.164 + fbuf.address = MMIO_READ( PVR2, DISP_ADDR1 );1.165 + } else {1.166 + /* Just display the field as is, folks. This is slightly tricky -1.167 + * we pick the field based on which frame is about to come through,1.168 + * which may not be the same as the odd_even_field.1.169 + */1.170 + gboolean oddfield = pvr2_state.odd_even_field;1.171 + if( pvr2_state.line_count >= pvr2_state.retrace_start_line ) {1.172 + oddfield = !oddfield;1.173 + }1.174 + if( oddfield ) {1.175 + fbuf.address = MMIO_READ( PVR2, DISP_ADDR1 );1.176 + } else {1.177 + fbuf.address = MMIO_READ( PVR2, DISP_ADDR2 );1.178 + }1.179 }1.180 - if( oddfield ) {1.181 - display_addr = MMIO_READ( PVR2, DISP_ADDR1 );1.182 - } else {1.183 - display_addr = MMIO_READ( PVR2, DISP_ADDR2 );1.184 - }1.185 + } else {1.186 + fbuf.address = MMIO_READ( PVR2, DISP_ADDR1 );1.187 }1.188 - } else {1.189 - display_addr = MMIO_READ( PVR2, DISP_ADDR1 );1.190 - }1.191 - switch( (dispmode & DISPMODE_COLFMT) >> 2 ) {1.192 - case 0:1.193 - buffer->colour_format = COLFMT_ARGB1555;1.194 - buffer->hres = vid_ppl << 1;1.195 - break;1.196 - case 1:1.197 - buffer->colour_format = COLFMT_RGB565;1.198 - buffer->hres = vid_ppl << 1;1.199 - break;1.200 - case 2:1.201 - buffer->colour_format = COLFMT_RGB888;1.202 - buffer->hres = (vid_ppl << 2) / 3;1.203 - break;1.204 - case 3:1.205 - buffer->colour_format = COLFMT_ARGB8888;1.206 - buffer->hres = vid_ppl;1.207 - break;1.208 - }1.209 -1.210 - if( buffer->hres <=8 )1.211 - buffer->hres = 640;1.212 - if( buffer->vres <=8 )1.213 - buffer->vres = 480;1.214 - if( display_driver != NULL ) {1.215 - if( buffer->hres != last->hres ||1.216 - buffer->vres != last->vres ||1.217 - buffer->colour_format != last->colour_format) {1.218 - display_driver->set_display_format( buffer->hres, buffer->vres,1.219 - buffer->colour_format );1.220 - }1.221 - if( !bEnabled ) {1.222 - display_driver->display_blank_frame( 0 );1.223 - } else if( MMIO_READ( PVR2, DISP_CFG2 ) & 0x08 ) { /* Blanked */1.224 - uint32_t colour = MMIO_READ( PVR2, DISP_BORDER );1.225 - display_driver->display_blank_frame( colour );1.226 - } else if( !pvr2_render_display_frame( PVR2_RAM_BASE + display_addr ) ) {1.227 - display_driver->display_frame( buffer );1.228 + fbuf.address = (fbuf.address & 0x00FFFFFF) + PVR2_RAM_BASE;1.229 +1.230 + render_buffer_t rbuf = pvr2_get_render_buffer( &fbuf );1.231 + if( rbuf != NULL ) {1.232 + display_driver->display_render_buffer( rbuf );1.233 + } else {1.234 + fbuf.data = video_base + (fbuf.address&0x00FFFFFF);1.235 + display_driver->display_frame_buffer( &fbuf );1.236 }1.237 }1.238 }1.239 @@ -349,7 +326,9 @@1.240 g_free( pvr2_state.save_next_render_filename );1.241 pvr2_state.save_next_render_filename = NULL;1.242 }1.243 - pvr2_render_scene();1.244 + render_buffer_t buffer = pvr2_next_render_buffer();1.245 + pvr2_render_scene( buffer );1.246 + asic_event( EVENT_PVR_RENDER_DONE );1.247 break;1.248 case RENDER_POLYBASE:1.249 MMIO_WRITE( PVR2, reg, val&0x00F00000 );1.250 @@ -372,23 +351,11 @@1.251 case DISP_ADDR1:1.252 val &= 0x00FFFFFC;1.253 MMIO_WRITE( PVR2, reg, val );1.254 - /*1.255 pvr2_update_raster_posn(sh4r.slice_cycle);1.256 - fprintf( stderr, "Set Field 1 addr: %08X\n", val );1.257 - if( (pvr2_state.line_count >= pvr2_state.retrace_start_line && !pvr2_state.odd_even_field) ||1.258 - (pvr2_state.line_count < pvr2_state.retrace_end_line && pvr2_state.odd_even_field) ) {1.259 - pvr2_display_frame();1.260 - }1.261 - */1.262 break;1.263 case DISP_ADDR2:1.264 MMIO_WRITE( PVR2, reg, val&0x00FFFFFC );1.265 pvr2_update_raster_posn(sh4r.slice_cycle);1.266 - /*1.267 - if( (pvr2_state.line_count >= pvr2_state.retrace_start_line && pvr2_state.odd_even_field) ||1.268 - (pvr2_state.line_count < pvr2_state.retrace_end_line && !pvr2_state.odd_even_field) ) {1.269 - pvr2_display_frame();1.270 - }*/1.271 break;1.272 case DISP_SIZE:1.273 MMIO_WRITE( PVR2, reg, val&0x3FFFFFFF );1.274 @@ -722,3 +689,139 @@1.275 pvr2_ta_write( (char *)&val, sizeof(uint32_t) );1.276 }1.278 +/**1.279 + * Find the render buffer corresponding to the requested output frame1.280 + * (does not consider texture renders).1.281 + * @return the render_buffer if found, or null if no such buffer.1.282 + *1.283 + * Note: Currently does not consider "partial matches", ie partial1.284 + * frame overlap - it probably needs to do this.1.285 + */1.286 +render_buffer_t pvr2_get_render_buffer( frame_buffer_t frame )1.287 +{1.288 + int i;1.289 + for( i=0; i<render_buffer_count; i++ ) {1.290 + if( render_buffers[i] != NULL && render_buffers[i]->address == frame->address ) {1.291 + return render_buffers[i];1.292 + }1.293 + }1.294 + return NULL;1.295 +}1.296 +1.297 +/**1.298 + * Determine the next render buffer to write into. The order of preference is:1.299 + * 1. An existing buffer with the same address. (not flushed unless the new1.300 + * size is smaller than the old one).1.301 + * 2. An existing buffer with the same size chosen by LRU order. Old buffer1.302 + * is flushed to vram.1.303 + * 3. A new buffer if one can be created.1.304 + * 4. The current display buff1.305 + * Note: The current display field(s) will never be overwritten except as a last1.306 + * resort.1.307 + */1.308 +render_buffer_t pvr2_next_render_buffer()1.309 +{1.310 + render_buffer_t result = NULL;1.311 + uint32_t render_addr = MMIO_READ( PVR2, RENDER_ADDR1 );1.312 + uint32_t render_mode = MMIO_READ( PVR2, RENDER_MODE );1.313 + uint32_t render_scale = MMIO_READ( PVR2, RENDER_SCALER );1.314 + uint32_t render_stride = MMIO_READ( PVR2, RENDER_SIZE ) << 3;1.315 + gboolean render_to_tex;1.316 + if( render_addr & 0x01000000 ) { /* vram64 */1.317 + render_addr = (render_addr & 0x00FFFFFF) + PVR2_RAM_BASE_INT;1.318 + } else { /* vram32 */1.319 + render_addr = (render_addr & 0x00FFFFFF) + PVR2_RAM_BASE;1.320 + }1.321 +1.322 + int width, height, i;1.323 + int colour_format = pvr2_render_colour_format[render_mode&0x07];1.324 + pvr2_render_getsize( &width, &height );1.325 +1.326 + /* Check existing buffers for an available buffer */1.327 + for( i=0; i<render_buffer_count; i++ ) {1.328 + if( render_buffers[i]->width == width && render_buffers[i]->height == height ) {1.329 + /* needs to be the right dimensions */1.330 + if( render_buffers[i]->address == render_addr ) {1.331 + /* perfect */1.332 + result = render_buffers[i];1.333 + break;1.334 + } else if( render_buffers[i]->address == -1 && result == NULL ) {1.335 + result = render_buffers[i];1.336 + }1.337 + } else if( render_buffers[i]->address == render_addr ) {1.338 + /* right address, wrong size - if it's larger, flush it, otherwise1.339 + * nuke it quietly */1.340 + if( render_buffers[i]->width * render_buffers[i]->height >1.341 + width*height ) {1.342 + pvr2_render_buffer_copy_to_sh4( render_buffers[i] );1.343 + }1.344 + render_buffers[i]->address == -1;1.345 + }1.346 + }1.347 +1.348 + /* Nothing available - make one */1.349 + if( result == NULL ) {1.350 + if( render_buffer_count == MAX_RENDER_BUFFERS ) {1.351 + /* maximum buffers reached - need to throw one away */1.352 + uint32_t field1_addr = MMIO_READ( PVR2, DISP_ADDR1 );1.353 + uint32_t field2_addr = MMIO_READ( PVR2, DISP_ADDR2 );1.354 + for( i=0; i<render_buffer_count; i++ ) {1.355 + if( render_buffers[i]->address != field1_addr &&1.356 + render_buffers[i]->address != field2_addr ) {1.357 + /* Never throw away the current "front buffer(s)" */1.358 + result = render_buffers[i];1.359 + pvr2_render_buffer_copy_to_sh4( result );1.360 + if( result->width != width || result->height != height ) {1.361 + display_driver->destroy_render_buffer(render_buffers[i]);1.362 + result = display_driver->create_render_buffer(width,height);1.363 + render_buffers[i] = result;1.364 + }1.365 + break;1.366 + }1.367 + }1.368 + } else {1.369 + result = display_driver->create_render_buffer(width,height);1.370 + if( result != NULL ) {1.371 + render_buffers[render_buffer_count++] = result;1.372 + } else {1.373 + ERROR( "Failed to obtain a render buffer!" );1.374 + return NULL;1.375 + }1.376 + }1.377 + }1.378 +1.379 + /* Setup the buffer */1.380 + result->rowstride = render_stride;1.381 + result->colour_format = colour_format;1.382 + result->scale = render_scale;1.383 + result->size = width * height * colour_formats[colour_format].bpp;1.384 + result->address = render_addr;1.385 + result->flushed = FALSE;1.386 + return result;1.387 +}1.388 +1.389 +/**1.390 + * Invalidate any caching on the supplied address. Specifically, if it falls1.391 + * within any of the render buffers, flush the buffer back to PVR2 ram.1.392 + */1.393 +gboolean pvr2_render_buffer_invalidate( sh4addr_t address, gboolean isWrite )1.394 +{1.395 + int i;1.396 + address = address & 0x1FFFFFFF;1.397 + for( i=0; i<render_buffer_count; i++ ) {1.398 + uint32_t bufaddr = render_buffers[i]->address;1.399 + uint32_t size = render_buffers[i]->size;1.400 + if( bufaddr != -1 && bufaddr <= address &&1.401 + (bufaddr + render_buffers[i]->size) > address ) {1.402 + if( !render_buffers[i]->flushed ) {1.403 + pvr2_render_buffer_copy_to_sh4( render_buffers[i] );1.404 + render_buffers[i]->flushed = TRUE;1.405 + }1.406 + if( isWrite ) {1.407 + render_buffers[i]->address = -1; /* Invalid */1.408 + }1.409 + return TRUE; /* should never have overlapping buffers */1.410 + }1.411 + }1.412 + return FALSE;1.413 +}
.