/* ZeroGS KOSMOS * Copyright (C) 2005-2006 zerofrog@gmail.com * * 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 */ #if defined(_WIN32) #include #include "Win32.h" #include #endif #include #include #include #include #include #include #include #include #include using namespace std; #include "GS.h" #include "Mem.h" #include "Regs.h" #include "zerogs.h" #include "targets.h" #include "ZZoglShaders.h" #ifdef _MSC_VER #pragma warning(disable:4244) #endif GSinternal gs; char GStitle[256]; GSconf conf; int ppf; primInfo *prim; FILE *gsLog; int g_GSMultiThreaded = 0; void (*GSirq)(); u8* g_pBasePS2Mem = NULL; int g_TransferredToGPU = 0; std::string s_strIniPath("inis/"); // Air's new ini path (r2361) static BOOL g_bHidden = 0; int g_GameSettings = 0; int CurrentSavestate = 0; // Number of SaveSlot. Default is 0 bool SaveStateExists = true; // We could not know save slot status before first change occured const char* SaveStateFile = NULL; // Name of SaveFile for access check. // statistics u32 g_nGenVars = 0, g_nTexVars = 0, g_nAlphaVars = 0, g_nResolve = 0; #define VER 23 const unsigned char zgsversion = PS2E_GS_VERSION; unsigned char zgsrevision = 0; // revision and build gives plugin version unsigned char zgsbuild = VER; unsigned char zgsminor = 226; #ifdef _DEBUG const char *libraryName = "ZZ Ogl (Debug) "; #elif defined(RELEASE_TO_PUBLIC) const char *libraryName = "ZZ Ogl "; #else const char *libraryName = "ZZ Ogl (Dev) "; #endif static const char* s_aa[5] = { "AA none |", "AA 2x |", "AA 4x |", "AA 8x |", "AA 16x |" }; static const char* s_naa[3] = { "nattive res |", "res /2 |", "res /4 |" }; static const char* pbilinear[] = { "off", "normal", "forced" }; static const char* g_pInterlace[3] = { "interlace 0 |", "interlace 1 |", "" }; static const char* g_pBilinear[3] = { "", "bilinear |", "forced bilinear |" }; extern GIFRegHandler g_GIFPackedRegHandlers[]; extern GIFRegHandler g_GIFRegHandlers[]; GIFRegHandler g_GIFTempRegHandlers[16] = {0}; int g_LastCRC = 0; // CRC of last runned game Game m_game; // structure with desctription of current game #ifdef RELEASE_TO_PUBLIC #define g_bWriteProfile 0 #else BOOL g_bWriteProfile = 0; #endif int s_frameskipping = 0; u32 CALLBACK PS2EgetLibType() { return PS2E_LT_GS; } char* CALLBACK PS2EgetLibName() { return (char *) libraryName; } u32 CALLBACK PS2EgetLibVersion2(u32 type) { return (zgsversion<<16) | (zgsrevision<<8) | zgsbuild | (zgsminor << 24); } static u64 luPerfFreq; #ifdef _WIN32 HWND GShwnd = NULL; void SysMessage(const char *fmt, ...) { va_list list; char tmp[512]; va_start(list,fmt); vsprintf(tmp,fmt,list); va_end(list); MessageBox(0, tmp, "GSsoftdx Msg", 0); } #else GLWindow GLWin; u32 THR_KeyEvent = 0; // Value for key event processing between threads bool THR_bShift = false; #endif void __Log(const char *fmt, ...) { va_list list; // gsLog can be null if the config dialog is used prior to Pcsx2 an emulation session. // (GSinit won't have been called then) if (gsLog == NULL || !conf.log) return; va_start(list, fmt); vfprintf(gsLog, fmt, list); va_end(list); } void __LogToConsole(const char *fmt, ...) { va_list list; va_start(list, fmt); // gsLog can be null if the config dialog is used prior to Pcsx2 an emulation session. // (GSinit won't have been called then) if( gsLog != NULL ) vfprintf(gsLog, fmt, list); printf("ZZogl: "); vprintf(fmt, list); va_end(list); } void CALLBACK GSsetBaseMem(void* pmem) { g_pBasePS2Mem = (u8*)pmem; } void CALLBACK GSsetSettingsDir(const char* dir) { s_strIniPath = (dir==NULL) ? "inis/" : dir; } void CALLBACK GSsetGameCRC(int crc, int options) { Game m_games[] = { {0x00000000, CRC_None, Unknown, 0}, {0x2113EA2E, MetalSlug6, Unknown, 0}, {0x42E05BAF, TomoyoAfter, JP, 0}, {0x7800DC84, Clannad, JP, 0}, {0xA6167B59, Lamune, JP, 0}, {0xDDB59F46, KyuuketsuKitanMoonties, JP, 0}, {0xC8EE2562, PiaCarroteYoukosoGPGakuenPrincess, JP, 0}, {0x6CF94A43, KazokuKeikakuKokoroNoKizuna, JP, 0}, {0xEDAF602D, DuelSaviorDestiny, JP, 0}, {0xa39517ab, FFX, EU, 0}, {0xa39517ae, FFX, FR, 0}, {0x941bb7d9, FFX, DE, 0}, {0xa39517a9, FFX, IT, 0}, {0x941bb7de, FFX, ES, 0}, {0xb4414ea1, FFX, RU, 0}, {0xee97db5b, FFX, RU, 0}, {0xaec495cc, FFX, RU, 0}, {0xbb3d833a, FFX, US, 0}, {0x6a4efe60, FFX, JP, 0}, {0x3866ca7e, FFX, ASIA, 0}, // int. {0x658597e2, FFX, JP, 0}, // int. {0x9aac5309, FFX2, EU, 0}, {0x9aac530c, FFX2, FR, 0}, {0x9aac530a, FFX2, FR, 0}, // ? {0x9aac530d, FFX2, DE, 0}, {0x9aac530b, FFX2, IT, 0}, {0x48fe0c71, FFX2, US, 0}, {0xe1fd9a2d, FFX2, JP, 0}, // int. {0x78da0252, FFXII, EU, 0}, {0xc1274668, FFXII, EU, 0}, {0xdc2a467e, FFXII, EU, 0}, {0xca284668, FFXII, EU, 0}, {0x280AD120, FFXII, JP, 0}, {0x08C1ED4D, HauntingGround, Unknown, 0}, {0x2CD5794C, HauntingGround, EU, 0}, {0x867BB945, HauntingGround, JP, 0}, {0xE263BC4B, HauntingGround, JP, 0}, {0x901AAC09, HauntingGround, US, 0}, {0x8BE3D7B2, ShadowHearts,Unknown, 0}, {0xDEFA4763, ShadowHearts, US, 0}, {0x21068223, Okami, US, 0}, {0x891f223f, Okami, FR, 0}, {0xC5DEFEA0, Okami, JP, 0}, {0x053D2239, MetalGearSolid3, US, 0}, {0x086273D2, MetalGearSolid3, FR, 0}, {0x26A6E286, MetalGearSolid3, EU, 0}, {0xAA31B5BF, MetalGearSolid3, Unknown, 0}, {0x9F185CE1, MetalGearSolid3, Unknown, 0}, {0x98D4BC93, MetalGearSolid3, EU, 0}, {0x86BC3040, MetalGearSolid3, US, 0}, //Subsistance disc 1 {0x0481AD8A, MetalGearSolid3, JP, 0}, {0x79ED26AD, MetalGearSolid3, EU, 0}, {0x5E31EA42, MetalGearSolid3, EU, 0}, {0xD7ED797D, MetalGearSolid3, EU, 0}, {0x278722BF, DBZBT2, US, 0}, {0xFE961D28, DBZBT2, US, 0}, {0x0393B6BE, DBZBT2, EU, 0}, {0xE2F289ED, DBZBT2, JP, 0}, // Sparking Neo! {0x35AA84D1, DBZBT2, Unknown, 0}, {0x428113C2, DBZBT3, US, 0}, {0xA422BB13, DBZBT3, EU, 0}, {0x983C53D2, DBZBT3, Unknown, 0}, {0x983C53D3, DBZBT3, Unknown, 0}, {0x72B3802A, SFEX3, US, 0}, {0x71521863, SFEX3, US, 0}, {0x28703748, Bully, US, 0}, {0xC78A495D, BullyCC, US, 0}, {0xC19A374E, SoTC, US, 0}, {0x7D8F539A, SoTC, EU, 0}, {0x3122B508, OnePieceGrandAdventure, US, 0}, {0x8DF14A24, OnePieceGrandAdventure, EU, 0}, {0xB049DD5E, OnePieceGrandBattle, US, 0}, {0x5D02CC5B, OnePieceGrandBattle, Unknown, 0}, {0x6F8545DB, ICO, US, 0}, {0xB01A4C95, ICO, JP, 0}, {0x5C991F4E, ICO, Unknown, 0}, {0x7ACF7E03, ICO, Unknown, 0}, {0xAEAD1CA3, GT4, JP, 0}, {0x44A61C8F, GT4, Unknown, 0}, {0x0086E35B, GT4, Unknown, 0}, {0x77E61C8A, GT4, Unknown, 0}, {0xC164550A, WildArms5, JPUNDUB, 0}, {0xC1640D2C, WildArms5, US, 0}, {0x0FCF8FE4, WildArms5, EU, 0}, {0x2294D322, WildArms5, JP, 0}, {0x565B6170, WildArms5, JP, 0}, {0xBBC3EFFA, WildArms4, US, 0}, {0xBBC396EC, WildArms4, US, 0}, //hmm such a small diff in the CRC.. {0x8B029334, Manhunt2, Unknown, 0}, {0x09F49E37, CrashBandicootWoC, Unknown, 0}, {0x013E349D, ResidentEvil4, US, 0}, {0x6BA2F6B9, ResidentEvil4, Unknown, 0}, {0x60FA8C69, ResidentEvil4, JP, 0}, {0x72E1E60E, Spartan, Unknown, 0}, {0x5ED8FB53, AceCombat4, JP, 0}, {0x1B9B7563, AceCombat4, Unknown, 0}, {0xEC432B24, Drakengard2, Unknown, 0}, {0xFC46EA61, Tekken5, JP, 0}, {0x1F88EE37, Tekken5, Unknown, 0}, {0x652050D2, Tekken5, Unknown, 0}, {0x9E98B8AE, IkkiTousen, JP, 0}, {0xD6385328, GodOfWar, US, GAME_FULL16BITRES | GAME_SKIPDRAW}, {0xFB0E6D72, GodOfWar, EU, GAME_FULL16BITRES | GAME_SKIPDRAW}, {0xEB001875, GodOfWar, EU, GAME_FULL16BITRES | GAME_SKIPDRAW}, {0xA61A4C6D, GodOfWar, Unknown, GAME_FULL16BITRES | GAME_SKIPDRAW}, {0xE23D532B, GodOfWar, Unknown, GAME_FULL16BITRES | GAME_SKIPDRAW}, {0xDF1AF973, GodOfWar, Unknown, GAME_FULL16BITRES | GAME_SKIPDRAW}, {0xD6385328, GodOfWar, Unknown, GAME_FULL16BITRES | GAME_SKIPDRAW}, {0x2F123FD8, GodOfWar2, RU, 0}, {0x2F123FD8, GodOfWar2, US, 0}, {0x44A8A22A, GodOfWar2, EU, 0}, {0x4340C7C6, GodOfWar2, Unknown, 0}, {0xF8CD3DF6, GodOfWar2, Unknown, 0}, {0x0B82BFF7, GodOfWar2, Unknown, 0}, {0x5D482F18, JackieChanAdv, Unknown, 0}, {0xf0a6d880, HarvestMoon, US, GAME_NOSTENCIL}, {0x75c01a04, NamcoXCapcom, US, 0}, {0xBF6F101F, GiTS, US, 0}, {0xA5768F53, GiTS, JP, 0}, {0x6BF11378, Onimusha3, US, 0}, {0xF442260C, MajokkoALaMode2, JP, 0}, {0x14FE77F7, TalesOfAbyss, US, 0}, {0x045D77E9, TalesOfAbyss, JPUNDUB, 0}, {0xAA5EC3A3, TalesOfAbyss, JP, 0}, {0xFB236A46, SonicUnleashed, US, GAME_FASTUPDATE | GAME_NOALPHAFAIL}, {0x4C7BB3C8, SimpsonsGame, Unknown, 0}, {0x4C94B32C, SimpsonsGame, Unknown, 0}, {0xD71B57F4, Genji, Unknown, 0}, {0x23A97857, StarOcean3, JPUNDUB, 0}, {0xBEC32D49, StarOcean3, JP, 0}, {0x8192A241, StarOcean3, JP, 0}, //NTSC JP special directors cut limited extra sugar on top edition (the special one :p) {0x23A97857, StarOcean3, JPUNDUB, 0}, {0xCC96CE93, ValkyrieProfile2, US, 0}, {0x774DE8E2, ValkyrieProfile2, JP, 0}, {0x04CCB600, ValkyrieProfile2, EU, 0}, {0xB65E141B, ValkyrieProfile2, EU, 0}, // PAL German {0x47B9B2FD, RadiataStories, US, 0}, {0xE8FCF8EC, SMTNocturne, US, 0}, {0xF0A31EE3, SMTNocturne, EU, 0}, // SMTNocturne (Lucifers Call in EU) {0xAE0DE7B7, SMTNocturne, EU, 0}, // SMTNocturne (Lucifers Call in EU) {0xD60DA6D4, SMTNocturne, JP, 0}, // SMTNocturne {0x0e762e8d, SMTNocturne, JP, 0}, // SMTNocturne Maniacs {0x47BA9034, SMTNocturne, JP, 0}, // SMTNocturne Maniacs Chronicle {0xD7273511, SMTDDS1, US, 0}, // SMT Digital Devil Saga {0x1683A6BE, SMTDDS1, EU, 0}, // SMT Digital Devil Saga {0x44865CE1, SMTDDS1, JP, 0}, // SMT Digital Devil Saga {0xD382C164, SMTDDS2, US, 0}, // SMT Digital Devil Saga 2 {0xD568B684, SMTDDS2, EU, 0}, // SMT Digital Devil Saga 2 {0xE47C1A9C, SMTDDS2, JP, 0}, // SMT Digital Devil Saga {0x0B8AB37B, RozenMaidenGebetGarden, JP, 0}, {0xA3D63039, Xenosaga, JP, GAME_DOPARALLELCTX}, {0x0E7807B2, Xenosaga, US, GAME_DOPARALLELCTX}, {0x7D2FE035, Espgaluda, JP, 0 }, {0xe0426fc6, OkageShadowKing, US, GAME_XENOSPECHACK}, {0xa5d29941, ShadowTheHedgehog, Unknown, GAME_FASTUPDATE | GAME_NOALPHAFAIL}, {0x7acf7e03, AtelierIris1, US, GAME_GUSTHACK}, {0x9AC65D6A, AtelierIris2, US, GAME_GUSTHACK}, {0x4ccc9212, AtelierIris3, US, GAME_GUSTHACK}, {0xF95F37EE, ArTonelico2, US, GAME_GUSTHACK}, {0x77b0236f, ManaKhemia, Unknown, GAME_GUSTHACK}, {0x433951e7, ManaKhemia2, Unknown, GAME_GUSTHACK}, {0x0baa8dd8, DarkCloud1, Unknown, GAME_NOTARGETRESOLVE}, {0x95cc86ef, GhostInTheShell, Unknown, GAME_NOALPHAFAIL}, }; m_game = m_games[0]; u32 i; for (i = 1; i < sizeof(m_games) / sizeof(Game); ++i) { if (m_games[i].crc == crc) { m_game = m_games[i]; break; } } if ( m_game.title != CRC_None ) ERROR_LOG("Found CRC in base 0x%x and set option to 0x%x.\n", m_game.crc, m_game.flags); else ERROR_LOG("Game CRC 0x%x not found in base.\n", crc); VALIDATE_THRESH = 8; conf.mrtdepth = ((conf.gamesettings & GAME_DISABLEMRTDEPTH) !=0); if (!conf.mrtdepth) ERROR_LOG("Disabling MRT depth writing\n"); else ERROR_LOG("Enabling MRT depth writing\n"); //conf.gamesettings|= GAME_PATH3HACK; // hard to tell if this is really needed. bool RunningFirstTime = (g_LastCRC == crc); g_LastCRC = crc; if (RunningFirstTime) { conf.gamesettings|= m_game.flags; if (m_game.title == Xenosaga) { VALIDATE_THRESH = 64; TEXDESTROY_THRESH = 32; } else if (m_game.title == Espgaluda) { VALIDATE_THRESH = 24; } } g_GameSettings = conf.gamesettings|options; } void CALLBACK GSsetFrameSkip(int frameskip) { FUNCLOG s_frameskipping |= frameskip; if( frameskip && g_nFrameRender > 1 ) { for(int i = 0; i < 16; ++i) { g_GIFPackedRegHandlers[i] = GIFPackedRegHandlerNOP; } // still keep certain handlers g_GIFPackedRegHandlers[6] = GIFRegHandlerTEX0_1; g_GIFPackedRegHandlers[7] = GIFRegHandlerTEX0_2; g_GIFPackedRegHandlers[14] = GIFPackedRegHandlerA_D; g_GIFRegHandlers[0] = GIFRegHandlerNOP; g_GIFRegHandlers[1] = GIFRegHandlerNOP; g_GIFRegHandlers[2] = GIFRegHandlerNOP; g_GIFRegHandlers[3] = GIFRegHandlerNOP; g_GIFRegHandlers[4] = GIFRegHandlerNOP; g_GIFRegHandlers[5] = GIFRegHandlerNOP; g_GIFRegHandlers[12] = GIFRegHandlerNOP; g_GIFRegHandlers[13] = GIFRegHandlerNOP; g_GIFRegHandlers[26] = GIFRegHandlerNOP; g_GIFRegHandlers[27] = GIFRegHandlerNOP; g_nFrameRender = 0; } else if( !frameskip && g_nFrameRender <= 0 ) { g_nFrameRender = 1; if( g_GIFTempRegHandlers[0] == NULL ) return; // not init yet // restore memcpy(g_GIFPackedRegHandlers, g_GIFTempRegHandlers, sizeof(g_GIFTempRegHandlers)); g_GIFRegHandlers[0] = GIFRegHandlerPRIM; g_GIFRegHandlers[1] = GIFRegHandlerRGBAQ; g_GIFRegHandlers[2] = GIFRegHandlerST; g_GIFRegHandlers[3] = GIFRegHandlerUV; g_GIFRegHandlers[4] = GIFRegHandlerXYZF2; g_GIFRegHandlers[5] = GIFRegHandlerXYZ2; g_GIFRegHandlers[12] = GIFRegHandlerXYZF3; g_GIFRegHandlers[13] = GIFRegHandlerXYZ2; g_GIFRegHandlers[26] = GIFRegHandlerPRMODECONT; g_GIFRegHandlers[27] = GIFRegHandlerPRMODE; } } void CALLBACK GSreset() { FUNCLOG memset(&gs, 0, sizeof(gs)); ZeroGS::GSStateReset(); gs.prac = 1; prim = &gs._prim[0]; gs.nTriFanVert = -1; gs.imageTransfer = -1; gs.q = 1; } void CALLBACK GSgifSoftReset(u32 mask){ FUNCLOG if( mask & 1 ) memset(&gs.path1, 0, sizeof(gs.path1)); if( mask & 2 ) memset(&gs.path2, 0, sizeof(gs.path2)); if( mask & 4 ) memset(&gs.path3, 0, sizeof(gs.path3)); gs.imageTransfer = -1; gs.q = 1; gs.nTriFanVert = -1; } s32 CALLBACK GSinit() { FUNCLOG memcpy(g_GIFTempRegHandlers, g_GIFPackedRegHandlers, sizeof(g_GIFTempRegHandlers)); #ifdef GS_LOG gsLog = fopen("logs/gsLog.txt", "w"); if (gsLog == NULL) { gsLog = fopen("gsLog.txt", "w"); if (gsLog == NULL) { SysMessage("Can't create gsLog.txt"); return -1; } } setvbuf(gsLog, NULL, _IONBF, 0); GS_LOG("GSinit\n"); #endif GSreset(); GS_LOG("GSinit ok\n"); return 0; } void CALLBACK GSshutdown() { FUNCLOG #ifdef GS_LOG if (gsLog != NULL) fclose(gsLog); #endif } // keyboard functions void OnKeyboardF5(int shift) { FUNCLOG char strtitle[256]; if( shift ) { if( g_nPixelShaderVer == SHADER_REDUCED ) { conf.bilinear = 0; sprintf(strtitle, "reduced shaders don't support bilinear filtering"); } else { conf.bilinear = (conf.bilinear+1)%3; sprintf(strtitle, "bilinear filtering - %s", pbilinear[conf.bilinear]); } } else { conf.interlace++; if( conf.interlace > 2 ) conf.interlace = 0; if( conf.interlace < 2 ) sprintf(strtitle, "interlace on - mode %d", conf.interlace); else sprintf(strtitle, "interlace off"); } ZeroGS::AddMessage(strtitle); SaveConfig(); } void OnKeyboardF6(int shift) { FUNCLOG char strtitle[256]; if( shift ) { conf.aa--; // -1 if( conf.aa > 4 ) conf.aa = 4; // u8 in unsigned, so negative value is 255. sprintf(strtitle, "anti-aliasing - %s", s_aa[conf.aa]); ZeroGS::SetAA(conf.aa); } else { conf.aa++; if( conf.aa > 4 ) conf.aa = 0; sprintf(strtitle, "anti-aliasing - %s", s_aa[conf.aa]); ZeroGS::SetAA(conf.aa); } ZeroGS::AddMessage(strtitle); SaveConfig(); } void OnKeyboardF7(int shift) { FUNCLOG char strtitle[256]; if( !shift ) { g_bDisplayFPS ^= 1; } else { conf.options ^= GSOPTION_WIREFRAME; glPolygonMode(GL_FRONT_AND_BACK, (conf.options&GSOPTION_WIREFRAME)?GL_LINE:GL_FILL); sprintf(strtitle, "wireframe rendering - %s", (conf.options&GSOPTION_WIREFRAME)?"on":"off"); } } void OnKeyboardF61(int shift) { FUNCLOG char strtitle[256]; if( shift ) { conf.negaa--; // -1 if( conf.negaa > 2 ) conf.negaa = 2; // u8 in unsigned, so negative value is 255. sprintf(strtitle, "down resolution - %s", s_naa[conf.negaa]); ZeroGS::SetNegAA(conf.negaa); } else { conf.negaa++; if( conf.negaa > 2 ) conf.negaa = 0; sprintf(strtitle, "down resolution - %s", s_naa[conf.negaa]); ZeroGS::SetNegAA(conf.negaa); } ZeroGS::AddMessage(strtitle); SaveConfig(); } typedef struct GameHackStruct { const char HackName[40]; u32 HackMask; } GameHack; #define HACK_NUMBER 31 GameHack HackinshTable[HACK_NUMBER] = { {"*** 0 No Hack", 0}, {"*** 1 TexTargets Check", GAME_TEXTURETARGS}, {"*** 2 Autoreset Targets", GAME_AUTORESET}, {"*** 3 Interlace 2x", GAME_INTERLACE2X}, {"*** 4 TexA hack", GAME_TEXAHACK}, {"*** 5 No Target Resolve", GAME_NOTARGETRESOLVE}, {"*** 6 Exact color", GAME_EXACTCOLOR}, {"*** 7 No color clamp", GAME_NOCOLORCLAMP}, {"*** 8 FFX hack", GAME_FFXHACK}, {"*** 9 No Alpha Fail", GAME_NOALPHAFAIL}, {"***10 No Depth Update", GAME_NODEPTHUPDATE}, {"***11 Quick Resolve 1", GAME_QUICKRESOLVE1}, {"***12 No quick resolve", GAME_NOQUICKRESOLVE}, {"***13 Notaget clut", GAME_NOTARGETCLUT}, {"***14 No Stencil", GAME_NOSTENCIL}, {"***15 No Depth resolve", GAME_NODEPTHRESOLVE}, {"***16 Full 16 bit", GAME_FULL16BITRES}, {"***17 Resolve promoted", GAME_RESOLVEPROMOTED}, {"***18 Fast Update", GAME_FASTUPDATE}, {"***19 No Apha Test", GAME_NOALPHATEST}, {"***20 Disable MRT deprh", GAME_DISABLEMRTDEPTH}, {"***21 32 bit targes", GAME_32BITTARGS}, {"***22 path 3 hack", GAME_PATH3HACK}, {"***23 parallelise calls", GAME_DOPARALLELCTX}, {"***24 specular highligths", GAME_XENOSPECHACK}, {"***25 partial pointers", GAME_PARTIALPOINTERS}, {"***26 partial depth", GAME_PARTIALDEPTH}, {"***27 reget hack", GAME_REGETHACK}, {"***28 gust hack", GAME_GUSTHACK}, {"***29 log-Z", GAME_NOLOGZ}, {"***30 SkipDraw", GAME_SKIPDRAW} }; int CurrentHackSetting = 0; void OnKeyboardF9(int shift) { FUNCLOG // printf ("A %d\n", HackinshTable[CurrentHackSetting].HackMask); conf.gamesettings &= !(HackinshTable[CurrentHackSetting].HackMask); if( shift ) { CurrentHackSetting--; if (CurrentHackSetting == -1) CurrentHackSetting = HACK_NUMBER-1; } else { CurrentHackSetting++; if (CurrentHackSetting == HACK_NUMBER) CurrentHackSetting = 0; } conf.gamesettings |= HackinshTable[CurrentHackSetting].HackMask; g_GameSettings = conf.gamesettings; ZeroGS::AddMessage(HackinshTable[CurrentHackSetting].HackName); SaveConfig(); } void OnKeyboardF1(int shift) { FUNCLOG char strtitle[256]; sprintf(strtitle, "Saving in savestate %d", CurrentSavestate); SaveStateExists = true; ZeroGS::AddMessage(HackinshTable[CurrentHackSetting].HackName); } #ifdef _WIN32 #ifdef _DEBUG HANDLE g_hCurrentThread = NULL; #endif LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) { static int nWindowWidth = 0, nWindowHeight = 0; switch( msg ) { case WM_DESTROY: PostQuitMessage( 0 ); return 0; case WM_KEYDOWN: // switch(wParam) { // case VK_ESCAPE: // SendMessage(hWnd, WM_DESTROY, 0L, 0L); // break; // } break; case WM_SIZE: nWindowWidth = lParam&0xffff; nWindowHeight = lParam>>16; ZeroGS::ChangeWindowSize(nWindowWidth, nWindowHeight); break; case WM_SIZING: // if button is 0, then just released so can resize if( GetSystemMetrics(SM_SWAPBUTTON) ? !GetAsyncKeyState(VK_RBUTTON) : !GetAsyncKeyState(VK_LBUTTON) ) { ZeroGS::SetChangeDeviceSize(nWindowWidth, nWindowHeight); } break; case WM_SETCURSOR: SetCursor(NULL); break; } return DefWindowProc( hWnd, msg, wParam, lParam ); } void CALLBACK GSconfigure() { DialogBox(hInst, MAKEINTRESOURCE(IDD_CONFIG), GetActiveWindow(), (DLGPROC)ConfigureDlgProc); if( g_nPixelShaderVer == SHADER_REDUCED ) conf.bilinear = 0; } s32 CALLBACK GSopen(void *pDsp, char *Title, int multithread) { g_GSMultiThreaded = multithread; GS_LOG("GSopen\n"); #ifdef _DEBUG g_hCurrentThread = GetCurrentThread(); #endif // assert( GSirq != NULL ); LoadConfig(); strcpy(GStitle, Title); RECT rc, rcdesktop; rc.left = 0; rc.top = 0; rc.right = conf.width; rc.bottom = conf.height; WNDCLASSEX wc; HINSTANCE hInstance = GetModuleHandle(NULL); DWORD dwExStyle, dwStyle; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_CLASSDC; wc.lpfnWndProc = (WNDPROC) MsgProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = NULL; wc.hIconSm = NULL; wc.hCursor = NULL; wc.hbrBackground = NULL; wc.lpszMenuName = NULL; wc.lpszClassName = "PS2EMU_ZEROGS"; RegisterClassEx( &wc ); if( conf.options & GSOPTION_FULLSCREEN) { dwExStyle = WS_EX_APPWINDOW; dwStyle = WS_POPUP; } else { dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; dwStyle = WS_OVERLAPPEDWINDOW; } AdjustWindowRectEx(&rc, dwStyle, FALSE, dwExStyle); GetWindowRect(GetDesktopWindow(), &rcdesktop); GShwnd = CreateWindowEx( dwExStyle, "PS2EMU_ZEROGS", "ZeroGS", dwStyle, (rcdesktop.right - (rc.right - rc.left)) / 2, (rcdesktop.bottom - (rc.bottom - rc.top)) / 2, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, hInstance, NULL); if(GShwnd == NULL) { GS_LOG("Failed to create window. Exiting..."); return -1; } if( pDsp != NULL ) *(HWND*)pDsp = GShwnd; ERROR_LOG("Using %s:%d.%d.%d\n", libraryName, zgsrevision, zgsbuild, zgsminor); ERROR_LOG("creating zerogs\n"); //if (conf.record) recOpen(); if( !ZeroGS::Create(conf.width, conf.height) ) return -1; ERROR_LOG("initialization successful\n"); if( conf.bilinear == 2 ) { ZeroGS::AddMessage("forced bilinear filtering - on", 1000); } else if( conf.bilinear == 1 ) { ZeroGS::AddMessage("normal bilinear filtering - on", 1000); } if( conf.aa ) { char strtitle[64]; sprintf(strtitle, "anti-aliasing - %s", s_aa[conf.aa], 1000); ZeroGS::AddMessage(strtitle); } // set just in case SetWindowLongPtr(GShwnd, GWLP_WNDPROC, (LPARAM)(WNDPROC)MsgProc); ShowWindow( GShwnd, SW_SHOWDEFAULT ); UpdateWindow( GShwnd ); SetFocus(GShwnd); GS_LOG("GSopen ok\n"); LARGE_INTEGER temp; QueryPerformanceFrequency(&temp); luPerfFreq = temp.QuadPart; gs.path1.mode = 0; gs.path2.mode = 0; gs.path3.mode = 0; return 0; } void ProcessMessages() { MSG msg; ZeroMemory( &msg, sizeof(msg) ); while( 1 ) { if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) ) { switch( msg.message ) { case WM_KEYDOWN : if( msg.wParam == VK_F5 ) { OnKeyboardF5(GetKeyState(VK_SHIFT)&0x8000); } else if( msg.wParam == VK_F6 ) { OnKeyboardF6(GetKeyState(VK_SHIFT)&0x8000); } else if( msg.wParam == VK_F7 ) { OnKeyboardF7(GetKeyState(VK_SHIFT)&0x8000); } else if( msg.wParam == VK_F9 ) { OnKeyboardF9(GetKeyState(VK_SHIFT)&0x8000); } else if( msg.wParam == VK_ESCAPE ) { if( conf.options & GSOPTION_FULLSCREEN ) { // destroy that msg conf.options &= ~GSOPTION_FULLSCREEN; ZeroGS::ChangeDeviceSize(conf.width, conf.height); UpdateWindow(GShwnd); continue; // so that msg doesn't get sent } else { SendMessage(GShwnd, WM_DESTROY, 0, 0); g_bHidden = 1; return; } } break; } TranslateMessage( &msg ); DispatchMessage( &msg ); } else break; } if( (GetKeyState(VK_MENU)&0x8000) && (GetKeyState(VK_RETURN)&0x8000) ) { conf.options ^= GSOPTION_FULLSCREEN; ZeroGS::SetChangeDeviceSize( (conf.options&GSOPTION_FULLSCREEN) ? 1280 : conf.width, (conf.options&GSOPTION_FULLSCREEN) ? 960 : conf.height); } // if( conf.fullscreen && (GetKeyState(VK_ESCAPE)&0x8000)) { // conf.fullscreen &= ~GSOPTION_FULLSCREEN; // ZeroGS::SetChangeDeviceSize(conf.width, conf.height); // } //if( conf.interlace && g_nGenVars + g_nTexVars + g_nAlphaVars + g_nResolve == 0 ) // CSR->FIELD = 0; // 0 should always be the repeating at 0 } #else // linux s32 CALLBACK GSopen(void *pDsp, char *Title, int multithread) { FUNCLOG GS_LOG("GSopen\n"); // assert( GSirq != NULL ); LoadConfig(); strcpy(GStitle, Title); GLWin.dpy = XOpenDisplay(0); GLWin.screen = DefaultScreen(GLWin.dpy); if( pDsp != NULL ) *(Display**)pDsp = GLWin.dpy; ERROR_LOG("Using %s:%d.%d.%d\n", libraryName, zgsrevision, zgsbuild, zgsminor); ERROR_LOG("creating zerogs\n"); //if (conf.record) recOpen(); if( !ZeroGS::Create(conf.width, conf.height) ) return -1; ERROR_LOG("initialization successful\n"); if( conf.bilinear == 2 ) { ZeroGS::AddMessage("bilinear filtering - forced", 1000); } else if( conf.bilinear == 1 ) { ZeroGS::AddMessage("bilinear filtering - normal", 1000); } if( conf.aa ) { char strtitle[64]; sprintf(strtitle, "anti-aliasing - %s", s_aa[conf.aa]); ZeroGS::AddMessage(strtitle,1000); } GS_LOG("GSopen ok\n"); gs.path1.mode = 0; gs.path2.mode = 0; gs.path3.mode = 0; luPerfFreq = 1; return 0; } void ProcessMessages() { FUNCLOG XEvent event; // check resizing while(XCheckTypedEvent(GLWin.dpy, ConfigureNotify, &event)) { if ((abs(event.xconfigure.width) != GLWin.width) || (abs(event.xconfigure.height) != GLWin.height)) { ZeroGS::ChangeWindowSize(event.xconfigure.width, event.xconfigure.height); GLWin.width = abs(event.xconfigure.width); GLWin.height = abs(event.xconfigure.height); } } if ( THR_KeyEvent ) { // This values was passed from GSKeyEvents witch could be in another thread int my_KeyEvent = THR_KeyEvent; bool my_bShift = THR_bShift; THR_KeyEvent = 0; switch ( my_KeyEvent ) { case XK_F5: OnKeyboardF5(my_bShift); break; case XK_F6: OnKeyboardF6(my_bShift); break; case XK_F7: OnKeyboardF7(my_bShift); break; case XK_F9: OnKeyboardF9(my_bShift); break; } } } #endif // linux void CALLBACK GSclose() { FUNCLOG ZeroGS::Destroy(1); #ifdef _WIN32 if( GShwnd != NULL ) { DestroyWindow(GShwnd); GShwnd = NULL; } #else if( GLWin.dpy != NULL ) { XCloseDisplay(GLWin.dpy); GLWin.dpy = NULL; } #endif SaveStateFile = NULL; SaveStateExists = true; // default value } void CALLBACK GSirqCallback(void (*callback)()) { FUNCLOG GSirq = callback; } void CALLBACK GSwriteCSR(u32 write) { FUNCLOG gs.CSRw = write; } void CALLBACK GSchangeSaveState(int newstate, const char* filename) { FUNCLOG char str[255]; sprintf(str, "save state %d", newstate); ZeroGS::AddMessage(str); CurrentSavestate = newstate; SaveStateFile = filename; SaveStateExists = (access(SaveStateFile, 0) == 0); } void CALLBACK GSmakeSnapshot(char *path) { FUNCLOG FILE *bmpfile; char filename[256]; u32 snapshotnr = 0; // increment snapshot value & try to get filename for (;;) { snapshotnr++; sprintf(filename,"%ssnap%03ld.%s", path, snapshotnr, (conf.options&GSOPTION_TGASNAP)?"bmp":"jpg"); bmpfile=fopen(filename,"rb"); if (bmpfile == NULL) break; fclose(bmpfile); } // try opening new snapshot file if((bmpfile=fopen(filename,"wb"))==NULL) { char strdir[255]; #ifdef _WIN32 sprintf(strdir, "%s", path); CreateDirectory(strdir, NULL); #else sprintf(strdir, "mkdir %s", path); system(strdir); #endif if((bmpfile=fopen(filename,"wb"))==NULL) return; } fclose(bmpfile); // get the bits ZeroGS::SaveSnapshot(filename); } int UPDATE_FRAMES = 16; int g_nFrame = 0; int g_nRealFrame = 0; float fFPS = 0; void CALLBACK GSvsync(int interlace) { FUNCLOG GS_LOG("\nGSvsync\n\n"); static u32 dwTime = timeGetTime(); static int nToNextUpdate = 1; char strtitle[256]; GL_REPORT_ERRORD(); g_nRealFrame++; ZeroGS::RenderCRTC(!interlace); ProcessMessages(); if( --nToNextUpdate <= 0 ) { u32 d = timeGetTime(); fFPS = UPDATE_FRAMES * 1000.0f / (float)max(d-dwTime,1); dwTime = d; g_nFrame += UPDATE_FRAMES; #ifdef RELEASE_TO_PUBLIC if (SaveStateFile != NULL && !SaveStateExists) SaveStateExists = (access(SaveStateFile, 0) == 0); else SaveStateExists = true; sprintf(strtitle, "ZZ Open GL 0.%d.%d | %.1f fps | %s%s%s savestate %d%s | shaders %s | (%.1f)", zgsbuild, zgsminor, fFPS, g_pInterlace[conf.interlace], g_pBilinear[conf.bilinear], (conf.aa >= conf.negaa) ? (conf.aa ? s_aa[conf.aa - conf.negaa] : "") : (conf.negaa ? s_naa[conf.negaa - conf.aa] : ""), CurrentSavestate, (SaveStateExists ? "": "*" ), g_pShaders[g_nPixelShaderVer], (ppf&0xfffff)/(float)UPDATE_FRAMES); #else sprintf(strtitle, "%d | %.1f fps (sk:%d%%) | g: %.1f, t: %.1f, a: %.1f, r: %.1f | p: %.1f | tex: %d %d (%d kbpf)", g_nFrame, fFPS, 100*g_nFramesSkipped/g_nFrame, g_nGenVars/(float)UPDATE_FRAMES, g_nTexVars/(float)UPDATE_FRAMES, g_nAlphaVars/(float)UPDATE_FRAMES, g_nResolve/(float)UPDATE_FRAMES, (ppf&0xfffff)/(float)UPDATE_FRAMES, ZeroGS::g_MemTargs.listTargets.size(), ZeroGS::g_MemTargs.listClearedTargets.size(), g_TransferredToGPU>>10); //_snprintf(strtitle, 512, "%x %x", *(int*)(g_pbyGSMemory + 256 * 0x3e0c + 4), *(int*)(g_pbyGSMemory + 256 * 0x3e04 + 4)); #endif // if( g_nFrame > 100 && fFPS > 60.0f ) { // DEBUG_LOG("set profile\n"); // g_bWriteProfile = 1; // } #ifdef _WIN32 if( !(conf.options&GSOPTION_FULLSCREEN) ) SetWindowText(GShwnd, strtitle); #else // linux XTextProperty prop; memset(&prop, 0, sizeof(prop)); char* ptitle = strtitle; if( XStringListToTextProperty(&ptitle, 1, &prop) ) XSetWMName(GLWin.dpy, GLWin.win, &prop); XFree(prop.value); #endif if( fFPS < 16 ) UPDATE_FRAMES = 4; else if( fFPS < 32 ) UPDATE_FRAMES = 8; else UPDATE_FRAMES = 16; nToNextUpdate = UPDATE_FRAMES; g_TransferredToGPU = 0; g_nGenVars = 0; g_nTexVars = 0; g_nAlphaVars = 0; g_nResolve = 0; ppf = 0; g_nFramesSkipped = 0; } #ifndef RELEASE_TO_PUBLIC if( g_bWriteProfile ) { //g_bWriteProfile = 0; DVProfWrite("prof.txt", UPDATE_FRAMES); DVProfClear(); } #endif GL_REPORT_ERRORD(); } void GIFtag(pathInfo *path, u32 *data) { FUNCLOG path->tag.nloop = data[0] & 0x7fff; path->tag.eop = (data[0] >> 15) & 0x1; u32 tagpre = (data[1] >> 14) & 0x1; u32 tagprim = (data[1] >> 15) & 0x7ff; u32 tagflg = (data[1] >> 26) & 0x3; path->tag.nreg = (data[1] >> 28)<<2; if (path->tag.nreg == 0) path->tag.nreg = 64; gs.q = 1; // GS_LOG("GIFtag: %8.8lx_%8.8lx_%8.8lx_%8.8lx: EOP=%d, NLOOP=%x, FLG=%x, NREG=%d, PRE=%d\n", // data[3], data[2], data[1], data[0], // path->tag.eop, path->tag.nloop, tagflg, path->tag.nreg, tagpre); path->mode = tagflg+1; switch (tagflg) { case 0x0: path->regs = *(u64 *)(data+2); path->regn = 0; if (tagpre) GIFRegHandlerPRIM((u32*)&tagprim); break; case 0x1: path->regs = *(u64 *)(data+2); path->regn = 0; break; } } void _GSgifPacket(pathInfo *path, u32 *pMem) { // 128bit FUNCLOG int reg = (int)((path->regs >> path->regn) & 0xf); g_GIFPackedRegHandlers[reg](pMem); path->regn += 4; if (path->tag.nreg == path->regn) { path->regn = 0; path->tag.nloop--; } } void _GSgifRegList(pathInfo *path, u32 *pMem) { // 64bit FUNCLOG int reg; reg = (int)((path->regs >> path->regn) & 0xf); g_GIFRegHandlers[reg](pMem); path->regn += 4; if (path->tag.nreg == path->regn) { path->regn = 0; path->tag.nloop--; } } static int nPath3Hack = 0; void CALLBACK GSgetLastTag(u64* ptag) { FUNCLOG *(u32*)ptag = nPath3Hack; nPath3Hack = 0; } void _GSgifTransfer(pathInfo *path, u32 *pMem, u32 size) { FUNCLOG #ifdef _WIN32 assert( g_hCurrentThread == GetCurrentThread() ); #endif #ifdef _DEBUG if( conf.log & 0x20 ) { static int nSaveIndex=0; GS_LOG("%d: p:%d %x\n", nSaveIndex++, (path==&gs.path3)?3:(path==&gs.path2?2:1), size); int vals[4] = {0}; for(int i = 0; i < size; i++) { for(int j = 0; j < 4; ++j ) vals[j] ^= pMem[4*i+j]; } GS_LOG("%x %x %x %x\n", vals[0], vals[1], vals[2], vals[3]); } #endif while(size > 0) { //LOG(_T("Transfer(%08x, %d) START\n"), pMem, size); if (path->tag.nloop == 0) { GIFtag(path, pMem); pMem+= 4; size--; if ((g_GameSettings & GAME_PATH3HACK) && path == &gs.path3 && gs.path3.tag.eop) nPath3Hack = 1; if (path == &gs.path1) { if (path->mode == 1) { // check if 0xb is in any reg, if yes, exit (kh2) for(int i = 0; i < path->tag.nreg; i += 4) { if (((path->regs >> i)&0xf) == 11) { ERROR_LOG_SPAM("Invalid unpack type\n"); path->tag.nloop = 0; return; } } } } if(path->tag.nloop == 0 ) { if( path == &gs.path1 ) { // ffx hack if( path->tag.eop ) return; continue; } if( !path->tag.eop ) { //DEBUG_LOG("continuing from eop\n"); continue; } // Issue 174 fix! continue; } } switch(path->mode) { case 1: // PACKED { assert( path->tag.nloop > 0 ); for(; size > 0; size--, pMem += 4) { int reg = (int)((path->regs >> path->regn) & 0xf); g_GIFPackedRegHandlers[reg](pMem); path->regn += 4; if (path->tag.nreg == path->regn) { path->regn = 0; if( path->tag.nloop-- <= 1 ) { size--; pMem += 4; break; } } } break; } case 2: // REGLIST { //GS_LOG("%8.8x%8.8x %d L\n", ((u32*)&gs.regs)[1], *(u32*)&gs.regs, path->tag.nreg/4); assert( path->tag.nloop > 0 ); size *= 2; for(; size > 0; pMem+= 2, size--) { int reg = (int)((path->regs >> path->regn) & 0xf); g_GIFRegHandlers[reg](pMem); path->regn += 4; if (path->tag.nreg == path->regn) { path->regn = 0; if( path->tag.nloop-- <= 1 ) { size--; pMem += 2; break; } } } if( size & 1 ) pMem += 2; size /= 2; break; } case 3: // GIF_IMAGE (FROM_VFRAM) case 4: // Used in the DirectX version, so we'll use it here too. { if(gs.imageTransfer >= 0 && gs.imageTransfer <= 1) { int process = min((int)size, path->tag.nloop); if( process > 0 ) { if ( gs.imageTransfer ) ZeroGS::TransferLocalHost(pMem, process); else ZeroGS::TransferHostLocal(pMem, process*4); path->tag.nloop -= process; pMem += process*4; size -= process; assert( size == 0 || path->tag.nloop == 0 ); } break; } else { // simulate int process = min((int)size, path->tag.nloop); path->tag.nloop -= process; pMem += process*4; size -= process; } break; } default: // GIF_IMAGE GS_LOG("*** WARNING **** Unexpected GIFTag flag\n"); assert(0); path->tag.nloop = 0; break; } if( path == &gs.path1 && path->tag.eop ) return; } // This is case when not all data was readed from one try: VU1 to much data. // So we should redone reading from start if (path == &gs.path1 && size == 0 && path->tag.nloop > 0) { ERROR_LOG_SPAMA("VU1 too much data, ignore if gfx are fine %d\n", path->tag.nloop) // TODO: this code is not working correctly. Anyway, ringing work only in single-threadred mode. // _GSgifTransfer(&gs.path1, (u32*)((u8*)pMem-0x4000), (0x4000)/16); } } void CALLBACK GSgifTransfer2(u32 *pMem, u32 size) { FUNCLOG //GS_LOG("GSgifTransfer2 size = %lx (mode %d, gs.path2.tag.nloop = %d)\n", size, gs.path2.mode, gs.path2.tag.nloop); _GSgifTransfer(&gs.path2, pMem, size); } void CALLBACK GSgifTransfer3(u32 *pMem, u32 size) { FUNCLOG //GS_LOG("GSgifTransfer3 size = %lx (mode %d, gs.path3.tag.nloop = %d)\n", size, gs.path3.mode, gs.path3.tag.nloop); nPath3Hack = 0; _GSgifTransfer(&gs.path3, pMem, size); } static int count = 0; void CALLBACK GSgifTransfer1(u32 *pMem, u32 addr) { FUNCLOG pathInfo *path = &gs.path1; //GS_LOG("GSgifTransfer1 0x%x (mode %d)\n", addr, path->mode); // addr &= 0x3fff; #ifdef _DEBUG PRIM_LOG("count: %d\n", count); count++; #endif gs.path1.tag.nloop = 0; gs.path1.tag.eop = 0; _GSgifTransfer(&gs.path1, (u32*)((u8*)pMem+addr), (0x4000 - addr)/16); if( !gs.path1.tag.eop && gs.path1.tag.nloop > 0 ) { assert( (addr&0xf) == 0 ); //BUG gs.path1.tag.nloop = 0; ERROR_LOG("Transfer1 - 2\n"); return; } } void CALLBACK GSreadFIFO(u64 *pMem) { FUNCLOG //GS_LOG("GSreadFIFO\n"); ZeroGS::TransferLocalHost((u32*)pMem, 1); } void CALLBACK GSreadFIFO2(u64 *pMem, int qwc) { FUNCLOG //GS_LOG("GSreadFIFO2\n"); ZeroGS::TransferLocalHost((u32*)pMem, qwc); } int CALLBACK GSsetupRecording(int start, void* pData) { FUNCLOG if( start ) { if( conf.options & GSOPTION_CAPTUREAVI ) return 1; ZeroGS::StartCapture(); conf.options |= GSOPTION_CAPTUREAVI; WARN_LOG("ZeroGS: started recording at zerogs.avi\n"); } else { if( !(conf.options & GSOPTION_CAPTUREAVI) ) return 1; conf.options &= ~GSOPTION_CAPTUREAVI; ZeroGS::StopCapture(); WARN_LOG("ZeroGS: stopped recording\n"); } return 1; } s32 CALLBACK GSfreeze(int mode, freezeData *data) { FUNCLOG switch (mode) { case FREEZE_LOAD: if (!ZeroGS::Load(data->data)) ERROR_LOG("GS: Bad load format!"); g_nRealFrame += 100; break; case FREEZE_SAVE: ZeroGS::Save(data->data); break; case FREEZE_SIZE: data->size = ZeroGS::Save(NULL); break; default: break; } return 0; } //////////////////// // Small profiler // //////////////////// #include #include #include using namespace std; #ifdef _WIN32 __forceinline u64 GET_PROFILE_TIME() { LARGE_INTEGER lu; QueryPerformanceCounter(&lu); return lu.QuadPart; } #else #define GET_PROFILE_TIME() //GetCpuTick() #endif struct DVPROFSTRUCT; struct DVPROFSTRUCT { struct DATA { DATA(u64 time, u32 user = 0) : dwTime(time), dwUserData(user) {} DATA() : dwTime(0), dwUserData(0) {} u64 dwTime; u32 dwUserData; }; ~DVPROFSTRUCT() { list::iterator it = listpChild.begin(); while(it != listpChild.end() ) { SAFE_DELETE(*it); ++it; } } list listTimes; // before DVProfEnd is called, contains the global time it started // after DVProfEnd is called, contains the time it lasted // the list contains all the tracked times char pname[256]; list listpChild; // other profilers called during this profiler period }; struct DVPROFTRACK { u32 dwUserData; DVPROFSTRUCT::DATA* pdwTime; DVPROFSTRUCT* pprof; }; list g_listCurTracking; // the current profiling functions, the back element is the // one that will first get popped off the list when DVProfEnd is called // the pointer is an element in DVPROFSTRUCT::listTimes list g_listProfilers; // the current profilers, note that these are the parents // any profiler started during the time of another is held in // DVPROFSTRUCT::listpChild list g_listAllProfilers; // ignores the hierarchy, pointer to elements in g_listProfilers void DVProfRegister(char* pname) { if( !g_bWriteProfile ) return; list::iterator it = g_listAllProfilers.begin(); // while(it != g_listAllProfilers.end() ) { // // if( _tcscmp(pname, (*it)->pname) == 0 ) { // (*it)->listTimes.push_back(timeGetTime()); // DVPROFTRACK dvtrack; // dvtrack.pdwTime = &(*it)->listTimes.back(); // dvtrack.pprof = *it; // g_listCurTracking.push_back(dvtrack); // return; // } // // ++it; // } // else add in a new profiler to the appropriate parent profiler DVPROFSTRUCT* pprof = NULL; if( g_listCurTracking.size() > 0 ) { assert( g_listCurTracking.back().pprof != NULL ); g_listCurTracking.back().pprof->listpChild.push_back(new DVPROFSTRUCT()); pprof = g_listCurTracking.back().pprof->listpChild.back(); } else { g_listProfilers.push_back(DVPROFSTRUCT()); pprof = &g_listProfilers.back(); } strncpy(pprof->pname, pname, 256); // setup the profiler for tracking pprof->listTimes.push_back(DVPROFSTRUCT::DATA(GET_PROFILE_TIME())); DVPROFTRACK dvtrack; dvtrack.pdwTime = &pprof->listTimes.back(); dvtrack.pprof = pprof; dvtrack.dwUserData = 0; g_listCurTracking.push_back(dvtrack); // add to all profiler list g_listAllProfilers.push_back(pprof); } void DVProfEnd(u32 dwUserData) { if( !g_bWriteProfile ) return; B_RETURN( g_listCurTracking.size() > 0 ); DVPROFTRACK dvtrack = g_listCurTracking.back(); assert( dvtrack.pdwTime != NULL && dvtrack.pprof != NULL ); dvtrack.pdwTime->dwTime = 1000000 * (GET_PROFILE_TIME()- dvtrack.pdwTime->dwTime) / luPerfFreq; dvtrack.pdwTime->dwUserData= dwUserData; g_listCurTracking.pop_back(); } struct DVTIMEINFO { DVTIMEINFO() : uInclusive(0), uExclusive(0) {} u64 uInclusive, uExclusive; }; map mapAggregateTimes; u64 DVProfWriteStruct(FILE* f, DVPROFSTRUCT* p, int ident) { fprintf(f, "%*s%s - ", ident, "", p->pname); list::iterator ittime = p->listTimes.begin(); u32 utime = 0; while(ittime != p->listTimes.end() ) { utime += (u32)ittime->dwTime; if( ittime->dwUserData ) fprintf(f, "time: %d, user: 0x%8.8x", (u32)ittime->dwTime, ittime->dwUserData); else fprintf(f, "time: %d", (u32)ittime->dwTime); ++ittime; } mapAggregateTimes[p->pname].uInclusive += utime; fprintf(f, "\n"); list::iterator itprof = p->listpChild.begin(); u32 uex = utime; while(itprof != p->listpChild.end() ) { uex -= DVProfWriteStruct(f, *itprof, ident+4); ++itprof; } mapAggregateTimes[p->pname].uExclusive += uex; return utime; } void DVProfWrite(char* pfilename, u32 frames) { assert( pfilename != NULL ); FILE* f = fopen(pfilename, "wb"); mapAggregateTimes.clear(); list::iterator it = g_listProfilers.begin(); while(it != g_listProfilers.end() ) { DVProfWriteStruct(f, &(*it), 0); ++it; } { map::iterator it; fprintf(f, "\n\n-------------------------------------------------------------------\n\n"); u64 uTotal[2] = {0}; double fiTotalTime[2]; for(it = mapAggregateTimes.begin(); it != mapAggregateTimes.end(); ++it) { uTotal[0] += it->second.uExclusive; uTotal[1] += it->second.uInclusive; } fprintf(f, "total times (%d): ex: %Lu ", frames, uTotal[0]/frames); fprintf(f, "inc: %Lu\n", uTotal[1]/frames); fiTotalTime[0] = 1.0 / (double)uTotal[0]; fiTotalTime[1] = 1.0 / (double)uTotal[1]; // output the combined times for(it = mapAggregateTimes.begin(); it != mapAggregateTimes.end(); ++it) { fprintf(f, "%s - ex: %f inc: %f\n", it->first.c_str(), (double)it->second.uExclusive * fiTotalTime[0], (double)it->second.uInclusive * fiTotalTime[1]); } } fclose(f); } void DVProfClear() { g_listCurTracking.clear(); g_listProfilers.clear(); g_listAllProfilers.clear(); }