#include "BFApp.h" #include "BFWindow.h" #include "gfx/RenderDevice.h" #include "FileStream.h" #include "util/BSpline.h" #include "util/PerfTimer.h" #include "sound/WwiseSound.h" #include "util/AllocDebug.h" #pragma warning(disable:4996) USING_NS_BF; BFApp* Beefy::gBFApp = NULL; BFApp::BFApp() { mTitle = "Beefy Application"; mRefreshRate = 60; mLastProcessTick = BFTickCount(); mFrameTimeAcc = 0; mDrawEnabled = true; mUpdateFunc = NULL; mDrawFunc = NULL; gBFApp = this; mSysDialogCnt = 0; mCursor = CURSOR_POINTER; mInProcess = false; mUpdateCnt = 0; mNumPhysUpdates = 0; mVSynched = true; mMaxUpdatesPerDraw = 60; // 8? mUpdateSampleCount = 0; mUpdateSampleTimes = 0; if (gPerfManager == NULL) gPerfManager = new PerfManager(); mRunning = false; mRenderDevice = NULL; mVSynched = false; } BFApp::~BFApp() { gBFApp = NULL; delete gPerfManager; for (auto window : mPendingWindowDeleteList) delete window; } void BFApp::Init() { } void BFApp::Run() { } void BFApp::Shutdown() { mRunning = false; } void BFApp::SetCursor(int cursor) { mCursor = cursor; PhysSetCursor(); } void BFApp::Update(bool batchStart) { //Beefy::DebugTimeGuard suspendTimeGuard(30, "BFApp::Update"); #ifdef BF_WWISE_ENABLED WWiseUpdate(); #endif mUpdateCnt++; gPerfManager->NextFrame(); gPerfManager->ZoneStart("BFApp::Update"); mUpdateFunc(batchStart); gPerfManager->ZoneEnd(); for (auto window : mPendingWindowDeleteList) delete window; mPendingWindowDeleteList.clear(); } void BFApp::Draw() { gPerfManager->ZoneStart("BFApp::Draw"); mDrawFunc(); gPerfManager->ZoneEnd(); } //#define PERIODIC_PERF_TIMING void BFApp::Process() { //Beefy::DebugTimeGuard suspendTimeGuard(30, "BFApp::Process"); if (mInProcess) return; // No reentry mInProcess = true; int updates; uint32 tickNow = BFTickCount(); const int vSyncTestingPeriod = 250; if (mRefreshRate != 0) { float ticksPerFrame = 1000.0f / mRefreshRate; int ticksSinceLastProcess = tickNow - mLastProcessTick; mUpdateSampleCount++; mUpdateSampleTimes += ticksSinceLastProcess; //TODO: Turn off mVSynched based on error calculations - (?) // Two VSync failures in a row means we set mVSyncFailed and permanently disable it if (mUpdateSampleTimes >= vSyncTestingPeriod) { int expectedFrames = (int)(mUpdateSampleTimes / ticksPerFrame); if (mUpdateSampleCount > expectedFrames * 1.5) { if (!mVSynched) mVSyncFailed = true; mVSynched = false; } else if (!mVSyncFailed) mVSynched = true; mUpdateSampleCount = 0; mUpdateSampleTimes = 0; } mFrameTimeAcc += tickNow - mLastProcessTick; bool vSynched = mVSynched; if (vSynched) { // For the startup, try not to go hyper during those first samplings if (mUpdateSampleTimes <= vSyncTestingPeriod) { if (ticksSinceLastProcess < ticksPerFrame / 1.5) vSynched = false; } } if (vSynched) { updates = std::max(1, (int)(mFrameTimeAcc / ticksPerFrame + 0.5f)); mFrameTimeAcc = std::max(0.0f, mFrameTimeAcc - ticksPerFrame * updates); } else { updates = std::max(0, (int)(mFrameTimeAcc / ticksPerFrame)); mFrameTimeAcc = mFrameTimeAcc - ticksPerFrame * updates; } if (updates > mRefreshRate) { // If more than 1 second of updates is queued, just re-sync updates = 1; mFrameTimeAcc = 0; } // Compensate for "slow start" by limiting the number of catchup-updates we can do when starting the app int maxUpdates = BF_MIN(mNumPhysUpdates + 1, mMaxUpdatesPerDraw); updates = BF_MIN(updates, maxUpdates); /*if (updates > 2) OutputDebugStrF("Updates: %d TickDelta: %d\n", updates, tickNow - mLastProcessTick);*/ } else updates = 1; // RefreshRate of 0 means to update as fast as possible if (updates == 0) { // Yield BfpThread_Sleep(1); } static uint32 lastUpdate = BFTickCount(); #ifdef PERIODIC_PERF_TIMING bool perfTime = (tickNow - lastUpdate >= 5000) && (updates > 0); if (perfTime) { updates = 1; lastUpdate = tickNow; if (perfTime) gPerfManager->StartRecording(); } #endif if (updates > 0) mNumPhysUpdates++; for (int updateNum = 0; updateNum < updates; updateNum++) { if (!mRunning) break; Update(updateNum == 0); } if ((mRunning) && (updates > 0)) Draw(); #ifdef PERIODIC_PERF_TIMING if (perfTime) { gPerfManager->StopRecording(); gPerfManager->DbgPrint(); } #endif mLastProcessTick = tickNow; mInProcess = false; } void BFApp::RemoveWindow(BFWindow* window) { auto itr = std::find(mWindowList.begin(), mWindowList.end(), window); if (itr == mWindowList.end()) // Allow benign failure (double removal) return; mWindowList.erase(itr); while (window->mChildren.size() > 0) RemoveWindow(window->mChildren.front()); if (window->mParent != NULL) { window->mParent->mChildren.erase(std::find(window->mParent->mChildren.begin(), window->mParent->mChildren.end(), window)); if (window->mFlags & BFWINDOW_MODAL) { bool hasModal = false; for (auto childWindow : window->mParent->mChildren) { if (childWindow->mFlags & BFWINDOW_MODAL) hasModal = true; } if (!hasModal) window->mParent->ModalsRemoved(); } } window->mClosedFunc(window); mRenderDevice->RemoveRenderWindow(window->mRenderWindow); window->Destroy(); mPendingWindowDeleteList.push_back(window); } FileStream* BFApp::OpenBinaryFile(const StringImpl& fileName) { FILE* fP = fopen(fileName.c_str(), "rb"); if (fP == NULL) return NULL; FileStream* fileStream = new FileStream(); fileStream->mFP = fP; return fileStream; }