2 * $Id: render.c,v 1.6 2006-03-23 13:19:55 nkeynes Exp $
4 * PVR2 Renderer support. This is where the real work happens.
6 * Copyright (c) 2005 Nathan Keynes.
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.
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.
19 #include "pvr2/pvr2.h"
23 #define POLY_COLOUR_PACKED 0x00000000
24 #define POLY_COLOUR_FLOAT 0x00000010
25 #define POLY_COLOUR_INTENSITY 0x00000020
26 #define POLY_COLOUR_INTENSITY_PREV 0x00000030
28 static int pvr2_poly_vertexes[4] = { 3, 4, 6, 8 };
29 static int pvr2_poly_type[4] = { GL_TRIANGLES, GL_QUADS, GL_TRIANGLE_STRIP, GL_TRIANGLE_STRIP };
30 static int pvr2_poly_depthmode[8] = { GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL,
31 GL_GREATER, GL_NOTEQUAL, GL_GEQUAL,
33 static int pvr2_poly_srcblend[8] = {
34 GL_ZERO, GL_ONE, GL_DST_COLOR, GL_ONE_MINUS_DST_COLOR,
35 GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA,
36 GL_ONE_MINUS_DST_ALPHA };
37 static int pvr2_poly_dstblend[8] = {
38 GL_ZERO, GL_ONE, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR,
39 GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA,
40 GL_ONE_MINUS_DST_ALPHA };
41 static int pvr2_poly_texblend[4] = {
42 GL_REPLACE, GL_BLEND, GL_DECAL, GL_MODULATE };
43 static int pvr2_render_colour_format[8] = {
44 COLFMT_ARGB1555, COLFMT_RGB565, COLFMT_ARGB4444, COLFMT_ARGB1555,
45 COLFMT_RGB888, COLFMT_ARGB8888, COLFMT_ARGB8888, COLFMT_ARGB4444 };
47 #define POLY_STRIP_TYPE(poly) ( pvr2_poly_type[((poly->command)>>18)&0x03] )
48 #define POLY_STRIP_VERTEXES(poly) ( pvr2_poly_vertexes[((poly->command)>>18)&0x03] )
49 #define POLY_DEPTH_MODE(poly) ( pvr2_poly_depthmode[poly->poly_cfg>>29] )
50 #define POLY_DEPTH_WRITE(poly) ((poly->poly_cfg&0x04000000) == 0 )
51 #define POLY_TEX_WIDTH(poly) ( 1<< (((poly->poly_mode >> 3) & 0x07 ) + 3) )
52 #define POLY_TEX_HEIGHT(poly) ( 1<< (((poly->poly_mode) & 0x07 ) + 3) )
53 #define POLY_BLEND_SRC(poly) ( pvr2_poly_srcblend[(poly->poly_mode) >> 29] )
54 #define POLY_BLEND_DEST(poly) ( pvr2_poly_dstblend[((poly->poly_mode)>>26)&0x07] )
55 #define POLY_TEX_BLEND(poly) ( pvr2_poly_texblend[((poly->poly_mode) >> 6)&0x03] )
56 #define POLY_COLOUR_TYPE(poly) ( poly->command & 0x00000030 )
58 extern uint32_t pvr2_frame_counter;
61 * Describes a rendering buffer that's actually held in GL, for when we need
62 * to fetch the bits back to vram.
64 typedef struct pvr2_render_buffer {
65 uint32_t render_addr; /* The actual address rendered to in pvr ram */
68 } *pvr2_render_buffer_t;
70 struct pvr2_render_buffer front_buffer;
71 struct pvr2_render_buffer back_buffer;
73 struct tile_descriptor {
75 struct tile_pointers {
78 uint32_t opaque_mod_ptr;
80 uint32_t trans_mod_ptr;
81 uint32_t punchout_ptr;
85 /* Textured polygon */
88 uint32_t poly_cfg; /* Bitmask */
89 uint32_t poly_mode; /* texture/blending mask */
90 uint32_t texture; /* texture data */
97 struct pvr2_specular_highlight {
109 struct pvr2_vertex_packed {
117 struct pvr2_vertex_float {
124 struct pvr2_vertex_packed pack;
125 struct pvr2_vertex_float flt;
128 typedef struct pvr2_bgplane_packed {
129 uint32_t poly_cfg, poly_mode;
130 uint32_t texture_mode;
137 } *pvr2_bgplane_packed_t;
141 void pvr2_render_copy_to_sh4( pvr2_render_buffer_t buffer,
142 gboolean backBuffer );
144 int pvr2_render_font_list = -1;
146 int glPrintf( const char *fmt, ... )
148 va_list ap; /* our argument pointer */
151 if (fmt == NULL) /* if there is no string to draw do nothing */
154 len = vsnprintf(buf, sizeof(buf), fmt, ap);
157 if( pvr2_render_font_list == -1 ) {
158 glColor3f( 1.0, 1.0, 1.0 );
159 pvr2_render_font_list = video_glx_load_font( "-*-helvetica-*-r-normal--16-*-*-*-p-*-iso8859-1");
162 glPushAttrib(GL_LIST_BIT);
163 glListBase(pvr2_render_font_list - 32);
164 glCallLists(len, GL_UNSIGNED_BYTE, buf);
171 gboolean pvr2_render_init( void )
173 front_buffer.render_addr = -1;
174 back_buffer.render_addr = -1;
178 * Display a rendered frame if one is available.
179 * @param address An address in PVR ram (0500000 range).
180 * @return TRUE if a frame was available to be displayed, otherwise false.
182 gboolean pvr2_render_display_frame( uint32_t address )
184 if( front_buffer.render_addr == address ) {
185 /* Current front buffer is already displayed, so do nothing
186 * and tell the caller that all is well.
190 if( back_buffer.render_addr == address ) {
191 /* The more useful case - back buffer is to be displayed. Swap
194 video_driver->display_back_buffer();
195 front_buffer = back_buffer;
196 back_buffer.render_addr = -1;
203 * Prepare the OpenGL context to receive instructions for a new frame.
205 static void pvr2_render_prepare_context( sh4addr_t render_addr,
206 uint32_t width, uint32_t height,
207 uint32_t colour_format,
209 gboolean texture_target )
211 /* Select and initialize the render context */
212 video_driver->set_render_format( width, height, colour_format, texture_target );
214 if( back_buffer.render_addr != -1 &&
215 back_buffer.render_addr != render_addr ) {
216 /* There's a current back buffer, and we're rendering somewhere else -
217 * flush the back buffer back to vram and start a new back buffer
219 pvr2_render_copy_to_sh4( &back_buffer, TRUE );
222 if( front_buffer.render_addr == render_addr ) {
223 /* In case we've been asked to render to the current front buffer -
224 * invalidate the front buffer and render to the back buffer, ensuring
225 * we swap at the next frame display.
227 front_buffer.render_addr = -1;
229 back_buffer.render_addr = render_addr;
230 back_buffer.width = width;
231 back_buffer.height = height;
232 back_buffer.colour_format = colour_format;
234 /* Setup the display model */
235 glDrawBuffer(GL_BACK);
236 glShadeModel(GL_SMOOTH);
237 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
238 glViewport( 0, 0, width, height );
239 glMatrixMode(GL_PROJECTION);
241 glOrtho( 0, width, height, 0, bgplanez, -1 );
242 glMatrixMode(GL_MODELVIEW);
244 glCullFace( GL_BACK );
246 /* Clear out the buffers */
247 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
248 glClearDepth(bgplanez);
249 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
252 static void pvr2_dump_display_list( uint32_t * display_list, uint32_t length )
255 gboolean vertex = FALSE;
256 for( i =0; i<length>>2; i++ ) {
259 fprintf( stderr, "\n" );
260 fprintf( stderr, "%08X:", i*32 );
261 if( display_list[i] == 0xE0000000 ||
262 display_list[i] == 0xF0000000 )
266 if( vertex && (i%8) > 0 && (i%8) < 4 )
267 fprintf( stderr, " %f", ((float *)display_list)[i] );
269 fprintf( stderr, " %08X", display_list[i] );
271 fprintf( stderr, "\n" );
274 static void pvr2_render_display_list( uint32_t *display_list, uint32_t length )
276 uint32_t *cmd_ptr = display_list;
277 int strip_length = 0, vertex_count = 0;
279 gboolean textured = FALSE;
280 struct pvr2_poly *poly;
281 fprintf( stderr, "-------- %d\n", pvr2_frame_counter );
282 pvr2_dump_display_list( display_list, length );
283 while( cmd_ptr < display_list+length ) {
284 unsigned int cmd = *cmd_ptr >> 24;
286 case PVR2_CMD_POLY_OPAQUE:
287 case PVR2_CMD_POLY_TRANS:
288 if( cmd == PVR2_CMD_POLY_TRANS ) {
289 glEnable( GL_BLEND );
291 glDisable( GL_BLEND );
294 poly = (struct pvr2_poly *)cmd_ptr;
295 if( poly->command & PVR2_POLY_TEXTURED ) {
296 uint32_t addr = PVR2_TEX_ADDR(poly->texture);
297 int width = POLY_TEX_WIDTH(poly);
298 int height = POLY_TEX_HEIGHT(poly);
299 glEnable( GL_TEXTURE_2D );
300 texcache_get_texture( addr, width, height, poly->texture );
302 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, POLY_TEX_BLEND(poly) );
305 glDisable( GL_TEXTURE_2D );
307 glBlendFunc( POLY_BLEND_SRC(poly), POLY_BLEND_DEST(poly) );
308 if( poly->command & PVR2_POLY_SPECULAR ) {
309 /* Second block expected */
311 if( POLY_DEPTH_WRITE(poly) ) {
312 glEnable( GL_DEPTH_TEST );
313 glDepthFunc( POLY_DEPTH_MODE(poly) );
315 glDisable( GL_DEPTH_TEST );
318 switch( (poly->poly_cfg >> 27) & 0x03 ) {
321 glDisable( GL_CULL_FACE );
324 glEnable( GL_CULL_FACE );
325 glFrontFace( GL_CW );
328 glEnable( GL_CULL_FACE );
329 glFrontFace( GL_CCW );
331 strip_length = POLY_STRIP_VERTEXES( poly );
332 colour_type = POLY_COLOUR_TYPE( poly );
335 case PVR2_CMD_VERTEX_LAST:
336 case PVR2_CMD_VERTEX:
337 if( vertex_count == 0 ) {
338 if( strip_length == 3 )
339 glBegin( GL_TRIANGLES );
341 glBegin( GL_TRIANGLE_STRIP );
345 struct pvr2_vertex_packed *vertex = (struct pvr2_vertex_packed *)cmd_ptr;
347 glTexCoord2f( vertex->s, vertex->t );
349 switch( colour_type ) {
350 case POLY_COLOUR_PACKED:
351 glColor4ub( vertex->colour >> 16, vertex->colour >> 8,
352 vertex->colour, vertex->colour >> 24 );
356 switch( colour_type ) {
357 case POLY_COLOUR_PACKED:
358 glColor4ub( vertex->colour >> 16, vertex->colour >> 8,
359 vertex->colour, vertex->colour >> 24 );
361 case POLY_COLOUR_FLOAT:
363 struct pvr2_vertex_float *v = (struct pvr2_vertex_float *)cmd_ptr;
364 glColor4f( v->r, v->g, v->b, v->a );
370 glVertex3f( vertex->x, vertex->y, vertex->z );
372 if( cmd == PVR2_CMD_VERTEX_LAST ) {
377 if( vertex_count >= strip_length ) {
378 ERROR( "Rendering long strip (expected end after %d)", strip_length );
379 pvr2_dump_display_list( display_list, length );
384 cmd_ptr += 8; /* Next record */
388 #define MIN3( a,b,c ) ((a) < (b) ? ( (a) < (c) ? (a) : (c) ) : ((b) < (c) ? (b) : (c)) )
389 #define MAX3( a,b,c ) ((a) > (b) ? ( (a) > (c) ? (a) : (c) ) : ((b) > (c) ? (b) : (c)) )
392 * Render the background plane as best we can. Unfortunately information
393 * is a little scant, to say the least.
395 void pvr2_render_draw_backplane( uint32_t mode, uint32_t *poly )
397 if( (mode >> 24) == 0x01 ) {
398 /* Packed colour. I think */
399 pvr2_bgplane_packed_t bg = (pvr2_bgplane_packed_t)poly;
400 if( bg->colour1 != bg->colour2 || bg->colour2 != bg->colour3 ) {
401 WARN( "Multiple background colours specified. Confused" );
403 float x1 = MIN3( bg->x1, bg->x2, bg->x3 );
404 float y1 = MIN3( bg->y1, bg->y2, bg->y3 );
405 float x2 = MAX3( bg->x1, bg->x2, bg->x3 );
406 float y2 = MAX3( bg->y1, bg->y2, bg->y3 );
407 float z = MIN3( bg->z1, bg->z2, bg->z3 );
408 glDisable( GL_TEXTURE_2D );
409 glDisable( GL_DEPTH_TEST );
410 glColor3ub( (uint8_t)(bg->colour1 >> 16), (uint8_t)(bg->colour1 >> 8),
411 (uint8_t)bg->colour1 );
413 glVertex3f( x1, y1, z );
414 glVertex3f( x2, y1, z );
415 glVertex3f( x2, y2, z );
416 glVertex3f( x1, y2, z );
419 WARN( "Unknown bgplane mode: %08X", mode );
420 fwrite_dump( poly, 48, stderr );
425 * Render a complete scene into the OpenGL back buffer.
426 * Note: this will probably need to be broken up eventually once timings are
429 void pvr2_render_scene( )
431 struct tile_descriptor *tile_desc =
432 (struct tile_descriptor *)mem_get_region(PVR2_RAM_BASE + MMIO_READ( PVR2, TILEBASE ));
434 uint32_t render_addr = MMIO_READ( PVR2, RENDADDR1 );
435 gboolean render_to_tex;
436 if( render_addr & 0x01000000 ) {
437 render_addr = (render_addr & 0x00FFFFFF) + PVR2_RAM_BASE_INT;
438 /* Heuristic - if we're rendering to the interlaced region we're
439 * probably creating a texture rather than rendering actual output.
440 * We can optimise for this case a little
442 render_to_tex = TRUE;
443 WARN( "Render to texture not supported properly yet" );
445 render_addr = (render_addr & 0x00FFFFFF) + PVR2_RAM_BASE;
446 render_to_tex = FALSE;
449 float bgplanez = MMIO_READF( PVR2, BGPLANEZ );
450 uint32_t render_mode = MMIO_READ( PVR2, RENDMODE );
451 int width = 640; /* FIXME - get this from the tile buffer */
453 int colour_format = pvr2_render_colour_format[render_mode&0x07];
454 pvr2_render_prepare_context( render_addr, width, height, colour_format,
455 bgplanez, render_to_tex );
457 uint32_t *display_list =
458 (uint32_t *)mem_get_region(PVR2_RAM_BASE + MMIO_READ( PVR2, OBJBASE ));
460 uint32_t display_length = *display_list++;
462 int clip_x = MMIO_READ( PVR2, HCLIP ) & 0x03FF;
463 int clip_y = MMIO_READ( PVR2, VCLIP ) & 0x03FF;
464 int clip_width = ((MMIO_READ( PVR2, HCLIP ) >> 16) & 0x03FF) - clip_x + 1;
465 int clip_height= ((MMIO_READ( PVR2, VCLIP ) >> 16) & 0x03FF) - clip_y + 1;
467 if( clip_x == 0 && clip_y == 0 && clip_width == width && clip_height == height ) {
468 glDisable( GL_SCISSOR_TEST );
470 glEnable( GL_SCISSOR_TEST );
471 glScissor( clip_x, clip_y, clip_width, clip_height );
474 /* Fog setup goes here */
476 /* Render the background plane */
477 uint32_t bgplane_mode = MMIO_READ(PVR2, BGPLANE);
478 uint32_t *bgplane = display_list + (((bgplane_mode & 0x00FFFFFF)) >> 3) - 1;
479 pvr2_render_draw_backplane( bgplane_mode, bgplane );
481 /* Render the display list */
482 pvr2_render_display_list( display_list, display_length );
484 /* Post-render cleanup and update */
486 /* Add frame, fps, etc data */
487 glRasterPos2i( 4, 16 );
488 glPrintf( "Frame %d", pvr2_frame_counter );
490 /* Generate end of render event */
491 asic_event( EVENT_PVR_RENDER_DONE );
492 DEBUG( "Rendered frame %d", pvr2_frame_counter );
497 * Flush the indicated render buffer back to PVR. Caller is responsible for
498 * tracking whether there is actually anything in the buffer.
500 * @param buffer A render buffer indicating the address to store to, and the
501 * format the data needs to be in.
502 * @param backBuffer TRUE to flush the back buffer, FALSE for
505 void pvr2_render_copy_to_sh4( pvr2_render_buffer_t buffer,
506 gboolean backBuffer )
508 if( buffer->render_addr == -1 )
510 GLenum type, format = GL_RGBA;
511 int size = buffer->width * buffer->height;
513 switch( buffer->colour_format ) {
515 type = GL_UNSIGNED_SHORT_5_6_5;
520 type = GL_UNSIGNED_INT;
522 size = (size<<1)+size;
524 case COLFMT_ARGB1555:
525 type = GL_UNSIGNED_SHORT_5_5_5_1;
528 case COLFMT_ARGB4444:
529 type = GL_UNSIGNED_SHORT_4_4_4_4;
532 case COLFMT_ARGB8888:
533 type = GL_UNSIGNED_INT_8_8_8_8;
540 glReadBuffer( GL_BACK );
542 glReadBuffer( GL_FRONT );
545 if( buffer->render_addr & 0xFF000000 == 0x04000000 ) {
546 /* Interlaced buffer. Go the double copy... :( */
548 glReadPixels( 0, 0, buffer->width, buffer->height, format, type, target );
549 pvr2_vram64_write( buffer->render_addr, target, size );
551 /* Regular buffer - go direct */
552 char *target = mem_get_region( buffer->render_addr );
553 glReadPixels( 0, 0, buffer->width, buffer->height, format, type, target );
559 * Copy data from PVR ram into the GL render buffer.
561 * @param buffer A render buffer indicating the address to read from, and the
562 * format the data is in.
563 * @param backBuffer TRUE to write the back buffer, FALSE for
566 void pvr2_render_copy_from_sh4( pvr2_render_buffer_t buffer,
567 gboolean backBuffer )
569 if( buffer->render_addr == -1 )
571 GLenum type, format = GL_RGBA;
572 int size = buffer->width * buffer->height;
574 switch( buffer->colour_format ) {
576 type = GL_UNSIGNED_SHORT_5_6_5;
581 type = GL_UNSIGNED_INT;
583 size = (size<<1)+size;
585 case COLFMT_ARGB1555:
586 type = GL_UNSIGNED_SHORT_5_5_5_1;
589 case COLFMT_ARGB4444:
590 type = GL_UNSIGNED_SHORT_4_4_4_4;
593 case COLFMT_ARGB8888:
594 type = GL_UNSIGNED_INT_8_8_8_8;
600 glDrawBuffer( GL_BACK );
602 glDrawBuffer( GL_FRONT );
605 glRasterPos2i( 0, 0 );
606 if( buffer->render_addr & 0xFF000000 == 0x04000000 ) {
607 /* Interlaced buffer. Go the double copy... :( */
609 pvr2_vram64_read( target, buffer->render_addr, size );
610 glDrawPixels( buffer->width, buffer->height,
611 format, type, target );
613 /* Regular buffer - go direct */
614 char *target = mem_get_region( buffer->render_addr );
615 glDrawPixels( buffer->width, buffer->height,
616 format, type, target );
.