nkeynes@100: /** nkeynes@331: * $Id: render.c,v 1.20 2007-01-26 01:37:39 nkeynes Exp $ nkeynes@100: * nkeynes@189: * PVR2 Renderer support. This part is primarily 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@103: struct pvr2_render_buffer front_buffer; nkeynes@103: struct pvr2_render_buffer back_buffer; nkeynes@103: 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@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@189: glDisable( GL_CULL_FACE ); 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@189: void glDrawGrid( int width, int height ) nkeynes@189: { nkeynes@189: int i; nkeynes@189: glDisable( GL_DEPTH_TEST ); nkeynes@189: glLineWidth(1); nkeynes@189: nkeynes@189: glBegin( GL_LINES ); nkeynes@189: glColor4f( 1.0, 1.0, 1.0, 1.0 ); nkeynes@189: for( i=32; i address ) { nkeynes@315: pvr2_render_buffer_copy_to_sh4( &front_buffer, FALSE ); nkeynes@161: front_buffer.render_addr = -1; nkeynes@161: return TRUE; nkeynes@161: } else if( back_buffer.render_addr != -1 && nkeynes@161: back_buffer.render_addr <= address && nkeynes@161: (back_buffer.render_addr + back_buffer.size) > address ) { nkeynes@315: pvr2_render_buffer_copy_to_sh4( &back_buffer, TRUE ); nkeynes@161: back_buffer.render_addr = -1; nkeynes@161: return TRUE; nkeynes@161: } nkeynes@161: return FALSE; nkeynes@161: } nkeynes@161: nkeynes@161: /** 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@322: float bgplanez, float nearz, 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@315: pvr2_render_buffer_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@161: back_buffer.size = width * height * colour_format_bytes[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@331: glOrtho( 0, width, height, 0, -bgplanez, -nearz ); nkeynes@103: glMatrixMode(GL_MODELVIEW); nkeynes@103: glLoadIdentity(); nkeynes@108: glCullFace( GL_BACK ); nkeynes@308: glEnable( GL_BLEND ); nkeynes@103: nkeynes@103: /* Clear out the buffers */ nkeynes@189: glDisable( GL_SCISSOR_TEST ); nkeynes@103: glClearColor(0.0f, 0.0f, 0.0f, 0.0f); nkeynes@299: glClearDepth(0); nkeynes@103: glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); nkeynes@308: nkeynes@103: } nkeynes@103: nkeynes@128: /** 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@191: (struct tile_descriptor *)mem_get_region(PVR2_RAM_BASE + MMIO_READ( PVR2, RENDER_TILEBASE )); nkeynes@100: nkeynes@191: uint32_t render_addr = MMIO_READ( PVR2, RENDER_ADDR1 ); 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@331: float bgplanez = 1/MMIO_READF( PVR2, RENDER_FARCLIP ); nkeynes@191: uint32_t render_mode = MMIO_READ( PVR2, RENDER_MODE ); 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@322: float maxz = pvr2_render_find_maximum_z(); nkeynes@103: pvr2_render_prepare_context( render_addr, width, height, colour_format, nkeynes@322: bgplanez, maxz, render_to_tex ); nkeynes@103: nkeynes@191: int clip_x = MMIO_READ( PVR2, RENDER_HCLIP ) & 0x03FF; nkeynes@191: int clip_y = MMIO_READ( PVR2, RENDER_VCLIP ) & 0x03FF; nkeynes@191: int clip_width = ((MMIO_READ( PVR2, RENDER_HCLIP ) >> 16) & 0x03FF) - clip_x + 1; nkeynes@191: int clip_height= ((MMIO_READ( PVR2, RENDER_VCLIP ) >> 16) & 0x03FF) - clip_y + 1; nkeynes@103: nkeynes@103: /* Fog setup goes here */ nkeynes@103: nkeynes@128: /* Render the background plane */ nkeynes@191: uint32_t bgplane_mode = MMIO_READ(PVR2, RENDER_BGPLANE); nkeynes@189: uint32_t *display_list = nkeynes@191: (uint32_t *)mem_get_region(PVR2_RAM_BASE + MMIO_READ( PVR2, RENDER_POLYBASE )); nkeynes@189: nkeynes@189: uint32_t *bgplane = display_list + (((bgplane_mode & 0x00FFFFFF)) >> 3) ; nkeynes@219: render_backplane( bgplane, width, height, bgplane_mode ); nkeynes@128: nkeynes@189: pvr2_render_tilebuffer( width, height, clip_x, clip_y, nkeynes@189: clip_x + clip_width, clip_y + clip_height ); nkeynes@103: nkeynes@103: /* Post-render cleanup and update */ nkeynes@103: nkeynes@108: /* Add frame, fps, etc data */ nkeynes@189: //glDrawGrid( width, height ); nkeynes@132: glPrintf( 4, 16, "Frame %d", pvr2_get_frame_count() ); 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: }