2 * $Id: render.c,v 1.4 2006-03-16 12:42:39 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_ARGB8888 0x00000000
24 #define POLY_COLOUR_ARGBFLOAT 0x00000010
26 static int pvr2_poly_vertexes[4] = { 3, 4, 6, 8 };
27 static int pvr2_poly_type[4] = { GL_TRIANGLES, GL_QUADS, GL_TRIANGLE_STRIP, GL_TRIANGLE_STRIP };
28 static int pvr2_poly_depthmode[8] = { GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL,
29 GL_GREATER, GL_NOTEQUAL, GL_GEQUAL,
31 static int pvr2_poly_srcblend[8] = {
32 GL_ZERO, GL_ONE, GL_DST_COLOR, GL_ONE_MINUS_DST_COLOR,
33 GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA,
34 GL_ONE_MINUS_DST_ALPHA };
35 static int pvr2_poly_dstblend[8] = {
36 GL_ZERO, GL_ONE, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR,
37 GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA,
38 GL_ONE_MINUS_DST_ALPHA };
39 static int pvr2_poly_texblend[4] = {
40 GL_REPLACE, GL_BLEND, GL_DECAL, GL_MODULATE };
41 static int pvr2_render_colour_format[8] = {
42 COLFMT_ARGB1555, COLFMT_RGB565, COLFMT_ARGB4444, COLFMT_ARGB1555,
43 COLFMT_RGB888, COLFMT_ARGB8888, COLFMT_ARGB8888, COLFMT_ARGB4444 };
45 #define POLY_STRIP_TYPE(poly) ( pvr2_poly_type[((poly->command)>>18)&0x03] )
46 #define POLY_STRIP_VERTEXES(poly) ( pvr2_poly_vertexes[((poly->command)>>18)&0x03] )
47 #define POLY_DEPTH_MODE(poly) ( pvr2_poly_depthmode[poly->poly_cfg>>29] )
48 #define POLY_DEPTH_WRITE(poly) (poly->poly_cfg&0x04000000)
49 #define POLY_TEX_WIDTH(poly) ( 1<< (((poly->poly_mode >> 3) & 0x07 ) + 3) )
50 #define POLY_TEX_HEIGHT(poly) ( 1<< (((poly->poly_mode) & 0x07 ) + 3) )
51 #define POLY_BLEND_SRC(poly) ( pvr2_poly_srcblend[(poly->poly_mode) >> 29] )
52 #define POLY_BLEND_DEST(poly) ( pvr2_poly_dstblend[((poly->poly_mode)>>26)&0x07] )
53 #define POLY_TEX_BLEND(poly) ( pvr2_poly_texblend[((poly->poly_mode) >> 6)&0x03] )
55 extern uint32_t pvr2_frame_counter;
58 * Describes a rendering buffer that's actually held in GL, for when we need
59 * to fetch the bits back to vram.
61 typedef struct pvr2_render_buffer {
62 uint32_t render_addr; /* The actual address rendered to in pvr ram */
65 } *pvr2_render_buffer_t;
67 struct pvr2_render_buffer front_buffer;
68 struct pvr2_render_buffer back_buffer;
70 struct tile_descriptor {
72 struct tile_pointers {
75 uint32_t opaque_mod_ptr;
77 uint32_t trans_mod_ptr;
78 uint32_t punchout_ptr;
82 /* Textured polygon */
85 uint32_t poly_cfg; /* Bitmask */
86 uint32_t poly_mode; /* texture/blending mask */
87 uint32_t texture; /* texture data */
94 struct pvr2_specular_highlight {
106 struct pvr2_vertex_basic {
115 void pvr2_render_copy_to_sh4( pvr2_render_buffer_t buffer,
116 gboolean backBuffer );
118 int pvr2_render_font_list = -1;
120 int glPrintf( const char *fmt, ... )
122 va_list ap; /* our argument pointer */
125 if (fmt == NULL) /* if there is no string to draw do nothing */
128 len = vsnprintf(buf, sizeof(buf), fmt, ap);
131 if( pvr2_render_font_list == -1 ) {
132 pvr2_render_font_list = video_glx_load_font( "-*-helvetica-*-r-normal--16-*-*-*-p-*-iso8859-1");
135 glPushAttrib(GL_LIST_BIT);
136 glListBase(pvr2_render_font_list - 32);
137 glCallLists(len, GL_UNSIGNED_BYTE, buf);
144 gboolean pvr2_render_init( void )
146 front_buffer.render_addr = -1;
147 back_buffer.render_addr = -1;
151 * Display a rendered frame if one is available.
152 * @param address An address in PVR ram (0500000 range).
153 * @return TRUE if a frame was available to be displayed, otherwise false.
155 gboolean pvr2_render_display_frame( uint32_t address )
157 if( front_buffer.render_addr == address ) {
158 /* Current front buffer is already displayed, so do nothing
159 * and tell the caller that all is well.
163 if( back_buffer.render_addr == address ) {
164 /* The more useful case - back buffer is to be displayed. Swap
167 video_driver->display_back_buffer();
168 front_buffer = back_buffer;
169 back_buffer.render_addr = -1;
176 * Prepare the OpenGL context to receive instructions for a new frame.
178 static void pvr2_render_prepare_context( sh4addr_t render_addr,
179 uint32_t width, uint32_t height,
180 uint32_t colour_format,
181 gboolean texture_target )
183 /* Select and initialize the render context */
184 video_driver->set_render_format( width, height, colour_format, texture_target );
186 if( back_buffer.render_addr != -1 &&
187 back_buffer.render_addr != render_addr ) {
188 /* There's a current back buffer, and we're rendering somewhere else -
189 * flush the back buffer back to vram and start a new back buffer
191 pvr2_render_copy_to_sh4( &back_buffer, TRUE );
194 if( front_buffer.render_addr == render_addr ) {
195 /* In case we've been asked to render to the current front buffer -
196 * invalidate the front buffer and render to the back buffer, ensuring
197 * we swap at the next frame display.
199 front_buffer.render_addr = -1;
201 back_buffer.render_addr = render_addr;
202 back_buffer.width = width;
203 back_buffer.height = height;
204 back_buffer.colour_format = colour_format;
206 /* Setup the display model */
207 glDrawBuffer(GL_BACK);
208 glShadeModel(GL_SMOOTH);
209 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
210 glViewport( 0, 0, width, height );
211 glMatrixMode(GL_PROJECTION);
213 glOrtho( 0, width, height, 0, 0, -65535 );
214 glMatrixMode(GL_MODELVIEW);
216 glCullFace( GL_BACK );
218 /* Clear out the buffers */
219 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
221 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
224 static void pvr2_render_display_list( uint32_t *display_list, uint32_t length )
226 uint32_t *cmd_ptr = display_list;
227 int expect_vertexes = 0;
228 gboolean textured = FALSE;
229 struct pvr2_poly *poly;
230 while( cmd_ptr < display_list+length ) {
231 switch( *cmd_ptr >> 24 ) {
232 case PVR2_CMD_POLY_OPAQUE:
233 poly = (struct pvr2_poly *)cmd_ptr;
234 if( poly->command & PVR2_POLY_TEXTURED ) {
235 uint32_t addr = PVR2_TEX_ADDR(poly->texture);
236 int width = POLY_TEX_WIDTH(poly);
237 int height = POLY_TEX_HEIGHT(poly);
238 glEnable( GL_TEXTURE_2D );
239 texcache_get_texture( addr, width, height, poly->texture );
241 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, POLY_TEX_BLEND(poly) );
244 glDisable( GL_TEXTURE_2D );
246 glBlendFunc( POLY_BLEND_SRC(poly), POLY_BLEND_DEST(poly) );
247 if( poly->command & PVR2_POLY_SPECULAR ) {
248 /* Second block expected */
250 if( POLY_DEPTH_WRITE(poly) ) {
251 glEnable( GL_DEPTH_TEST );
252 glDepthFunc( POLY_DEPTH_MODE(poly) );
254 glDisable( GL_DEPTH_TEST );
257 switch( (poly->poly_cfg >> 27) & 0x03 ) {
260 glDisable( GL_CULL_FACE );
263 glEnable( GL_CULL_FACE );
264 glFrontFace( GL_CW );
267 glEnable( GL_CULL_FACE );
268 glFrontFace( GL_CCW );
270 expect_vertexes = POLY_STRIP_VERTEXES( poly );
271 if( expect_vertexes == 3 )
272 glBegin( GL_TRIANGLES );
274 glBegin( GL_TRIANGLE_STRIP );
276 case PVR2_CMD_VERTEX_LAST:
277 case PVR2_CMD_VERTEX:
278 if( expect_vertexes == 0 ) {
279 ERROR( "Unexpected vertex!" );
283 struct pvr2_vertex_basic *vertex = (struct pvr2_vertex_basic *)cmd_ptr;
285 glTexCoord2f( vertex->s, vertex->t );
287 glVertex3f( vertex->x, vertex->y, vertex->z );
289 if( expect_vertexes == 0 ) {
294 cmd_ptr += 8; /* Next record */
299 * Render a complete scene into the OpenGL back buffer.
300 * Note: this will probably need to be broken up eventually once timings are
303 void pvr2_render_scene( )
305 struct tile_descriptor *tile_desc =
306 (struct tile_descriptor *)mem_get_region(PVR2_RAM_BASE + MMIO_READ( PVR2, TILEBASE ));
308 uint32_t render_addr = MMIO_READ( PVR2, RENDADDR1 );
309 gboolean render_to_tex;
310 if( render_addr & 0x01000000 ) {
311 render_addr = (render_addr & 0x00FFFFFF) + PVR2_RAM_BASE_INT;
312 /* Heuristic - if we're rendering to the interlaced region we're
313 * probably creating a texture rather than rendering actual output.
314 * We can optimise for this case a little
316 render_to_tex = TRUE;
318 render_addr = (render_addr & 0x00FFFFFF) + PVR2_RAM_BASE;
319 render_to_tex = FALSE;
321 uint32_t render_mode = MMIO_READ( PVR2, RENDMODE );
322 int width = 640; /* FIXME - get this from the tile buffer */
324 int colour_format = pvr2_render_colour_format[render_mode&0x07];
325 pvr2_render_prepare_context( render_addr, width, height, colour_format,
328 uint32_t *display_list =
329 (uint32_t *)mem_get_region(PVR2_RAM_BASE + MMIO_READ( PVR2, OBJBASE ));
330 uint32_t display_length = *display_list++;
332 int clip_x = MMIO_READ( PVR2, HCLIP ) & 0x03FF;
333 int clip_y = MMIO_READ( PVR2, VCLIP ) & 0x03FF;
334 int clip_width = ((MMIO_READ( PVR2, HCLIP ) >> 16) & 0x03FF) - clip_x + 1;
335 int clip_height= ((MMIO_READ( PVR2, VCLIP ) >> 16) & 0x03FF) - clip_y + 1;
337 if( clip_x == 0 && clip_y == 0 && clip_width == width && clip_height == height ) {
338 glDisable( GL_SCISSOR_TEST );
340 glEnable( GL_SCISSOR_TEST );
341 glScissor( clip_x, clip_y, clip_width, clip_height );
344 /* Fog setup goes here */
346 /* Render the display list */
347 pvr2_render_display_list( display_list, display_length );
349 /* Post-render cleanup and update */
351 /* Add frame, fps, etc data */
352 glRasterPos2i( 4, 16 );
353 // glColor3f( 0.0f, 0.0f, 1.0f );
354 glPrintf( "Frame %d", pvr2_frame_counter );
356 /* Generate end of render event */
357 asic_event( EVENT_PVR_RENDER_DONE );
358 DEBUG( "Rendered frame %d", pvr2_frame_counter );
363 * Flush the indicated render buffer back to PVR. Caller is responsible for
364 * tracking whether there is actually anything in the buffer.
366 * @param buffer A render buffer indicating the address to store to, and the
367 * format the data needs to be in.
368 * @param backBuffer TRUE to flush the back buffer, FALSE for
371 void pvr2_render_copy_to_sh4( pvr2_render_buffer_t buffer,
372 gboolean backBuffer )
374 if( buffer->render_addr == -1 )
376 GLenum type, format = GL_RGBA;
377 int size = buffer->width * buffer->height;
379 switch( buffer->colour_format ) {
381 type = GL_UNSIGNED_SHORT_5_6_5;
386 type = GL_UNSIGNED_INT;
388 size = (size<<1)+size;
390 case COLFMT_ARGB1555:
391 type = GL_UNSIGNED_SHORT_5_5_5_1;
394 case COLFMT_ARGB4444:
395 type = GL_UNSIGNED_SHORT_4_4_4_4;
398 case COLFMT_ARGB8888:
399 type = GL_UNSIGNED_INT_8_8_8_8;
406 glReadBuffer( GL_BACK );
408 glReadBuffer( GL_FRONT );
411 if( buffer->render_addr & 0xFF000000 == 0x04000000 ) {
412 /* Interlaced buffer. Go the double copy... :( */
414 glReadPixels( 0, 0, buffer->width, buffer->height, format, type, target );
415 pvr2_vram64_write( buffer->render_addr, target, size );
417 /* Regular buffer - go direct */
418 char *target = mem_get_region( buffer->render_addr );
419 glReadPixels( 0, 0, buffer->width, buffer->height, format, type, target );
425 * Copy data from PVR ram into the GL render buffer.
427 * @param buffer A render buffer indicating the address to read from, and the
428 * format the data is in.
429 * @param backBuffer TRUE to write the back buffer, FALSE for
432 void pvr2_render_copy_from_sh4( pvr2_render_buffer_t buffer,
433 gboolean backBuffer )
435 if( buffer->render_addr == -1 )
437 GLenum type, format = GL_RGBA;
438 int size = buffer->width * buffer->height;
440 switch( buffer->colour_format ) {
442 type = GL_UNSIGNED_SHORT_5_6_5;
447 type = GL_UNSIGNED_INT;
449 size = (size<<1)+size;
451 case COLFMT_ARGB1555:
452 type = GL_UNSIGNED_SHORT_5_5_5_1;
455 case COLFMT_ARGB4444:
456 type = GL_UNSIGNED_SHORT_4_4_4_4;
459 case COLFMT_ARGB8888:
460 type = GL_UNSIGNED_INT_8_8_8_8;
466 glDrawBuffer( GL_BACK );
468 glDrawBuffer( GL_FRONT );
471 glRasterPos2i( 0, 0 );
472 if( buffer->render_addr & 0xFF000000 == 0x04000000 ) {
473 /* Interlaced buffer. Go the double copy... :( */
475 pvr2_vram64_read( target, buffer->render_addr, size );
476 glDrawPixels( buffer->width, buffer->height,
477 format, type, target );
479 /* Regular buffer - go direct */
480 char *target = mem_get_region( buffer->render_addr );
481 glDrawPixels( buffer->width, buffer->height,
482 format, type, target );
.