BFApp.cpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. #include "BFApp.h"
  2. #include "BFWindow.h"
  3. #include "gfx/RenderDevice.h"
  4. #include "FileStream.h"
  5. #include "util/BSpline.h"
  6. #include "util/PerfTimer.h"
  7. #include "sound/WwiseSound.h"
  8. #include "util/AllocDebug.h"
  9. #pragma warning(disable:4996)
  10. USING_NS_BF;
  11. BFApp* Beefy::gBFApp = NULL;
  12. BFApp::BFApp()
  13. {
  14. mTitle = "Beefy Application";
  15. mRefreshRate = 60;
  16. mLastProcessTick = BFTickCount();
  17. mPhysFrameTimeAcc = 0;
  18. mDrawEnabled = true;
  19. mUpdateFunc = NULL;
  20. mUpdateFFunc = NULL;
  21. mDrawFunc = NULL;
  22. gBFApp = this;
  23. mSysDialogCnt = 0;
  24. mCursor = CURSOR_POINTER;
  25. mInProcess = false;
  26. mUpdateCnt = 0;
  27. mNumPhysUpdates = 0;
  28. mVSynched = true;
  29. mMaxUpdatesPerDraw = 60; // 8?
  30. mUpdateSampleCount = 0;
  31. mUpdateSampleTimes = 0;
  32. if (gPerfManager == NULL)
  33. gPerfManager = new PerfManager();
  34. mRunning = false;
  35. mRenderDevice = NULL;
  36. mVSynched = false;
  37. mVSyncActive = false;
  38. mForceNextDraw = false;
  39. mUpdateCnt = 0;
  40. mUpdateCntF = 0;
  41. mClientUpdateCntF = 0;
  42. }
  43. BFApp::~BFApp()
  44. {
  45. gBFApp = NULL;
  46. delete gPerfManager;
  47. for (auto window : mPendingWindowDeleteList)
  48. delete window;
  49. }
  50. void BFApp::Init()
  51. {
  52. }
  53. void BFApp::Run()
  54. {
  55. }
  56. void BFApp::Shutdown()
  57. {
  58. mRunning = false;
  59. }
  60. void BFApp::SetCursor(int cursor)
  61. {
  62. mCursor = cursor;
  63. PhysSetCursor();
  64. }
  65. void BFApp::Update(bool batchStart)
  66. {
  67. //Beefy::DebugTimeGuard suspendTimeGuard(30, "BFApp::Update");
  68. #ifdef BF_WWISE_ENABLED
  69. WWiseUpdate();
  70. #endif
  71. mUpdateCnt++;
  72. gPerfManager->NextFrame();
  73. gPerfManager->ZoneStart("BFApp::Update");
  74. mUpdateFunc(batchStart);
  75. gPerfManager->ZoneEnd();
  76. for (auto window : mPendingWindowDeleteList)
  77. delete window;
  78. mPendingWindowDeleteList.clear();
  79. }
  80. void BFApp::UpdateF(float updatePct)
  81. {
  82. mUpdateFFunc(updatePct);
  83. }
  84. void BFApp::Draw()
  85. {
  86. gPerfManager->ZoneStart("BFApp::Draw");
  87. mDrawFunc(mForceNextDraw);
  88. mForceNextDraw = false;
  89. gPerfManager->ZoneEnd();
  90. }
  91. //#define PERIODIC_PERF_TIMING
  92. void BFApp::Process()
  93. {
  94. //Beefy::DebugTimeGuard suspendTimeGuard(30, "BFApp::Process");
  95. RenderWindow* headRenderWindow = NULL;
  96. float physRefreshRate = 0;
  97. if ((mRenderDevice != NULL) && (!mRenderDevice->mRenderWindowList.IsEmpty()))
  98. {
  99. headRenderWindow = mRenderDevice->mRenderWindowList[0];
  100. physRefreshRate = headRenderWindow->GetRefreshRate();
  101. }
  102. if (physRefreshRate <= 0)
  103. physRefreshRate = 60.0f;
  104. float ticksPerFrame = 1;
  105. float physTicksPerFrame = 1000.0f / physRefreshRate;
  106. if (mInProcess)
  107. return; // No reentry
  108. mInProcess = true;
  109. uint32 tickNow = BFTickCount();
  110. const int vSyncTestingPeriod = 250;
  111. bool didVBlankWait = false;
  112. if (mVSyncActive)
  113. {
  114. // Have a time limit in the cases we miss the vblank
  115. if (mVSyncEvent.WaitFor((int)(physTicksPerFrame + 1)))
  116. didVBlankWait = true;
  117. }
  118. if (mRefreshRate > 0)
  119. ticksPerFrame = 1000.0f / mRefreshRate;
  120. int ticksSinceLastProcess = tickNow - mLastProcessTick;
  121. mUpdateSampleCount++;
  122. mUpdateSampleTimes += ticksSinceLastProcess;
  123. //TODO: Turn off mVSynched based on error calculations - (?)
  124. // Two VSync failures in a row means we set mVSyncFailed and permanently disable it
  125. if (mUpdateSampleTimes >= vSyncTestingPeriod)
  126. {
  127. int expectedFrames = (int)(mUpdateSampleTimes / ticksPerFrame);
  128. if (mUpdateSampleCount > expectedFrames * 1.5)
  129. {
  130. if (!mVSynched)
  131. mVSyncFailed = true;
  132. mVSynched = false;
  133. }
  134. else
  135. {
  136. if (!mVSyncFailed)
  137. mVSynched = true;
  138. }
  139. mUpdateSampleCount = 0;
  140. mUpdateSampleTimes = 0;
  141. }
  142. mPhysFrameTimeAcc += tickNow - mLastProcessTick;
  143. if (didVBlankWait)
  144. {
  145. // Try to keep time synced with vblank
  146. if (mPhysFrameTimeAcc < physTicksPerFrame * 2)
  147. {
  148. float timeAdjust = physTicksPerFrame - mPhysFrameTimeAcc + 0.001f;
  149. mPhysFrameTimeAcc += timeAdjust;
  150. }
  151. }
  152. /*if (updates > 2)
  153. OutputDebugStrF("Updates: %d TickDelta: %d\n", updates, tickNow - mLastProcessTick);*/
  154. // Compensate for "slow start" by limiting the number of catchup-updates we can do when starting the app
  155. int maxUpdates = BF_MIN(mNumPhysUpdates + 1, mMaxUpdatesPerDraw);
  156. while (mPhysFrameTimeAcc >= physTicksPerFrame)
  157. {
  158. mPhysFrameTimeAcc -= physTicksPerFrame;
  159. mUpdateCntF += physTicksPerFrame / ticksPerFrame;
  160. }
  161. static uint32 lastUpdate = BFTickCount();
  162. #ifdef PERIODIC_PERF_TIMING
  163. bool perfTime = (tickNow - lastUpdate >= 5000) && (updates > 0);
  164. if (perfTime)
  165. {
  166. updates = 1;
  167. lastUpdate = tickNow;
  168. if (perfTime)
  169. gPerfManager->StartRecording();
  170. }
  171. #endif
  172. int didUpdateCnt = 0;
  173. if (mUpdateCntF - mClientUpdateCntF > physRefreshRate / 2)
  174. {
  175. // Too large of a difference, just sync
  176. mClientUpdateCntF = mUpdateCntF - 1;
  177. }
  178. while ((mRunning) && ((int)mClientUpdateCntF < (int)mUpdateCntF))
  179. {
  180. Update(didUpdateCnt == 0);
  181. didUpdateCnt++;
  182. mClientUpdateCntF = (int)mClientUpdateCntF + 1.000001;
  183. if (didUpdateCnt >= maxUpdates)
  184. break;
  185. }
  186. // Only attempt UpdateF updates if our rates aren't nearly the same
  187. if ((mRunning) && (mRefreshRate != 0) && (fabs(physRefreshRate - mRefreshRate) / (float)mRefreshRate > 0.1f))
  188. {
  189. float updateFAmt = (float)(mUpdateCntF - mClientUpdateCntF);
  190. if ((updateFAmt > 0.05f) && (updateFAmt < 1.0f) && (didUpdateCnt < maxUpdates))
  191. {
  192. UpdateF(updateFAmt);
  193. didUpdateCnt++;
  194. mClientUpdateCntF = mUpdateCntF;
  195. }
  196. }
  197. if (didUpdateCnt > 0)
  198. mNumPhysUpdates++;
  199. if ((mRunning) && (didUpdateCnt == 0))
  200. {
  201. BfpThread_Sleep(1);
  202. }
  203. if ((mRunning) &&
  204. ((didUpdateCnt != 0) || (mForceNextDraw)))
  205. Draw();
  206. #ifdef PERIODIC_PERF_TIMING
  207. if (perfTime)
  208. {
  209. gPerfManager->StopRecording();
  210. gPerfManager->DbgPrint();
  211. }
  212. #endif
  213. mLastProcessTick = tickNow;
  214. mInProcess = false;
  215. }
  216. void BFApp::RemoveWindow(BFWindow* window)
  217. {
  218. AutoCrit autoCrit(mCritSect);
  219. auto itr = std::find(mWindowList.begin(), mWindowList.end(), window);
  220. if (itr == mWindowList.end()) // Allow benign failure (double removal)
  221. return;
  222. mWindowList.erase(itr);
  223. while (window->mChildren.size() > 0)
  224. RemoveWindow(window->mChildren.front());
  225. if (window->mParent != NULL)
  226. {
  227. window->mParent->mChildren.erase(std::find(window->mParent->mChildren.begin(), window->mParent->mChildren.end(), window));
  228. if (window->mFlags & BFWINDOW_MODAL)
  229. {
  230. bool hasModal = false;
  231. for (auto childWindow : window->mParent->mChildren)
  232. {
  233. if (childWindow->mFlags & BFWINDOW_MODAL)
  234. hasModal = true;
  235. }
  236. if (!hasModal)
  237. window->mParent->ModalsRemoved();
  238. }
  239. }
  240. window->mClosedFunc(window);
  241. mRenderDevice->RemoveRenderWindow(window->mRenderWindow);
  242. window->Destroy();
  243. mPendingWindowDeleteList.push_back(window);
  244. }
  245. FileStream* BFApp::OpenBinaryFile(const StringImpl& fileName)
  246. {
  247. FILE* fP = fopen(fileName.c_str(), "rb");
  248. if (fP == NULL)
  249. return NULL;
  250. FileStream* fileStream = new FileStream();
  251. fileStream->mFP = fP;
  252. return fileStream;
  253. }