// Note EXPORT_C GSReplay(char* lpszCmdLine, int renderer) { GLLoader::in_replayer = true; GSRendererType m_renderer; // Allow to easyly switch between SW/HW renderer -> this effectively removes the ability to select the renderer by function args m_renderer = static_cast<GSRendererType>(theApp.GetConfigI("Renderer")); // alternatively: // m_renderer = static_cast<GSRendererType>(renderer); if (m_renderer != GSRendererType::OGL_HW && m_renderer != GSRendererType::OGL_SW) { fprintf(stderr, "wrong renderer selected %d\n", static_cast<int>(m_renderer)); return; } struct Packet {uint8 type, param; uint32 size, addr; vector<uint8> buff;}; list<Packet*> packets; vector<uint8> buff; vector<float> stats; stats.clear(); uint8 regs[0x2000]; GSinit(); GSsetBaseMem(regs); s_vsync = theApp.GetConfigB("vsync"); void* hWnd = NULL; int err = _GSopen((void**)&hWnd, "", m_renderer); if (err != 0) { fprintf(stderr, "Error failed to GSopen\n"); return; } if (s_gs->m_wnd == NULL) return; { // Read .gs content std::string f(lpszCmdLine); #ifdef LZMA_SUPPORTED GSDumpFile* file = (f.size() >= 4) && (f.compare(f.size()-3, 3, ".xz") == 0) ? (GSDumpFile*) new GSDumpLzma(lpszCmdLine) : (GSDumpFile*) new GSDumpRaw(lpszCmdLine); #else GSDumpFile* file = new GSDumpRaw(lpszCmdLine); #endif uint32 crc; file->Read(&crc, 4); GSsetGameCRC(crc, 0); GSFreezeData fd; file->Read(&fd.size, 4); fd.data = new uint8[fd.size]; file->Read(fd.data, fd.size); GSfreeze(FREEZE_LOAD, &fd); delete [] fd.data; file->Read(regs, 0x2000); GSvsync(1); while(!file->IsEof()) { uint8 type; file->Read(&type, 1); Packet* p = new Packet(); p->type = type; switch(type) { case 0: file->Read(&p->param, 1); file->Read(&p->size, 4); switch(p->param) { case 0: p->buff.resize(0x4000); p->addr = 0x4000 - p->size; file->Read(&p->buff[p->addr], p->size); break; case 1: case 2: case 3: p->buff.resize(p->size); file->Read(&p->buff[0], p->size); break; } break; case 1: file->Read(&p->param, 1); break; case 2: file->Read(&p->size, 4); break; case 3: p->buff.resize(0x2000); file->Read(&p->buff[0], 0x2000); break; } packets.push_back(p); } delete file; } sleep(1); //while(IsWindowVisible(hWnd)) //FIXME map? int finished = theApp.GetConfigI("linux_replay"); if (theApp.GetConfigI("dump")) { fprintf(stderr, "Dump is enabled. Replay will be disabled\n"); finished = 1; } unsigned long frame_number = 0; unsigned long total_frame_nb = 0; while(finished > 0) { frame_number = 0; unsigned long start = timeGetTime(); for(auto i = packets.begin(); i != packets.end(); i++) { Packet* p = *i; switch(p->type) { case 0: switch(p->param) { case 0: GSgifTransfer1(&p->buff[0], p->addr); break; case 1: GSgifTransfer2(&p->buff[0], p->size / 16); break; case 2: GSgifTransfer3(&p->buff[0], p->size / 16); break; case 3: GSgifTransfer(&p->buff[0], p->size / 16); break; } break; case 1: GSvsync(p->param); frame_number++; break; case 2: if(buff.size() < p->size) buff.resize(p->size); GSreadFIFO2(&buff[0], p->size / 16); break; case 3: memcpy(regs, &p->buff[0], 0x2000); break; } } // Ensure the rendering is complete to measure correctly the time. glFinish(); if (finished > 90) { sleep(1); } else { unsigned long end = timeGetTime(); frame_number = std::max(1ul, frame_number); // avoid a potential division by 0 fprintf(stderr, "The %ld frames of the scene was render on %ldms\n", frame_number, end - start); fprintf(stderr, "A means of %fms by frame\n", (float)(end - start)/(float)frame_number); stats.push_back((float)(end - start)); finished--; total_frame_nb += frame_number; } } if (theApp.GetConfigI("linux_replay") > 1) { // Print some nice stats // Skip first frame (shader compilation populate the result) // it divides by 10 the standard deviation... float n = (float)theApp.GetConfigI("linux_replay") - 1.0f; float mean = 0; float sd = 0; for (auto i = stats.begin()+1; i != stats.end(); i++) { mean += *i; } mean = mean/n; for (auto i = stats.begin()+1; i != stats.end(); i++) { sd += pow((*i)-mean, 2); } sd = sqrt(sd/n); fprintf(stderr, "\n\nMean: %fms\n", mean); fprintf(stderr, "Standard deviation: %fms\n", sd); fprintf(stderr, "Mean by frame: %fms (%ffps)\n", mean/(float)frame_number, 1000.0f*frame_number/mean); fprintf(stderr, "Standard deviatin by frame: %fms\n", sd/(float)frame_number); } #ifdef ENABLE_OGL_DEBUG_MEM_BW total_frame_nb *= 1024; fprintf(stderr, "memory bandwith. T: %f KB/f. V: %f KB/f. U: %f KB/f\n", (float)g_real_texture_upload_byte/(float)total_frame_nb, (float)g_vertex_upload_byte/(float)total_frame_nb, (float)g_uniform_upload_byte/(float)total_frame_nb ); #endif for(auto i = packets.begin(); i != packets.end(); i++) { delete *i; } packets.clear(); sleep(1); GSclose(); GSshutdown(); }
// Note EXPORT_C GSReplay(char* lpszCmdLine, int renderer) { GLLoader::in_replayer = true; GSRendererType m_renderer; // Allow to easyly switch between SW/HW renderer -> this effectively removes the ability to select the renderer by function args m_renderer = static_cast<GSRendererType>(theApp.GetConfigI("Renderer")); // alternatively: // m_renderer = static_cast<GSRendererType>(renderer); if (m_renderer != GSRendererType::OGL_HW && m_renderer != GSRendererType::OGL_SW) { fprintf(stderr, "wrong renderer selected %d\n", static_cast<int>(m_renderer)); return; } struct Packet {uint8 type, param; uint32 size, addr; vector<uint8> buff;}; list<Packet*> packets; vector<uint8> buff; uint8 regs[0x2000]; GSinit(); GSsetBaseMem(regs); s_vsync = theApp.GetConfigB("vsync"); void* hWnd = NULL; int err = _GSopen((void**)&hWnd, "", m_renderer); if (err != 0) { fprintf(stderr, "Error failed to GSopen\n"); return; } if (s_gs->m_wnd == NULL) return; { // Read .gs content std::string f(lpszCmdLine); #ifdef LZMA_SUPPORTED GSDumpFile* file = (f.size() >= 4) && (f.compare(f.size()-3, 3, ".xz") == 0) ? (GSDumpFile*) new GSDumpLzma(lpszCmdLine) : (GSDumpFile*) new GSDumpRaw(lpszCmdLine); #else GSDumpFile* file = new GSDumpRaw(lpszCmdLine); #endif uint32 crc; file->Read(&crc, 4); GSsetGameCRC(crc, 0); GSFreezeData fd; file->Read(&fd.size, 4); fd.data = new uint8[fd.size]; file->Read(fd.data, fd.size); GSfreeze(FREEZE_LOAD, &fd); delete [] fd.data; file->Read(regs, 0x2000); GSvsync(1); while(!file->IsEof()) { uint8 type; file->Read(&type, 1); Packet* p = new Packet(); p->type = type; switch(type) { case 0: file->Read(&p->param, 1); file->Read(&p->size, 4); switch(p->param) { case 0: p->buff.resize(0x4000); p->addr = 0x4000 - p->size; file->Read(&p->buff[p->addr], p->size); break; case 1: case 2: case 3: p->buff.resize(p->size); file->Read(&p->buff[0], p->size); break; } break; case 1: file->Read(&p->param, 1); break; case 2: file->Read(&p->size, 4); break; case 3: p->buff.resize(0x2000); file->Read(&p->buff[0], 0x2000); break; } packets.push_back(p); } delete file; } sleep(1); //while(IsWindowVisible(hWnd)) //FIXME map? int finished = theApp.GetConfigI("linux_replay"); if (theApp.GetConfigI("dump")) { fprintf(stderr, "Dump is enabled. Replay will be disabled\n"); finished = 1; } unsigned long frame_number = 0; while(finished > 0) { for(auto i = packets.begin(); i != packets.end(); i++) { Packet* p = *i; switch(p->type) { case 0: switch(p->param) { case 0: GSgifTransfer1(&p->buff[0], p->addr); break; case 1: GSgifTransfer2(&p->buff[0], p->size / 16); break; case 2: GSgifTransfer3(&p->buff[0], p->size / 16); break; case 3: GSgifTransfer(&p->buff[0], p->size / 16); break; } break; case 1: GSvsync(p->param); frame_number++; break; case 2: if(buff.size() < p->size) buff.resize(p->size); GSreadFIFO2(&buff[0], p->size / 16); break; case 3: memcpy(regs, &p->buff[0], 0x2000); break; } } if (finished >= 200) { ; // Nop for Nvidia Profiler } else if (finished > 90) { sleep(1); } else { finished--; } } #ifdef ENABLE_OGL_DEBUG_MEM_BW unsigned long total_frame_nb = std::max(1ul, frame_number) << 10; fprintf(stderr, "memory bandwith. T: %f KB/f. V: %f KB/f. U: %f KB/f\n", (float)g_real_texture_upload_byte/(float)total_frame_nb, (float)g_vertex_upload_byte/(float)total_frame_nb, (float)g_uniform_upload_byte/(float)total_frame_nb ); #endif for(auto i = packets.begin(); i != packets.end(); i++) { delete *i; } packets.clear(); sleep(1); GSclose(); GSshutdown(); }