/* ZZ Open GL graphics plugin * Copyright (c)2009 zeydlitz@gmail.com * Based on Zerofrog's ZeroGS KOSMOS (c)2005-2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // Realisation of RenderCRTC function ONLY. // It draw picture direct on screen, so here we have interlacing and frame skipping. //------------------ Includes #include #include "zerogs.h" #include "targets.h" using namespace ZeroGS; //------------------ Defines #ifdef RELEASE_TO_PUBLIC #define g_bSaveFrame 0 #define g_bSaveFinalFrame 0 #else // NOT RELEASE_TO_PUBLIC BOOL g_bSaveFrame = 0; // saves the current psurfTarget BOOL g_bSaveFinalFrame = 0; // saves the input to the CRTC #endif // RELEASE_TO_PUBLIC #define INTERLACE_COUNT (bInterlace && interlace == (conf.interlace)) // ----------------- Types //------------------ Dummies //------------------ variables #ifdef _WIN32 extern HDC hDC; // Private GDI Device Context extern HGLRC hRC; // Permanent Rendering Context #endif BOOL g_bCRTCBilinear = TRUE; extern bool g_bIsLost; int g_nFrameRender = 10; int g_nFramesSkipped = 0; extern int s_frameskipping; extern float fFPS; extern unsigned char zgsrevision, zgsbuild, zgsminor; extern u32 g_SaveFrameNum; extern int s_nWriteDepthCount; extern int s_nWireframeCount; extern int s_nWriteDestAlphaTest; extern int g_PrevBitwiseTexX, g_PrevBitwiseTexY; // textures stored in SAMP_BITWISEANDX and SAMP_BITWISEANDY BOOL g_bDisplayFPS = FALSE; extern bool s_bDestAlphaTest; extern int s_ClutResolve; extern int s_nLastResolveReset; extern int g_nDepthUpdateCount; extern int s_nResolveCounts[30]; // resolve counts for last 30 frames static int s_nCurResolveIndex = 0; int s_nResolved = 0; // number of targets resolved this frame extern int g_nDepthUsed; // ffx2 pal movies extern vector s_vecTempTextures; // temporary textures, released at the end of every frame //------------------ Namespace namespace ZeroGS { extern int s_nNewWidth, s_nNewHeight; extern CRangeManager s_RangeMngr; // manages overwritten memory extern void FlushTransferRanges(const tex0Info* ptex); extern void ProcessMessages(); void AdjustTransToAspect(Vector& v); // Interlace texture is lazy 1*(height) array of 1 and 0. // It height (it named s_nInterlaceTexWidth here) hanging we must redone // the texture. // FIXME: If this function would spammed to often, we could use // width < s_nInterlaceTexWidth as correct for old texture static int s_nInterlaceTexWidth = 0; // width of texture inline u32 CreateInterlaceTex(int width) { if (width == s_nInterlaceTexWidth && s_ptexInterlace != 0) return s_ptexInterlace; SAFE_RELEASE_TEX(s_ptexInterlace); s_nInterlaceTexWidth = width; vector data(width); for (int i = 0; i < width; ++i) data[i] = (i & 1) ? 0xffffffff : 0; glGenTextures(1, &s_ptexInterlace); glBindTexture(GL_TEXTURE_RECTANGLE_NV, s_ptexInterlace); glTexImage2D(GL_TEXTURE_RECTANGLE_NV, 0, 4, width, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &data[0]); glTexParameteri(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_MIN_FILTER, GL_NEAREST); GL_REPORT_ERRORD(); return s_ptexInterlace; } } //------------------ Code // Adjusts vertex shader BitBltPos vector v to preserve aspect ratio. It used to emulate 4:3 or 16:9. void ZeroGS::AdjustTransToAspect(Vector& v) { double temp; float f; int dispwidth = 640; int dispheight = conf.isWideScreen ? 360 : 480; if( dispwidth * nBackbufferHeight > dispheight * nBackbufferWidth) { // limited by width // change in ratio f = ((float)nBackbufferWidth / (float)dispwidth) / ((float)nBackbufferHeight / (float)dispheight); v.y *= f; v.w *= f; // scanlines mess up when not aligned right v.y += (1-(float)modf(v.y*(float)nBackbufferHeight*0.5f+0.05f, &temp))*2.0f/(float)nBackbufferHeight; v.w += (1-(float)modf(v.w*(float)nBackbufferHeight*0.5f+0.05f, &temp))*2.0f/(float)nBackbufferHeight; } else { // limited by height f = ((float)nBackbufferHeight / (float)dispheight) / ((float)nBackbufferWidth / (float)dispwidth); f -= (float)modf(f*nBackbufferWidth, &temp)/(float)nBackbufferWidth; v.x *= f; v.z *= f; } v *= 1/32767.0f; } // Helper for skip frames. int TimeLastSkip=0; inline bool FrameSkippingHelper() { bool ShouldSkip = false; if( g_nFrameRender > 0 ) { if( g_nFrameRender < 8 ) { g_nFrameRender++; if( g_nFrameRender <= 3 ) { g_nFramesSkipped++; ShouldSkip = true; } } } else { if( g_nFrameRender < -1 ) { g_nFramesSkipped++; ShouldSkip = true; } g_nFrameRender--; } #if defined _DEBUG if (timeGetTime() - TimeLastSkip > 15000 && ShouldSkip) { ERROR_LOG("ZZogl Skipped frames\n"); TimeLastSkip = timeGetTime(); } #endif return ShouldSkip; } // helper function for save frame in picture. inline void FrameSavingHelper() { if( g_bSaveFrame ) { if( vb[0].prndr != NULL ) { SaveTexture("frame1.tga", GL_TEXTURE_RECTANGLE_NV, vb[0].prndr->ptex, RW(vb[0].prndr->fbw), RH(vb[0].prndr->fbh)); } if( vb[1].prndr != NULL && vb[0].prndr != vb[1].prndr ) { SaveTexture("frame2.tga", GL_TEXTURE_RECTANGLE_NV, vb[1].prndr->ptex, RW(vb[1].prndr->fbw), RH(vb[1].prndr->fbh)); } #ifdef _WIN32 else DeleteFile("frame2.tga"); #endif } g_SaveFrameNum = 0; g_bSaveFlushedFrame = 1; } // Function populated tex0Info[2] array inline void FrameObtainDispinfo(u32 bInterlace, tex0Info* dispinfo) { for(int i = 0; i < 2; ++i) { if( !(*(u32*)(PMODE) & (1<MAGH+1; int magv = pd->MAGV+1; dispinfo[i].tbp0 = pfb->FBP << 5; dispinfo[i].tbw = pfb->FBW << 6; dispinfo[i].tw = (pd->DW + 1) / magh; dispinfo[i].th = (pd->DH + 1) / magv; dispinfo[i].psm = pfb->PSM; // hack!! // 2 * dispinfo[i].tw / dispinfo[i].th <= 1, metal slug 4 if( bInterlace && 2 * dispinfo[i].tw / dispinfo[i].th <= 1 && !(g_GameSettings&GAME_INTERLACE2X) ) { dispinfo[i].th >>= 1; } } } // Something should be done before Renderer the picture. inline void RenderStartHelper(u32 bInterlace){ // Crashes Final Fantasy X at startup if uncommented. --arcum42 //#ifdef RELEASE_TO_PUBLIC // if(g_nRealFrame < 80 ) { // RenderCustom( min(1.0f, 2.0f - (float)g_nRealFrame / 40.0f) ); // // if( g_nRealFrame == 79 ) // SAFE_RELEASE_TEX(ptexLogo); // return; // } //#endif if( conf.mrtdepth && pvs[8] == NULL ) { conf.mrtdepth = 0; s_bWriteDepth = FALSE; #ifdef DEBUG ERROR_LOG("Disabling MRT depth writing\n"); #endif } Flush(0); Flush(1); GL_REPORT_ERRORD(); FrameSavingHelper(); if( s_RangeMngr.ranges.size() > 0 ) FlushTransferRanges(NULL); SetShaderCaller("RenderStartHelper"); // reset fba after every frame vb[0].fba.fba = 0; vb[1].fba.fba = 0; glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); // switch to the backbuffer glViewport(0, 0, nBackbufferWidth, nBackbufferHeight); // if interlace, only clear every other vsync if(!bInterlace ) { //u32 color = COLOR_ARGB(0, BGCOLOR->R, BGCOLOR->G, BGCOLOR->B); glClear(GL_COLOR_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); } SETVERTEXSHADER(pvsBitBlt.prog); glBindBuffer(GL_ARRAY_BUFFER, vboRect); SET_STREAM(); GL_REPORT_ERRORD(); if( conf.options & GSOPTION_WIREFRAME ) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); DisableAllgl() ; GL_REPORT_ERRORD(); if( bInterlace ) g_PrevBitwiseTexX = -1; // reset since will be using } // It is setting for intrelace texture multiplied vector; // Idea is: (x, y) -- position on screen, than interlaced texture get F = 1 ot 0 depends // on image y coord. So it we write valpha.z * F + valpha.w + 0.5 it would be swicthig odd // and even strings at each frame // valpha.x and y used for image blending. inline Vector RenderGetForClip(u32 bInterlace, int interlace, int psm, FRAGMENTSHADER* prog){ SetShaderCaller("RenderGetForClip"); Vector valpha; // first render the current render targets, then from ptexMem if( psm == 1 ) { valpha.x = 1; valpha.y = 0; } else { valpha.x = 0; valpha.y = 1; } if (bInterlace) { if( interlace == (conf.interlace&1) ) { // pass if odd valpha.z = 1.0f; valpha.w = -0.4999f; } else { // pass if even valpha.z = -1.0f; valpha.w = 0.5001f; } } else { // always pass interlace test valpha.z = 0; valpha.w = 1; } ZZcgSetParameter4fv(prog->sOneColor, valpha, "g_fOneColor"); return valpha; } // Put interlaced texture in use for shader prog. // Note: if frame interlaced it's th is halved, so we should x2 it. inline void RenderCreateInterlaceTex(u32 bInterlace, int th, FRAGMENTSHADER* prog) { if (!bInterlace) return; int interlacetex = CreateInterlaceTex(2*th); cgGLSetTextureParameter(prog->sInterlace, interlacetex); cgGLEnableTextureParameter(prog->sInterlace); } // Well, do blending setup prior to second pass of half-frame drawing inline void RenderSetupBlending() { // setup right blending glEnable(GL_BLEND); zgsBlendEquationSeparateEXT(GL_FUNC_ADD, GL_FUNC_ADD); if (PMODE->MMOD) { glBlendColorEXT(PMODE->ALP*(1/255.0f), PMODE->ALP*(1/255.0f), PMODE->ALP*(1/255.0f), 0.5f); s_srcrgb = GL_CONSTANT_COLOR_EXT; s_dstrgb = GL_ONE_MINUS_CONSTANT_COLOR_EXT; } else { s_srcrgb = GL_SRC_ALPHA; s_dstrgb = GL_ONE_MINUS_SRC_ALPHA; } s_srcalpha = PMODE->AMOD ? GL_ZERO : GL_ONE; s_dstalpha = PMODE->AMOD? GL_ONE : GL_ZERO; zgsBlendFuncSeparateEXT(s_srcrgb, s_dstrgb, s_srcalpha, s_dstalpha); } // each frame could be drawed in two stages, so blending should be different for them inline void RenderSetupStencil(int i) { glStencilMask(1<fbp)/texframe.tbw; if( *dby < 0 ) { *movy = -*dby; *dby = 0; } return min(ptarg->fbh - *dby, texframe.th - *movy); } // BltBit shader calculate vertex (4 coord's pixel) position at the viewport. inline Vector RenderSetTargetBitPos(int dh, int th, int movy, bool isInterlace) { SetShaderCaller("RenderSetTargetBitPos"); Vector v; // dest rect v.x = 1; v.y = dh/(float)th; v.z = 0; v.w = 1-v.y; if( movy > 0 ) v.w -= movy/(float)th; AdjustTransToAspect(v); if (isInterlace) { // move down by 1 pixel v.w += 1.0f / (float)dh ; } ZZcgSetParameter4fv(pvsBitBlt.sBitBltPos, v, "g_fBitBltPos"); return v; } // Important stuff. We could use this coordinated to change viewport position on frame // For example use tw / X and tw / X magnify the vieport. // Interlaced output is little out of VB, it could be see as evil blinking line on top // and bottom, so we try to remove it inline Vector RenderSetTargetBitTex(float th, float tw, float dh, float dw, bool isInterlace) { SetShaderCaller("RenderSetTargetBitTex"); Vector v; v = Vector(th, tw, dh, dw); // Incorrect Asect ration on interlaced frames if (isInterlace) { v.y -= 1.0f/480 ; v.w += 1.0f/480 ; } ZZcgSetParameter4fv(pvsBitBlt.sBitBltTex, v, "g_fBitBltTex"); return v; } // Translator for POSITION coordinats (-1.0:+1.0f at x axis, +1.0f:-1.0y at y) into target frame ones // We don't need x coordinate, bvecause interlacing is y-axis only. inline Vector RenderSetTargetBitTrans(int th) { SetShaderCaller("RenderSetTargetBitTrans"); Vector v = Vector( float(th), -float(th), float(th), float(th)); ZZcgSetParameter4fv(pvsBitBlt.fBitBltTrans, v, "g_fBitBltTrans"); return v; } // use g_fInvTexDims to store inverse texture dims // Seems, that Targ shader does not use it inline Vector RenderSetTargetInvTex(int bInterlace, int tw, int th, FRAGMENTSHADER* prog) { SetShaderCaller("RenderSetTargetInvTex"); Vector v = Vector(0, 0, 0, 0); if (prog->sInvTexDims) { v.x = 1.0f / (float)tw; v.y = 1.0f / (float)th; v.z = (float)0.0; v.w = -0.5f / (float)th; ZZcgSetParameter4fv(prog->sInvTexDims, v, "g_fInvTexDims"); } return v; } // Metal Slug 5 hack (as was written). If tarhet tbp not equal to framed fbp, than we look for better possibility, // Note, than after true result iterator it could not be use. inline bool RenderLookForABetterTarget(int fbp, int tbp, list& listTargs, list::iterator& it) { if (fbp == tbp) return false; // look for a better target (metal slug 5) list::iterator itbetter; for(itbetter = listTargs.begin(); itbetter != listTargs.end(); ++itbetter ) { if( (*itbetter)->fbp == tbp ) break; } if( itbetter != listTargs.end() ) { it = listTargs.erase(it); return true; } return false; } // First try to draw frame from targets. It's inline bool RenderCheckForTargets(tex0Info& texframe, list& listTargs, int i, bool* bUsingStencil, int interlace, int bInterlace) { // get the start and end addresses of the buffer int bpp = RenderGetBpp(texframe.psm); GSRegDISPFB* pfb = i ? DISPFB2 : DISPFB1; int start, end; GetRectMemAddress(start, end, texframe.psm, 0, 0, texframe.tw, texframe.th, texframe.tbp0, texframe.tbw); // We need share list of targets beetween functions s_RTs.GetTargs(start, end, listTargs); for(list::iterator it = listTargs.begin(); it != listTargs.end(); ) { CRenderTarget* ptarg = *it; if( ptarg->fbw == texframe.tbw && !(ptarg->status&CRenderTarget::TS_NeedUpdate) && ((256/bpp)*(texframe.tbp0-ptarg->fbp))%texframe.tbw == 0 ) { if (RenderLookForABetterTarget(ptarg->fbp, texframe.tbp0, listTargs, it)) continue; if( g_bSaveFinalFrame ) SaveTexture("frame1.tga", GL_TEXTURE_RECTANGLE_NV, ptarg->ptex, RW(ptarg->fbw), RH(ptarg->fbh)); // determine the rectangle to render int dby = pfb->DBY; int movy = 0; int dh = RenderGetOffsets(&dby, &movy, texframe, ptarg, bpp); if( dh >= 64 ) { if( ptarg->fbh - dby < texframe.th - movy && !(*bUsingStencil) ) RenderUpdateStencil(i, bUsingStencil); SetShaderCaller("RenderCheckForTargets"); // Texture Vector v = RenderSetTargetBitTex((float)RW(texframe.tw), (float)RH(dh), (float)RW(pfb->DBX), (float)RH(dby), INTERLACE_COUNT ); // dest rect v = RenderSetTargetBitPos(dh, texframe.th, movy, INTERLACE_COUNT); v = RenderSetTargetBitTrans(ptarg->fbh); v = RenderSetTargetInvTex(bInterlace, texframe.tbw, ptarg->fbh, &ppsCRTCTarg[bInterlace]) ; // FIXME. This is no use Vector valpha = RenderGetForClip(bInterlace, interlace, texframe.psm, &ppsCRTCTarg[bInterlace]); // inside vb[0]'s target area, so render that region only cgGLSetTextureParameter(ppsCRTCTarg[bInterlace].sFinal, ptarg->ptex); cgGLEnableTextureParameter(ppsCRTCTarg[bInterlace].sFinal); RenderCreateInterlaceTex(bInterlace, texframe.th, &ppsCRTCTarg[bInterlace]); SETPIXELSHADER(ppsCRTCTarg[bInterlace].prog); GL_REPORT_ERRORD(); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); if( abs(dh - (int)texframe.th) <= 1 ) return true; if( abs(dh - (int)ptarg->fbh) <= 1 ) { it = listTargs.erase(it); continue; } } } ++it; } return false; } // The same of the previous, but from memory inline void RenderCheckForMemory(tex0Info& texframe, list& listTargs, int interlace, int bInterlace) { for(list::iterator it = listTargs.begin(); it != listTargs.end(); ++it) (*it)->Resolve(); // context has to be 0 CMemoryTarget* pmemtarg = g_MemTargs.GetMemoryTarget(texframe, 1); if ((pmemtarg == NULL) || (bInterlace >= 2)) ERROR_LOG("CRCR Check for memory shader fault"); SetShaderCaller("RenderCheckForMemory"); SetTexVariablesInt(0, g_bCRTCBilinear ? 2 : 0, texframe, pmemtarg, &ppsCRTC[bInterlace], 1); cgGLSetTextureParameter(ppsCRTC[bInterlace].sMemory, pmemtarg->ptex->tex); cgGLEnableTextureParameter(ppsCRTC[bInterlace].sMemory); if( g_bSaveFinalFrame ) SaveTex(&texframe, g_bSaveFinalFrame - 1 > 0); // finally render from the memory (note that the stencil buffer will keep previous regions) Vector v = RenderSetTargetBitPos(1, 1, 0, INTERLACE_COUNT); // Fixme: Why this if here? if( g_bCRTCBilinear ) ZZcgSetParameter4fv(pvsBitBlt.sBitBltTex, Vector(texframe.tw,texframe.th,-0.5f,-0.5f), "g_fBitBltTex"); else ZZcgSetParameter4fv(pvsBitBlt.sBitBltTex, Vector(1,1,-0.5f/(float)texframe.tw,-0.5f/(float)texframe.th), "g_fBitBltTex"); v = RenderSetTargetBitTrans(texframe.th); v = RenderSetTargetInvTex(bInterlace, texframe.tw, texframe.th, &ppsCRTC[bInterlace]); Vector valpha = RenderGetForClip(bInterlace, interlace, texframe.psm, &ppsCRTC[bInterlace]); RenderCreateInterlaceTex(bInterlace, texframe.th, &ppsCRTC[bInterlace]); SETPIXELSHADER(ppsCRTC[bInterlace].prog); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } // Put FPS counter on scren (not in window title) inline void AfterRenderDisplayFPS() { char str[64]; int left = 10, top = 15; sprintf(str, "%.1f fps", fFPS); DrawText(str, left+1, top+1, 0xff000000); DrawText(str, left, top, 0xffc0ffff); } // Swaping buffers, so we could use another windows inline void AfterRenderSwapBuffers() { if (glGetError() != GL_NO_ERROR) DEBUG_LOG("glerror before swap!\n"); GLWin.SwapGLBuffers(); } // SnapeShoot helper inline void AfterRenderMadeSnapshoot() { char str[64]; int left = 200, top = 15; sprintf(str, "ZeroGS %d.%d.%d - %.1f fps %s", zgsrevision, zgsbuild, zgsminor, fFPS, s_frameskipping?" - frameskipping":""); DrawText(str, left+1, top+1, 0xff000000); DrawText(str, left, top, 0xffc0ffff); if( SaveRenderTarget(strSnapshot != ""?strSnapshot.c_str():"temp.jpg", nBackbufferWidth, -nBackbufferHeight, 0)) {//(conf.options&GSOPTION_TGASNAP)?0:1) ) { char str[255]; sprintf(str, "saved %s\n", strSnapshot.c_str()); AddMessage(str, 500); } } // If needed reset inline void AfterRendererResizeWindow() { Reset(); ChangeDeviceSize(s_nNewWidth, s_nNewHeight); s_nNewWidth = s_nNewHeight = -1; } // Put new values on statistic variable inline void AfterRenderCountStatistics() { if( s_nWriteDepthCount > 0 ) { assert( conf.mrtdepth ); if( --s_nWriteDepthCount <= 0 ) { s_bWriteDepth = FALSE; } } if( s_nWriteDestAlphaTest > 0 ) { if( --s_nWriteDestAlphaTest <= 0 ) { s_bDestAlphaTest = FALSE; } } if( g_nDepthUsed > 0 ) --g_nDepthUsed; s_ClutResolve = 0; g_nDepthUpdateCount = 0; } // This all could be easily forefeit inline void AfterRendererUnimportantJob() { ProcessMessages(); if( g_bDisplayFPS ) AfterRenderDisplayFPS(); AfterRenderSwapBuffers(); if( conf.options & GSOPTION_WIREFRAME ) { // clear all targets s_nWireframeCount = 1; } if( g_bMakeSnapshot ) { AfterRenderMadeSnapshoot(); g_bMakeSnapshot = 0; } if( s_avicapturing ) CaptureFrame(); AfterRenderCountStatistics(); if( s_nNewWidth >= 0 && s_nNewHeight >= 0 && !g_bIsLost ) AfterRendererResizeWindow(); maxmin = 608; } // Swich Frabuffers inline void AfterRendererSwitchBackToTextures() { glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, s_uFramebuffer ); g_MemTargs.DestroyCleared(); if( s_vecTempTextures.size() > 0 ) glDeleteTextures((GLsizei)s_vecTempTextures.size(), &s_vecTempTextures[0]); s_vecTempTextures.clear(); if( EXTWRITE->WRITE&1 ) { WARN_LOG("EXTWRITE\n"); ExtWrite(); EXTWRITE->WRITE = 0; } if( conf.options & GSOPTION_WIREFRAME ) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glEnable(GL_SCISSOR_TEST); if( icurctx >= 0 ) { vb[icurctx].bVarsSetTarg = FALSE; vb[icurctx].bVarsTexSync = FALSE; vb[0].bVarsTexSync = FALSE; } } // Reset Targets Helper, for hack. inline void AfterRendererAutoresetTargets() { if( g_GameSettings & GAME_AUTORESET ) { s_nResolveCounts[s_nCurResolveIndex] = s_nResolved; s_nCurResolveIndex = (s_nCurResolveIndex+1)%ARRAY_SIZE(s_nResolveCounts); int total = 0; for(int i = 0; i < ARRAY_SIZE(s_nResolveCounts); ++i) total += s_nResolveCounts[i]; if( total / ARRAY_SIZE(s_nResolveCounts) > 3 ) { if( s_nLastResolveReset > (int)(fFPS * 8) ) { // reset ERROR_LOG("video mem reset\n"); s_nLastResolveReset = 0; memset(s_nResolveCounts, 0, sizeof(s_nResolveCounts)); s_RTs.ResolveAll(); return; s_RTs.Destroy(); s_DepthRTs.ResolveAll(); s_DepthRTs.Destroy(); vb[0].prndr = NULL; vb[0].pdepth = NULL; vb[0].bNeedFrameCheck = 1; vb[0].bNeedZCheck = 1; vb[1].prndr = NULL; vb[1].pdepth = NULL; vb[1].bNeedFrameCheck = 1; vb[1].bNeedZCheck = 1; } } s_nLastResolveReset++; } if( s_nResolved > 8 ) s_nResolved = 2; else if( s_nResolved > 0 ) --s_nResolved; } int count = 0; // The main renderer function void ZeroGS::RenderCRTC(int interlace) { if( g_bIsLost ) return; if (FrameSkippingHelper()) return; u32 bInterlace = SMODE2->INT && SMODE2->FFMD && (conf.interlace < 2); RenderStartHelper(bInterlace); bool bUsingStencil = 0; tex0Info dispinfo[2]; FrameObtainDispinfo(bInterlace, dispinfo); // start from the last circuit for(int i = !PMODE->SLBG; i >= 0; --i) { tex0Info& texframe = dispinfo[i]; if( texframe.th <= 1 ) continue; if (SMODE2->INT && SMODE2->FFMD) texframe.th >>= 1; if( i == 0 ) RenderSetupBlending(); if( bUsingStencil ) RenderSetupStencil(i); if( texframe.psm == 0x12 ) { RenderCRTC24helper(bInterlace, interlace, texframe.psm); continue; } // We shader targets between two functions, so declare it here; list listTargs; // if we could not draw image from target's do it from memory if ( !RenderCheckForTargets(texframe, listTargs, i, &bUsingStencil, interlace, bInterlace) ) RenderCheckForMemory(texframe, listTargs, interlace, bInterlace); } GL_REPORT_ERRORD(); glDisable(GL_BLEND); AfterRendererUnimportantJob(); AfterRendererSwitchBackToTextures(); AfterRendererAutoresetTargets(); }