From e87bf5b0294c07b8906af932c071a79b647f686a Mon Sep 17 00:00:00 2001 From: Brian Fiete Date: Sun, 15 May 2022 08:00:55 -0700 Subject: [PATCH] UpdateF, dx reinit --- BeefLibs/Beefy2D/src/BFApp.bf | 47 +- BeefLibs/Beefy2D/src/BFWindow.bf | 5 + .../Beefy2D/src/theme/dark/DarkListView.bf | 36 +- BeefLibs/Beefy2D/src/utils/SmoothValue.bf | 4 +- BeefLibs/Beefy2D/src/widgets/ListView.bf | 9 +- .../Beefy2D/src/widgets/ScrollableWidget.bf | 23 +- BeefLibs/Beefy2D/src/widgets/Widget.bf | 35 + BeefLibs/Beefy2D/src/widgets/WidgetWindow.bf | 12 + BeefLibs/corlib/src/GC.bf | 3 + BeefRT/dbg/gc.cpp | 24 +- BeefRT/dbg/gc.h | 8 +- BeefySysLib/BFApp.cpp | 210 +++-- BeefySysLib/BFApp.h | 18 +- BeefySysLib/BeefySysLib.cpp | 8 +- BeefySysLib/gfx/RenderDevice.cpp | 1 + BeefySysLib/gfx/RenderDevice.h | 13 +- BeefySysLib/platform/win/DXRenderDevice.cpp | 719 +++++++++++------- BeefySysLib/platform/win/DXRenderDevice.h | 44 +- BeefySysLib/platform/win/WinBFApp.cpp | 98 ++- BeefySysLib/platform/win/WinBFApp.h | 19 +- IDE/src/IDEApp.bf | 7 +- IDE/src/ui/AboutDialog.bf | 69 +- IDE/src/ui/SourceEditWidgetContent.bf | 19 +- IDE/src/ui/SourceViewPanel.bf | 13 +- 24 files changed, 1029 insertions(+), 415 deletions(-) diff --git a/BeefLibs/Beefy2D/src/BFApp.bf b/BeefLibs/Beefy2D/src/BFApp.bf index 2fd528e6..9159aeef 100644 --- a/BeefLibs/Beefy2D/src/BFApp.bf +++ b/BeefLibs/Beefy2D/src/BFApp.bf @@ -45,7 +45,8 @@ namespace Beefy #endif { public delegate void UpdateDelegate(bool batchStart); - public delegate void DrawDelegate(); + public delegate void UpdateFDelegate(float updatePct); + public delegate void DrawDelegate(bool forceDraw); public static BFApp sApp; public int32 mUpdateCnt; @@ -106,7 +107,7 @@ namespace Beefy static extern void BFApp_Shutdown(); [CallingConvention(.Stdcall), CLink] - static extern void BFApp_SetCallbacks(void* updateDelegate, void* drawDelegate); + static extern void BFApp_SetCallbacks(void* updateDelegate, void* updateFDelegate, void* drawDelegate); [CallingConvention(.Stdcall), CLink] static extern char8* BFApp_GetInstallDir(); @@ -132,7 +133,11 @@ namespace Beefy [CallingConvention(.Stdcall), CLink] public static extern void* BFApp_GetSoundManager(); + [CallingConvention(.Stdcall), CLink] + public static extern int BFApp_GetCriticalThreadId(int32 idx); + UpdateDelegate mUpdateDelegate ~ delete _; + UpdateFDelegate mUpdateFDelegate ~ delete _; DrawDelegate mDrawDelegate ~ delete _; #if STUDIO_CLIENT @@ -165,9 +170,9 @@ namespace Beefy } #endif - static void Static_Draw() + static void Static_Draw(bool forceDraw) { - sApp.Draw(); + sApp.Draw(forceDraw); } static void Static_Update(bool batchStart) @@ -175,6 +180,11 @@ namespace Beefy sApp.Update(batchStart); } + static void Static_UpdateF(float updatePct) + { + sApp.UpdateF(updatePct); + } + float mLastUpdateDelta; // In seconds public this() @@ -198,9 +208,10 @@ namespace Beefy BFApp_SetRefreshRate(mRefreshRate); mUpdateDelegate = new => Static_Update; - mDrawDelegate = new => Static_Draw; + mUpdateFDelegate = new => Static_UpdateF; + mDrawDelegate = new => Static_Draw; #endif - BFApp_SetCallbacks(mUpdateDelegate.GetFuncPtr(), mDrawDelegate.GetFuncPtr()); + BFApp_SetCallbacks(mUpdateDelegate.GetFuncPtr(), mUpdateFDelegate.GetFuncPtr(), mDrawDelegate.GetFuncPtr()); } #if STUDIO_CLIENT @@ -515,6 +526,14 @@ namespace Beefy structuredData.Load(resFileName); mResourceManager.ParseConfigData(structuredData); } + + for (int32 i = 0; true; i++) + { + int threadId = BFApp_GetCriticalThreadId(i); + if (threadId == 0) + break; + GC.ExcludeThreadId(threadId); + } } public void InitGraphics() @@ -671,6 +690,14 @@ namespace Beefy //Utils.BFRT_CPP("gBFGC.MutatorSectionExit()"); } + public virtual void UpdateF(float updatePct) + { + for (int32 windowIdx = 0; windowIdx < mWindows.Count; windowIdx++) + { + mWindows[windowIdx].UpdateF(updatePct); + } + } + public virtual void DoDraw() { } @@ -695,12 +722,11 @@ namespace Beefy } #endif - public virtual void Draw() + public virtual void Draw(bool forceDraw) { #if STUDIO_CLIENT -#endif - +#endif PerfTimer.ZoneStart("BFApp.Draw"); PerfTimer.Message("Client Draw Start"); @@ -716,7 +742,8 @@ namespace Beefy for (BFWindow window in mWindows) { - if ((window.mVisible) && ((window.mIsDirty) || (mAutoDirty))) + if ((window.mVisible) && + ((window.mIsDirty) || (mAutoDirty) || (forceDraw))) { window.PreDraw(mGraphics); if (mColorMatrix != null) diff --git a/BeefLibs/Beefy2D/src/BFWindow.bf b/BeefLibs/Beefy2D/src/BFWindow.bf index 42cd4de1..dab70ed6 100644 --- a/BeefLibs/Beefy2D/src/BFWindow.bf +++ b/BeefLibs/Beefy2D/src/BFWindow.bf @@ -664,6 +664,11 @@ namespace Beefy public virtual void Update() { } + + public virtual void UpdateF(float updatePct) + { + + } } #else public class BFWindow : BFWindowBase, IStudioClientWindow diff --git a/BeefLibs/Beefy2D/src/theme/dark/DarkListView.bf b/BeefLibs/Beefy2D/src/theme/dark/DarkListView.bf index f433f187..10def9f4 100644 --- a/BeefLibs/Beefy2D/src/theme/dark/DarkListView.bf +++ b/BeefLibs/Beefy2D/src/theme/dark/DarkListView.bf @@ -18,7 +18,12 @@ namespace Beefy.theme.dark public bool mIsOpen; public bool mAllowOpen = true; public bool mIsReversed; - + + public this() + { + mAlwaysUpdateF = true; + } + public override void Draw(Graphics g) { base.Draw(g); @@ -52,9 +57,9 @@ namespace Beefy.theme.dark mRot = mIsOpen ? (Math.PI_f / 2) : 0; } - public override void Update() + public override void UpdateF(float updatePct) { - base.Update(); + base.UpdateF(updatePct); int childCount = mItem.mChildItems.Count; @@ -62,15 +67,17 @@ namespace Beefy.theme.dark if ((mIsOpen) && (mRot < Math.PI_f / 2)) { - mRot = Math.Min(Math.PI_f / 2, mRot + rotSpeed); + mRot = Math.Min(Math.PI_f / 2, mRot + rotSpeed * updatePct); mItem.mListView.mListSizeDirty = true; MarkDirty(); + mWidgetWindow.mTempWantsUpdateF = true; } else if ((!mIsOpen) && (mRot > 0)) { - mRot = (float)Math.Max(0, mRot - rotSpeed); + mRot = (float)Math.Max(0, mRot - rotSpeed * updatePct); mItem.mListView.mListSizeDirty = true; MarkDirty(); + mWidgetWindow.mTempWantsUpdateF = true; } float x; @@ -876,6 +883,25 @@ namespace Beefy.theme.dark } } } + + public override void UpdateFAll(float updatePct) + { + if (mVisible) + { + base.UpdateFAll(updatePct); + + if (mChildItems != null) + { + for (int32 anIdx = 0; anIdx < mChildItems.Count; anIdx++) + { + Widget child = mChildItems[anIdx]; + Debug.Assert(child.mParent == this); + Debug.Assert(child.mWidgetWindow == mWidgetWindow); + child.UpdateFAll(updatePct); + } + } + } + } } public class DarkListView : ListView diff --git a/BeefLibs/Beefy2D/src/utils/SmoothValue.bf b/BeefLibs/Beefy2D/src/utils/SmoothValue.bf index bdebc408..4c47e4ce 100644 --- a/BeefLibs/Beefy2D/src/utils/SmoothValue.bf +++ b/BeefLibs/Beefy2D/src/utils/SmoothValue.bf @@ -37,9 +37,9 @@ namespace Beefy.utils get { return mPct != 1.0f; } } - public void Update() + public void Update(float updatePct = 1.0f) { - mPct = Math.Min(1.0f, mPct + mSpeed * mSpeedScale); + mPct = Math.Min(1.0f, mPct + mSpeed * mSpeedScale * updatePct); } public void Set(double val, bool immediate = false) diff --git a/BeefLibs/Beefy2D/src/widgets/ListView.bf b/BeefLibs/Beefy2D/src/widgets/ListView.bf index 3466b3e2..540238de 100644 --- a/BeefLibs/Beefy2D/src/widgets/ListView.bf +++ b/BeefLibs/Beefy2D/src/widgets/ListView.bf @@ -788,10 +788,15 @@ namespace Beefy.widgets public override void UpdateAll() { base.UpdateAll(); - UpdateListSize(); } + public override void UpdateFAll(float updatePct) + { + base.UpdateFAll(updatePct); + UpdateListSize(); + } + public virtual float GetListWidth() { float columnWidths = 0; @@ -802,7 +807,7 @@ namespace Beefy.widgets public void UpdateListSize() { - // Do this in UpdateAll to give children a change to resize items + // Do this in UpdateAll to give children a chance to resize items if (mListSizeDirty) { float listWidth = GetListWidth(); diff --git a/BeefLibs/Beefy2D/src/widgets/ScrollableWidget.bf b/BeefLibs/Beefy2D/src/widgets/ScrollableWidget.bf index e44f76e0..c6860053 100644 --- a/BeefLibs/Beefy2D/src/widgets/ScrollableWidget.bf +++ b/BeefLibs/Beefy2D/src/widgets/ScrollableWidget.bf @@ -50,6 +50,7 @@ namespace Beefy.widgets mScrollContentContainer.mClipGfx = true; mScrollContentContainer.mClipMouse = true; AddWidget(mScrollContentContainer); + mAlwaysUpdateF = true; } public ~this() @@ -248,15 +249,23 @@ namespace Beefy.widgets { base.Update(); - if ((mHorzPos.IsMoving) || (mVertPos.IsMoving)) - { - mHorzPos.Update(); - mVertPos.Update(); - UpdateContentPosition(); - MarkDirty(); - } + } + public override void UpdateF(float updatePct) + { + base.UpdateF(updatePct); + + if ((mHorzPos.IsMoving) || (mVertPos.IsMoving)) + { + mWidgetWindow.mTempWantsUpdateF = true; + mHorzPos.Update(updatePct); + mVertPos.Update(updatePct); + UpdateContentPosition(); + MarkDirty(); + } + } + public override void MouseWheel(float x, float y, float deltaX, float deltaY) { base.MouseWheel(x, y, deltaX, deltaY); diff --git a/BeefLibs/Beefy2D/src/widgets/Widget.bf b/BeefLibs/Beefy2D/src/widgets/Widget.bf index 2b48dd91..324f7911 100644 --- a/BeefLibs/Beefy2D/src/widgets/Widget.bf +++ b/BeefLibs/Beefy2D/src/widgets/Widget.bf @@ -39,6 +39,7 @@ namespace Beefy.widgets public float mWidth; public float mHeight; public int32 mUpdateCnt; + public double mUpdateCntF; public String mIdStr ~ delete _; public List mChildWidgets; public MouseFlag mMouseFlags; @@ -56,6 +57,7 @@ namespace Beefy.widgets public bool mDeferredDelete; public Insets mMouseInsets ~ delete _; public bool mAutoFocus; + public bool mAlwaysUpdateF; public float X { get { return mX; } set { mX = value; } } public float Y { get { return mY; } set { mY = value; } } @@ -455,8 +457,16 @@ namespace Beefy.widgets public virtual void Update() { mUpdateCnt++; + if ((mAlwaysUpdateF) && (mWidgetWindow != null)) + UpdateF((float)(mUpdateCnt - mUpdateCntF)); + mUpdateCntF = mUpdateCnt; } + public virtual void UpdateF(float updatePct) + { + mUpdateCntF += updatePct; + } + public void DeferDelete() { mDeferredDelete = true; @@ -487,6 +497,31 @@ namespace Beefy.widgets } } + public virtual void UpdateFAll(float updatePct) + { + UpdateF(updatePct); + if (mDeferredDelete) + { + delete this; + return; + } + + // Removed self? + if (mWidgetWindow == null) + return; + + if (mChildWidgets != null) + { + for (int32 anIdx = 0; anIdx < mChildWidgets.Count; anIdx++) + { + Widget child = mChildWidgets[anIdx]; + Debug.Assert(child.mParent == this); + Debug.Assert(child.mWidgetWindow == mWidgetWindow); + child.UpdateFAll(updatePct); + } + } + } + public virtual void Resize(float x, float y, float width, float height) { Debug.Assert(width >= 0); diff --git a/BeefLibs/Beefy2D/src/widgets/WidgetWindow.bf b/BeefLibs/Beefy2D/src/widgets/WidgetWindow.bf index f800a16e..b74d48b5 100644 --- a/BeefLibs/Beefy2D/src/widgets/WidgetWindow.bf +++ b/BeefLibs/Beefy2D/src/widgets/WidgetWindow.bf @@ -62,6 +62,8 @@ namespace Beefy.widgets public bool mHasMouseInside; public bool mHasProxyMouseInside; public bool mIsKeyDownHandled; + public bool mWantsUpdateF; + public bool mTempWantsUpdateF; public int32 mContentClientWidth; public int32 mContentClientHeight; @@ -153,8 +155,18 @@ namespace Beefy.widgets return; base.Update(); RehupMouse(false); + mTempWantsUpdateF = false; mRootWidget.UpdateAll(); } + + public override void UpdateF(float updatePct) + { + if (mRootWidget == null) + return; + base.Update(); + if (mWantsUpdateF || mTempWantsUpdateF) + mRootWidget.UpdateFAll(updatePct); + } public override int32 CloseQuery() { diff --git a/BeefLibs/corlib/src/GC.bf b/BeefLibs/corlib/src/GC.bf index 98055aa4..f8c0e176 100644 --- a/BeefLibs/corlib/src/GC.bf +++ b/BeefLibs/corlib/src/GC.bf @@ -133,6 +133,8 @@ namespace System public extern static void SetMaxPausePercentage(int maxPausePercentage); // 0 = disabled. Defaults to 20. [CallingConvention(.Cdecl)] extern static void AddPendingThread(void* internalThread); + [CallingConvention(.Cdecl)] + public extern static void ExcludeThreadId(int thereadId); #else public static void Collect(bool async = true) {} private static void MarkAllStaticMembers() {} @@ -144,6 +146,7 @@ namespace System public static void SetCollectFreeThreshold(int freeBytes) {} public static void SetMaxPausePercentage(int maxPausePercentage) {} static void AddPendingThread(void* internalThreadInfo) {} + public static void ExcludeThreadId(int thereadId) {} #endif static void MarkDerefedObject(Object* obj) diff --git a/BeefRT/dbg/gc.cpp b/BeefRT/dbg/gc.cpp index 4cd5ed47..82fba408 100644 --- a/BeefRT/dbg/gc.cpp +++ b/BeefRT/dbg/gc.cpp @@ -1429,6 +1429,9 @@ bool BFGC::ScanThreads() thread = mThreadList[threadIdx++]; } + if (thread->mExcluded) + continue; + if (!thread->mRunning) { AutoCrit autoCrit(mCritSect); @@ -2367,11 +2370,12 @@ void BFGC::SuspendThreads() auto curThreadId = GetCurrentThreadId(); for (auto thread : mThreadList) { - if ((thread->mThreadId != curThreadId) && (thread->mRunning) && (thread->WantsSuspend())) + if ((thread->mThreadId != curThreadId) && (!thread->mExcluded) && (thread->mRunning) && (thread->WantsSuspend())) { // We must lock this before suspending so we can access mStackMarkableObjects // Otherwise we could deadlock thread->mCritSect.Lock(); + thread->mSuspended = true; BfpThreadResult result; BfpThread_Suspend(thread->mThreadHandle, &result); @@ -2386,11 +2390,12 @@ void BFGC::ResumeThreads() auto curThreadId = GetCurrentThreadId(); for (auto thread : mThreadList) { - if ((thread->mThreadId != curThreadId) && (thread->mRunning) && (thread->WantsSuspend())) + if ((thread->mThreadId != curThreadId) && (thread->mSuspended) && (thread->mRunning) && (thread->WantsSuspend())) { // Previously locked in SuspendThreads thread->mCritSect.Unlock(); + thread->mSuspended = false; BfpThread_Resume(thread->mThreadHandle, NULL); } } @@ -2743,6 +2748,16 @@ void BFGC::SetMaxRawDeferredObjectFreePercentage(intptr maxPercentage) mMaxRawDeferredObjectFreePercentage = maxPercentage; } +void BFGC::ExcludeThreadId(intptr threadId) +{ + Beefy::AutoCrit autoCrit(mCritSect); + for (auto thread : mThreadList) + { + if (thread->mThreadId == threadId) + thread->mExcluded = true; + } +} + using namespace bf::System; void GC::Run() @@ -2832,6 +2847,11 @@ BFRT_EXPORT void bf::System::GC::SetMaxRawDeferredObjectFreePercentage(intptr ma gBFGC.SetMaxRawDeferredObjectFreePercentage(maxPercentage); } +BFRT_EXPORT void bf::System::GC::ExcludeThreadId(intptr threadId) +{ + gBFGC.ExcludeThreadId(threadId); +} + #else // BF_GC_SUPPORTED void* BfObjectAllocate(intptr size, bf::System::Type* type) diff --git a/BeefRT/dbg/gc.h b/BeefRT/dbg/gc.h index cc36270b..130605b6 100644 --- a/BeefRT/dbg/gc.h +++ b/BeefRT/dbg/gc.h @@ -181,6 +181,8 @@ public: intptr mStackStart; intptr mLastStackPtr; bool mRunning; + bool mExcluded; + bool mSuspended; Beefy::Array mStackMarkableObjects; ThreadInfo() @@ -192,6 +194,8 @@ public: mTEB = NULL; mStackStart = NULL; mRunning = true; + mExcluded = false; + mSuspended = false; } ~ThreadInfo(); @@ -424,6 +428,7 @@ public: void SetCollectFreeThreshold(int freeBytes); void SetMaxPausePercentage(int maxPausePercentage); void SetMaxRawDeferredObjectFreePercentage(intptr maxPercentage); + void ExcludeThreadId(intptr threadId); }; extern BFGC gBFGC; @@ -466,7 +471,7 @@ namespace bf BFRT_EXPORT static void StopCollecting(); BFRT_EXPORT static void AddStackMarkableObject(Object* obj); BFRT_EXPORT static void RemoveStackMarkableObject(Object* obj); - BFRT_EXPORT static void AddPendingThread(void* internalThreadInfo); + BFRT_EXPORT static void AddPendingThread(void* internalThreadInfo); public: BFRT_EXPORT static void Shutdown(); @@ -488,6 +493,7 @@ namespace bf BFRT_EXPORT static void SetCollectFreeThreshold(intptr freeBytes); BFRT_EXPORT static void SetMaxPausePercentage(intptr maxPausePercentage); BFRT_EXPORT static void SetMaxRawDeferredObjectFreePercentage(intptr maxPercentage); + BFRT_EXPORT static void ExcludeThreadId(intptr threadId); }; } } diff --git a/BeefySysLib/BFApp.cpp b/BeefySysLib/BFApp.cpp index 3405502b..40915237 100644 --- a/BeefySysLib/BFApp.cpp +++ b/BeefySysLib/BFApp.cpp @@ -19,10 +19,11 @@ BFApp::BFApp() mTitle = "Beefy Application"; mRefreshRate = 60; mLastProcessTick = BFTickCount(); - mFrameTimeAcc = 0; + mPhysFrameTimeAcc = 0; mDrawEnabled = true; mUpdateFunc = NULL; + mUpdateFFunc = NULL; mDrawFunc = NULL; gBFApp = this; @@ -43,6 +44,12 @@ BFApp::BFApp() mRunning = false; mRenderDevice = NULL; mVSynched = false; + mVSyncActive = false; + mForceNextDraw = false; + + mUpdateCnt = 0; + mUpdateCntF = 0; + mClientUpdateCntF = 0; } BFApp::~BFApp() @@ -90,10 +97,16 @@ void BFApp::Update(bool batchStart) mPendingWindowDeleteList.clear(); } +void BFApp::UpdateF(float updatePct) +{ + mUpdateFFunc(updatePct); +} + void BFApp::Draw() { gPerfManager->ZoneStart("BFApp::Draw"); - mDrawFunc(); + mDrawFunc(mForceNextDraw); + mForceNextDraw = false; gPerfManager->ZoneEnd(); } @@ -103,90 +116,88 @@ void BFApp::Process() { //Beefy::DebugTimeGuard suspendTimeGuard(30, "BFApp::Process"); + RenderWindow* headRenderWindow = NULL; + + float physRefreshRate = 0; + if (!mRenderDevice->mRenderWindowList.IsEmpty()) + { + headRenderWindow = mRenderDevice->mRenderWindowList[0]; + physRefreshRate = headRenderWindow->GetRefreshRate(); + } + + if (physRefreshRate <= 0) + physRefreshRate = 60.0f; + + float ticksPerFrame = 1; + float physTicksPerFrame = 1000.0f / physRefreshRate; + 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);*/ + + bool didVBlankWait = false; + + if (mVSyncActive) + { + // Have a time limit in the cases we miss the vblank + if (mVSyncEvent.WaitFor((int)(physTicksPerFrame + 1))) + didVBlankWait = true; } - else - updates = 1; // RefreshRate of 0 means to update as fast as possible + + if (mRefreshRate > 0) + 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; + } + + mPhysFrameTimeAcc += tickNow - mLastProcessTick; + + if (didVBlankWait) + { + // Try to keep time synced with vblank + if (mPhysFrameTimeAcc < physTicksPerFrame * 2) + { + float timeAdjust = physTicksPerFrame - mPhysFrameTimeAcc + 0.001f; + mPhysFrameTimeAcc += timeAdjust; + } + } + + /*if (updates > 2) + OutputDebugStrF("Updates: %d TickDelta: %d\n", updates, tickNow - mLastProcessTick);*/ - if (updates == 0) - { - // Yield - BfpThread_Sleep(1); - } + // 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); + + while (mPhysFrameTimeAcc >= physTicksPerFrame) + { + mPhysFrameTimeAcc -= physTicksPerFrame; + mUpdateCntF += physTicksPerFrame / ticksPerFrame; + } static uint32 lastUpdate = BFTickCount(); @@ -202,17 +213,46 @@ void BFApp::Process() } #endif - if (updates > 0) + + int didUpdateCnt = 0; + + if (mClientUpdateCntF - mUpdateCntF > physRefreshRate / 2) + { + // Too large of a difference, just sync + mClientUpdateCntF = mUpdateCntF - 1; + } + + while ((int)mClientUpdateCntF < (int)mUpdateCntF) + { + Update(didUpdateCnt == 0); + didUpdateCnt++; + mClientUpdateCntF = (int)mClientUpdateCntF + 1.000001; + if (didUpdateCnt >= maxUpdates) + break; + } + + // Only attempt UpdateF updates if our rates aren't nearl) the same + if ((mRefreshRate != 0) && (fabs(physRefreshRate - mRefreshRate) / (float)mRefreshRate > 0.1f)) + { + float updateFAmt = (float)(mUpdateCntF - mClientUpdateCntF); + if ((updateFAmt > 0.05f) && (updateFAmt < 1.0f) && (didUpdateCnt < maxUpdates)) + { + UpdateF(updateFAmt); + didUpdateCnt++; + mClientUpdateCntF = mUpdateCntF; + } + } + + if (didUpdateCnt > 0) mNumPhysUpdates++; - for (int updateNum = 0; updateNum < updates; updateNum++) + if ((mRunning) && (didUpdateCnt == 0)) { - if (!mRunning) - break; - Update(updateNum == 0); + BfpThread_Sleep(1); } - if ((mRunning) && (updates > 0)) + if ((mRunning) && + ((didUpdateCnt != 0) || (mForceNextDraw))) Draw(); #ifdef PERIODIC_PERF_TIMING @@ -229,6 +269,8 @@ void BFApp::Process() void BFApp::RemoveWindow(BFWindow* window) { + AutoCrit autoCrit(mCritSect); + auto itr = std::find(mWindowList.begin(), mWindowList.end(), window); if (itr == mWindowList.end()) // Allow benign failure (double removal) return; diff --git a/BeefySysLib/BFApp.h b/BeefySysLib/BFApp.h index 2498da12..7de83fbd 100644 --- a/BeefySysLib/BFApp.h +++ b/BeefySysLib/BFApp.h @@ -1,12 +1,14 @@ #pragma once #include "Common.h" +#include "util/CritSect.h" #include NS_BF_BEGIN; typedef void (*BFApp_UpdateFunc)(bool batchStart); -typedef void (*BFApp_DrawFunc)(); +typedef void (*BFApp_UpdateFFunc)(float updatePct); +typedef void (*BFApp_DrawFunc)(bool forceDraw); class BFApp; class BFSoundManager; @@ -50,12 +52,15 @@ public: class BFApp { public: + CritSect mCritSect; String mTitle; String mInstallDir; String mDataDir; bool mDrawEnabled; float mRefreshRate; - int mMaxUpdatesPerDraw; + int mMaxUpdatesPerDraw; + double mUpdateCntF; + double mClientUpdateCntF; bool mInProcess; bool mRunning; @@ -63,16 +68,20 @@ public: int mSysDialogCnt; int mUpdateCnt; int mNumPhysUpdates; + SyncEvent mVSyncEvent; + volatile bool mVSyncActive; bool mVSynched; bool mVSyncFailed; + bool mForceNextDraw; int mUpdateSampleCount; int mUpdateSampleTimes; uint32 mLastProcessTick; - float mFrameTimeAcc; + float mPhysFrameTimeAcc; BFApp_UpdateFunc mUpdateFunc; + BFApp_UpdateFFunc mUpdateFFunc; BFApp_DrawFunc mDrawFunc; int mCursor; @@ -81,6 +90,7 @@ public: public: virtual void Update(bool batchStart); + virtual void UpdateF(float updatePct); virtual void Draw(); virtual void Process(); virtual void PhysSetCursor() = 0; @@ -114,6 +124,8 @@ public: virtual FileStream* OpenBinaryFile(const StringImpl& fileName); virtual BFSoundManager* GetSoundManager() { return NULL; } + + virtual intptr GetCriticalThreadId(int idx) { return 0; } }; extern BFApp* gBFApp; diff --git a/BeefySysLib/BeefySysLib.cpp b/BeefySysLib/BeefySysLib.cpp index 6abda980..b65f4bb7 100644 --- a/BeefySysLib/BeefySysLib.cpp +++ b/BeefySysLib/BeefySysLib.cpp @@ -210,9 +210,10 @@ BF_EXPORT const char* BF_CALLTYPE BFApp_GetDataDir() return gBFApp->mDataDir.c_str(); } -BF_EXPORT void BF_CALLTYPE BFApp_SetCallbacks(BFApp_UpdateFunc updateFunc, BFApp_DrawFunc drawFunc) +BF_EXPORT void BF_CALLTYPE BFApp_SetCallbacks(BFApp_UpdateFunc updateFunc, BFApp_UpdateFFunc updateFFunc, BFApp_DrawFunc drawFunc) { gBFApp->mUpdateFunc = updateFunc; + gBFApp->mUpdateFFunc = updateFFunc; gBFApp->mDrawFunc = drawFunc; //public delegate void UpdateProc(); } @@ -276,6 +277,11 @@ BF_EXPORT BFSoundManager* BF_CALLTYPE BFApp_GetSoundManager() return gBFApp->GetSoundManager(); } +BF_EXPORT intptr BF_CALLTYPE BFApp_GetCriticalThreadId(int idx) +{ + return gBFApp->GetCriticalThreadId(idx); +} + /// BF_EXPORT void BF_CALLTYPE BFWindow_SetCallbacks(BFWindow* window, BFWindow_MovedFunc movedFunc, BFWindow_CloseQueryFunc closeQueryFunc, BFWindow_ClosedFunc closedFunc, diff --git a/BeefySysLib/gfx/RenderDevice.cpp b/BeefySysLib/gfx/RenderDevice.cpp index 61af4300..be628982 100644 --- a/BeefySysLib/gfx/RenderDevice.cpp +++ b/BeefySysLib/gfx/RenderDevice.cpp @@ -62,6 +62,7 @@ RenderDevice::RenderDevice() : mCurRenderTarget = NULL; mCurDrawLayer = NULL; mPhysRenderWindow = NULL; + mApp = NULL; } RenderDevice::~RenderDevice() diff --git a/BeefySysLib/gfx/RenderDevice.h b/BeefySysLib/gfx/RenderDevice.h index 1333aaef..c189fbdb 100644 --- a/BeefySysLib/gfx/RenderDevice.h +++ b/BeefySysLib/gfx/RenderDevice.h @@ -76,6 +76,8 @@ public: virtual void SetAsTarget() = 0; virtual void Resized() = 0; virtual void Present() = 0; + virtual float GetRefreshRate() { return 60.0f; } + virtual bool WaitForVBlank() { return false; } }; const int DRAWBUFFER_IDXBUFFER_SIZE = 8*1024; @@ -175,6 +177,14 @@ public: mElementData = NULL; mNumElements = 0; } + + VertexDefinition(VertexDefinition* src) + { + mElementData = new VertexDefData[src->mNumElements]; + mNumElements = src->mNumElements; + memcpy(mElementData, src->mElementData, sizeof(VertexDefData) * mNumElements); + } + virtual ~VertexDefinition() { delete [] mElementData; @@ -262,6 +272,7 @@ class RenderDevice public: Array mDrawBatchPool; + BFApp* mApp; RenderWindow* mPhysRenderWindow; RenderState* mPhysRenderState; int mResizeCount; @@ -283,7 +294,7 @@ public: public: RenderDevice(); virtual ~RenderDevice(); - virtual bool Init(BFApp* app) = 0; + virtual bool Init(BFApp* app) = 0; virtual void AddRenderWindow(RenderWindow* renderWindow); virtual void RemoveRenderWindow(RenderWindow* renderWindow); diff --git a/BeefySysLib/platform/win/DXRenderDevice.cpp b/BeefySysLib/platform/win/DXRenderDevice.cpp index 65bc0059..123b2531 100644 --- a/BeefySysLib/platform/win/DXRenderDevice.cpp +++ b/BeefySysLib/platform/win/DXRenderDevice.cpp @@ -18,6 +18,7 @@ using namespace DirectX; //#include //#include //#include +#include #pragma warning(pop) #include "util/AllocDebug.h" @@ -192,6 +193,7 @@ void DXShaderParam::SetFloat4(float x, float y, float z, float w) DXShader::DXShader() { //? mD3DEffect = NULL; + mVertexDef = NULL; mD3DPixelShader = NULL; mD3DVertexShader = NULL; mD3DLayout = NULL; @@ -201,25 +203,235 @@ DXShader::DXShader() DXShader::~DXShader() { - DXShaderParamMap::iterator itr = mParamsMap.begin(); - while (itr != mParamsMap.end()) - { - delete itr->second; - ++itr; - } - if (mD3DLayout != NULL) - mD3DLayout->Release(); - if (mD3DVertexShader != NULL) - mD3DVertexShader->Release(); - if (mD3DPixelShader != NULL) - mD3DPixelShader->Release(); - if (mConstBuffer != NULL) - mConstBuffer->Release(); - + delete mVertexDef; + ReleaseNative(); + //? if (mD3DEffect != NULL) //? mD3DEffect->Release(); } +void DXShader::ReleaseNative() +{ + if (mD3DLayout != NULL) + mD3DLayout->Release(); + mD3DLayout = NULL; + if (mD3DVertexShader != NULL) + mD3DVertexShader->Release(); + mD3DVertexShader = NULL; + if (mD3DPixelShader != NULL) + mD3DPixelShader->Release(); + mD3DPixelShader = NULL; + if (mConstBuffer != NULL) + mConstBuffer->Release(); + mConstBuffer = NULL; +} + +extern "C" +typedef HRESULT(WINAPI* Func_D3DX10CompileFromFileW)(LPCWSTR pSrcFile, CONST D3D10_SHADER_MACRO* pDefines, LPD3D10INCLUDE pInclude, + LPCSTR pFunctionName, LPCSTR pProfile, UINT Flags1, UINT Flags2, ID3D10Blob** ppShader, ID3D10Blob** ppErrorMsgs); + +static Func_D3DX10CompileFromFileW gFunc_D3DX10CompileFromFileW; + +static bool LoadDXShader(const StringImpl& filePath, const StringImpl& entry, const StringImpl& profile, ID3D10Blob** outBuffer) +{ + HRESULT hr; + String outObj = filePath + "_" + entry + "_" + profile; + + bool useCache = false; + auto srcDate = ::BfpFile_GetTime_LastWrite(filePath.c_str()); + auto cacheDate = ::BfpFile_GetTime_LastWrite(outObj.c_str()); + if (cacheDate >= srcDate) + useCache = true; + + if (!useCache) + { + if (gFunc_D3DX10CompileFromFileW == NULL) + { + auto lib = LoadLibraryA("D3DCompiler_47.dll"); + if (lib != NULL) + gFunc_D3DX10CompileFromFileW = (Func_D3DX10CompileFromFileW)::GetProcAddress(lib, "D3DCompileFromFile"); + } + + if (gFunc_D3DX10CompileFromFileW == NULL) + useCache = true; + } + + if (!useCache) + { + if (gFunc_D3DX10CompileFromFileW == NULL) + { + auto lib = LoadLibraryA("D3DCompiler_47.dll"); + if (lib != NULL) + gFunc_D3DX10CompileFromFileW = (Func_D3DX10CompileFromFileW)::GetProcAddress(lib, "D3DCompileFromFile"); + } + + ID3D10Blob* errorMessage = NULL; + auto dxResult = gFunc_D3DX10CompileFromFileW(UTF8Decode(filePath).c_str(), NULL, NULL, entry.c_str(), profile.c_str(), + D3D10_SHADER_DEBUG | D3D10_SHADER_ENABLE_STRICTNESS, 0, outBuffer, &errorMessage); + + if (DXFAILED(dxResult)) + { + if (errorMessage != NULL) + { + BF_FATAL(StrFormat("Vertex shader load failed: %s", (char*)errorMessage->GetBufferPointer()).c_str()); + errorMessage->Release(); + } + else + BF_FATAL("Shader load failed"); + return false; + } + + auto ptr = (*outBuffer)->GetBufferPointer(); + int size = (int)(*outBuffer)->GetBufferSize(); + + FILE* fp = fopen(outObj.c_str(), "wb"); + if (fp != NULL) + { + fwrite(ptr, 1, size, fp); + fclose(fp); + } + return true; + } + + FILE* fp = fopen(outObj.c_str(), "rb"); + if (fp == NULL) + { + BF_FATAL("Failed to load compiled shader"); + return false; + } + + fseek(fp, 0, SEEK_END); + int size = ftell(fp); + fseek(fp, 0, SEEK_SET); + D3D10CreateBlob(size, outBuffer); + auto ptr = (*outBuffer)->GetBufferPointer(); + fread(ptr, 1, size, fp); + fclose(fp); + + return true; +} + +bool DXShader::Load() +{ + //HRESULT hr; + + ID3D10Blob* errorMessage = NULL; + ID3D10Blob* vertexShaderBuffer = NULL; + ID3D10Blob* pixelShaderBuffer = NULL; + + LoadDXShader(mSrcPath + ".fx", "VS", "vs_4_0", &vertexShaderBuffer); + LoadDXShader(mSrcPath + ".fx", "PS", "ps_4_0", &pixelShaderBuffer); + + defer( + { + vertexShaderBuffer->Release(); + pixelShaderBuffer->Release(); + }); + + mHas2DPosition = false; + mVertexSize = 0; + mD3DLayout = NULL; + + static const char* semanticNames[] = { + "POSITION", + "POSITION", + "COLOR", + "TEXCOORD", + "NORMAL", + "BINORMAL", + "TANGENT", + "BLENDINDICES", + "BLENDWEIGHT", + "DEPTH", + "FOG", + "POINTSIZE", + "SAMPLE", + "TESSELLATEFACTOR" }; + + static const DXGI_FORMAT dxgiFormat[] = { + DXGI_FORMAT_R32_FLOAT/*VertexElementFormat_Single*/, + DXGI_FORMAT_R32G32_FLOAT/*VertexElementFormat_Vector2*/, + DXGI_FORMAT_R32G32B32_FLOAT/*VertexElementFormat_Vector3*/, + DXGI_FORMAT_R32G32B32A32_FLOAT/*VertexElementFormat_Vector4*/, + DXGI_FORMAT_R8G8B8A8_UNORM/*VertexElementFormat_Color*/, + DXGI_FORMAT_R8G8B8A8_UINT/*VertexElementFormat_Byte4*/, + DXGI_FORMAT_R16G16_UINT/*VertexElementFormat_Short2*/, + DXGI_FORMAT_R16G16B16A16_UINT/*VertexElementFormat_Short4*/, + DXGI_FORMAT_R16G16_UNORM/*VertexElementFormat_NormalizedShort2*/, + DXGI_FORMAT_R16G16B16A16_UNORM/*VertexElementFormat_NormalizedShort4*/, + DXGI_FORMAT_R16G16_FLOAT/*VertexElementFormat_HalfVector2*/, + DXGI_FORMAT_R16G16B16A16_FLOAT/*VertexElementFormat_HalfVector4*/ + }; + + static const int dxgiSize[] = { + sizeof(float) * 1/*VertexElementFormat_Single*/, + sizeof(float) * 2/*VertexElementFormat_Vector2*/, + sizeof(float) * 3/*VertexElementFormat_Vector3*/, + sizeof(float) * 4/*VertexElementFormat_Vector4*/, + sizeof(uint32)/*VertexElementFormat_Color*/, + sizeof(uint8) * 4/*VertexElementFormat_Byte4*/, + sizeof(uint16) * 2/*VertexElementFormat_Short2*/, + sizeof(uint16) * 4/*VertexElementFormat_Short4*/, + sizeof(uint16) * 2/*VertexElementFormat_NormalizedShort2*/, + sizeof(uint16) * 4/*VertexElementFormat_NormalizedShort4*/, + sizeof(uint16) * 2/*VertexElementFormat_HalfVector2*/, + sizeof(uint16) * 4/*VertexElementFormat_HalfVector4*/ + }; + + D3D11_INPUT_ELEMENT_DESC layout[64]; + for (int elementIdx = 0; elementIdx < mVertexDef->mNumElements; elementIdx++) + { + VertexDefData* vertexDefData = &mVertexDef->mElementData[elementIdx]; + + if (vertexDefData->mUsage == VertexElementUsage_Position2D) + mHas2DPosition = true; + + D3D11_INPUT_ELEMENT_DESC* elementDesc = &layout[elementIdx]; + elementDesc->SemanticName = semanticNames[vertexDefData->mUsage]; + elementDesc->SemanticIndex = vertexDefData->mUsageIndex; + elementDesc->Format = dxgiFormat[vertexDefData->mFormat]; + elementDesc->InputSlot = 0; + elementDesc->AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT; + elementDesc->InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + elementDesc->InstanceDataStepRate = 0; + mVertexSize += dxgiSize[vertexDefData->mFormat]; + } + + /* = + { + { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 20, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + }; + UINT numElements = sizeof(layout) / sizeof(layout[0]);*/ + HRESULT result = mRenderDevice->mD3DDevice->CreateInputLayout(layout, mVertexDef->mNumElements, vertexShaderBuffer->GetBufferPointer(), + vertexShaderBuffer->GetBufferSize(), &mD3DLayout); + DXCHECK(result); + if (FAILED(result)) + return false; + + // Create the vertex shader from the buffer. + result = mRenderDevice->mD3DDevice->CreateVertexShader(vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), NULL, &mD3DVertexShader); + DXCHECK(result); + if (FAILED(result)) + return false; + + // Create the pixel shader from the buffer. + result = mRenderDevice->mD3DDevice->CreatePixelShader(pixelShaderBuffer->GetBufferPointer(), pixelShaderBuffer->GetBufferSize(), NULL, &mD3DPixelShader); + DXCHECK(result); + if (FAILED(result)) + return false; + + Init(); + return true; +} + +void DXShader::ReinitNative() +{ + ReleaseNative(); + Load(); +} + ShaderParam* DXShader::GetShaderParam(const StringImpl& name) { DXShaderParamMap::iterator itr = mParamsMap.find(name); @@ -248,8 +460,8 @@ DXTexture::DXTexture() mD3DRenderTargetView = NULL; mRenderDevice = NULL; mD3DDepthBuffer = NULL; - mD3DDepthStencilView = NULL; - mImageData = NULL; + mD3DDepthStencilView = NULL; + mContentBits = NULL; } DXTexture::~DXTexture() @@ -258,7 +470,7 @@ DXTexture::~DXTexture() ((DXRenderDevice*)mRenderDevice)->mTextureMap.Remove(mPath); //OutputDebugStrF("DXTexture::~DXTexture %@\n", this); - delete mImageData; + delete mContentBits; if (mD3DResourceView != NULL) mD3DResourceView->Release(); if (mD3DDepthStencilView != NULL) @@ -267,6 +479,8 @@ DXTexture::~DXTexture() mD3DDepthBuffer->Release(); if (mD3DTexture != NULL) mD3DTexture->Release(); + if (mRenderDevice != NULL) + mRenderDevice->mTextures.Remove(this); } void DXTexture::ReleaseNative() @@ -297,6 +511,44 @@ void DXTexture::ReleaseNative() void DXTexture::ReinitNative() { + ReleaseNative(); + + int aWidth = 0; + int aHeight = 0; + + D3D11_SUBRESOURCE_DATA resData; + resData.pSysMem = mContentBits; + resData.SysMemPitch = mWidth * 4; + resData.SysMemSlicePitch = mWidth * mHeight * 4; + + // Create the target texture + D3D11_TEXTURE2D_DESC desc; + ZeroMemory(&desc, sizeof(desc)); + desc.Width = mWidth; + desc.Height = mHeight; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + desc.SampleDesc.Count = 1; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.CPUAccessFlags = 0; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + + //OutputDebugStrF("Creating texture\n"); + + auto dxRenderDevice = (DXRenderDevice*)mRenderDevice; + + DXCHECK(dxRenderDevice->mD3DDevice->CreateTexture2D(&desc, (mContentBits != NULL) ? &resData : NULL, &mD3DTexture)); + + D3D11_SHADER_RESOURCE_VIEW_DESC srDesc; + srDesc.Format = desc.Format; + srDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + srDesc.Texture2D.MostDetailedMip = 0; + srDesc.Texture2D.MipLevels = 1; + + DXCHECK(dxRenderDevice->mD3DDevice->CreateShaderResourceView(mD3DTexture, &srDesc, &mD3DResourceView)); + + OutputDebugStrF("DXTexture::ReinitNative %p\n", this); } void DXTexture::PhysSetAsTarget() @@ -338,12 +590,24 @@ void DXTexture::Blt(ImageData* imageData, int x, int y) box.front = 0; box.back = 1; mRenderDevice->mD3DDeviceContext->UpdateSubresource(mD3DTexture, 0, &box, imageData->mBits, imageData->mWidth * sizeof(uint32), 0); + + if (mContentBits != NULL) + { + for (int yOfs = 0; yOfs < imageData->mHeight; yOfs++) + memcpy(mContentBits + x + (y + yOfs) * mWidth, imageData->mBits + yOfs * imageData->mWidth, imageData->mWidth * 4); + } } void DXTexture::SetBits(int destX, int destY, int destWidth, int destHeight, int srcPitch, uint32* bits) { D3D11_BOX box = { (UINT)destX, (UINT)destY, (UINT)0, (UINT)(destX + destWidth), (UINT)(destY + destHeight), 1 }; mRenderDevice->mD3DDeviceContext->UpdateSubresource(mD3DTexture, 0, &box, bits, srcPitch * sizeof(uint32), 0); + + if (mContentBits != NULL) + { + for (int y = 0; y < destHeight; y++) + memcpy(mContentBits + destX + (destY + y) * mWidth, bits + (y * srcPitch), destWidth * 4); + } } void DXTexture::GetBits(int srcX, int srcY, int srcWidth, int srcHeight, int destPitch, uint32* bits) @@ -367,7 +631,7 @@ void DXTexture::GetBits(int srcX, int srcY, int srcWidth, int srcHeight, int des srcBox.top = srcY; srcBox.right = srcX + srcWidth; srcBox.bottom = srcY + srcHeight; - srcBox.back = 1; + srcBox.back = 1; ID3D11Texture2D *texture; DXCHECK(mRenderDevice->mD3DDevice->CreateTexture2D(&texDesc, 0, &texture)); @@ -532,20 +796,18 @@ void DXRenderDevice::PhysSetRenderState(RenderState* renderState) { HRESULT result = NULL; - D3D11_BUFFER_DESC matrixBufferDesc; - matrixBufferDesc.Usage = D3D11_USAGE_DYNAMIC; - matrixBufferDesc.ByteWidth = sizeof(float[4]); - matrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; - matrixBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; - matrixBufferDesc.MiscFlags = 0; - matrixBufferDesc.StructureByteStride = 0; - - static ID3D11Buffer* matrixBuffer = NULL; - - if (matrixBuffer == NULL) + if (mMatrix2DBuffer == NULL) { + D3D11_BUFFER_DESC matrixBufferDesc; + matrixBufferDesc.Usage = D3D11_USAGE_DYNAMIC; + matrixBufferDesc.ByteWidth = sizeof(float[4]); + matrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + matrixBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + matrixBufferDesc.MiscFlags = 0; + matrixBufferDesc.StructureByteStride = 0; + // Create the constant buffer pointer so we can access the vertex shader constant buffer from within this class. - result = mD3DDevice->CreateBuffer(&matrixBufferDesc, NULL, &matrixBuffer); + result = mD3DDevice->CreateBuffer(&matrixBufferDesc, NULL, &mMatrix2DBuffer); if (FAILED(result)) { return; @@ -554,7 +816,7 @@ void DXRenderDevice::PhysSetRenderState(RenderState* renderState) // Lock the constant buffer so it can be written to. D3D11_MAPPED_SUBRESOURCE mappedResource; - result = mD3DDeviceContext->Map(matrixBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource); + result = mD3DDeviceContext->Map(mMatrix2DBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource); if (FAILED(result)) { return; @@ -568,10 +830,10 @@ void DXRenderDevice::PhysSetRenderState(RenderState* renderState) dataPtr[3] = 0; // Unlock the constant buffer. - mD3DDeviceContext->Unmap(matrixBuffer, 0); + mD3DDeviceContext->Unmap(mMatrix2DBuffer, 0); //float params[4] = {mCurRenderTarget->mWidth, mCurRenderTarget->mHeight, 0, 0}; - mD3DDeviceContext->VSSetConstantBuffers(0, 1, &matrixBuffer); + mD3DDeviceContext->VSSetConstantBuffers(0, 1, &mMatrix2DBuffer); } } @@ -1003,7 +1265,7 @@ void DXRenderState::ReleaseNative() void DXRenderState::ReinitNative() { - + ReleaseNative(); } void DXRenderState::InvalidateRasterizerState() @@ -1264,6 +1526,8 @@ DXRenderWindow::DXRenderWindow(DXRenderDevice* renderDevice, WinBFWindow* window mD3DRenderTargetView = NULL; mD3DDepthBuffer = NULL; mD3DDepthStencilView = NULL; + mRefreshRate = 0; + mFrameWaitObject = NULL; mRenderDevice = renderDevice; mDXRenderDevice = renderDevice; @@ -1275,30 +1539,31 @@ DXRenderWindow::DXRenderWindow(DXRenderDevice* renderDevice, WinBFWindow* window ReinitNative(); } - DXRenderWindow::~DXRenderWindow() { ReleaseNative(); } - void DXRenderWindow::ReleaseNative() { - if (mD3DRenderTargetView != NULL) - { + if (mFrameWaitObject != NULL) + ::CloseHandle(mFrameWaitObject); + mFrameWaitObject = NULL; + if (mD3DRenderTargetView != NULL) mD3DRenderTargetView->Release(); - mD3DRenderTargetView = NULL; - } - if (mD3DBackBuffer != NULL) - { + mD3DRenderTargetView = NULL; + if (mD3DBackBuffer != NULL) mD3DBackBuffer->Release(); - mD3DBackBuffer = NULL; - } - if (mDXSwapChain != NULL) - { + mD3DBackBuffer = NULL; + if (mDXSwapChain != NULL) mDXSwapChain->Release(); - mDXSwapChain = NULL; - } + mDXSwapChain = NULL; + if (mD3DRenderTargetView != NULL) + mD3DRenderTargetView->Release(); + mD3DRenderTargetView = NULL; + if (mD3DDepthStencilView != NULL) + mD3DDepthStencilView->Release(); + mD3DDepthStencilView = NULL; } void DXRenderWindow::ReinitNative() @@ -1314,8 +1579,8 @@ void DXRenderWindow::ReinitNative() swapChainDesc.SampleDesc.Count = 1; swapChainDesc.SampleDesc.Quality = 0; swapChainDesc.Windowed = mWindowed ? TRUE : FALSE; - swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; - swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;// DXGI_SWAP_EFFECT_FLIP_DISCARD; + swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH /*| DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT*/; IDXGIDevice* pDXGIDevice = NULL; mDXRenderDevice->mD3DDevice->QueryInterface(__uuidof(IDXGIDevice), (void**)&pDXGIDevice); @@ -1324,6 +1589,14 @@ void DXRenderWindow::ReinitNative() pDXGIDevice->Release(); pDXGIDevice = NULL; +// IDXGISwapChain2* swapChain2 = NULL; +// mDXSwapChain->QueryInterface(__uuidof(IDXGISwapChain2), (void**)&swapChain2); +// if (swapChain2 != NULL) +// { +// mFrameWaitObject = swapChain2->GetFrameLatencyWaitableObject(); +// swapChain2->Release(); +// } + DXCHECK(mDXSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&mD3DBackBuffer)); DXCHECK(mDXRenderDevice->mD3DDevice->CreateRenderTargetView(mD3DBackBuffer, NULL, &mD3DRenderTargetView)); @@ -1406,7 +1679,14 @@ void DXRenderWindow::Resized() mD3DDepthBuffer->Release(); mD3DRenderTargetView->Release(); mD3DDepthStencilView->Release(); - DXCHECK(mDXSwapChain->ResizeBuffers(0, mWidth, mHeight, DXGI_FORMAT_UNKNOWN, 0)); + + HRESULT hr = mDXSwapChain->ResizeBuffers(0, mWidth, mHeight, DXGI_FORMAT_UNKNOWN, + DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH /*| DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT*/); + + if ((hr == DXGI_ERROR_DEVICE_REMOVED) || (hr == DXGI_ERROR_DEVICE_RESET)) + ((DXRenderDevice*)mRenderDevice)->mNeedsReinitNative = true; + else + DXCHECK(hr); D3D11_TEXTURE2D_DESC descDepth; ZeroMemory(&descDepth, sizeof(descDepth)); @@ -1435,8 +1715,10 @@ void DXRenderWindow::Resized() void DXRenderWindow::Present() { - mDXSwapChain->Present((mWindow->mFlags & BFWINDOW_VSYNC) ? 1 : 0, 0); - //DXCHECK(); + HRESULT hr = mDXSwapChain->Present((mWindow->mFlags & BFWINDOW_VSYNC) ? 1 : 0, 0); + + if ((hr == DXGI_ERROR_DEVICE_REMOVED) || (hr == DXGI_ERROR_DEVICE_RESET)) + ((DXRenderDevice*)mRenderDevice)->mNeedsReinitNative = true; } void DXRenderWindow::CopyBitsTo(uint32* dest, int width, int height) @@ -1476,28 +1758,110 @@ void DXRenderWindow::CopyBitsTo(uint32* dest, int width, int height) texture->Release(); } +float DXRenderWindow::GetRefreshRate() +{ + if (mRefreshRate == 0) + { + mRefreshRate = -1; + + IDXGIOutput* output = NULL; + mDXSwapChain->GetContainingOutput(&output); + if (output != NULL) + { + DXGI_OUTPUT_DESC outputDesc; + output->GetDesc(&outputDesc); + + MONITORINFOEXW info; + info.cbSize = sizeof(info); + // get the associated monitor info + if (GetMonitorInfoW(outputDesc.Monitor, &info) != 0) + { + // using the CCD get the associated path and display configuration + UINT32 requiredPaths, requiredModes; + if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &requiredPaths, &requiredModes) == ERROR_SUCCESS) + { + std::vector paths(requiredPaths); + std::vector modes2(requiredModes); + if (QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &requiredPaths, paths.data(), &requiredModes, modes2.data(), nullptr) == ERROR_SUCCESS) + { + // iterate through all the paths until find the exact source to match + for (auto& p : paths) + { + DISPLAYCONFIG_SOURCE_DEVICE_NAME sourceName; + sourceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME; + sourceName.header.size = sizeof(sourceName); + sourceName.header.adapterId = p.sourceInfo.adapterId; + sourceName.header.id = p.sourceInfo.id; + if (DisplayConfigGetDeviceInfo(&sourceName.header) == ERROR_SUCCESS) + { + // find the matched device which is associated with current device + // there may be the possibility that display may be duplicated and windows may be one of them in such scenario + // there may be two callback because source is same target will be different + // as window is on both the display so either selecting either one is ok + if (wcscmp(info.szDevice, sourceName.viewGdiDeviceName) == 0) + { + // get the refresh rate + UINT numerator = p.targetInfo.refreshRate.Numerator; + UINT denominator = p.targetInfo.refreshRate.Denominator; + mRefreshRate = (float)numerator / (float)denominator; + break; + } + } + } + } + } + } + + output->Release(); + } + } + + return mRefreshRate; +} + +bool DXRenderWindow::WaitForVBlank() +{ + IDXGIOutput* output = NULL; + mDXSwapChain->GetContainingOutput(&output); + if (output == NULL) + return false; + bool success = output->WaitForVBlank() == 0; + return success; +} + /// DXRenderDevice::DXRenderDevice() { - mD3DDevice = NULL; - + mD3DDevice = NULL; + mNeedsReinitNative = false; + mMatrix2DBuffer = NULL; } DXRenderDevice::~DXRenderDevice() { - for (auto& kv : mTextureMap) - kv.mValue->mRenderDevice = NULL; + for (auto window : mRenderWindowList) + ((DXRenderWindow*)window)->ReleaseNative(); + for (auto shader : mShaders) + shader->ReleaseNative(); + for (auto renderState : mRenderStates) + renderState->ReleaseNative(); + for (auto texture : mTextures) + { + texture->ReleaseNative(); + texture->mRenderDevice = NULL; + } - mD3DVertexBuffer->Release(); - mD3DIndexBuffer->Release(); - delete mDefaultRenderState; + ReleaseNative(); + + delete mDefaultRenderState; } bool DXRenderDevice::Init(BFApp* app) { BP_ZONE("DXRenderDevice::Init"); + mApp = app; WinBFApp* winApp = (WinBFApp*) app; D3D_FEATURE_LEVEL featureLevelArr[] = @@ -1626,6 +1990,9 @@ void DXRenderDevice::ReleaseNative() { mD3DVertexBuffer->Release(); mD3DVertexBuffer = NULL; + if (mMatrix2DBuffer != NULL) + mMatrix2DBuffer->Release(); + mMatrix2DBuffer = NULL; mD3DIndexBuffer->Release(); mD3DIndexBuffer = NULL; mD3DNormalBlendState->Release(); @@ -1636,20 +2003,37 @@ void DXRenderDevice::ReleaseNative() mD3DWrapSamplerState = NULL; mD3DDeviceContext->Release(); mD3DDeviceContext = NULL; + +// ID3D11Debug* debug = NULL; +// mD3DDevice->QueryInterface(__uuidof(ID3D11Debug), (void**)&debug); +// if (debug != NULL) +// { +// debug->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); +// debug->Release(); +// } + mD3DDevice->Release(); mD3DDevice = NULL; } void DXRenderDevice::ReinitNative() { - Init(NULL); + AutoCrit autoCrit(mApp->mCritSect); + + if (mMatrix2DBuffer != NULL) + mMatrix2DBuffer->Release(); + mMatrix2DBuffer = NULL; + + Init(mApp); for (auto window : mRenderWindowList) ((DXRenderWindow*)window)->ReinitNative(); - for (auto kv : mRenderStates) - kv->ReinitNative(); - for (auto kv : mRenderStates) - kv->ReinitNative(); + for (auto shader : mShaders) + shader->ReinitNative(); + for (auto renderState : mRenderStates) + renderState->ReinitNative(); + for (auto tex : mTextures) + tex->ReinitNative(); } void DXRenderDevice::FrameStart() @@ -1918,6 +2302,10 @@ Texture* DXRenderDevice::LoadTexture(ImageData* imageData, int flags) DXCHECK(mD3DDevice->CreateShaderResourceView(d3DTexture, &srDesc, &d3DShaderResourceView)); DXTexture* aTexture = new DXTexture(); + + aTexture->mContentBits = new uint32[aWidth * aHeight]; + memcpy(aTexture->mContentBits, imageData->mBits, aWidth * aHeight * 4); + aTexture->mRenderDevice = this; aTexture->mWidth = aWidth; aTexture->mHeight = aHeight; @@ -1976,204 +2364,21 @@ Texture* DXRenderDevice::CreateDynTexture(int width, int height) return aTexture; } -extern "C" -typedef HRESULT (WINAPI* Func_D3DX10CompileFromFileW)(LPCWSTR pSrcFile, CONST D3D10_SHADER_MACRO* pDefines, LPD3D10INCLUDE pInclude, - LPCSTR pFunctionName, LPCSTR pProfile, UINT Flags1, UINT Flags2, ID3D10Blob** ppShader, ID3D10Blob** ppErrorMsgs); - -static Func_D3DX10CompileFromFileW gFunc_D3DX10CompileFromFileW; - -static bool LoadDXShader(const StringImpl& filePath, const StringImpl& entry, const StringImpl& profile, ID3D10Blob** outBuffer) -{ - HRESULT hr; - String outObj = filePath + "_" + entry + "_" + profile; - - bool useCache = false; - auto srcDate = ::BfpFile_GetTime_LastWrite(filePath.c_str()); - auto cacheDate = ::BfpFile_GetTime_LastWrite(outObj.c_str()); - if (cacheDate >= srcDate) - useCache = true; - - if (!useCache) - { - if (gFunc_D3DX10CompileFromFileW == NULL) - { - auto lib = LoadLibraryA("D3DCompiler_47.dll"); - if (lib != NULL) - gFunc_D3DX10CompileFromFileW = (Func_D3DX10CompileFromFileW)::GetProcAddress(lib, "D3DCompileFromFile"); - } - - if (gFunc_D3DX10CompileFromFileW == NULL) - useCache = true; - } - - if (!useCache) - { - if (gFunc_D3DX10CompileFromFileW == NULL) - { - auto lib = LoadLibraryA("D3DCompiler_47.dll"); - if (lib != NULL) - gFunc_D3DX10CompileFromFileW = (Func_D3DX10CompileFromFileW)::GetProcAddress(lib, "D3DCompileFromFile"); - } - - ID3D10Blob* errorMessage = NULL; - auto dxResult = gFunc_D3DX10CompileFromFileW(UTF8Decode(filePath).c_str(), NULL, NULL, entry.c_str(), profile.c_str(), - D3D10_SHADER_DEBUG | D3D10_SHADER_ENABLE_STRICTNESS, 0, outBuffer, &errorMessage); - - if (DXFAILED(dxResult)) - { - if (errorMessage != NULL) - { - BF_FATAL(StrFormat("Vertex shader load failed: %s", (char*)errorMessage->GetBufferPointer()).c_str()); - errorMessage->Release(); - } - else - BF_FATAL("Shader load failed"); - return false; - } - - auto ptr = (*outBuffer)->GetBufferPointer(); - int size = (int)(*outBuffer)->GetBufferSize(); - - FILE* fp = fopen(outObj.c_str(), "wb"); - if (fp != NULL) - { - fwrite(ptr, 1, size, fp); - fclose(fp); - } - return true; - } - - FILE* fp = fopen(outObj.c_str(), "rb"); - if (fp == NULL) - { - BF_FATAL("Failed to load compiled shader"); - return false; - } - - fseek(fp, 0, SEEK_END); - int size = ftell(fp); - fseek(fp, 0, SEEK_SET); - D3D10CreateBlob(size, outBuffer); - auto ptr = (*outBuffer)->GetBufferPointer(); - fread(ptr, 1, size, fp); - fclose(fp); - - return true; -} - Shader* DXRenderDevice::LoadShader(const StringImpl& fileName, VertexDefinition* vertexDefinition) { BP_ZONE("DXRenderDevice::LoadShader"); - //HRESULT hr; - - ID3D10Blob* errorMessage = NULL; - ID3D10Blob* vertexShaderBuffer = NULL; - ID3D10Blob* pixelShaderBuffer = NULL; - - LoadDXShader(fileName + ".fx", "VS", "vs_4_0", &vertexShaderBuffer); - LoadDXShader(fileName + ".fx", "PS", "ps_4_0", &pixelShaderBuffer); - DXShader* dxShader = new DXShader(); - - dxShader->mVertexSize = 0; - dxShader->mD3DLayout = NULL; - - static const char* semanticNames[] = { - "POSITION", - "POSITION", - "COLOR", - "TEXCOORD", - "NORMAL", - "BINORMAL", - "TANGENT", - "BLENDINDICES", - "BLENDWEIGHT", - "DEPTH", - "FOG", - "POINTSIZE", - "SAMPLE", - "TESSELLATEFACTOR"}; - - static const DXGI_FORMAT dxgiFormat[] = { - DXGI_FORMAT_R32_FLOAT/*VertexElementFormat_Single*/, - DXGI_FORMAT_R32G32_FLOAT/*VertexElementFormat_Vector2*/, - DXGI_FORMAT_R32G32B32_FLOAT/*VertexElementFormat_Vector3*/, - DXGI_FORMAT_R32G32B32A32_FLOAT/*VertexElementFormat_Vector4*/, - DXGI_FORMAT_R8G8B8A8_UNORM/*VertexElementFormat_Color*/, - DXGI_FORMAT_R8G8B8A8_UINT/*VertexElementFormat_Byte4*/, - DXGI_FORMAT_R16G16_UINT/*VertexElementFormat_Short2*/, - DXGI_FORMAT_R16G16B16A16_UINT/*VertexElementFormat_Short4*/, - DXGI_FORMAT_R16G16_UNORM/*VertexElementFormat_NormalizedShort2*/, - DXGI_FORMAT_R16G16B16A16_UNORM/*VertexElementFormat_NormalizedShort4*/, - DXGI_FORMAT_R16G16_FLOAT/*VertexElementFormat_HalfVector2*/, - DXGI_FORMAT_R16G16B16A16_FLOAT/*VertexElementFormat_HalfVector4*/ - }; - - static const int dxgiSize[] = { - sizeof(float) * 1/*VertexElementFormat_Single*/, - sizeof(float) * 2/*VertexElementFormat_Vector2*/, - sizeof(float) * 3/*VertexElementFormat_Vector3*/, - sizeof(float) * 4/*VertexElementFormat_Vector4*/, - sizeof(uint32)/*VertexElementFormat_Color*/, - sizeof(uint8) * 4/*VertexElementFormat_Byte4*/, - sizeof(uint16) * 2/*VertexElementFormat_Short2*/, - sizeof(uint16) * 4/*VertexElementFormat_Short4*/, - sizeof(uint16) * 2/*VertexElementFormat_NormalizedShort2*/, - sizeof(uint16) * 4/*VertexElementFormat_NormalizedShort4*/, - sizeof(uint16) * 2/*VertexElementFormat_HalfVector2*/, - sizeof(uint16) * 4/*VertexElementFormat_HalfVector4*/ - }; - - D3D11_INPUT_ELEMENT_DESC layout[64]; - for (int elementIdx = 0; elementIdx < vertexDefinition->mNumElements; elementIdx++) + dxShader->mRenderDevice = this; + dxShader->mSrcPath = fileName; + dxShader->mVertexDef = new VertexDefinition(vertexDefinition); + if (!dxShader->Load()) { - VertexDefData* vertexDefData = &vertexDefinition->mElementData[elementIdx]; - - if (vertexDefData->mUsage == VertexElementUsage_Position2D) - dxShader->mHas2DPosition = true; - - D3D11_INPUT_ELEMENT_DESC* elementDesc = &layout[elementIdx]; - elementDesc->SemanticName = semanticNames[vertexDefData->mUsage]; - elementDesc->SemanticIndex = vertexDefData->mUsageIndex; - elementDesc->Format = dxgiFormat[vertexDefData->mFormat]; - elementDesc->InputSlot = 0; - elementDesc->AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT; - elementDesc->InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; - elementDesc->InstanceDataStepRate = 0; - dxShader->mVertexSize += dxgiSize[vertexDefData->mFormat]; + delete dxShader; + return NULL; } - - /* = - { - { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, - { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, - { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 20, D3D11_INPUT_PER_VERTEX_DATA, 0 }, - }; - UINT numElements = sizeof(layout) / sizeof(layout[0]);*/ - HRESULT result = mD3DDevice->CreateInputLayout(layout, vertexDefinition->mNumElements, vertexShaderBuffer->GetBufferPointer(), - vertexShaderBuffer->GetBufferSize(), &dxShader->mD3DLayout); - DXCHECK(result); - if (FAILED(result)) - return NULL; - - // Create the vertex shader from the buffer. - result = mD3DDevice->CreateVertexShader(vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), NULL, &dxShader->mD3DVertexShader); - DXCHECK(result); - if (FAILED(result)) - return NULL; - - // Create the pixel shader from the buffer. - result = mD3DDevice->CreatePixelShader(pixelShaderBuffer->GetBufferPointer(), pixelShaderBuffer->GetBufferSize(), NULL, &dxShader->mD3DPixelShader); - DXCHECK(result); - if (FAILED(result)) - return NULL; - - vertexShaderBuffer->Release(); - pixelShaderBuffer->Release(); - - dxShader->Init(); - return dxShader; + mShaders.Add(dxShader); + return dxShader; } void DXRenderDevice::SetRenderState(RenderState* renderState) diff --git a/BeefySysLib/platform/win/DXRenderDevice.h b/BeefySysLib/platform/win/DXRenderDevice.h index b9bce08d..1e5cfb09 100644 --- a/BeefySysLib/platform/win/DXRenderDevice.h +++ b/BeefySysLib/platform/win/DXRenderDevice.h @@ -60,22 +60,22 @@ public: DXRenderDevice* mRenderDevice; ID3D11Texture2D* mD3DTexture; ID3D11ShaderResourceView* mD3DResourceView; - ID3D11RenderTargetView* mD3DRenderTargetView; + ID3D11RenderTargetView* mD3DRenderTargetView; ID3D11Texture2D* mD3DDepthBuffer; - ID3D11DepthStencilView* mD3DDepthStencilView; - ImageData* mImageData; + ID3D11DepthStencilView* mD3DDepthStencilView; + uint32* mContentBits; public: DXTexture(); ~DXTexture(); - - void ReleaseNative(); - void ReinitNative(); + + void ReleaseNative(); + void ReinitNative(); virtual void PhysSetAsTarget() override; virtual void Blt(ImageData* imageData, int x, int y) override; virtual void SetBits(int destX, int destY, int destWidth, int destHeight, int srcPitch, uint32* bits) override; - virtual void GetBits(int srcX, int srcY, int srcWidth, int srcHeight, int destPitch, uint32* bits) override; + virtual void GetBits(int srcX, int srcY, int srcWidth, int srcHeight, int destPitch, uint32* bits) override; }; class DXShaderParam : public ShaderParam @@ -95,7 +95,11 @@ typedef std::map DXShaderParamMap; class DXShader : public Shader { -public: +public: + DXRenderDevice* mRenderDevice; + String mSrcPath; + VertexDefinition* mVertexDef; + ID3D11InputLayout* mD3DLayout; ID3D11VertexShader* mD3DVertexShader; ID3D11PixelShader* mD3DPixelShader; @@ -107,7 +111,11 @@ public: DXShader(); ~DXShader(); - virtual ShaderParam* GetShaderParam(const StringImpl& name) override; + void ReleaseNative(); + void ReinitNative(); + + bool Load(); + virtual ShaderParam* GetShaderParam(const StringImpl& name) override; }; class DXDrawBatch : public DrawBatch @@ -144,8 +152,10 @@ public: ID3D11RenderTargetView* mD3DRenderTargetView; ID3D11Texture2D* mD3DDepthBuffer; ID3D11DepthStencilView* mD3DDepthStencilView; + HANDLE mFrameWaitObject; + float mRefreshRate; bool mResizePending; - bool mWindowed; + bool mWindowed; int mPendingWidth; int mPendingHeight; @@ -164,6 +174,8 @@ public: virtual void Present() override; void CopyBitsTo(uint32* dest, int width, int height); + virtual float GetRefreshRate() override; + virtual bool WaitForVBlank() override; }; typedef std::vector DXDrawBatchVector; @@ -174,7 +186,7 @@ typedef std::vector DXDrawBatchVector; class DXDrawBufferPool { public: - std::vector mPooledIndexBuffers; + std::vector mPooledIndexBuffers; int mIdxPoolIdx; std::vector mPooledVertexBuffers; int mVtxPoolIdx; @@ -237,8 +249,8 @@ public: class DXModelInstance : public ModelInstance { public: - DXRenderDevice* mD3DRenderDevice; - Array mDXModelMeshs; + DXRenderDevice* mD3DRenderDevice; + Array mDXModelMeshs; public: DXModelInstance(ModelDef* modelDef); @@ -282,11 +294,12 @@ public: IDXGIFactory* mDXGIFactory; ID3D11Device* mD3DDevice; ID3D11DeviceContext* mD3DDeviceContext; - ID3D11BlendState* mD3DNormalBlendState; + ID3D11BlendState* mD3DNormalBlendState; ID3D11SamplerState* mD3DDefaultSamplerState; ID3D11SamplerState* mD3DWrapSamplerState; - bool mHasVSync; + bool mNeedsReinitNative; + ID3D11Buffer* mMatrix2DBuffer; ID3D11Buffer* mD3DVertexBuffer; ID3D11Buffer* mD3DIndexBuffer; int mVtxByteIdx; @@ -294,6 +307,7 @@ public: HashSet mRenderStates; HashSet mTextures; + HashSet mShaders; Dictionary mTextureMap; Dictionary mBufferMap; diff --git a/BeefySysLib/platform/win/WinBFApp.cpp b/BeefySysLib/platform/win/WinBFApp.cpp index 8909f50f..b1b2c328 100644 --- a/BeefySysLib/platform/win/WinBFApp.cpp +++ b/BeefySysLib/platform/win/WinBFApp.cpp @@ -600,6 +600,9 @@ LRESULT WinBFWindow::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar switch (uMsg) { + case WM_DISPLAYCHANGE: + ((DXRenderWindow*)mRenderWindow)->mRefreshRate = 0; + break; case WM_SIZE: mRenderWindow->Resized(); if (mMovedFunc != NULL) @@ -986,7 +989,6 @@ LRESULT WinBFWindow::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar case WM_SYSKEYDOWN: case WM_KEYDOWN: { - mIsMenuKeyHandled = false; int keyCode = (int) wParam; if (keyCode == VK_APPS) @@ -995,8 +997,13 @@ LRESULT WinBFWindow::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar if ((mKeyLayoutHasAltGr) && (keyCode == VK_MENU) && ((lParam & 0x01000000) != 0)) keyCode = VK_RMENU; - mIsKeyDown[keyCode] = true; + mIsKeyDown[keyCode] = true; +// if ((keyCode == 192) && (mIsKeyDown[VK_MENU])) +// { +// ((DXRenderDevice*)mRenderWindow->mRenderDevice)->mNeedsReinitNative = true; +// } + for (auto kv : *menuIDMap) { WinBFMenu* aMenu = kv.mValue; @@ -1275,10 +1282,71 @@ WinBFApp::WinBFApp() mInMsgProc = false; mDSoundManager = NULL; mDInputManager = NULL; + + mVSyncThreadId = 0; + mClosing = false; + mVSyncActive = false; + mVSyncThread = BfpThread_Create(VSyncThreadProcThunk, (void*)this, 128 * 1024, BfpThreadCreateFlag_StackSizeReserve, &mVSyncThreadId); + BfpThread_SetPriority(mVSyncThread, BfpThreadPriority_High, NULL); +} + +void BFP_CALLTYPE WinBFApp::VSyncThreadProcThunk(void* ptr) +{ + ((WinBFApp*)ptr)->VSyncThreadProc(); +} + +void WinBFApp::VSyncThreadProc() +{ + while (!mClosing) + { + bool didWait = false; + + IDXGIOutput* output = NULL; + + // + { + AutoCrit autoCrit(mCritSect); + if ((mRenderDevice != NULL) && (!mRenderDevice->mRenderWindowList.IsEmpty())) + { + auto renderWindow = (DXRenderWindow*)mRenderDevice->mRenderWindowList[0]; + renderWindow->mDXSwapChain->GetContainingOutput(&output); + } + } + + if (output != NULL) + { + DWORD start = GetTickCount(); + bool success = output->WaitForVBlank() == 0; + int elapsed = (int)(GetTickCount() - start); + if (elapsed >= 20) + { + NOP; + } + + if (success) + { + didWait = true; + mVSyncActive = true; + mVSyncEvent.Set(); + } + + output->Release(); + } + + if (!didWait) + { + mVSyncActive = false; + BfpThread_Sleep(20); + } + } } WinBFApp::~WinBFApp() { + mClosing = true; + BfpThread_WaitFor(mVSyncThread, -1); + BfpThread_Release(mVSyncThread); + delete mRenderDevice; delete mDSoundManager; delete mDInputManager; @@ -1288,6 +1356,8 @@ void WinBFApp::Init() { BP_ZONE("WinBFApp::Init"); + AutoCrit autoCrit(mCritSect); + mRunning = true; mInMsgProc = false; @@ -1311,10 +1381,23 @@ void WinBFApp::Run() } } +void WinBFApp::Process() +{ + BFApp::Process(); + + auto dxRenderDevice = (DXRenderDevice*)mRenderDevice; + if (dxRenderDevice->mNeedsReinitNative) + { + dxRenderDevice->mNeedsReinitNative = false; + dxRenderDevice->ReinitNative(); + mForceNextDraw = true; + } +} + void WinBFApp::Draw() { mRenderDevice->FrameStart(); - BFApp::Draw(); + BFApp::Draw(); mRenderDevice->FrameEnd(); } @@ -1392,6 +1475,8 @@ void WinBFApp::GetWorkspaceRectFrom(int fromX, int fromY, int fromWidth, int fro BFWindow* WinBFApp::CreateNewWindow(BFWindow* parent, const StringImpl& title, int x, int y, int width, int height, int windowFlags) { + AutoCrit autoCrit(mCritSect); + BFWindow* aWindow = new WinBFWindow(parent, title, x, y, width, height, windowFlags); mWindowList.push_back(aWindow); @@ -2025,6 +2110,13 @@ BFSoundManager* WinBFApp::GetSoundManager() return mDSoundManager; } +intptr WinBFApp::GetCriticalThreadId(int idx) +{ + if (idx == 0) + return mVSyncThreadId; + return 0; +} + void WinBFWindow::ModalsRemoved() { ::EnableWindow(mHWnd, TRUE); diff --git a/BeefySysLib/platform/win/WinBFApp.h b/BeefySysLib/platform/win/WinBFApp.h index 1e2efcd1..c000d002 100644 --- a/BeefySysLib/platform/win/WinBFApp.h +++ b/BeefySysLib/platform/win/WinBFApp.h @@ -32,7 +32,7 @@ typedef Dictionary WinHMenuMap; class WinBFWindow : public BFWindow { -public: +public: HWND mHWnd; bool mIsMouseInside; WinMenuIDMap mMenuIDMap; @@ -93,13 +93,19 @@ public: class WinBFApp : public BFApp { -public: +public: bool mInMsgProc; StringToUIntMap mClipboardFormatMap; DSoundManager* mDSoundManager; DInputManager* mDInputManager; - + BfpThreadId mVSyncThreadId; + BfpThread* mVSyncThread; + volatile bool mClosing; + protected: + void VSyncThreadProc(); + static void BFP_CALLTYPE VSyncThreadProcThunk(void* ptr); + virtual void Draw() override; virtual void PhysSetCursor() override; @@ -107,10 +113,11 @@ protected: public: WinBFApp(); - virtual ~WinBFApp(); + virtual ~WinBFApp(); virtual void Init() override; - virtual void Run() override; + virtual void Run() override; + virtual void Process() override; virtual void GetDesktopResolution(int& width, int& height) override; virtual void GetWorkspaceRect(int& x, int& y, int& width, int& height) override; @@ -129,6 +136,8 @@ public: virtual BFSysBitmap* LoadSysBitmap(const WCHAR* fileName) override; virtual BFSoundManager* GetSoundManager() override; + + virtual intptr GetCriticalThreadId(int idx) override; }; NS_BF_END; diff --git a/IDE/src/IDEApp.bf b/IDE/src/IDEApp.bf index f7b3d79b..0f819624 100644 --- a/IDE/src/IDEApp.bf +++ b/IDE/src/IDEApp.bf @@ -11767,7 +11767,6 @@ namespace IDE public override void Init() { scope AutoBeefPerf("IDEApp.Init"); - //int zag = 123; if (mVerbosity == .Default) @@ -14203,7 +14202,7 @@ namespace IDE //mDebugger.InitiateHotResolve(.ActiveMethods | .Allocations); } - public override void Draw() + public override void Draw(bool forceDraw) { scope AutoBeefPerf("IDEApp.Draw"); #if CLI @@ -14213,7 +14212,7 @@ namespace IDE if (mBfResolveSystem != null) { mBfResolveSystem.PerfZoneStart("IDEApp.Draw"); - base.Draw(); + base.Draw(forceDraw); mBfResolveSystem.PerfZoneEnd(); if (mBfResolveSystem.mIsTiming) { @@ -14222,7 +14221,7 @@ namespace IDE } } else - base.Draw(); + base.Draw(forceDraw); } public void DrawSquiggle(Graphics g, float x, float y, float width) diff --git a/IDE/src/ui/AboutDialog.bf b/IDE/src/ui/AboutDialog.bf index 5fb02b6f..903e69a0 100644 --- a/IDE/src/ui/AboutDialog.bf +++ b/IDE/src/ui/AboutDialog.bf @@ -3,11 +3,15 @@ using Beefy.gfx; using System; using Beefy.events; using Beefy.widgets; +using System.Collections; +using System.Diagnostics; namespace IDE.ui { class AboutDialog : IDEDialog { + static AboutDialog gAboutDialog; + const int cWidth = 320; const int cHeight = 240; const int cRandSize = 3777; @@ -21,6 +25,14 @@ namespace IDE.ui uint8[cRandSize] mRand; int mRandIdx; + Stopwatch mStopwatch = new .()..Start() ~ delete _; + struct Entry + { + public double mUpdateCntF; + public float mTimeMS; + } + List mEntries = new .() ~ delete _; + struct CColor { public uint8 r; @@ -70,8 +82,24 @@ namespace IDE.ui Color colorOut = Color.ToNative(Color.Lerp(mainColors[(int)colorPos], mainColors[(int)colorPos + 1], colorPos - (int)colorPos)); mPalette[i] = colorOut; } + + /*mWindowFlags |= .Resizable; + mWindowFlags &= ~.Modal;*/ + + gAboutDialog = this; } - + + ~this() + { + gAboutDialog = null; + } + + public override void AddedToParent() + { + base.AddedToParent(); + mWidgetWindow.mWantsUpdateF = true; + } + public override void CalcSize() { mWidth = GS!(640); @@ -140,12 +168,12 @@ namespace IDE.ui mImage.SetBits(0, 0, cWidth, cHeight, cWidth, newBits); - float ang = Math.Min(mUpdateCnt * 0.006f, Math.PI_f / 2); + float ang = Math.Min((float)(mUpdateCntF * 0.006f), Math.PI_f / 2); g.SetFont(mBigFont); g.DrawString("Beef IDE", 0, GS!(20) + (1.0f - Math.Sin(ang))*GS!(300), .Centered, mWidth); - float angMed = Math.Min(mUpdateCnt * 0.0055f, Math.PI_f / 2); - float alpha = Math.Clamp(mUpdateCnt * 0.007f - 1.3f, 0, 1.0f); + float angMed = Math.Min((float)(mUpdateCntF * 0.0055f), Math.PI_f / 2); + float alpha = Math.Clamp((float)(mUpdateCntF * 0.007f) - 1.3f, 0, 1.0f); using (g.PushColor(Color.Get(alpha))) { @@ -164,6 +192,33 @@ namespace IDE.ui } g.DrawQuad(mImage, 0, 0, 0.0f, 0.0f, mWidth, mHeight, 1.0f, 1.0f); + + /*if (gAboutDialog == this) + { + /*Entry entry; + entry.mTimeMS = mStopwatch.ElapsedMicroseconds / 1000.0f; + entry.mUpdateCntF = mUpdateCntF;*/ + mEntries.Add(Entry() {mTimeMS = mStopwatch.ElapsedMicroseconds / 1000.0f, mUpdateCntF = mUpdateCntF}); + + if (mEntries.Count == 100) + { + float prevTime = 0; + + for (var entry in mEntries) + { + Debug.WriteLine($"Entry Time:{entry.mTimeMS - prevTime:0.00} UpdateCntF:{entry.mUpdateCntF:0.00}"); + prevTime = entry.mTimeMS; + + if (@entry.Index == 20) + break; + } + + mEntries.Clear(); + } + } + + using (g.PushColor(0xFFFFFFFF)) + g.FillRect(Math.Cos((float)(mUpdateCntF * 0.2f)) * 600 + mWidth / 2, 0, 8, mHeight);*/ } public override void Update() @@ -177,6 +232,12 @@ namespace IDE.ui DoFire(); } + public override void UpdateF(float updatePct) + { + base.UpdateF(updatePct); + MarkDirty(); + } + public override void KeyDown(KeyCode keyCode, bool isRepeat) { base.KeyDown(keyCode, isRepeat); diff --git a/IDE/src/ui/SourceEditWidgetContent.bf b/IDE/src/ui/SourceEditWidgetContent.bf index be884a07..d7a6467a 100644 --- a/IDE/src/ui/SourceEditWidgetContent.bf +++ b/IDE/src/ui/SourceEditWidgetContent.bf @@ -6508,7 +6508,7 @@ namespace IDE.ui } } - public void UpdateCollapse() + public void UpdateCollapse(float updatePct) { MarkDirty(); @@ -6564,9 +6564,12 @@ namespace IDE.ui { if (emitEmbed.mIsOpen) { - emitEmbed.mOpenPct = Math.Min(1.0f, emitEmbed.mOpenPct + 0.1f); + emitEmbed.mOpenPct = Math.Min(1.0f, emitEmbed.mOpenPct + 0.1f * updatePct); if (emitEmbed.mOpenPct != 1.0f) + { mCollapseNeedsUpdate = true; + mWidgetWindow.mTempWantsUpdateF = true; + } if (emitEmbed.mView == null) { @@ -6577,7 +6580,7 @@ namespace IDE.ui } else { - emitEmbed.mOpenPct = Math.Max(0.0f, emitEmbed.mOpenPct - 0.1f); + emitEmbed.mOpenPct = Math.Max(0.0f, emitEmbed.mOpenPct - 0.1f * updatePct); if (emitEmbed.mOpenPct == 0.0f) { if (emitEmbed.mView != null) @@ -6587,7 +6590,10 @@ namespace IDE.ui } } else + { mCollapseNeedsUpdate = true; + mWidgetWindow.mTempWantsUpdateF = true; + } } } } @@ -6664,13 +6670,14 @@ namespace IDE.ui var entry = mOrderedCollapseEntries[animIdx]; if ((entry.mIsOpen) && (entry.mOpenPct < 1.0f)) { - entry.mOpenPct = Math.Min(entry.mOpenPct + animSpeed, 1.0f); + entry.mOpenPct = Math.Min(entry.mOpenPct + animSpeed * updatePct, 1.0f); + mWidgetWindow.mTempWantsUpdateF = true; } if ((!entry.mIsOpen) && (entry.mOpenPct > 0)) { - entry.mOpenPct = Math.Max(entry.mOpenPct - animSpeed, 0.0f); - + entry.mOpenPct = Math.Max(entry.mOpenPct - animSpeed * updatePct, 0.0f); + mWidgetWindow.mTempWantsUpdateF = true; if (entry.mOpenPct == 0.0f) FinishCollapseClose(animIdx, entry); } diff --git a/IDE/src/ui/SourceViewPanel.bf b/IDE/src/ui/SourceViewPanel.bf index 1987a97b..93353fc1 100644 --- a/IDE/src/ui/SourceViewPanel.bf +++ b/IDE/src/ui/SourceViewPanel.bf @@ -639,6 +639,7 @@ namespace IDE.ui mNavigationBar = new NavigationBar(this); AddWidget(mNavigationBar); } + mAlwaysUpdateF = true; } public ~this() { @@ -6811,13 +6812,19 @@ namespace IDE.ui UpdateQueuedEmitShowData(); - if (ewc.mCollapseNeedsUpdate) - ewc.UpdateCollapse(); - // Process after mQueuedCollapseData so mCharIdSpan is still valid ProcessDeferredResolveResults(0); } + public override void UpdateF(float updatePct) + { + base.UpdateF(updatePct); + + var ewc = (SourceEditWidgetContent)mEditWidget.Content; + if (ewc.mCollapseNeedsUpdate) + ewc.UpdateCollapse(updatePct); + } + public void UpdateQueuedEmitShowData() { var compiler = ResolveCompiler;