nkeynes@100: /** nkeynes@144: * $Id: render.c,v 1.8 2006-05-15 08:28:52 nkeynes Exp $ nkeynes@100: * nkeynes@100: * PVR2 Renderer support. This is where the real work happens. nkeynes@100: * nkeynes@100: * Copyright (c) 2005 Nathan Keynes. nkeynes@100: * nkeynes@100: * This program is free software; you can redistribute it and/or modify nkeynes@100: * it under the terms of the GNU General Public License as published by nkeynes@100: * the Free Software Foundation; either version 2 of the License, or nkeynes@100: * (at your option) any later version. nkeynes@100: * nkeynes@100: * This program is distributed in the hope that it will be useful, nkeynes@100: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@100: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@100: * GNU General Public License for more details. nkeynes@100: */ nkeynes@100: nkeynes@100: #include "pvr2/pvr2.h" nkeynes@100: #include "asic.h" nkeynes@103: nkeynes@103: nkeynes@118: #define POLY_COLOUR_PACKED 0x00000000 nkeynes@118: #define POLY_COLOUR_FLOAT 0x00000010 nkeynes@118: #define POLY_COLOUR_INTENSITY 0x00000020 nkeynes@118: #define POLY_COLOUR_INTENSITY_PREV 0x00000030 nkeynes@103: nkeynes@103: static int pvr2_poly_vertexes[4] = { 3, 4, 6, 8 }; nkeynes@103: static int pvr2_poly_type[4] = { GL_TRIANGLES, GL_QUADS, GL_TRIANGLE_STRIP, GL_TRIANGLE_STRIP }; nkeynes@103: static int pvr2_poly_depthmode[8] = { GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL, nkeynes@103: GL_GREATER, GL_NOTEQUAL, GL_GEQUAL, nkeynes@103: GL_ALWAYS }; nkeynes@103: static int pvr2_poly_srcblend[8] = { nkeynes@103: GL_ZERO, GL_ONE, GL_DST_COLOR, GL_ONE_MINUS_DST_COLOR, nkeynes@103: GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, nkeynes@103: GL_ONE_MINUS_DST_ALPHA }; nkeynes@103: static int pvr2_poly_dstblend[8] = { nkeynes@103: GL_ZERO, GL_ONE, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, nkeynes@103: GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, nkeynes@103: GL_ONE_MINUS_DST_ALPHA }; nkeynes@108: static int pvr2_poly_texblend[4] = { nkeynes@108: GL_REPLACE, GL_BLEND, GL_DECAL, GL_MODULATE }; nkeynes@103: static int pvr2_render_colour_format[8] = { nkeynes@103: COLFMT_ARGB1555, COLFMT_RGB565, COLFMT_ARGB4444, COLFMT_ARGB1555, nkeynes@103: COLFMT_RGB888, COLFMT_ARGB8888, COLFMT_ARGB8888, COLFMT_ARGB4444 }; nkeynes@103: nkeynes@103: #define POLY_STRIP_TYPE(poly) ( pvr2_poly_type[((poly->command)>>18)&0x03] ) nkeynes@103: #define POLY_STRIP_VERTEXES(poly) ( pvr2_poly_vertexes[((poly->command)>>18)&0x03] ) nkeynes@103: #define POLY_DEPTH_MODE(poly) ( pvr2_poly_depthmode[poly->poly_cfg>>29] ) nkeynes@128: #define POLY_DEPTH_WRITE(poly) ((poly->poly_cfg&0x04000000) == 0 ) nkeynes@103: #define POLY_TEX_WIDTH(poly) ( 1<< (((poly->poly_mode >> 3) & 0x07 ) + 3) ) nkeynes@103: #define POLY_TEX_HEIGHT(poly) ( 1<< (((poly->poly_mode) & 0x07 ) + 3) ) nkeynes@103: #define POLY_BLEND_SRC(poly) ( pvr2_poly_srcblend[(poly->poly_mode) >> 29] ) nkeynes@103: #define POLY_BLEND_DEST(poly) ( pvr2_poly_dstblend[((poly->poly_mode)>>26)&0x07] ) nkeynes@108: #define POLY_TEX_BLEND(poly) ( pvr2_poly_texblend[((poly->poly_mode) >> 6)&0x03] ) nkeynes@118: #define POLY_COLOUR_TYPE(poly) ( poly->command & 0x00000030 ) nkeynes@100: nkeynes@100: /** nkeynes@103: * Describes a rendering buffer that's actually held in GL, for when we need nkeynes@103: * to fetch the bits back to vram. nkeynes@103: */ nkeynes@103: typedef struct pvr2_render_buffer { nkeynes@103: uint32_t render_addr; /* The actual address rendered to in pvr ram */ nkeynes@103: int width, height; nkeynes@103: int colour_format; nkeynes@103: } *pvr2_render_buffer_t; nkeynes@103: nkeynes@103: struct pvr2_render_buffer front_buffer; nkeynes@103: struct pvr2_render_buffer back_buffer; nkeynes@103: nkeynes@103: struct tile_descriptor { nkeynes@103: uint32_t header[6]; nkeynes@103: struct tile_pointers { nkeynes@103: uint32_t tile_id; nkeynes@103: uint32_t opaque_ptr; nkeynes@103: uint32_t opaque_mod_ptr; nkeynes@103: uint32_t trans_ptr; nkeynes@103: uint32_t trans_mod_ptr; nkeynes@103: uint32_t punchout_ptr; nkeynes@103: } tile[0]; nkeynes@103: }; nkeynes@103: nkeynes@103: /* Textured polygon */ nkeynes@103: struct pvr2_poly { nkeynes@103: uint32_t command; nkeynes@103: uint32_t poly_cfg; /* Bitmask */ nkeynes@103: uint32_t poly_mode; /* texture/blending mask */ nkeynes@103: uint32_t texture; /* texture data */ nkeynes@103: float alpha; nkeynes@103: float red; nkeynes@103: float green; nkeynes@103: float blue; nkeynes@103: }; nkeynes@103: nkeynes@103: struct pvr2_specular_highlight { nkeynes@103: float base_alpha; nkeynes@103: float base_red; nkeynes@103: float base_green; nkeynes@103: float base_blue; nkeynes@103: float offset_alpha; nkeynes@103: float offset_red; nkeynes@103: float offset_green; nkeynes@103: float offset_blue; nkeynes@103: }; nkeynes@103: nkeynes@103: nkeynes@118: struct pvr2_vertex_packed { nkeynes@103: uint32_t command; nkeynes@103: float x, y, z; nkeynes@103: float s,t; nkeynes@118: uint32_t colour; nkeynes@103: float f; nkeynes@103: }; nkeynes@103: nkeynes@128: struct pvr2_vertex_float { nkeynes@128: uint32_t command; nkeynes@128: float x,y,z; nkeynes@128: float a, r, g, b; nkeynes@128: }; nkeynes@128: nkeynes@128: union pvr2_vertex { nkeynes@128: struct pvr2_vertex_packed pack; nkeynes@128: struct pvr2_vertex_float flt; nkeynes@128: }; nkeynes@128: nkeynes@128: typedef struct pvr2_bgplane_packed { nkeynes@128: uint32_t poly_cfg, poly_mode; nkeynes@128: uint32_t texture_mode; nkeynes@128: float x1, y1, z1; nkeynes@128: uint32_t colour1; nkeynes@128: float x2, y2, z2; nkeynes@128: uint32_t colour2; nkeynes@128: float x3, y3, z3; nkeynes@128: uint32_t colour3; nkeynes@128: } *pvr2_bgplane_packed_t; nkeynes@128: nkeynes@128: nkeynes@103: nkeynes@103: void pvr2_render_copy_to_sh4( pvr2_render_buffer_t buffer, nkeynes@103: gboolean backBuffer ); nkeynes@103: nkeynes@108: int pvr2_render_font_list = -1; nkeynes@132: int pvr2_render_trace = 0; nkeynes@108: nkeynes@132: int glPrintf( int x, int y, const char *fmt, ... ) nkeynes@108: { nkeynes@108: va_list ap; /* our argument pointer */ nkeynes@108: char buf[256]; nkeynes@108: int len; nkeynes@108: if (fmt == NULL) /* if there is no string to draw do nothing */ nkeynes@108: return; nkeynes@108: va_start(ap, fmt); nkeynes@108: len = vsnprintf(buf, sizeof(buf), fmt, ap); nkeynes@108: va_end(ap); nkeynes@108: nkeynes@108: nkeynes@108: glPushAttrib(GL_LIST_BIT); nkeynes@132: glDisable( GL_DEPTH_TEST ); nkeynes@132: glDisable( GL_BLEND ); nkeynes@132: glDisable( GL_TEXTURE_2D ); nkeynes@132: glDisable( GL_ALPHA_TEST ); nkeynes@108: glListBase(pvr2_render_font_list - 32); nkeynes@132: glColor3f( 1.0, 1.0, 1.0 ); nkeynes@132: glRasterPos2i( x, y ); nkeynes@108: glCallLists(len, GL_UNSIGNED_BYTE, buf); nkeynes@108: glPopAttrib(); nkeynes@108: nkeynes@108: return len; nkeynes@108: } nkeynes@108: nkeynes@103: nkeynes@103: gboolean pvr2_render_init( void ) nkeynes@103: { nkeynes@103: front_buffer.render_addr = -1; nkeynes@103: back_buffer.render_addr = -1; nkeynes@103: } nkeynes@103: nkeynes@103: /** nkeynes@103: * Display a rendered frame if one is available. nkeynes@103: * @param address An address in PVR ram (0500000 range). nkeynes@103: * @return TRUE if a frame was available to be displayed, otherwise false. nkeynes@103: */ nkeynes@103: gboolean pvr2_render_display_frame( uint32_t address ) nkeynes@103: { nkeynes@103: if( front_buffer.render_addr == address ) { nkeynes@103: /* Current front buffer is already displayed, so do nothing nkeynes@103: * and tell the caller that all is well. nkeynes@103: */ nkeynes@103: return TRUE; nkeynes@103: } nkeynes@103: if( back_buffer.render_addr == address ) { nkeynes@103: /* The more useful case - back buffer is to be displayed. Swap nkeynes@103: * the buffers nkeynes@103: */ nkeynes@144: display_driver->display_back_buffer(); nkeynes@103: front_buffer = back_buffer; nkeynes@103: back_buffer.render_addr = -1; nkeynes@103: return TRUE; nkeynes@103: } nkeynes@103: return FALSE; nkeynes@108: } nkeynes@103: nkeynes@103: /** nkeynes@103: * Prepare the OpenGL context to receive instructions for a new frame. nkeynes@103: */ nkeynes@103: static void pvr2_render_prepare_context( sh4addr_t render_addr, nkeynes@103: uint32_t width, uint32_t height, nkeynes@103: uint32_t colour_format, nkeynes@128: float bgplanez, nkeynes@103: gboolean texture_target ) nkeynes@103: { nkeynes@103: /* Select and initialize the render context */ nkeynes@144: display_driver->set_render_format( width, height, colour_format, texture_target ); nkeynes@103: nkeynes@132: if( pvr2_render_font_list == -1 ) { nkeynes@132: pvr2_render_font_list = video_glx_load_font( "-*-helvetica-*-r-normal--16-*-*-*-p-*-iso8859-1"); nkeynes@132: } nkeynes@132: nkeynes@103: if( back_buffer.render_addr != -1 && nkeynes@103: back_buffer.render_addr != render_addr ) { nkeynes@103: /* There's a current back buffer, and we're rendering somewhere else - nkeynes@103: * flush the back buffer back to vram and start a new back buffer nkeynes@103: */ nkeynes@103: pvr2_render_copy_to_sh4( &back_buffer, TRUE ); nkeynes@103: } nkeynes@103: nkeynes@103: if( front_buffer.render_addr == render_addr ) { nkeynes@103: /* In case we've been asked to render to the current front buffer - nkeynes@103: * invalidate the front buffer and render to the back buffer, ensuring nkeynes@103: * we swap at the next frame display. nkeynes@103: */ nkeynes@103: front_buffer.render_addr = -1; nkeynes@103: } nkeynes@103: back_buffer.render_addr = render_addr; nkeynes@103: back_buffer.width = width; nkeynes@103: back_buffer.height = height; nkeynes@103: back_buffer.colour_format = colour_format; nkeynes@103: nkeynes@103: /* Setup the display model */ nkeynes@103: glDrawBuffer(GL_BACK); nkeynes@103: glShadeModel(GL_SMOOTH); nkeynes@103: glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); nkeynes@103: glViewport( 0, 0, width, height ); nkeynes@103: glMatrixMode(GL_PROJECTION); nkeynes@103: glLoadIdentity(); nkeynes@128: glOrtho( 0, width, height, 0, bgplanez, -1 ); nkeynes@103: glMatrixMode(GL_MODELVIEW); nkeynes@103: glLoadIdentity(); nkeynes@108: glCullFace( GL_BACK ); nkeynes@103: nkeynes@103: /* Clear out the buffers */ nkeynes@103: glClearColor(0.0f, 0.0f, 0.0f, 0.0f); nkeynes@128: glClearDepth(bgplanez); nkeynes@103: glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); nkeynes@103: } nkeynes@103: nkeynes@118: static void pvr2_dump_display_list( uint32_t * display_list, uint32_t length ) nkeynes@118: { nkeynes@118: uint32_t i; nkeynes@128: gboolean vertex = FALSE; nkeynes@128: for( i =0; i>2; i++ ) { nkeynes@128: if( (i % 8) == 0 ) { nkeynes@118: if( i != 0 ) nkeynes@118: fprintf( stderr, "\n" ); nkeynes@132: fprintf( stderr, "%08X:", i*4 ); nkeynes@128: if( display_list[i] == 0xE0000000 || nkeynes@128: display_list[i] == 0xF0000000 ) nkeynes@128: vertex = TRUE; nkeynes@128: else vertex = FALSE; nkeynes@118: } nkeynes@128: if( vertex && (i%8) > 0 && (i%8) < 4 ) nkeynes@128: fprintf( stderr, " %f", ((float *)display_list)[i] ); nkeynes@128: else nkeynes@128: fprintf( stderr, " %08X", display_list[i] ); nkeynes@118: } nkeynes@128: fprintf( stderr, "\n" ); nkeynes@118: } nkeynes@118: nkeynes@103: static void pvr2_render_display_list( uint32_t *display_list, uint32_t length ) nkeynes@103: { nkeynes@103: uint32_t *cmd_ptr = display_list; nkeynes@118: int strip_length = 0, vertex_count = 0; nkeynes@118: int colour_type; nkeynes@103: gboolean textured = FALSE; nkeynes@132: gboolean shaded = FALSE; nkeynes@103: struct pvr2_poly *poly; nkeynes@132: if( pvr2_render_trace ) { nkeynes@132: fprintf( stderr, "-------- %d\n", pvr2_get_frame_count() ); nkeynes@132: pvr2_dump_display_list( display_list, length ); nkeynes@132: } nkeynes@132: while( cmd_ptr < display_list+(length>>2) ) { nkeynes@118: unsigned int cmd = *cmd_ptr >> 24; nkeynes@118: switch( cmd ) { nkeynes@103: case PVR2_CMD_POLY_OPAQUE: nkeynes@128: case PVR2_CMD_POLY_TRANS: nkeynes@132: case PVR2_CMD_POLY_PUNCHOUT: nkeynes@103: poly = (struct pvr2_poly *)cmd_ptr; nkeynes@103: if( poly->command & PVR2_POLY_TEXTURED ) { nkeynes@103: uint32_t addr = PVR2_TEX_ADDR(poly->texture); nkeynes@103: int width = POLY_TEX_WIDTH(poly); nkeynes@103: int height = POLY_TEX_HEIGHT(poly); nkeynes@108: glEnable( GL_TEXTURE_2D ); nkeynes@103: texcache_get_texture( addr, width, height, poly->texture ); nkeynes@103: textured = TRUE; nkeynes@108: glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, POLY_TEX_BLEND(poly) ); nkeynes@103: } else { nkeynes@103: textured = FALSE; nkeynes@103: glDisable( GL_TEXTURE_2D ); nkeynes@103: } nkeynes@103: glBlendFunc( POLY_BLEND_SRC(poly), POLY_BLEND_DEST(poly) ); nkeynes@103: if( poly->command & PVR2_POLY_SPECULAR ) { nkeynes@103: /* Second block expected */ nkeynes@103: } nkeynes@103: if( POLY_DEPTH_WRITE(poly) ) { nkeynes@103: glEnable( GL_DEPTH_TEST ); nkeynes@103: glDepthFunc( POLY_DEPTH_MODE(poly) ); nkeynes@103: } else { nkeynes@103: glDisable( GL_DEPTH_TEST ); nkeynes@103: } nkeynes@103: nkeynes@108: switch( (poly->poly_cfg >> 27) & 0x03 ) { nkeynes@108: case 0: nkeynes@108: case 1: nkeynes@108: glDisable( GL_CULL_FACE ); nkeynes@108: break; nkeynes@108: case 2: nkeynes@108: glEnable( GL_CULL_FACE ); nkeynes@108: glFrontFace( GL_CW ); nkeynes@108: break; nkeynes@108: case 3: nkeynes@108: glEnable( GL_CULL_FACE ); nkeynes@108: glFrontFace( GL_CCW ); nkeynes@108: } nkeynes@118: strip_length = POLY_STRIP_VERTEXES( poly ); nkeynes@118: colour_type = POLY_COLOUR_TYPE( poly ); nkeynes@118: vertex_count = 0; nkeynes@132: if( poly->command & PVR2_POLY_SHADED ) { nkeynes@132: shaded = TRUE; nkeynes@132: } else { nkeynes@132: shaded = FALSE; nkeynes@132: } nkeynes@132: if( poly->poly_mode & PVR2_POLY_MODE_TEXALPHA ) { nkeynes@132: glDisable( GL_BLEND ); nkeynes@132: } else { nkeynes@132: glEnable( GL_BLEND ); nkeynes@132: } nkeynes@132: nkeynes@132: break; nkeynes@132: case PVR2_CMD_MOD_OPAQUE: nkeynes@132: case PVR2_CMD_MOD_TRANS: nkeynes@132: /* TODO */ nkeynes@132: break; nkeynes@132: case PVR2_CMD_END_OF_LIST: nkeynes@103: break; nkeynes@103: case PVR2_CMD_VERTEX_LAST: nkeynes@103: case PVR2_CMD_VERTEX: nkeynes@118: if( vertex_count == 0 ) { nkeynes@132: glBegin( GL_TRIANGLE_STRIP ); nkeynes@103: } nkeynes@118: vertex_count++; nkeynes@118: nkeynes@118: struct pvr2_vertex_packed *vertex = (struct pvr2_vertex_packed *)cmd_ptr; nkeynes@103: if( textured ) { nkeynes@103: glTexCoord2f( vertex->s, vertex->t ); nkeynes@128: nkeynes@132: if( shaded || vertex_count == 1) { nkeynes@132: switch( colour_type ) { nkeynes@132: case POLY_COLOUR_PACKED: nkeynes@132: glColor4ub( vertex->colour >> 16, vertex->colour >> 8, nkeynes@132: vertex->colour, vertex->colour >> 24 ); nkeynes@132: break; nkeynes@132: } nkeynes@128: } nkeynes@128: } else { nkeynes@132: if( shaded || vertex_count == 1 ) { nkeynes@132: switch( colour_type ) { nkeynes@132: case POLY_COLOUR_PACKED: nkeynes@132: glColor4ub( vertex->colour >> 16, vertex->colour >> 8, nkeynes@132: vertex->colour, vertex->colour >> 24 ); nkeynes@132: break; nkeynes@132: case POLY_COLOUR_FLOAT: nkeynes@132: { nkeynes@132: struct pvr2_vertex_float *v = (struct pvr2_vertex_float *)cmd_ptr; nkeynes@132: glColor4f( v->r, v->g, v->b, v->a ); nkeynes@132: } nkeynes@132: break; nkeynes@128: } nkeynes@128: } nkeynes@103: } nkeynes@118: nkeynes@103: glVertex3f( vertex->x, vertex->y, vertex->z ); nkeynes@103: nkeynes@118: if( cmd == PVR2_CMD_VERTEX_LAST ) { nkeynes@103: glEnd(); nkeynes@118: vertex_count = 0; nkeynes@118: } nkeynes@103: break; nkeynes@132: default: nkeynes@132: ERROR( "Unhandled command %08X in display list", *cmd_ptr ); nkeynes@132: pvr2_dump_display_list( display_list, length ); nkeynes@132: return; nkeynes@103: } nkeynes@103: cmd_ptr += 8; /* Next record */ nkeynes@103: } nkeynes@103: } nkeynes@103: nkeynes@128: #define MIN3( a,b,c ) ((a) < (b) ? ( (a) < (c) ? (a) : (c) ) : ((b) < (c) ? (b) : (c)) ) nkeynes@128: #define MAX3( a,b,c ) ((a) > (b) ? ( (a) > (c) ? (a) : (c) ) : ((b) > (c) ? (b) : (c)) ) nkeynes@128: nkeynes@128: /** nkeynes@128: * Render the background plane as best we can. Unfortunately information nkeynes@128: * is a little scant, to say the least. nkeynes@128: */ nkeynes@128: void pvr2_render_draw_backplane( uint32_t mode, uint32_t *poly ) nkeynes@128: { nkeynes@128: if( (mode >> 24) == 0x01 ) { nkeynes@128: /* Packed colour. I think */ nkeynes@128: pvr2_bgplane_packed_t bg = (pvr2_bgplane_packed_t)poly; nkeynes@128: if( bg->colour1 != bg->colour2 || bg->colour2 != bg->colour3 ) { nkeynes@128: WARN( "Multiple background colours specified. Confused" ); nkeynes@128: } nkeynes@128: float x1 = MIN3( bg->x1, bg->x2, bg->x3 ); nkeynes@128: float y1 = MIN3( bg->y1, bg->y2, bg->y3 ); nkeynes@128: float x2 = MAX3( bg->x1, bg->x2, bg->x3 ); nkeynes@128: float y2 = MAX3( bg->y1, bg->y2, bg->y3 ); nkeynes@128: float z = MIN3( bg->z1, bg->z2, bg->z3 ); nkeynes@128: glDisable( GL_TEXTURE_2D ); nkeynes@128: glDisable( GL_DEPTH_TEST ); nkeynes@128: glColor3ub( (uint8_t)(bg->colour1 >> 16), (uint8_t)(bg->colour1 >> 8), nkeynes@128: (uint8_t)bg->colour1 ); nkeynes@128: glBegin( GL_QUADS ); nkeynes@128: glVertex3f( x1, y1, z ); nkeynes@128: glVertex3f( x2, y1, z ); nkeynes@128: glVertex3f( x2, y2, z ); nkeynes@128: glVertex3f( x1, y2, z ); nkeynes@128: glEnd(); nkeynes@128: } else { nkeynes@128: WARN( "Unknown bgplane mode: %08X", mode ); nkeynes@128: fwrite_dump( poly, 48, stderr ); nkeynes@128: } nkeynes@128: } nkeynes@128: nkeynes@103: /** nkeynes@103: * Render a complete scene into the OpenGL back buffer. nkeynes@103: * Note: this will probably need to be broken up eventually once timings are nkeynes@100: * determined. nkeynes@100: */ nkeynes@103: void pvr2_render_scene( ) nkeynes@100: { nkeynes@103: struct tile_descriptor *tile_desc = nkeynes@103: (struct tile_descriptor *)mem_get_region(PVR2_RAM_BASE + MMIO_READ( PVR2, TILEBASE )); nkeynes@100: nkeynes@103: uint32_t render_addr = MMIO_READ( PVR2, RENDADDR1 ); nkeynes@103: gboolean render_to_tex; nkeynes@103: if( render_addr & 0x01000000 ) { nkeynes@103: render_addr = (render_addr & 0x00FFFFFF) + PVR2_RAM_BASE_INT; nkeynes@103: /* Heuristic - if we're rendering to the interlaced region we're nkeynes@103: * probably creating a texture rather than rendering actual output. nkeynes@103: * We can optimise for this case a little nkeynes@103: */ nkeynes@103: render_to_tex = TRUE; nkeynes@118: WARN( "Render to texture not supported properly yet" ); nkeynes@103: } else { nkeynes@103: render_addr = (render_addr & 0x00FFFFFF) + PVR2_RAM_BASE; nkeynes@103: render_to_tex = FALSE; nkeynes@103: } nkeynes@128: nkeynes@128: float bgplanez = MMIO_READF( PVR2, BGPLANEZ ); nkeynes@103: uint32_t render_mode = MMIO_READ( PVR2, RENDMODE ); nkeynes@103: int width = 640; /* FIXME - get this from the tile buffer */ nkeynes@103: int height = 480; nkeynes@103: int colour_format = pvr2_render_colour_format[render_mode&0x07]; nkeynes@103: pvr2_render_prepare_context( render_addr, width, height, colour_format, nkeynes@128: bgplanez, render_to_tex ); nkeynes@103: nkeynes@103: uint32_t *display_list = nkeynes@103: (uint32_t *)mem_get_region(PVR2_RAM_BASE + MMIO_READ( PVR2, OBJBASE )); nkeynes@128: nkeynes@103: uint32_t display_length = *display_list++; nkeynes@103: nkeynes@103: int clip_x = MMIO_READ( PVR2, HCLIP ) & 0x03FF; nkeynes@103: int clip_y = MMIO_READ( PVR2, VCLIP ) & 0x03FF; nkeynes@103: int clip_width = ((MMIO_READ( PVR2, HCLIP ) >> 16) & 0x03FF) - clip_x + 1; nkeynes@103: int clip_height= ((MMIO_READ( PVR2, VCLIP ) >> 16) & 0x03FF) - clip_y + 1; nkeynes@103: nkeynes@103: if( clip_x == 0 && clip_y == 0 && clip_width == width && clip_height == height ) { nkeynes@103: glDisable( GL_SCISSOR_TEST ); nkeynes@103: } else { nkeynes@103: glEnable( GL_SCISSOR_TEST ); nkeynes@103: glScissor( clip_x, clip_y, clip_width, clip_height ); nkeynes@103: } nkeynes@103: nkeynes@103: /* Fog setup goes here */ nkeynes@103: nkeynes@128: /* Render the background plane */ nkeynes@128: uint32_t bgplane_mode = MMIO_READ(PVR2, BGPLANE); nkeynes@128: uint32_t *bgplane = display_list + (((bgplane_mode & 0x00FFFFFF)) >> 3) - 1; nkeynes@128: pvr2_render_draw_backplane( bgplane_mode, bgplane ); nkeynes@128: nkeynes@103: /* Render the display list */ nkeynes@103: pvr2_render_display_list( display_list, display_length ); nkeynes@103: nkeynes@103: /* Post-render cleanup and update */ nkeynes@103: nkeynes@108: /* Add frame, fps, etc data */ nkeynes@132: glPrintf( 4, 16, "Frame %d", pvr2_get_frame_count() ); nkeynes@103: nkeynes@103: /* Generate end of render event */ nkeynes@100: asic_event( EVENT_PVR_RENDER_DONE ); nkeynes@132: DEBUG( "Rendered frame %d", pvr2_get_frame_count() ); nkeynes@100: } nkeynes@103: nkeynes@103: nkeynes@103: /** nkeynes@103: * Flush the indicated render buffer back to PVR. Caller is responsible for nkeynes@103: * tracking whether there is actually anything in the buffer. nkeynes@103: * nkeynes@103: * @param buffer A render buffer indicating the address to store to, and the nkeynes@103: * format the data needs to be in. nkeynes@103: * @param backBuffer TRUE to flush the back buffer, FALSE for nkeynes@103: * the front buffer. nkeynes@103: */ nkeynes@103: void pvr2_render_copy_to_sh4( pvr2_render_buffer_t buffer, nkeynes@103: gboolean backBuffer ) nkeynes@103: { nkeynes@103: if( buffer->render_addr == -1 ) nkeynes@103: return; nkeynes@103: GLenum type, format = GL_RGBA; nkeynes@103: int size = buffer->width * buffer->height; nkeynes@103: nkeynes@103: switch( buffer->colour_format ) { nkeynes@103: case COLFMT_RGB565: nkeynes@103: type = GL_UNSIGNED_SHORT_5_6_5; nkeynes@103: format = GL_RGB; nkeynes@103: size <<= 1; nkeynes@103: break; nkeynes@103: case COLFMT_RGB888: nkeynes@103: type = GL_UNSIGNED_INT; nkeynes@103: format = GL_RGB; nkeynes@103: size = (size<<1)+size; nkeynes@103: break; nkeynes@103: case COLFMT_ARGB1555: nkeynes@103: type = GL_UNSIGNED_SHORT_5_5_5_1; nkeynes@103: size <<= 1; nkeynes@103: break; nkeynes@103: case COLFMT_ARGB4444: nkeynes@103: type = GL_UNSIGNED_SHORT_4_4_4_4; nkeynes@103: size <<= 1; nkeynes@103: break; nkeynes@103: case COLFMT_ARGB8888: nkeynes@103: type = GL_UNSIGNED_INT_8_8_8_8; nkeynes@103: size <<= 2; nkeynes@103: break; nkeynes@103: } nkeynes@103: nkeynes@103: if( backBuffer ) { nkeynes@103: glFinish(); nkeynes@103: glReadBuffer( GL_BACK ); nkeynes@103: } else { nkeynes@103: glReadBuffer( GL_FRONT ); nkeynes@103: } nkeynes@103: nkeynes@103: if( buffer->render_addr & 0xFF000000 == 0x04000000 ) { nkeynes@103: /* Interlaced buffer. Go the double copy... :( */ nkeynes@103: char target[size]; nkeynes@103: glReadPixels( 0, 0, buffer->width, buffer->height, format, type, target ); nkeynes@103: pvr2_vram64_write( buffer->render_addr, target, size ); nkeynes@103: } else { nkeynes@103: /* Regular buffer - go direct */ nkeynes@103: char *target = mem_get_region( buffer->render_addr ); nkeynes@103: glReadPixels( 0, 0, buffer->width, buffer->height, format, type, target ); nkeynes@103: } nkeynes@103: } nkeynes@103: nkeynes@103: nkeynes@103: /** nkeynes@103: * Copy data from PVR ram into the GL render buffer. nkeynes@103: * nkeynes@103: * @param buffer A render buffer indicating the address to read from, and the nkeynes@103: * format the data is in. nkeynes@103: * @param backBuffer TRUE to write the back buffer, FALSE for nkeynes@103: * the front buffer. nkeynes@103: */ nkeynes@103: void pvr2_render_copy_from_sh4( pvr2_render_buffer_t buffer, nkeynes@103: gboolean backBuffer ) nkeynes@103: { nkeynes@103: if( buffer->render_addr == -1 ) nkeynes@103: return; nkeynes@103: GLenum type, format = GL_RGBA; nkeynes@103: int size = buffer->width * buffer->height; nkeynes@103: nkeynes@103: switch( buffer->colour_format ) { nkeynes@103: case COLFMT_RGB565: nkeynes@103: type = GL_UNSIGNED_SHORT_5_6_5; nkeynes@103: format = GL_RGB; nkeynes@103: size <<= 1; nkeynes@103: break; nkeynes@103: case COLFMT_RGB888: nkeynes@103: type = GL_UNSIGNED_INT; nkeynes@103: format = GL_RGB; nkeynes@103: size = (size<<1)+size; nkeynes@103: break; nkeynes@103: case COLFMT_ARGB1555: nkeynes@103: type = GL_UNSIGNED_SHORT_5_5_5_1; nkeynes@103: size <<= 1; nkeynes@103: break; nkeynes@103: case COLFMT_ARGB4444: nkeynes@103: type = GL_UNSIGNED_SHORT_4_4_4_4; nkeynes@103: size <<= 1; nkeynes@103: break; nkeynes@103: case COLFMT_ARGB8888: nkeynes@103: type = GL_UNSIGNED_INT_8_8_8_8; nkeynes@103: size <<= 2; nkeynes@103: break; nkeynes@103: } nkeynes@103: nkeynes@103: if( backBuffer ) { nkeynes@103: glDrawBuffer( GL_BACK ); nkeynes@103: } else { nkeynes@103: glDrawBuffer( GL_FRONT ); nkeynes@103: } nkeynes@103: nkeynes@103: glRasterPos2i( 0, 0 ); nkeynes@103: if( buffer->render_addr & 0xFF000000 == 0x04000000 ) { nkeynes@103: /* Interlaced buffer. Go the double copy... :( */ nkeynes@103: char target[size]; nkeynes@103: pvr2_vram64_read( target, buffer->render_addr, size ); nkeynes@103: glDrawPixels( buffer->width, buffer->height, nkeynes@103: format, type, target ); nkeynes@103: } else { nkeynes@103: /* Regular buffer - go direct */ nkeynes@103: char *target = mem_get_region( buffer->render_addr ); nkeynes@103: glDrawPixels( buffer->width, buffer->height, nkeynes@103: format, type, target ); nkeynes@103: } nkeynes@103: }