mupen64plus-oldsvn/z64/rgl.cpp

1676 lines
52 KiB
C++

/*
* z64
*
* This program is free software; you can redistribute it and/
* or modify it under the terms of the GNU General Public Li-
* cence as published by the Free Software Foundation; either
* version 2 of the Licence, or any later version.
*
* This program is distributed in the hope that it will be use-
* ful, but WITHOUT ANY WARRANTY; without even the implied war-
* ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public Licence for more details.
*
* You should have received a copy of the GNU General Public
* Licence along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**/
/**
*
* TODO
*
* - hires framebuffer : scale them accordingly to the GL screen resolution
*
* - multitexturing avec tilenum == 7, ca marche comment ?
* --> tilenum=7 should be translated to tilenum=0 for triangle and texrec primitives
*
* - CvgXAlpha mode not really correct, effect probably depends on the texture format
* --> apparently fixed, Intensity textures shouldn't be affected by this effect
* --> correction : it had nothing to do with the texture format, but with
* the alpha_cvg_select flag
*
* - fix fbo depth clear in LEGO racer (also affects beetle and reflection in rally 99)
* (also affects Zelda OOT subscreen)
* --> DONE
*
* - frame buffer ordering (LEGO racer)
* --> DONE but required also less conservative framebuffer check
*
* - format conversion for hires framebuffers when required (CBFD , Banjo)
* --> DONE (quick hack)
*
* - some texture (4 ? 8 bits ?) problems
* --> either they're fixed, either I forgot which problem it was
*
* - mirrored textures
* --> done but rely on a GL extension, do we care ?
*
* - better blend
* --> mostly done slow way, now need to implement the quick way
* --> done faster way, seems to work reasonably well
*
* - need to sort out combiner clamp modes
* --> started but not complete
* --> the problem is much more complicated, it's not combiner clamping
* but coverage calculation modes.
*
* - fog !!
* --> done
*
* PROBLEMS
*
* - this list needs to be updated :)
* - links not always rendered in the subscreen
* --> depth clear problem, anything else ? FIXED
* - texture problems in beetle
* --> mostly fixed, it was multitexturing, the sky still has a weird problem though
* --> completely fixed at last (the sky uses tex2 in the second combiner cycle,
* which should be interpreted as tex1 (apparently tex2 isn't available in the
* second cycle)
* UPDATE : in fact tex1 and tex2 need to be swapped in the second step of
* the combiner, weird but it fixes a few other problems as well
*
*/
#include "Gfx #1.3.h"
#include "rdp.h"
#include "rgl.h"
#include "rgl_glut.h"
#include <SDL/SDL.h>
//#define NOFBO
#define ZTEX
#define FBORGBA
rglTexCache_t rglTexCache[0x1000];
uint8_t rglTmpTex[1024*1024*4];
uint8_t rglTmpTex2[1024*1024*4];
volatile int rglStatus, rglNextStatus;
static int wireframe;
static uint32_t old_vi_origin;
int rglFrameCounter;
extern int viewportOffset;
rglSettings_t rglSettings;
rglDepthBuffer_t zBuffers[MAX_DEPTH_BUFFERS];
int nbZBuffers;
rglRenderBuffer_t rBuffers[MAX_RENDER_BUFFERS];
int nbRBuffers;
rglRenderBuffer_t * curRBuffer;
rglRenderBuffer_t * curZBuffer;
rglRenderBufferHead_t rBufferHead;
int rglTexCacheCounter = 1;
rglTexture_t rglTextures[RGL_TEX_CACHE_SIZE];
rglRenderChunk_t chunks[MAX_RENDER_CHUNKS];
rglRenderChunk_t * curChunk;
int nbChunks, renderedChunks;
rglStrip_t strips[MAX_STRIPS];
rglVertex_t vtxs[6*MAX_STRIPS];
int nbStrips, nbVtxs;
rglRenderMode_t renderModesDb[MAX_RENDER_MODES];
int nbRenderModes;
rglShader_t * rglCopyShader;
rglShader_t * rglCopyDepthShader;
int rglScreenWidth = 320, rglScreenHeight = 240;
#define CHECK_FRAMEBUFFER_STATUS() \
{\
GLenum status; \
status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); \
/*LOGERROR("%x\n", status);*/\
switch(status) { \
case GL_FRAMEBUFFER_COMPLETE_EXT: \
/*LOGERROR("framebuffer complete!\n");*/\
break; \
case GL_FRAMEBUFFER_UNSUPPORTED_EXT: \
LOGERROR("framebuffer GL_FRAMEBUFFER_UNSUPPORTED_EXT\n");\
/* you gotta choose different formats */ \
/*rglAssert(0);*/ \
break; \
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: \
LOGERROR("framebuffer INCOMPLETE_ATTACHMENT\n");\
break; \
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: \
LOGERROR("framebuffer FRAMEBUFFER_MISSING_ATTACHMENT\n");\
break; \
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: \
LOGERROR("framebuffer FRAMEBUFFER_DIMENSIONS\n");\
break; \
case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: \
LOGERROR("framebuffer INCOMPLETE_FORMATS\n");\
break; \
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: \
LOGERROR("framebuffer INCOMPLETE_DRAW_BUFFER\n");\
break; \
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: \
LOGERROR("framebuffer INCOMPLETE_READ_BUFFER\n");\
break; \
case GL_FRAMEBUFFER_BINDING_EXT: \
LOGERROR("framebuffer BINDING_EXT\n");\
break; \
default: \
LOGERROR("framebuffer generic error\n");\
break; \
/* programming error; will fail on all hardware */ \
/*rglAssert(0);*/ \
}\
}
// case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT: \
// LOGERROR("framebuffer INCOMPLETE_DUPLICATE_ATTACHMENT\n");\
// break; \
rglDepthBuffer_t * rglFindDepthBuffer(uint32_t address, int width, int height)
{
int i;
rglDepthBuffer_t * buffer;
for (i=0; i<nbZBuffers; i++)
if (zBuffers[i].address == address &&
zBuffers[i].width == width &&
zBuffers[i].height == height)
return zBuffers+i;
rglAssert(nbZBuffers < MAX_DEPTH_BUFFERS);
buffer = zBuffers + nbZBuffers++;
LOG("Creating depth buffer %x %d x %d\n", address, width, height);
buffer->address = address;
buffer->width = width;
buffer->height = height;
// glGenRenderbuffersEXT(1, &buffer->zbid);
// glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, buffer->zbid);
// rglAssert(glGetError() == GL_NO_ERROR);
// glRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT,
// buffer->width, buffer->height);
// rglAssert(glGetError() == GL_NO_ERROR);
#ifdef ZTEX
glGenTextures(1, &buffer->zbid);
rglAssert(glGetError() == GL_NO_ERROR);
glBindTexture(GL_TEXTURE_2D, buffer->zbid);
rglAssert(glGetError() == GL_NO_ERROR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
buffer->width, buffer->height, 0,
GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL);
rglAssert(glGetError() == GL_NO_ERROR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
#else
glGenRenderbuffersEXT(1, &buffer->zbid);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, buffer->zbid);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT,
buffer->width, buffer->height);
#endif
return buffer;
}
void rglDeleteRenderBuffer(rglRenderBuffer_t & buffer)
{
buffer.mod.xl = buffer.mod.yl = 0;
buffer.mod.xh = buffer.mod.yh = 8192;
buffer.depthBuffer = 0;
#ifndef NOFBO
if (buffer.fbid) {
glDeleteFramebuffersEXT(1, &buffer.fbid);
buffer.fbid = 0;
}
if (buffer.texid) {
glDeleteTextures(1, &buffer.texid);
buffer.texid = 0;
}
buffer.nbDepthSections = 0;
#ifdef RGL_EXACT_BLEND
glDeleteFramebuffersEXT(1, &buffer.fbid2);
buffer.fbid2 = 0;
glDeleteTextures(1, &buffer.texid2);
buffer.texid2 = 0;
#endif
#endif
}
void rglFullSync()
{
if (rglSettings.forceSwap)
// hack for starwars, perfect dark subscreen to prevent filling up our chunk table
old_vi_origin = ~0;
}
// note : if "same" is 1 then both tiles use the same texture, in this
// case we can't safely modify the clamping mode
void rglFixupMapping(rglStrip_t & strip, rglTile_t & tile,
float ds, float dt, float ss, float st,
float & dsm, float & dtm, int same)
{
float mins = strip.vtxs[0].s;
float mint = strip.vtxs[0].t;
int i;
if ( (tile.mask_s && !tile.cs) || (tile.mask_t && !tile.ct) )
for (i=1; i<strip.nbVtxs; i++) {
if (strip.vtxs[i].s < mins)
mins = strip.vtxs[i].s;
if (strip.vtxs[i].t < mint)
mint = strip.vtxs[i].t;
}
if (tile.mask_s && !tile.cs)
dsm = -((int(mins+0.5f - tile.sl*float(1<<tile.shift_s+4)/64.0f) + (tile.ms<<tile.mask_s)) & ((~tile.ms)<<tile.mask_s+tile.shift_s+4>>4));
else
dsm = 0;
if (tile.mask_t && !tile.ct)
dtm = -((int(mint+0.5f - tile.tl*float(1<<tile.shift_t+4)/64.0f) + (tile.mt<<tile.mask_t)) & ((~tile.mt)<<tile.mask_t+tile.shift_t+4>>4));
else
dtm = 0;
if (rglSettings.hiresFb && tile.hiresBuffer)
return;
else {
GLuint wws = tile.ws, wwt = tile.wt;
if (same || wws != GL_REPEAT)
goto skips;
for (i=0; i<strip.nbVtxs; i++) {
float a = (strip.vtxs[i].s + ds + dsm);
if ((a-0.5f)/ss > 1 || (a+0.5f)/ss < 0) {
goto skips;
}
}
//LOG("fixing S clamp\n");
wws = GL_CLAMP_TO_EDGE;
skips:
if (tile.tex->ws != wws) {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wws);
tile.tex->ws = wws;
}
if (same || wwt != GL_REPEAT)
goto skipt;
for (i=0; i<strip.nbVtxs; i++) {
float a = (strip.vtxs[i].t + dt + dtm);
if ((a-0.5f)/st > 1 || (a+0.5f)/st < 0)
goto skipt;
}
//LOG("fixing T clamp\n");
wwt = GL_CLAMP_TO_EDGE;
skipt:
if (tile.tex->wt != wwt) {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wwt);
tile.tex->wt = wwt;
}
}
}
int rglUseTile(rglTile_t & tile, float & ds, float & dt, float & ss, float & st)
{
int res = 0;
ds = -tile.sl*float(1<<tile.shift_s+4)/64.0f;
dt = -tile.tl*float(1<<tile.shift_t+4)/64.0f;
if (rglSettings.hiresFb && tile.hiresBuffer) {
rglRenderBuffer_t & hbuf = *tile.hiresBuffer;
// if (hbuf.flags & RGL_RB_DEPTH) {
// glBindTexture(GL_TEXTURE_2D, hbuf.depthBuffer->zbid);
// res = RGL_COMB_IN0_DEPTH;
// } else
glBindTexture(GL_TEXTURE_2D, hbuf.texid);
rglAssert(glGetError() == GL_NO_ERROR);
ss = -(hbuf.width<<tile.shift_s+4>>4);
st = -(hbuf.height<<tile.shift_t+4>>4);
ds = -ds - (((int32_t(tile.hiresAddress) - int32_t(hbuf.addressStart)) % hbuf.line) >> hbuf.size << 1);
dt = -dt - (int32_t(tile.hiresAddress) - int32_t(hbuf.addressStart)) / hbuf.line;
ss /= float(hbuf.realWidth)/hbuf.fboWidth;
st /= float(hbuf.realHeight)/hbuf.fboHeight;
ds = ss - ds;
dt = st - dt;
DUMP("texture fb %x shift %g x %g (scale %g x %g) tile %d x %d (sl %d tl %d)\n",
&hbuf, ds, dt, ss, st, tile.w, tile.h,
tile.sl, tile.tl);
} else {
glBindTexture(GL_TEXTURE_2D, tile.tex->id);
rglAssert(glGetError() == GL_NO_ERROR);
ss = tile.w<<tile.shift_s+4>>4; st = tile.h<<tile.shift_t+4>>4;
if (tile.tex->filter != tile.filter) {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, tile.filter);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, tile.filter);
rglAssert(glGetError() == GL_NO_ERROR);
tile.tex->filter = tile.filter;
}
}
return res;
}
void rglPrepareFramebuffer(rglRenderBuffer_t & buffer)
{
int olderased = buffer.flags & RGL_RB_ERASED;
if (buffer.area.xh == 8192)
return;
GLuint restoreId = 0, restoreFbid;
float d2 = -1;
float d = 0;
float restoreW = buffer.width+d2, restoreH = buffer.height+d2;
int w, h;
restoreW *= float(buffer.fboWidth+d) / (buffer.realWidth+d);
restoreH *= float(buffer.fboHeight+d) / (buffer.realHeight+d);
buffer.flags &= ~RGL_RB_ERASED;
// buffer.width = ((buffer.area.xl - buffer.area.xh >>2) + 15)&~15;
// buffer.height = ((buffer.area.yl - buffer.area.yh >>2) + 15)&~15;
// buffer.width = ((buffer.area.xl >>2) + 3)&~3;
// buffer.height = ((buffer.area.yl >>2) + 3)&~3;
//buffer.width = ((buffer.area.xl >>2))&~7;
buffer.width = buffer.fbWidth;
//buffer.height = ((buffer.area.yl >>2))&~7;
buffer.height = ((buffer.area.yl >>2));
if (!buffer.width) buffer.width = 1;
if (!buffer.height) buffer.height = 1;
buffer.addressStop = buffer.addressStart + buffer.line * ((buffer.area.yl >>2)+1);
if (rglSettings.lowres) {
buffer.realWidth = buffer.width;
buffer.realHeight = buffer.height;
} else {
if (buffer.width <= 128 || buffer.height <= 128) {
buffer.realWidth = buffer.width*4;
buffer.realHeight = buffer.height*4;
buffer.flags &= ~RGL_RB_FULL;
} else {
buffer.realWidth = screen_width * buffer.width / rglScreenWidth;
buffer.realHeight = screen_height * buffer.height / rglScreenHeight;
// buffer.realWidth = screen_width * buffer.width / vi_width;
// if (buffer.height > 250)
// buffer.realHeight = screen_height * buffer.height / 480;
// else
// buffer.realHeight = screen_height * buffer.height / 240;
buffer.flags |= RGL_RB_FULL;
}
}
if (rglSettings.noNpotFbos) {
w = 1; h = 1;
while (w < buffer.realWidth) w <<= 1;
while (h < buffer.realHeight) h <<= 1;
} else {
w = buffer.realWidth;
h = buffer.realHeight;
}
#ifndef NOFBO
if (buffer.fboWidth == w && buffer.fboHeight == h)
buffer.redimensionStamp = rglFrameCounter;
if (buffer.fbid &&
(//buffer.fboWidth < w || buffer.fboHeight < h ||
(rglFrameCounter - buffer.redimensionStamp > 4))) {
LOG("Redimensionning buffer\n");
restoreId = buffer.texid;
restoreFbid = buffer.fbid;
buffer.texid = buffer.fbid = 0;
rglDeleteRenderBuffer(buffer);
}
DUMP("Render buffer %x at %x --> %x\n", &buffer,
buffer.addressStart, buffer.addressStop);
if (!buffer.fbid) {
int glfmt;
switch (buffer.format) {
// case RDP_FORMAT_I:
// case RDP_FORMAT_CI:
// glfmt = GL_LUMINANCE;
// break;
default:
#ifdef FBORGBA
glfmt = GL_RGBA;
#else
glfmt = GL_RGB;
#endif
}
LOG("creating fbo %x %dx%d (%dx%d) fmt %x\n", buffer.addressStart, buffer.width, buffer.height, w, h, buffer.format);
buffer.fboWidth = w;
buffer.fboHeight = h;
#ifdef RGL_EXACT_BLEND
glGenFramebuffersEXT(1, &buffer.fbid2);
rglAssert(glGetError() == GL_NO_ERROR);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, buffer.fbid2);
// FIXME we should not need to allocate a color texture for depth only rendering
if (1||!(buffer.flags & RGL_RB_DEPTH)) {
glGenTextures(1, &buffer.texid2);
rglAssert(glGetError() == GL_NO_ERROR);
glBindTexture(GL_TEXTURE_2D, buffer.texid2);
rglAssert(glGetError() == GL_NO_ERROR);
glTexImage2D(GL_TEXTURE_2D, 0, glfmt, w, h, 0,
glfmt, GL_UNSIGNED_BYTE, NULL);
rglAssert(glGetError() == GL_NO_ERROR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
// glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
// glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
rglAssert(glGetError() == GL_NO_ERROR);
glBindTexture(GL_TEXTURE_2D, 0);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D,
buffer.texid2, 0);
}
#endif
if (restoreId) {
buffer.fbid = restoreFbid;
} else {
glGenFramebuffersEXT(1, &buffer.fbid);
rglAssert(glGetError() == GL_NO_ERROR);
}
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, buffer.fbid);
// FIXME we should not need to allocate a color texture for depth only rendering
if (1||!(buffer.flags & RGL_RB_DEPTH)) {
glGenTextures(1, &buffer.texid);
rglAssert(glGetError() == GL_NO_ERROR);
glBindTexture(GL_TEXTURE_2D, buffer.texid);
rglAssert(glGetError() == GL_NO_ERROR);
glTexImage2D(GL_TEXTURE_2D, 0, glfmt, w, h, 0,
glfmt, GL_UNSIGNED_BYTE, NULL);
rglAssert(glGetError() == GL_NO_ERROR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
// glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
// glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
rglAssert(glGetError() == GL_NO_ERROR);
glBindTexture(GL_TEXTURE_2D, 0);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D,
buffer.texid, 0);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0 );
if (!restoreId) {
glClearColor(0, 0, 0, 1);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glClear(GL_COLOR_BUFFER_BIT);
} else {
glViewport(0, 0, buffer.realWidth, buffer.realHeight);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDisable(GL_DEPTH_TEST);
glBindTexture(GL_TEXTURE_2D, restoreId);
rglUseShader(rglCopyShader);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f((buffer.width+d2)/restoreW, 0); glVertex2f(1, 0);
glTexCoord2f(0, 0); glVertex2f(0, 0);
glTexCoord2f((buffer.width+d2)/restoreW, (buffer.height+d2)/restoreH); glVertex2f(1, 1);
glTexCoord2f(0, (buffer.height+d2)/restoreH); glVertex2f(0, 1);
glEnd();
glDeleteTextures(1, &restoreId);
}
}
} else
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, buffer.fbid);
#endif
rglAssert(glGetError() == GL_NO_ERROR);
// hack for LEGO racer, real fix coming soon
// if (olderased)
// {
// glDepthMask(GL_TRUE);
// glClearDepth(1);
// glClear(GL_DEPTH_BUFFER_BIT);
// }
}
void rglRenderChunks(rglRenderBuffer_t * upto)
{
if (upto->area.xh != 8192 && renderedChunks < upto->chunkId)
rglRenderChunks(upto->chunkId);
}
void rglRenderChunks(int upto)
{
int i;
//printf("vi_origin %x nbChunks %d\n", vi_origin, nbChunks);
rglRenderBuffer_t * lastBuffer = 0;
uint32_t lastDepthAddress = ~0;
float zb = 0.0f;
DUMP("rendering chunks upto %d / %d\n", upto, nbChunks);
glEnable(GL_SCISSOR_TEST);
for (i=renderedChunks; i<upto; i++) {
int j;
rglRenderChunk_t & chunk = chunks[i];
if (chunk.renderBuffer->nbDepthSections) {
// reselect the renderbuffer with correct width (needed by LEGO racer,
// because they clear a 320x240 depth buffer to render in small 64x64 framebuffer)
// and adjust the area to the associated color buffer
// no need to optimize this search because it's rare (i.e. mainly depth clear,
// so once per frame)
for (j=chunk.renderBuffer->nbDepthSections-1; j>=0; j--) {
// LOG("j %d %d %d %d\n", j, i, chunk.renderBuffer->depthSections[j].chunkId,
// chunk.renderBuffer->depthSections[j].buffer - rBuffers);
if (i >= chunk.renderBuffer->depthSections[j].chunkId)
break;
}
//rglAssert(j < chunk.renderBuffer->nbDepthSections-1);
if (j < chunk.renderBuffer->nbDepthSections-1) {
rglRenderBuffer_t * cbuffer = chunk.renderBuffer->depthSections[j+1].buffer;
chunk.renderBuffer = rglSelectRenderBuffer(chunk.renderBuffer->addressStart, cbuffer->fbWidth, chunk.renderBuffer->size, chunk.renderBuffer->format);
chunk.renderBuffer->area = cbuffer->area;
chunk.renderBuffer->flags |= RGL_RB_DEPTH;
}
}
rglRenderBuffer_t & buffer = *chunk.renderBuffer;
int oldFlags = ~0;
int oldTilenum = ~0;
int combFormat = 0;
rglAssert(buffer.area.xh != 8192);
if (lastBuffer != &buffer)
rglPrepareFramebuffer(buffer);
DUMP("Buffer %x at %x area %d -> %d x %d -> %d\n",
&buffer, buffer.addressStart,
buffer.area.xh>>2, buffer.area.xl>>2,
buffer.area.yh>>2, buffer.area.yl>>2);
// if (buffer.addressStart != vi_origin)
// continue;
if (buffer.flags & RGL_RB_DEPTH)
chunk.depthAddress = buffer.addressStart;
if (lastBuffer != &buffer ||
lastDepthAddress != chunk.depthAddress) {
lastBuffer = &buffer;
lastDepthAddress = chunk.depthAddress;
int j;
for (j=0; j<nbRBuffers; j++) {
int overlap = int(rBuffers[j].addressStop) - int(buffer.addressStart);
if (int(buffer.addressStop) - int(rBuffers[j].addressStart) < overlap)
overlap = int(buffer.addressStop) - int(rBuffers[j].addressStart);
// LOG("checking #%d %x --> %x with %x --> %x overlap %x\n",
// j,
// rBuffers[j].addressStart, rBuffers[j].addressStop,
// buffer.addressStart, buffer.addressStop,
// overlap);
// check if more than 10% of the buffer was erased
if (rBuffers+j != &buffer &&
overlap > int(rBuffers[j].addressStop - rBuffers[j].addressStart)/10
// rBuffers[j].addressStop > buffer.addressStart &&
// rBuffers[j].addressStart < buffer.addressStop
) {
rBuffers[j].flags |= RGL_RB_ERASED;
DUMP("erasing fb #%d\n", j);
}
}
#ifndef NOFBO
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, buffer.fbid);
rglDepthBuffer_t * zbuf = rglFindDepthBuffer(chunk.depthAddress,
buffer.fboWidth, buffer.fboHeight);
if (zbuf != buffer.depthBuffer) {
buffer.depthBuffer = zbuf;
#ifdef ZTEX
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D,
buffer.depthBuffer->zbid, 0);
#else
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, buffer.depthBuffer->zbid );
#endif
// glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
// GL_RENDERBUFFER_EXT, depthBuffer->zbid );
CHECK_FRAMEBUFFER_STATUS();
}
#endif
glViewport(0, 0, buffer.realWidth, buffer.realHeight);
}
if (chunk.rdpState.clip.yl < chunk.rdpState.clip.yh ||
chunk.rdpState.clip.xl < chunk.rdpState.clip.xh)
continue;
glScissor((chunk.rdpState.clip.xh >>2)*buffer.realWidth/buffer.width,
(chunk.rdpState.clip.yh >>2)*buffer.realHeight/buffer.height,
(chunk.rdpState.clip.xl-chunk.rdpState.clip.xh >>2)*buffer.realWidth/buffer.width,
(chunk.rdpState.clip.yl-chunk.rdpState.clip.yh >>2)*buffer.realHeight/buffer.height);
rglAssert(glGetError() == GL_NO_ERROR);
#ifndef NOFBO
#ifdef RGL_EXACT_BLEND
glPushAttrib(GL_ALL_ATTRIB_BITS);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, buffer.fbid2);
glBindTexture(GL_TEXTURE_2D, buffer.texid);
glEnable(GL_TEXTURE_2D);
rglUseShader(rglCopyShader);
glColor4ub(255,255,255,255);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDisable(GL_DEPTH_TEST);
for (j=0; j<chunk.nbStrips; j++) {
rglStrip_t & strip = chunk.strips[j];
int k;
glBegin(GL_TRIANGLE_STRIP);
for (k=0; k<strip.nbVtxs; k++) {
glTexCoord2f((strip.vtxs[k].x/(buffer.width)),
(strip.vtxs[k].y/(buffer.height)));
glVertex2f((strip.vtxs[k].x/(buffer.width)),
(strip.vtxs[k].y/(buffer.height)));
}
glEnd();
}
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, buffer.fbid);
glPopAttrib();
#endif
#endif
if (buffer.flags & RGL_RB_DEPTH) {
DUMP("depth write\n");
//rglRenderMode(chunk);
glDepthMask(GL_TRUE);
glDepthFunc(GL_ALWAYS);
glDisable( GL_ALPHA_TEST );
glDisable( GL_POLYGON_OFFSET_FILL ); // ?
//glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
} else {
rglRenderMode(chunk);
}
rglAssert(glGetError() == GL_NO_ERROR);
if (RDP_GETOM_Z_MODE(chunk.rdpState.otherModes) & 1) {
switch(RDP_GETOM_Z_MODE(chunk.rdpState.otherModes)) {
case 3:
zb = 20;
break;
default:
zb = 4;
break;
}
} else {
zb = 0;
}
zb *= 16e-6f;
#ifdef RGL_EXACT_BLEND
glDisable(GL_BLEND);
glActiveTextureARB(GL_TEXTURE1_ARB);
glBindTexture(GL_TEXTURE_2D, buffer.texid2);
glEnable(GL_TEXTURE_2D);
rglAssert(glGetError() == GL_NO_ERROR);
glActiveTextureARB(GL_TEXTURE0_ARB);
#endif
combFormat = (buffer.size == 1? RGL_COMB_FMT_I : RGL_COMB_FMT_RGBA);
// not yet
// if (buffer.flags & RGL_RB_DEPTH)
// combFormat = RGL_COMB_FMT_DEPTH;
float ds[2], dt[2], ss[2], st[2];
float dsm[2], dtm[2];
for (j=0; j<chunk.nbStrips; j++) {
rglStrip_t & strip = chunk.strips[j];
int k;
int tilenum = strip.tilenum;
if (tilenum == 7 && RDP_GETOM_CYCLE_TYPE(chunk.rdpState.otherModes)==1) {
tilenum = 0;
combFormat |= RGL_COMB_TILE7;
}
rglTile_t & tile = chunk.tiles[tilenum];
rglTile_t & tile2 = chunk.tiles[tilenum+1];
if (strip.flags != oldFlags || tilenum != oldTilenum) {
oldTilenum = tilenum;
if (strip.flags & RGL_STRIP_TEX1) {
//if (tile.hiresBuffer) continue;
combFormat |= rglUseTile(tile, ds[0], dt[0], ss[0], st[0]);
glEnable(GL_TEXTURE_2D);
} else
glDisable(GL_TEXTURE_2D);
glActiveTextureARB(GL_TEXTURE2_ARB);
if (strip.flags & RGL_STRIP_TEX2) {
//if (tile2.hiresBuffer) continue;
combFormat |= rglUseTile(tile2, ds[1], dt[1], ss[1], st[1]) << 1;
glEnable(GL_TEXTURE_2D);
} else
glDisable(GL_TEXTURE_2D);
glActiveTextureARB(GL_TEXTURE0_ARB);
}
if (j == 0)
rglSetCombiner(chunk, combFormat);
if (strip.flags != oldFlags) {
oldFlags = strip.flags;
if (strip.flags & RGL_STRIP_ZBUFFER)
glEnable(GL_DEPTH_TEST);
else
glDisable(GL_DEPTH_TEST);
if (!(strip.flags & RGL_STRIP_SHADE))
// TODO take in account the framebuffer size (16b or 32b)
glColor4f(RDP_GETC16_R(chunk.rdpState.fillColor)/31.0f,
RDP_GETC16_G(chunk.rdpState.fillColor)/31.0f,
RDP_GETC16_B(chunk.rdpState.fillColor)/31.0f,
RDP_GETC16_A(chunk.rdpState.fillColor));
if (wireframe)
if (!(strip.flags & (RGL_STRIP_SHADE | RGL_STRIP_TEX1 | RGL_STRIP_TEX2)))
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
else
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
// FIXME
// if (RDP_GETOM_CYCLE_TYPE(curChunk->rdpState.otherModes) < 2)
// glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
// else
// glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
if (strip.flags & RGL_STRIP_TEX1)
rglFixupMapping(strip, tile,
ds[0], dt[0], ss[0], st[0], dsm[0], dtm[0],
(strip.flags & RGL_STRIP_TEX2) && tile.tex == tile2.tex);
if (strip.flags & RGL_STRIP_TEX2) {
glActiveTextureARB(GL_TEXTURE2_ARB);
rglFixupMapping(strip, tile2,
ds[1], dt[1], ss[1], st[1], dsm[1], dtm[1],
(strip.flags & RGL_STRIP_TEX1) && tile.tex == tile2.tex);
glActiveTextureARB(GL_TEXTURE0_ARB);
}
glBegin(GL_TRIANGLE_STRIP);
for (k=0; k<strip.nbVtxs; k++) {
if (strip.flags & RGL_STRIP_SHADE)
glColor4ub(strip.vtxs[k].r, strip.vtxs[k].g, strip.vtxs[k].b,
strip.vtxs[k].a);
if (strip.flags & RGL_STRIP_TEX1)
glMultiTexCoord2fARB(GL_TEXTURE0_ARB,
1-(strip.vtxs[k].s + ds[0] + dsm[0]) / ss[0],
1-(strip.vtxs[k].t + dt[0] + dtm[0]) / st[0]);
if (strip.flags & RGL_STRIP_TEX2)
glMultiTexCoord2fARB(GL_TEXTURE2_ARB,
1-(strip.vtxs[k].s + ds[1] + dsm[1]) / ss[1],
1-(strip.vtxs[k].t + dt[1] + dtm[1]) / st[1]);
#ifdef RGL_EXACT_BLEND
// glMultiTexCoord2fARB(GL_TEXTURE1_ARB,
// (strip.vtxs[k].x/(buffer.width))/**strip.vtxs[k].w*/,
// (strip.vtxs[k].y/(buffer.height))/**strip.vtxs[k].w*/);
// used only to pass the viewport information :/
// tried with light position --> but it seems less precise !
glMultiTexCoord2fARB(GL_TEXTURE1_ARB,
buffer.realWidth/2048.f,
buffer.realHeight/2048.f);
#endif
// if (ds || dt || ss!=1 || st!=1) {
// printf("%g x %g --> %g x %g\n",
// strip.vtxs[k].s*tile.w,
// strip.vtxs[k].t*tile.h,
// (strip.vtxs[k].s + ds) * ss,
// (strip.vtxs[k].t + dt) * st);
// }
float
x = strip.vtxs[k].x*strip.vtxs[k].w,
y = strip.vtxs[k].y*strip.vtxs[k].w;
if (buffer.flags & RGL_RB_DEPTH)
glVertex3f((strip.vtxs[k].x/(buffer.width)),
(strip.vtxs[k].y/(buffer.height)),
//rglZscale(chunk.rdpState.fillColor&0xffff));
float(chunk.rdpState.fillColor&0xffff)/0xffff);
// glVertex4f((strip.vtxs[k].x/(buffer.width))*strip.vtxs[k].w,
// (strip.vtxs[k].y/(buffer.height))*strip.vtxs[k].w,
// float(chunk.rdpState.fillColor&0xffff)/0xffff*strip.vtxs[k].w,
// strip.vtxs[k].w);
else {
// glVertex4f(x/buffer.width, y/buffer.height,
// (strip.vtxs[k].z - 1.5f*zb)*(strip.vtxs[k].w),
// strip.vtxs[k].w);
float iw = strip.vtxs[k].w;
if (iw > 1000) {
glVertex4f(x/buffer.width, y/buffer.height,
(strip.vtxs[k].z - 1.5f*zb)*strip.vtxs[k].w,
strip.vtxs[k].w);
} else {
iw = 1.0f/iw;
glVertex4f(x/buffer.width, y/buffer.height,
(strip.vtxs[k].z) / (iw + zb*0.35f),
strip.vtxs[k].w);
}
// glVertex4f(x/buffer.width, y/buffer.height,
// (strip.vtxs[k].z)*strip.vtxs[k].w,
// strip.vtxs[k].w);
}
if (x < chunk.rdpState.clip.xh/4)
x = chunk.rdpState.clip.xh/4;
if (x > chunk.rdpState.clip.xl/4)
x = chunk.rdpState.clip.xl/4;
if (y < chunk.rdpState.clip.yh/4)
y = chunk.rdpState.clip.yh/4;
if (y > chunk.rdpState.clip.yl/4)
y = chunk.rdpState.clip.yl/4;
if (buffer.mod.xh > x)
buffer.mod.xh = x;
if (buffer.mod.xl < x)
buffer.mod.xl = x;
if (buffer.mod.yh > y)
buffer.mod.yh = y;
if (buffer.mod.yl < y)
buffer.mod.yl = y;
}
glEnd();
// FIXME
// glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
}
buffer.flags |= RGL_RB_FBMOD;
#ifdef RGL_EXACT_BLEND
glActiveTextureARB(GL_TEXTURE1_ARB);
glDisable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTextureARB(GL_TEXTURE0_ARB);
#endif
}
glActiveTextureARB(GL_TEXTURE2_ARB);
glDisable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTextureARB(GL_TEXTURE0_ARB);
renderedChunks = i;
}
void rglDisplayFramebuffers()
{
int i;
if (!(vi_control & 3))
return;
#ifdef RDP_DEBUG
extern int nbFullSync;
LOG("nbFyllSync %d\n", nbFullSync);
nbFullSync = 0;
#endif
int height = (vi_control & 0x40) ? 480 : 240;
int width = vi_width;
// from glide64
DWORD scale_x = *gfx.VI_X_SCALE_REG & 0xFFF;
if (!scale_x) return;
DWORD scale_y = *gfx.VI_Y_SCALE_REG & 0xFFF;
if (!scale_y) return;
float fscale_x = (float)scale_x / 1024.0f;
float fscale_y = (float)scale_y / 1024.0f;
DWORD dwHStartReg = *gfx.VI_H_START_REG;
DWORD dwVStartReg = *gfx.VI_V_START_REG;
DWORD hstart = dwHStartReg >> 16;
DWORD hend = dwHStartReg & 0xFFFF;
// dunno... but sometimes this happens
if (hend == hstart) {
LOG("fix hend\n");
hend = (int)(*gfx.VI_WIDTH_REG / fscale_x);
}
if (hstart > hend) {
DWORD tmp=hstart; hstart=hend; hend=tmp;
LOG("swap hstart hend\n");
}
DWORD vstart = dwVStartReg >> 16;
DWORD vend = dwVStartReg & 0xFFFF;
if (vstart > vend) {
DWORD tmp=vstart; vstart=vend; vend=tmp;
LOG("swap vstart vend\n");
}
//if (*gfx.VI_WIDTH_REG != 0x500)
if (*gfx.VI_WIDTH_REG < 0x400)
fscale_y /= 2.0f;
// fscale_x *= screen_width / float(vi_width);
// fscale_y *= screen_height / height;
//glViewport(0*hstart*fscale_x, 0*vstart*fscale_y, (hend-hstart)*fscale_x, (vend-vstart)*fscale_y);
width = (hend-hstart)*fscale_x;
height = (vend-vstart)*fscale_y;
if (!width || !height) return;
static int oldw, oldh;
if (width == oldw && width > 200)
rglScreenWidth = width;
if (height == oldh && height > 200)
rglScreenHeight = height;
oldw = width;
oldh = height;
int vi_line = vi_width * 2; // TODO take in account the format
int vi_start = *gfx.VI_ORIGIN_REG;// - vi_line;
int vi_stop = vi_start + height * vi_line;
if (*gfx.VI_WIDTH_REG >= 0x400)
vi_line /= 2;
DUMP("%x screen %x --> %x %d --> %d x %d --> %d scale %g x %g clip %g --> %g x %g --> %g %dx%d\n",
vi_line,
vi_start, vi_stop,
hstart, hend, vstart, vend,
fscale_x, fscale_y,
hstart*fscale_x, hend*fscale_x, vstart*fscale_y, vend*fscale_y,
width, height
);
#ifdef NOFBO
return;
#endif
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
glDrawBuffer(GL_BACK);
glViewport(0, viewportOffset, screen_width, screen_height);
glDisable(GL_SCISSOR_TEST);
// wine seems to catch scissor test disabling so need to define an area nevertheless
glScissor(0, viewportOffset, screen_width, screen_height);
glClearColor(0, 0, 0, 0);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glClear(GL_COLOR_BUFFER_BIT); // TODO clear a minimal area
rglRenderBuffer_t * buffer;
CIRCLEQ_FOREACH(rglRenderBuffer_t, buffer, &rBufferHead, link)
if (!(buffer->flags & RGL_RB_ERASED) &&
vi_stop > buffer->addressStart &&
vi_start < buffer->addressStop) {
if (buffer->size != 2 || buffer->format != RDP_FORMAT_RGBA)
continue; // FIXME
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
glDrawBuffer(GL_BACK);
glViewport(0, viewportOffset, screen_width, screen_height);
glDisable(GL_SCISSOR_TEST);
// wine seems to catch scissor test disabling so need to define an area nevertheless
glScissor(0, viewportOffset, screen_width, screen_height);
glDisable(GL_ALPHA_TEST);
glDisable(GL_BLEND);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glActiveTextureARB(GL_TEXTURE1_ARB);
glDisable(GL_TEXTURE_2D);
glActiveTextureARB(GL_TEXTURE0_ARB);
float x = (int32_t(buffer->addressStart - vi_start) % int(vi_line)) / 2;
float y = height - buffer->height - (int32_t(buffer->addressStart - vi_start) / int(vi_line));
//x=y=0;
DUMP("displaying fb %x %d x %d (%d x %d) at %g x %g\n", buffer->addressStart,
buffer->width, buffer->height,
buffer->realWidth, buffer->realHeight,
x, y);
y -= *gfx.VI_V_CURRENT_LINE_REG & 1; // prevent interlaced modes flickering
x = x / width;
y = y / height;
rglUseShader(rglCopyShader);
glBindTexture(GL_TEXTURE_2D, buffer->texid);
glEnable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
glColor4ub(255, 255, 255, 255);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(float(buffer->realWidth)/buffer->fboWidth, float(buffer->realHeight)/buffer->fboHeight); glVertex2f(x+float(buffer->width-1)/(width-1), y+0);
glTexCoord2f(0, float(buffer->realHeight)/buffer->fboHeight); glVertex2f(x+0, y+0);
glTexCoord2f(float(buffer->realWidth)/buffer->fboWidth, 0); glVertex2f(x+float(buffer->width-1)/(width-1), y+float(buffer->height-1)/(height-1));
glTexCoord2f(0, 0); glVertex2f(x+0, y+float(buffer->height-1)/(height-1));
glEnd();
}
}
void rglUpdate()
{
int i;
if (old_vi_origin == vi_origin) {
//printf("same\n");
return;
}
old_vi_origin = vi_origin;
DUMP("updating vi_origin %x vi_hstart %d vi_vstart %d\n",
vi_origin, *gfx.VI_H_START_REG, *gfx.VI_V_START_REG);
glPolygonMode(GL_FRONT_AND_BACK, wireframe? GL_LINE : GL_FILL);
rglRenderChunks(nbChunks);
rglDisplayFramebuffers();
#ifndef NOFBO
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
#endif
rglUseShader(0);
glDrawBuffer(GL_BACK);
rglSwapBuffers();
rglFrameCounter++;
// for (i=0; i<nbRBuffers; i++)
// rglFramebuffer2Rdram(rBuffers[i],
// rBuffers[i].addressStart, rBuffers[i].addressStop);
#ifdef RDP_DEBUG
if (rdp_dump) {
LOG("DUMP %d\n", rdp_dump);
rdp_dump--;
}
#ifdef WIN32
if (GetAsyncKeyState ('P') & 0x0001) {
rglDebugger();
}
if (GetAsyncKeyState ('D') & 0x0001) {
rdp_dump = 2;
}
if (GetAsyncKeyState ('W') & 0x0001) {
wireframe = !wireframe;
}
#else
SDL_Event event;
while (nbChunks && SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_KEYDOWN:
switch (event.key.keysym.sym) {
case 'd':
rdp_dump = 2;
break;
case 'w': {
wireframe = !wireframe;
break;
}
case 'p': {
rglDebugger();
break;
}
}
break;
}
}
#endif
#endif
#ifdef RDP_DEBUG
rdpTracePos = 0;
#endif
renderedChunks = 0;
nbChunks = 0;
nbStrips = 0;
nbVtxs = 0;
for (i=0; i<nbRBuffers; i++) {
rglRenderBuffer_t & buffer = rBuffers[i];
buffer.area.xl = buffer.area.yl = 0;
buffer.area.xh = buffer.area.yh = 8192;
buffer.chunkId = 0;
buffer.nbDepthSections = 0;
}
// force a render buffer update
rdpChanged |= (RDP_BITS_ZB_SETTINGS | RDP_BITS_FB_SETTINGS);
}
void rglClearRenderBuffers()
{
int i;
for (i=0; i<nbRBuffers; i++)
rglDeleteRenderBuffer(rBuffers[i]);
for (i=0; i<nbZBuffers; i++) {
glDeleteRenderbuffersEXT(1, &zBuffers[i].zbid);
zBuffers[i].zbid = 0;
}
for (i=0; i<MAX_RENDER_BUFFERS; i++) {
rglRenderBuffer_t & buffer = rBuffers[i];
buffer.mod.xl = buffer.mod.yl = 0;
buffer.mod.xh = buffer.mod.yh = 8192;
buffer.area.xl = buffer.area.yl = 0;
buffer.area.xh = buffer.area.yh = 8192;
}
CIRCLEQ_INIT(rglRenderBuffer_t, &rBufferHead);
nbRBuffers = 0;
nbZBuffers = 0;
curRBuffer = 0;
curZBuffer = 0;
}
rglRenderBuffer_t * rglSelectRenderBuffer(uint32_t addr, int width, int size, int format)
{
int i;
for (i=nbRBuffers-1; i>=0; i--)
if (rBuffers[i].addressStart == addr &&
rBuffers[i].fbWidth == width &&
rBuffers[i].size == size)
break;
if (i >= 0) {
return rBuffers + i;
// TODO need to take care of framebuffer format possible change (?)
}
rglAssert(nbRBuffers < MAX_RENDER_BUFFERS);
// if (nbRBuffers == MAX_RENDER_BUFFERS)
// rglClearRenderBuffers();
i = nbRBuffers++;
rglRenderBuffer_t * cur = rBuffers + i;
cur->addressStart = addr;
cur->format = format;
cur->size = size;
cur->fbWidth = width;
cur->area = rdpState.clip;
cur->line = (width << size >> 1);
cur->flags = 0;
CIRCLEQ_INSERT_HEAD(rglRenderBuffer_t, &rBufferHead, cur, link);
return cur;
}
void rglPrepareRendering(int texturing, int tilenum, int recth, int depth)
{
if (!rdpChanged)
goto ok;
//rglUpdate();
depth = /*depth && */(RDP_GETOM_CYCLE_TYPE(rdpState.otherModes) < 2) &&
(RDP_GETOM_Z_UPDATE_EN(rdpState.otherModes) ||
RDP_GETOM_Z_COMPARE_EN(rdpState.otherModes));
if (curRBuffer)
curRBuffer->chunkId = nbChunks;
if (!curZBuffer ||
(rdpChanged & (RDP_BITS_ZB_SETTINGS | RDP_BITS_FB_SETTINGS)) ||
curZBuffer->addressStart != rdpZbAddress) {
// first search the most recent without considering the width of the buffer
rglRenderBuffer_t * buf;
curZBuffer = 0;
CIRCLEQ_FOREACH(rglRenderBuffer_t, buf, &rBufferHead, link)
if (buf->addressStart == rdpZbAddress) {
curZBuffer = buf;
break;
}
if (!curZBuffer) {
curZBuffer = rglSelectRenderBuffer(rdpZbAddress, rdpFbWidth, 2, RDP_FORMAT_RGBA);
CIRCLEQ_REMOVE(&rBufferHead, curZBuffer, link);
CIRCLEQ_INSERT_HEAD(rglRenderBuffer_t, &rBufferHead, curZBuffer, link);
}
}
if (rdpChanged & (RDP_BITS_ZB_SETTINGS | RDP_BITS_FB_SETTINGS)) {
curRBuffer = rglSelectRenderBuffer(rdpFbAddress, rdpFbWidth, rdpFbSize, rdpFbFormat);
CIRCLEQ_REMOVE(&rBufferHead, curRBuffer, link);
CIRCLEQ_INSERT_HEAD(rglRenderBuffer_t, &rBufferHead, curRBuffer, link);
}
if (rdpChanged & (RDP_BITS_TMEM | RDP_BITS_TLUT | RDP_BITS_TILE_SETTINGS))
rglTouchTMEM();
if (rdpChanged & (RDP_BITS_CLIP | RDP_BITS_ZB_SETTINGS | RDP_BITS_FB_SETTINGS) &&
rdpState.clip.xh <= rdpState.clip.xl && rdpState.clip.yh <= rdpState.clip.yl)
{
if (curRBuffer->area.xh == 8192)
curRBuffer->flags &= ~RGL_RB_HASTRIANGLES;
if (curRBuffer->area.xh > rdpState.clip.xh)
curRBuffer->area.xh = rdpState.clip.xh;
if (curRBuffer->area.xl < rdpState.clip.xl)
curRBuffer->area.xl = rdpState.clip.xl;
if (curRBuffer->area.yh > rdpState.clip.yh)
curRBuffer->area.yh = rdpState.clip.yh;
if (curRBuffer->area.yl < rdpState.clip.yl)
curRBuffer->area.yl = rdpState.clip.yl;
}
curRBuffer->chunkId = nbChunks; // don't include THIS chunk yet in case of feedback rendering (cf CBFD)
// if (curZBuffer)
// curZBuffer->chunkId = nbChunks;
curChunk = chunks + nbChunks++;
rglAssert(nbChunks < MAX_RENDER_CHUNKS);
curChunk->strips = strips + nbStrips;
curChunk->nbStrips = 0;
curChunk->renderBuffer = curRBuffer;
curChunk->flags = 0;
curChunk->rdpState = rdpState;
curChunk->depthAddress = rdpZbAddress;
#ifdef RDP_DEBUG
curChunk->tracePos = rdpTracePos;
#endif
if (depth) {
curZBuffer->flags |= RGL_RB_DEPTH;
//rglRenderChunks(curZBuffer);
if (rdpFbAddress != rdpZbAddress) {
if (!curZBuffer->nbDepthSections ||
curZBuffer->depthSections[curZBuffer->nbDepthSections-1].buffer != curRBuffer) {
rglAssert(curZBuffer->nbDepthSections < RGL_MAX_DEPTH_SECTIONS);
curZBuffer->depthSections[curZBuffer->nbDepthSections].buffer = curRBuffer;
curZBuffer->nbDepthSections++;
}
curZBuffer->depthSections[curZBuffer->nbDepthSections-1].chunkId = nbChunks;
}
}
{
// eliminate useless bits
int cycle = RDP_GETOM_CYCLE_TYPE(curChunk->rdpState.otherModes);
curChunk->rdpState.otherModes.w2 &= rdpBlendMasks[cycle].w2;
curChunk->rdpState.combineModes.w1 &= rdpCombineMasks[cycle].w1;
curChunk->rdpState.combineModes.w2 &= rdpCombineMasks[cycle].w2;
}
rdpChanged = 0;
ok:
if (texturing && !(curChunk->flags & (1<<tilenum))) {
curChunk->flags |= (1<<tilenum);
rglTile(rdpTiles[tilenum], curChunk->tiles[tilenum], recth);
}
}
void rglClose()
{
printf("rglClose()\n");
#ifdef RDP_DEBUG
rglCloseDebugger();
#endif
rglClearRenderBuffers();
rglResetTextureCache();
nbChunks = 0;
nbStrips = 0;
nbVtxs = 0;
if (rglCopyShader) rglDeleteShader(rglCopyShader);
rglCopyShader = 0;
if (rglCopyDepthShader) rglDeleteShader(rglCopyDepthShader);
rglCopyDepthShader = 0;
rglClearCombiners();
}
int rglInit()
{
printf("rglInit()\n");
static int init;
if (!init) {
init = 1;
glewInit();
}
glViewport(0, 0, screen_width, screen_height);
glLoadIdentity();
#ifdef NOFBO
glScalef(2, -2, 1);
#else
glScalef(2, 2, 1);
#endif
glTranslatef(-0.5, -0.5, 0);
glEnable(GL_DEPTH_TEST);
rglClose();
rglCopyShader = rglCreateShader(
"void main() \n"
"{ \n"
" gl_Position = ftransform(); \n"
" gl_FrontColor = gl_Color; \n"
" gl_TexCoord[0] = gl_MultiTexCoord0; \n"
"} \n"
,
"uniform sampler2D texture0; \n"
" \n"
"void main() \n"
"{ \n"
" gl_FragColor = gl_Color * texture2D(texture0, vec2(gl_TexCoord[0])); \n"
"} \n"
);
rglCopyDepthShader = rglCreateShader(
"void main() \n"
"{ \n"
" gl_Position = ftransform(); \n"
" gl_FrontColor = gl_Color; \n"
" gl_TexCoord[0] = gl_MultiTexCoord0; \n"
"} \n"
,
"uniform sampler2D texture0; \n"
" \n"
"void main() \n"
"{ \n"
" gl_FragDepth = texture2D(texture0, vec2(gl_TexCoord[0]))[0]; \n"
"} \n"
);
rdpChanged = ~0;
}
EXPORT void CALL FBWrite(DWORD addr, DWORD size)
{
if (!rglSettings.fbInfo || rglSettings.async)
return;
//LOG("FBWrite %x\n", addr);
rglRenderBuffer_t * buffer;
addr &= 0x7fffff;
CIRCLEQ_FOREACH(rglRenderBuffer_t, buffer, &rBufferHead, link) {
if (addr >= buffer->addressStart && addr+size <= buffer->addressStop) {
//LOG("FBWrite in fb #%d\n", buffer - rBuffers);
buffer->flags &= ~RGL_RB_FBMOD;
buffer->mod.xl = buffer->mod.yl = 0;
buffer->mod.xh = buffer->mod.yh = 8192;
//break;
}
}
//LOG("FBWrite %x %d\n", addr, size);
}
EXPORT void CALL FBWList(FrameBufferModifyEntry *plist, DWORD size)
{
LOG("FBWList size %d\n", size);
}
EXPORT void CALL FBRead(DWORD addr)
{
if (!rglSettings.fbInfo || rglSettings.async)
return;
//LOG("FBRead %x\n", addr);
rglRenderBuffer_t * buffer;
addr &= 0x7fffff;
CIRCLEQ_FOREACH(rglRenderBuffer_t, buffer, &rBufferHead, link) {
if (addr >= buffer->addressStart && addr < buffer->addressStop) {
// LOG("writing to rdram buffer %x --> %x\n",
// buffer->addressStart, buffer->addressStop);
rglFramebuffer2Rdram(*buffer, buffer->addressStart, buffer->addressStop);
break;
}
}
}
EXPORT void CALL FBGetFrameBufferInfo(void *p)
{
typedef struct
{
DWORD addr;
DWORD size;
DWORD width;
DWORD height;
} FrameBufferInfo;
FrameBufferInfo * pinfo = (FrameBufferInfo *)p;
int i;
if (!rglSettings.fbInfo)
return;
//LOG("GetFbInfo\n");
rglRenderBuffer_t * buffer;
i=0;
CIRCLEQ_FOREACH(rglRenderBuffer_t, buffer, &rBufferHead, link) {
// printf("#%d (%dx%d) %x --> %x\n", i,
// buffer->width, buffer->height,
// buffer->addressStart,
// buffer->addressStart + buffer->width*buffer->height*2);
pinfo[i].addr = buffer->addressStart;
pinfo[i].size = 2; // FIXME
pinfo[i].width = buffer->width;
pinfo[i].height = buffer->height;
i++; if (i>=6) break;
}
for ( ; i<6; i++) {
pinfo[i].addr = 0;
pinfo[i].size = 0;
pinfo[i].width = 4;
pinfo[i].height = 4;
}
}
static char exptable[256];
static void build_exptable()
{
LOG("Building depth exp table\n");
int i;
for (i=0; i<256; i++) {
int s;
for (s=0; s<7; s++)
if (!(i&(1<<6-s)))
break;
exptable[i] = s;
}
}
void rglFramebuffer2Rdram(rglRenderBuffer_t & buffer, uint32_t start, uint32_t stop)
{
int depth;
rglRenderChunks(&buffer);
if (!(buffer.flags & RGL_RB_FBMOD))
return;
// if (buffer.area.xh == 8192)
// return;
// rglAssert (buffer.area.xh != 8192);
depth = buffer.flags & RGL_RB_DEPTH;
//depth = 1;
int glfmt, packed;
int x, y;
int rw, rh;
int rx, ry;
uint8_t * ram = gfx.RDRAM + buffer.addressStart;
static uint8_t * fb = rglTmpTex;
if (depth) {
glfmt = GL_DEPTH_COMPONENT;
//packed = GL_UNSIGNED_SHORT;
packed = GL_FLOAT;
} else {
glfmt = GL_RGBA;
packed = GL_UNSIGNED_BYTE;
}
rx = buffer.mod.xh;
ry = buffer.mod.yh;
rw = (int(buffer.mod.xl) - int(buffer.mod.xh));
rh = (int(buffer.mod.yl) - int(buffer.mod.yh));
if (rw > buffer.fbWidth)
rw = buffer.fbWidth;
LOG("writing to rdram %x %s-%d %d %dx%d %dx%d %dx%d\n",
buffer.addressStart, depth? "depth":rdpImageFormats[buffer.format], buffer.size,
buffer.fbWidth,
buffer.width, buffer.height,
rx, ry,
rw, rh);
fflush(stderr);
if (rw <= 0 || rh <= 0)
return;
// rx=ry=0;
// rw = buffer.width;
// rh = buffer.height;
glPushAttrib(GL_ALL_ATTRIB_BITS);
#ifndef NOFBO
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
#endif
glDrawBuffer(GL_BACK);
glReadBuffer(GL_BACK);
glDisable(GL_SCISSOR_TEST);
glViewport(0, 0, buffer.width, buffer.height); // FIXME why +1 ?
// wine seems to catch scissor test disabling so need to define an area nevertheless
glScissor(0, 0, buffer.width+1, buffer.height+1);
glEnable(GL_TEXTURE_2D);
glDisable( GL_ALPHA_TEST );
if (depth) {
glBindTexture(GL_TEXTURE_2D, buffer.depthBuffer->zbid);
rglUseShader(rglCopyDepthShader);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_ALWAYS);
glDepthMask(GL_TRUE);
glDisable( GL_POLYGON_OFFSET_FILL );
} else {
glBindTexture(GL_TEXTURE_2D, buffer.texid);
rglUseShader(rglCopyShader);
glDisable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
glColor4ub(255, 255, 255, 255);
}
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(1, 1); glVertex2f(1, 1);
glTexCoord2f(0, 1); glVertex2f(0, 1);
glTexCoord2f(1, 0); glVertex2f(1, 0);
glTexCoord2f(0, 0); glVertex2f(0, 0);
glEnd();
glReadPixels(rx, ry, rw, rh,
glfmt, packed,
fb);
if (depth) {
if (!exptable[255])
build_exptable();
for (x=rx; x<rx+rw; x++)
for (y=ry; y<ry+rh; y++) {
uint32_t a = *(float *)&fb[(x-rx)*4 + (y-ry)*rw*4] * ((1<<18)-1);
//uint32_t a = uint32_t(*(uint16_t *)&fb[(x-rx)*4 + (y-ry)*rw*4]) << 2;
int e = exptable[a>>18-8];
a = ( ( (e>=6? a : (a>>6-e)) & ((1<<11)-1) ) << 2 ) | (e<<16-3);
*(uint16_t *)&ram[x*2 + y*buffer.line ^ 2] =
a;
//int(*(uint16_t *)&fb[(x-rx)*2 + (y-ry)*rw*2])-2;
//(*(uint16_t *)&fb[(x-rx)*2 + (y-ry)*rw*2] - int(0x8000))*2;
//(*(float *)&fb[(x-rx)*4 + (y-ry)*rw*4]-0.5)*0x1ffff;
}
} else {
switch (buffer.size) {
case 1:
for (x=rx; x<rx+rw; x++)
for (y=ry; y<ry+rh; y++) {
int r = fb[(x-rx + (y-ry)*rw)*4 + 0];
// int g = fb[(x-rx + (y-ry)*rw)*4 + 1];
// int b = fb[(x-rx + (y-ry)*rw)*4 + 2];
// int a = fb[(x-rx + (y-ry)*rw)*4 + 3];
*(uint8_t *)&ram[x + y*buffer.line ^ 3] =
r;
//(r+g+b)/3; // FIXME just R ?
}
break;
case 2:
for (x=rx; x<rx+rw; x++)
for (y=ry; y<ry+rh; y++) {
int r = fb[(x-rx + (y-ry)*rw)*4 + 0];
int g = fb[(x-rx + (y-ry)*rw)*4 + 1];
int b = fb[(x-rx + (y-ry)*rw)*4 + 2];
int a = fb[(x-rx + (y-ry)*rw)*4 + 3];
*(uint16_t *)&ram[x*2 + y*buffer.line ^ 2] =
((r&0xf8)<<8) | ((g&0xf8)<<3) | ((b&0xf8)>>2) |
((a&0x80)>>7);
}
break;
}
}
buffer.mod.xl = buffer.mod.yl = 0;
buffer.mod.xh = buffer.mod.yh = 8192;
//if (start <= buffer.addressStart && stop >= buffer.addressStop)
buffer.flags &= ~RGL_RB_FBMOD;
glPopAttrib();
}
void rglUpdateStatus()
{
if (rglNextStatus != rglStatus) {
const char * status[] = { "closed", "windowed", "fullscreen" };
LOG("Status %s --> %s\n", status[rglStatus], status[rglNextStatus]);
rglCloseScreen();
rglStatus = rglNextStatus;
if (rglNextStatus != RGL_STATUS_CLOSED)
rglOpenScreen();
}
}