/* * 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 //#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; iaddress = 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>4)); else dsm = 0; if (tile.mask_t && !tile.ct) dtm = -((int(mint+0.5f - tile.tl*float(1<>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 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 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<zbid); // res = RGL_COMB_IN0_DEPTH; // } else glBindTexture(GL_TEXTURE_2D, hbuf.texid); rglAssert(glGetError() == GL_NO_ERROR); ss = -(hbuf.width<>4); st = -(hbuf.height<>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<>4; st = tile.h<>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; inbDepthSections) { // 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 %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; jrdpState.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 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=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<flags |= (1<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>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>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(); } }