filename | src/pvr2/render.c |
changeset | 221:cf5c6d326162 |
prev | 219:dfd3292143f2 |
next | 222:541d9d899aba |
author | nkeynes |
date | Tue Sep 12 08:38:38 2006 +0000 (14 years ago) |
permissions | -rw-r--r-- |
last change | Bug #0010 Move polygon macros to pvr2.h Implement background rendering more fully |
view | annotate | diff | log | raw |
1 /**
2 * $Id: render.c,v 1.14 2006-09-12 08:38:38 nkeynes Exp $
3 *
4 * PVR2 Renderer support. This part is primarily
5 *
6 * Copyright (c) 2005 Nathan Keynes.
7 *
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.
12 *
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.
17 */
19 #include "pvr2/pvr2.h"
20 #include "asic.h"
22 /**
23 * Describes a rendering buffer that's actually held in GL, for when we need
24 * to fetch the bits back to vram.
25 */
26 typedef struct pvr2_render_buffer {
27 sh4addr_t render_addr; /* The actual address rendered to in pvr ram */
28 uint32_t size; /* Length of rendering region in bytes */
29 int width, height;
30 int colour_format;
31 } *pvr2_render_buffer_t;
33 struct pvr2_render_buffer front_buffer;
34 struct pvr2_render_buffer back_buffer;
36 typedef struct pvr2_bgplane_packed {
37 uint32_t poly_cfg, poly_mode;
38 uint32_t texture_mode;
39 float x1, y1, z1;
40 uint32_t colour1;
41 float x2, y2, z2;
42 uint32_t colour2;
43 float x3, y3, z3;
44 uint32_t colour3;
45 } *pvr2_bgplane_packed_t;
49 void pvr2_render_copy_to_sh4( pvr2_render_buffer_t buffer,
50 gboolean backBuffer );
52 int pvr2_render_font_list = -1;
53 int pvr2_render_trace = 0;
55 int glPrintf( int x, int y, const char *fmt, ... )
56 {
57 va_list ap; /* our argument pointer */
58 char buf[256];
59 int len;
60 if (fmt == NULL) /* if there is no string to draw do nothing */
61 return;
62 va_start(ap, fmt);
63 len = vsnprintf(buf, sizeof(buf), fmt, ap);
64 va_end(ap);
67 glPushAttrib(GL_LIST_BIT);
68 glDisable( GL_DEPTH_TEST );
69 glDisable( GL_BLEND );
70 glDisable( GL_TEXTURE_2D );
71 glDisable( GL_ALPHA_TEST );
72 glDisable( GL_CULL_FACE );
73 glListBase(pvr2_render_font_list - 32);
74 glColor3f( 1.0, 1.0, 1.0 );
75 glRasterPos2i( x, y );
76 glCallLists(len, GL_UNSIGNED_BYTE, buf);
77 glPopAttrib();
79 return len;
80 }
82 void glDrawGrid( int width, int height )
83 {
84 int i;
85 glDisable( GL_DEPTH_TEST );
86 glLineWidth(1);
88 glBegin( GL_LINES );
89 glColor4f( 1.0, 1.0, 1.0, 1.0 );
90 for( i=32; i<width; i+=32 ) {
91 glVertex3f( i, 0.0, 3.0 );
92 glVertex3f( i,height-1, 3.0 );
93 }
95 for( i=32; i<height; i+=32 ) {
96 glVertex3f( 0.0, i, 3.0 );
97 glVertex3f( width, i, 3.0 );
98 }
99 glEnd();
101 }
104 gboolean pvr2_render_init( void )
105 {
106 front_buffer.render_addr = -1;
107 back_buffer.render_addr = -1;
108 }
110 /**
111 * Invalidate any caching on the supplied address. Specifically, if it falls
112 * within either the front buffer or back buffer, flush the buffer back to
113 * PVR2 ram (note that front buffer flush may be corrupt under some
114 * circumstances).
115 */
116 gboolean pvr2_render_invalidate( sh4addr_t address )
117 {
118 address = address & 0x1FFFFFFF;
119 if( front_buffer.render_addr != -1 &&
120 front_buffer.render_addr <= address &&
121 (front_buffer.render_addr + front_buffer.size) > address ) {
122 pvr2_render_copy_to_sh4( &front_buffer, FALSE );
123 front_buffer.render_addr = -1;
124 return TRUE;
125 } else if( back_buffer.render_addr != -1 &&
126 back_buffer.render_addr <= address &&
127 (back_buffer.render_addr + back_buffer.size) > address ) {
128 pvr2_render_copy_to_sh4( &back_buffer, TRUE );
129 back_buffer.render_addr = -1;
130 return TRUE;
131 }
132 return FALSE;
133 }
135 /**
136 * Display a rendered frame if one is available.
137 * @param address An address in PVR ram (0500000 range).
138 * @return TRUE if a frame was available to be displayed, otherwise false.
139 */
140 gboolean pvr2_render_display_frame( uint32_t address )
141 {
142 if( front_buffer.render_addr == address ) {
143 /* Current front buffer is already displayed, so do nothing
144 * and tell the caller that all is well.
145 */
146 return TRUE;
147 }
148 if( back_buffer.render_addr == address ) {
149 /* The more useful case - back buffer is to be displayed. Swap
150 * the buffers
151 */
152 display_driver->display_back_buffer();
153 front_buffer = back_buffer;
154 back_buffer.render_addr = -1;
155 return TRUE;
156 }
157 return FALSE;
158 }
160 /**
161 * Prepare the OpenGL context to receive instructions for a new frame.
162 */
163 static void pvr2_render_prepare_context( sh4addr_t render_addr,
164 uint32_t width, uint32_t height,
165 uint32_t colour_format,
166 float bgplanez,
167 gboolean texture_target )
168 {
169 /* Select and initialize the render context */
170 display_driver->set_render_format( width, height, colour_format, texture_target );
172 if( pvr2_render_font_list == -1 ) {
173 pvr2_render_font_list = video_glx_load_font( "-*-helvetica-*-r-normal--16-*-*-*-p-*-iso8859-1");
174 }
176 if( back_buffer.render_addr != -1 &&
177 back_buffer.render_addr != render_addr ) {
178 /* There's a current back buffer, and we're rendering somewhere else -
179 * flush the back buffer back to vram and start a new back buffer
180 */
181 pvr2_render_copy_to_sh4( &back_buffer, TRUE );
182 }
184 if( front_buffer.render_addr == render_addr ) {
185 /* In case we've been asked to render to the current front buffer -
186 * invalidate the front buffer and render to the back buffer, ensuring
187 * we swap at the next frame display.
188 */
189 front_buffer.render_addr = -1;
190 }
191 back_buffer.render_addr = render_addr;
192 back_buffer.width = width;
193 back_buffer.height = height;
194 back_buffer.colour_format = colour_format;
195 back_buffer.size = width * height * colour_format_bytes[colour_format];
197 /* Setup the display model */
198 glDrawBuffer(GL_BACK);
199 glShadeModel(GL_SMOOTH);
200 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
201 glViewport( 0, 0, width, height );
202 glMatrixMode(GL_PROJECTION);
203 glLoadIdentity();
204 glOrtho( 0, width, height, 0, bgplanez, -4 );
205 glMatrixMode(GL_MODELVIEW);
206 glLoadIdentity();
207 glCullFace( GL_BACK );
209 /* Clear out the buffers */
210 glDisable( GL_SCISSOR_TEST );
211 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
212 glClearDepth(bgplanez);
213 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
214 }
217 #define MIN3( a,b,c ) ((a) < (b) ? ( (a) < (c) ? (a) : (c) ) : ((b) < (c) ? (b) : (c)) )
218 #define MAX3( a,b,c ) ((a) > (b) ? ( (a) > (c) ? (a) : (c) ) : ((b) > (c) ? (b) : (c)) )
220 /**
221 * Render a complete scene into the OpenGL back buffer.
222 * Note: this will probably need to be broken up eventually once timings are
223 * determined.
224 */
225 void pvr2_render_scene( )
226 {
227 struct tile_descriptor *tile_desc =
228 (struct tile_descriptor *)mem_get_region(PVR2_RAM_BASE + MMIO_READ( PVR2, RENDER_TILEBASE ));
230 uint32_t render_addr = MMIO_READ( PVR2, RENDER_ADDR1 );
231 gboolean render_to_tex;
232 if( render_addr & 0x01000000 ) {
233 render_addr = (render_addr & 0x00FFFFFF) + PVR2_RAM_BASE_INT;
234 /* Heuristic - if we're rendering to the interlaced region we're
235 * probably creating a texture rather than rendering actual output.
236 * We can optimise for this case a little
237 */
238 render_to_tex = TRUE;
239 WARN( "Render to texture not supported properly yet" );
240 } else {
241 render_addr = (render_addr & 0x00FFFFFF) + PVR2_RAM_BASE;
242 render_to_tex = FALSE;
243 }
245 float bgplanez = MMIO_READF( PVR2, RENDER_FARCLIP );
246 uint32_t render_mode = MMIO_READ( PVR2, RENDER_MODE );
247 int width = 640; /* FIXME - get this from the tile buffer */
248 int height = 480;
249 int colour_format = pvr2_render_colour_format[render_mode&0x07];
250 pvr2_render_prepare_context( render_addr, width, height, colour_format,
251 bgplanez, render_to_tex );
253 int clip_x = MMIO_READ( PVR2, RENDER_HCLIP ) & 0x03FF;
254 int clip_y = MMIO_READ( PVR2, RENDER_VCLIP ) & 0x03FF;
255 int clip_width = ((MMIO_READ( PVR2, RENDER_HCLIP ) >> 16) & 0x03FF) - clip_x + 1;
256 int clip_height= ((MMIO_READ( PVR2, RENDER_VCLIP ) >> 16) & 0x03FF) - clip_y + 1;
258 /* Fog setup goes here */
260 /* Render the background plane */
261 uint32_t bgplane_mode = MMIO_READ(PVR2, RENDER_BGPLANE);
262 uint32_t *display_list =
263 (uint32_t *)mem_get_region(PVR2_RAM_BASE + MMIO_READ( PVR2, RENDER_POLYBASE ));
265 uint32_t *bgplane = display_list + (((bgplane_mode & 0x00FFFFFF)) >> 3) ;
266 render_backplane( bgplane, width, height, bgplane_mode );
268 pvr2_render_tilebuffer( width, height, clip_x, clip_y,
269 clip_x + clip_width, clip_y + clip_height );
271 /* Post-render cleanup and update */
273 /* Add frame, fps, etc data */
274 //glDrawGrid( width, height );
275 glPrintf( 4, 16, "Frame %d", pvr2_get_frame_count() );
276 /* Generate end of render event */
277 asic_event( EVENT_PVR_RENDER_DONE );
278 DEBUG( "Rendered frame %d", pvr2_get_frame_count() );
279 }
282 /**
283 * Flush the indicated render buffer back to PVR. Caller is responsible for
284 * tracking whether there is actually anything in the buffer.
285 *
286 * @param buffer A render buffer indicating the address to store to, and the
287 * format the data needs to be in.
288 * @param backBuffer TRUE to flush the back buffer, FALSE for
289 * the front buffer.
290 */
291 void pvr2_render_copy_to_sh4( pvr2_render_buffer_t buffer,
292 gboolean backBuffer )
293 {
294 if( buffer->render_addr == -1 )
295 return;
296 GLenum type, format = GL_RGBA;
297 int line_size = buffer->width, size;
299 switch( buffer->colour_format ) {
300 case COLFMT_RGB565:
301 type = GL_UNSIGNED_SHORT_5_6_5;
302 format = GL_RGB;
303 line_size <<= 1;
304 break;
305 case COLFMT_RGB888:
306 type = GL_UNSIGNED_INT;
307 format = GL_RGB;
308 line_size = (line_size<<1)+line_size;
309 break;
310 case COLFMT_ARGB1555:
311 type = GL_UNSIGNED_SHORT_5_5_5_1;
312 line_size <<= 1;
313 break;
314 case COLFMT_ARGB4444:
315 type = GL_UNSIGNED_SHORT_4_4_4_4;
316 line_size <<= 1;
317 break;
318 case COLFMT_ARGB8888:
319 type = GL_UNSIGNED_INT_8_8_8_8;
320 line_size <<= 2;
321 break;
322 }
323 size = line_size * buffer->height;
325 if( backBuffer ) {
326 glFinish();
327 glReadBuffer( GL_BACK );
328 } else {
329 glReadBuffer( GL_FRONT );
330 }
332 if( buffer->render_addr & 0xFF000000 == 0x04000000 ) {
333 /* Interlaced buffer. Go the double copy... :( */
334 char target[size];
335 glReadPixels( 0, 0, buffer->width, buffer->height, format, type, target );
336 pvr2_vram64_write( buffer->render_addr, target, size );
337 } else {
338 /* Regular buffer */
339 char target[size];
340 glReadPixels( 0, 0, buffer->width, buffer->height, format, type, target );
341 pvr2_vram_write_invert( buffer->render_addr, target, size, line_size );
342 }
343 }
346 /**
347 * Copy data from PVR ram into the GL render buffer.
348 *
349 * @param buffer A render buffer indicating the address to read from, and the
350 * format the data is in.
351 * @param backBuffer TRUE to write the back buffer, FALSE for
352 * the front buffer.
353 */
354 void pvr2_render_copy_from_sh4( pvr2_render_buffer_t buffer,
355 gboolean backBuffer )
356 {
357 if( buffer->render_addr == -1 )
358 return;
359 GLenum type, format = GL_RGBA;
360 int size = buffer->width * buffer->height;
362 switch( buffer->colour_format ) {
363 case COLFMT_RGB565:
364 type = GL_UNSIGNED_SHORT_5_6_5;
365 format = GL_RGB;
366 size <<= 1;
367 break;
368 case COLFMT_RGB888:
369 type = GL_UNSIGNED_INT;
370 format = GL_RGB;
371 size = (size<<1)+size;
372 break;
373 case COLFMT_ARGB1555:
374 type = GL_UNSIGNED_SHORT_5_5_5_1;
375 size <<= 1;
376 break;
377 case COLFMT_ARGB4444:
378 type = GL_UNSIGNED_SHORT_4_4_4_4;
379 size <<= 1;
380 break;
381 case COLFMT_ARGB8888:
382 type = GL_UNSIGNED_INT_8_8_8_8;
383 size <<= 2;
384 break;
385 }
387 if( backBuffer ) {
388 glDrawBuffer( GL_BACK );
389 } else {
390 glDrawBuffer( GL_FRONT );
391 }
393 glRasterPos2i( 0, 0 );
394 if( buffer->render_addr & 0xFF000000 == 0x04000000 ) {
395 /* Interlaced buffer. Go the double copy... :( */
396 char target[size];
397 pvr2_vram64_read( target, buffer->render_addr, size );
398 glDrawPixels( buffer->width, buffer->height,
399 format, type, target );
400 } else {
401 /* Regular buffer - go direct */
402 char *target = mem_get_region( buffer->render_addr );
403 glDrawPixels( buffer->width, buffer->height,
404 format, type, target );
405 }
406 }
.