Search
lxdream.org :: lxdream/src/pvr2/pvr2.c :: diff
lxdream 0.9.1
released Jun 29
Download Now
filename src/pvr2/pvr2.c
changeset 352:f0df7a6d4703
prev337:cdd757aa8e8c
next373: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 +0000
1.2 +++ b/src/pvr2/pvr2.c Sun Feb 11 10:09:32 2007 +0000
1.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.11
1.12 char *video_base;
1.13
1.14 +#define MAX_RENDER_BUFFERS 4
1.15 +
1.16 #define HPOS_PER_FRAME 0
1.17 #define HPOS_PER_LINECOUNT 1
1.18
1.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.26
1.27 void pvr2_display_frame( void );
1.28
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.31
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.35
1.36 display_driver_t display_driver = NULL;
1.37
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.57
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.62
1.63 /**
1.64 * Event handler for the hpos callback
1.65 @@ -127,6 +120,7 @@
1.66
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.82
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.97
1.98 pvr2_ta_init();
1.99 - pvr2_render_init();
1.100 texcache_flush();
1.101 }
1.102
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.277
1.278 +/**
1.279 + * Find the render buffer corresponding to the requested output frame
1.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 partial
1.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 new
1.300 + * size is smaller than the old one).
1.301 + * 2. An existing buffer with the same size chosen by LRU order. Old buffer
1.302 + * is flushed to vram.
1.303 + * 3. A new buffer if one can be created.
1.304 + * 4. The current display buff
1.305 + * Note: The current display field(s) will never be overwritten except as a last
1.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, otherwise
1.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 falls
1.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 +}
.