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