From 1de66142b5e70b0e130dbb42e2c55751477c8923 Mon Sep 17 00:00:00 2001 From: Tian Liao Date: Wed, 3 Apr 2024 19:06:36 +0800 Subject: [PATCH] refactor draft --- src/GraphControl/Control/Grapher.cpp | 65 +- src/GraphControl/Control/Grapher.h | 43 +- src/GraphControl/DirectX/DeviceResources.cpp | 10 +- src/GraphControl/DirectX/DeviceResources.h | 11 +- src/GraphControl/DirectX/RenderMain.cpp | 601 +++---------------- src/GraphControl/DirectX/RenderMain.h | 231 ++----- 6 files changed, 177 insertions(+), 784 deletions(-) diff --git a/src/GraphControl/Control/Grapher.cpp b/src/GraphControl/Control/Grapher.cpp index 564188e4..2db1bf5b 100644 --- a/src/GraphControl/Control/Grapher.cpp +++ b/src/GraphControl/Control/Grapher.cpp @@ -97,19 +97,11 @@ namespace GraphControl { if (auto renderer = m_graph->GetRenderer()) { - m_renderMain->GetCriticalSection().lock(); - if (SUCCEEDED(renderer->ScaleRange(centerX, centerY, scale))) { - m_renderMain->GetCriticalSection().unlock(); - - m_renderMain->RunRenderPass(); + m_renderMain->FireRenderPass(); GraphViewChangedEvent(this, GraphViewChangedReason::Manipulation); } - else - { - m_renderMain->GetCriticalSection().unlock(); - } } } } @@ -145,7 +137,7 @@ namespace GraphControl if (SUCCEEDED(hr)) { - m_renderMain->RunRenderPass(); + m_renderMain->FireRenderPass(); GraphViewChangedEvent(this, GraphViewChangedReason::Reset); } } @@ -158,8 +150,8 @@ namespace GraphControl if (swapChainPanel) { swapChainPanel->AllowFocusOnInteraction = true; - m_renderMain = ref new RenderMain(swapChainPanel); - m_renderMain->BackgroundColor = GraphBackground; + m_renderMain = std::make_unique(swapChainPanel, m_graph.get()); + m_renderMain->BackgroundColor(GraphBackground); } TryUpdateGraph(false); @@ -220,7 +212,7 @@ namespace GraphControl if (m_renderMain) { - m_renderMain->RunRenderPass(); + m_renderMain->FireRenderPass(); } } @@ -404,11 +396,9 @@ namespace GraphControl UpdateGraphOptions(m_graph->GetOptions(), validEqs); SetGraphArgs(m_graph); - m_renderMain->Graph = m_graph; - // It is possible that the render fails, in that case fall through to explicit empty initialization - co_await m_renderMain->RunRenderPassAsync(false); - if (m_renderMain->IsRenderPassSuccesful()) + auto succ = co_await m_renderMain->RunRenderPassAsync(); + if (succ) { UpdateVariables(); successful = true; @@ -438,7 +428,6 @@ namespace GraphControl UpdateGraphOptions(m_graph->GetOptions(), vector()); SetGraphArgs(m_graph); - m_renderMain->Graph = m_graph; co_await m_renderMain->RunRenderPassAsync(); UpdateVariables(); @@ -479,8 +468,6 @@ namespace GraphControl { if (graph != nullptr && m_renderMain != nullptr) { - critical_section::scoped_lock lock(m_renderMain->GetCriticalSection()); - for (auto variablePair : Variables) { graph->SetArgValue(variablePair->Key->Data(), variablePair->Value->Value); @@ -557,15 +544,8 @@ namespace GraphControl if (m_graph != nullptr && m_renderMain != nullptr) { - auto workItemHandler = ref new WorkItemHandler([this, variableName, newValue](IAsyncAction ^ action) { - m_renderMain->GetCriticalSection().lock(); - m_graph->SetArgValue(variableName->Data(), newValue); - m_renderMain->GetCriticalSection().unlock(); - - m_renderMain->RunRenderPass(); - }); - - ThreadPool::RunAsync(workItemHandler, WorkItemPriority::High, WorkItemOptions::None); + m_graph->SetArgValue(variableName->Data(), newValue); + m_renderMain->FireRenderPass(); } } @@ -668,7 +648,7 @@ namespace GraphControl m_renderMain->DrawNearestPoint = true; Point currPosition = e->GetCurrentPoint(/* relativeTo */ this)->Position; - if (m_renderMain->ActiveTracing) + if (m_renderMain->ActiveTracing()) { PointerValueChangedEvent(currPosition); ActiveTraceCursorPosition = currPosition; @@ -681,7 +661,7 @@ namespace GraphControl } else if (m_cachedCursor != nullptr) { - m_renderMain->PointerLocation = currPosition; + m_renderMain->PointerLocation(currPosition); ::CoreWindow::GetForCurrentThread()->PointerCursor = m_cachedCursor; m_cachedCursor = nullptr; @@ -690,7 +670,7 @@ namespace GraphControl } else { - m_renderMain->PointerLocation = currPosition; + m_renderMain->PointerLocation(currPosition); UpdateTracingChanged(); } @@ -783,15 +763,10 @@ namespace GraphControl translationX /= -width; translationY /= height; - m_renderMain->GetCriticalSection().lock(); - if (FAILED(renderer->MoveRangeByRatio(translationX, translationY))) { - m_renderMain->GetCriticalSection().unlock(); return; } - - m_renderMain->GetCriticalSection().unlock(); needsRenderPass = true; } @@ -805,21 +780,16 @@ namespace GraphControl const auto& pos = e->Position; const auto [centerX, centerY] = PointerPositionToGraphPosition(pos.X, pos.Y, width, height); - m_renderMain->GetCriticalSection().lock(); - if (FAILED(renderer->ScaleRange(centerX, centerY, scale))) { - m_renderMain->GetCriticalSection().unlock(); return; } - - m_renderMain->GetCriticalSection().unlock(); needsRenderPass = true; } if (needsRenderPass) { - m_renderMain->RunRenderPass(); + m_renderMain->FireRenderPass(); GraphViewChangedEvent(this, GraphViewChangedReason::Manipulation); } } @@ -1075,7 +1045,7 @@ void Grapher::OnGraphBackgroundPropertyChanged(Windows::UI::Color /*oldValue*/, { if (m_renderMain) { - m_renderMain->BackgroundColor = newValue; + m_renderMain->BackgroundColor(newValue); } if (m_graph) { @@ -1091,7 +1061,7 @@ void Grapher::OnGridLinesColorPropertyChanged(Windows::UI::Color /*oldValue*/, W { auto gridLinesColor = Graphing::Color(newValue.R, newValue.G, newValue.B, newValue.A); m_graph->GetOptions().SetGridColor(gridLinesColor); - m_renderMain->RunRenderPassAsync(); + m_renderMain->FireRenderPass(); } } @@ -1102,8 +1072,8 @@ void Grapher::OnLineWidthPropertyChanged(double oldValue, double newValue) UpdateGraphOptions(m_graph->GetOptions(), GetGraphableEquations()); if (m_renderMain) { - m_renderMain->SetPointRadius(LineWidth + 1); - m_renderMain->RunRenderPass(); + m_renderMain->PointRadius(LineWidth + 1); + m_renderMain->FireRenderPass(); TraceLogger::GetInstance()->LogLineWidthChanged(); } @@ -1112,7 +1082,6 @@ void Grapher::OnLineWidthPropertyChanged(double oldValue, double newValue) optional>> Grapher::TryInitializeGraph(bool keepCurrentView, const IExpression* graphingExp) { - critical_section::scoped_lock lock(m_renderMain->GetCriticalSection()); if (keepCurrentView || IsKeepCurrentView) { auto renderer = m_graph->GetRenderer(); diff --git a/src/GraphControl/Control/Grapher.h b/src/GraphControl/Control/Grapher.h index a4de85b4..045e7bbb 100644 --- a/src/GraphControl/Control/Grapher.h +++ b/src/GraphControl/Control/Grapher.h @@ -25,11 +25,12 @@ public public delegate void PointerValueChangedEventHandler(Windows::Foundation::Point value); -public enum class GraphViewChangedReason -{ - Manipulation, - Reset -}; +public + enum class GraphViewChangedReason + { + Manipulation, + Reset + }; [Windows::UI::Xaml::Markup::ContentPropertyAttribute(Name = L"Equations")] public ref class Grapher sealed : public Windows::UI::Xaml::Controls::Control, @@ -65,14 +66,14 @@ public enum class GraphViewChangedReason { bool get() { - return m_renderMain != nullptr && m_renderMain->ActiveTracing; + return m_renderMain != nullptr && m_renderMain->ActiveTracing(); } void set(bool value) { - if (m_renderMain != nullptr && m_renderMain->ActiveTracing != value) + if (m_renderMain != nullptr && m_renderMain->ActiveTracing() != value) { - m_renderMain->ActiveTracing = value; + m_renderMain->ActiveTracing(value); UpdateTracingChanged(); PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs(L"ActiveTracing")); } @@ -86,7 +87,7 @@ public enum class GraphViewChangedReason { Windows::Foundation::Point get() { - return m_renderMain->TraceLocation; + return m_renderMain->TraceLocation(); } } @@ -94,14 +95,14 @@ public enum class GraphViewChangedReason { Windows::Foundation::Point get() { - return m_renderMain->ActiveTraceCursorPosition; + return m_renderMain->ActiveTraceCursorPosition(); } void set(Windows::Foundation::Point newValue) { - if (m_renderMain->ActiveTraceCursorPosition != newValue) + if (m_renderMain->ActiveTraceCursorPosition() != newValue) { - m_renderMain->ActiveTraceCursorPosition = newValue; + m_renderMain->ActiveTraceCursorPosition(newValue); UpdateTracingChanged(); } } @@ -154,7 +155,7 @@ public enum class GraphViewChangedReason m_graph->GetOptions().SetDefaultXRange(newValue); if (m_renderMain != nullptr) { - m_renderMain->RunRenderPass(); + m_renderMain->FireRenderPass(); } } } @@ -174,7 +175,7 @@ public enum class GraphViewChangedReason m_graph->GetOptions().SetDefaultXRange(newValue); if (m_renderMain != nullptr) { - m_renderMain->RunRenderPass(); + m_renderMain->FireRenderPass(); } } } @@ -194,7 +195,7 @@ public enum class GraphViewChangedReason m_graph->GetOptions().SetDefaultYRange(newValue); if (m_renderMain != nullptr) { - m_renderMain->RunRenderPass(); + m_renderMain->FireRenderPass(); } } } @@ -214,7 +215,7 @@ public enum class GraphViewChangedReason m_graph->GetOptions().SetDefaultYRange(newValue); if (m_renderMain != nullptr) { - m_renderMain->RunRenderPass(); + m_renderMain->FireRenderPass(); } } } @@ -228,7 +229,6 @@ public enum class GraphViewChangedReason { if (auto render = m_graph->GetRenderer()) { - Concurrency::critical_section::scoped_lock lock(m_renderMain->GetCriticalSection()); render->GetDisplayRanges(*xMin, *xMax, *yMin, *yMax); } } @@ -249,7 +249,7 @@ public enum class GraphViewChangedReason m_rangeUpdatedBySettings = true; if (m_renderMain) { - m_renderMain->RunRenderPass(); + m_renderMain->FireRenderPass(); GraphViewChangedEvent(this, GraphViewChangedReason::Manipulation); } } @@ -304,12 +304,10 @@ public enum class GraphViewChangedReason void SetEquationsAsValid(); void SetEquationErrors(); - std::optional>> TryInitializeGraph(bool keepCurrentView, _In_ const Graphing::IExpression* graphingExp = nullptr); - + std::optional>> + TryInitializeGraph(bool keepCurrentView, _In_ const Graphing::IExpression* graphingExp = nullptr); private: - DX::RenderMain ^ m_renderMain = nullptr; - static Windows::UI::Xaml::DependencyProperty ^ s_equationTemplateProperty; static Windows::UI::Xaml::DependencyProperty ^ s_equationsSourceProperty; @@ -326,6 +324,7 @@ public enum class GraphViewChangedReason const std::unique_ptr m_solver; const std::shared_ptr m_graph; + std::unique_ptr m_renderMain; bool m_calculatedForceProportional = false; bool m_tracingTracking; bool m_trigUnitsChanged; diff --git a/src/GraphControl/DirectX/DeviceResources.cpp b/src/GraphControl/DirectX/DeviceResources.cpp index f5e6b285..df9041cf 100644 --- a/src/GraphControl/DirectX/DeviceResources.cpp +++ b/src/GraphControl/DirectX/DeviceResources.cpp @@ -71,7 +71,7 @@ namespace ScreenRotation namespace GraphControl::DX { // Constructor for DeviceResources. - DeviceResources::DeviceResources(SwapChainPanel^ panel) : + DeviceResources::DeviceResources(SwapChainPanel^ panel, IDeviceNotify* deviceNotify) : m_screenViewport(), m_d3dFeatureLevel(D3D_FEATURE_LEVEL_9_1), m_d3dRenderTargetSize(), @@ -83,7 +83,7 @@ namespace GraphControl::DX m_effectiveDpi(-1.0f), m_compositionScaleX(1.0f), m_compositionScaleY(1.0f), - m_deviceNotify(nullptr) + m_deviceNotify(deviceNotify) { CreateDeviceIndependentResources(); CreateDeviceResources(); @@ -612,12 +612,6 @@ namespace GraphControl::DX } } - // Register our DeviceNotify to be informed on device lost and creation. - void DeviceResources::RegisterDeviceNotify(DX::IDeviceNotify^ deviceNotify) - { - m_deviceNotify = deviceNotify; - } - // Call this method when the app suspends. It provides a hint to the driver that the app // is entering an idle state and that temporary buffers can be reclaimed for use by other apps. void DeviceResources::Trim() diff --git a/src/GraphControl/DirectX/DeviceResources.h b/src/GraphControl/DirectX/DeviceResources.h index 92072ec8..604acdd8 100644 --- a/src/GraphControl/DirectX/DeviceResources.h +++ b/src/GraphControl/DirectX/DeviceResources.h @@ -8,17 +8,17 @@ namespace GraphControl::DX { // Provides an interface for an application that owns DeviceResources to be notified of the device being lost or created. - interface class IDeviceNotify + struct IDeviceNotify { - virtual void OnDeviceLost(); - virtual void OnDeviceRestored(); + virtual void OnDeviceLost() = 0; + virtual void OnDeviceRestored() = 0; }; // Controls all the DirectX device resources. class DeviceResources { public: - DeviceResources(Windows::UI::Xaml::Controls::SwapChainPanel^ panel); + explicit DeviceResources(Windows::UI::Xaml::Controls::SwapChainPanel^ panel, IDeviceNotify* deviceNotify); void SetSwapChainPanel(Windows::UI::Xaml::Controls::SwapChainPanel^ panel); void SetLogicalSize(Windows::Foundation::Size logicalSize); void SetCurrentOrientation(Windows::Graphics::Display::DisplayOrientations currentOrientation); @@ -26,7 +26,6 @@ namespace GraphControl::DX void SetCompositionScale(float compositionScaleX, float compositionScaleY); void ValidateDevice(); void HandleDeviceLost(); - void RegisterDeviceNotify(IDeviceNotify^ deviceNotify); void Trim(); void Present(); @@ -106,6 +105,6 @@ namespace GraphControl::DX DirectX::XMFLOAT4X4 m_orientationTransform3D; // The IDeviceNotify can be held directly as it owns the DeviceResources. - IDeviceNotify^ m_deviceNotify; + IDeviceNotify* m_deviceNotify; }; } diff --git a/src/GraphControl/DirectX/RenderMain.cpp b/src/GraphControl/DirectX/RenderMain.cpp index 1e53821b..a35bc391 100644 --- a/src/GraphControl/DirectX/RenderMain.cpp +++ b/src/GraphControl/DirectX/RenderMain.cpp @@ -5,16 +5,7 @@ #include "RenderMain.h" #include "DirectXHelper.h" -using namespace Concurrency; -using namespace Graphing; -using namespace Platform; -using namespace std; -using namespace Windows::Foundation; -using namespace Windows::Graphics::Display; -using namespace Windows::System::Threading; -using namespace Windows::UI::Core; -using namespace Windows::UI::Xaml; -using namespace Windows::UI::Xaml::Controls; +namespace ctrls = Windows::UI::Xaml::Controls; namespace { @@ -23,524 +14,100 @@ namespace constexpr unsigned int s_BlueChannelIndex = 2; constexpr unsigned int s_AlphaChannelIndex = 3; constexpr float s_MaxChannelValue = 255.0f; - constexpr float nearestPointRadius = 3; + + struct RenderMainHelper + { + static std::thread CreateRenderWorker(GraphControl::DX::RenderMain::RenderContext& context, ctrls::SwapChainPanel ^ panel, Graphing::IGraph* graph) + { + context.Graph = graph; + return std::thread{ [&] + { + struct DeviceNotify : GraphControl::DX::IDeviceNotify + { + explicit DeviceNotify(GraphControl::DX::RenderMain::RenderContext& context) + : Context(context) + { + } + void OnDeviceLost() override + { + } + void OnDeviceRestored() override + { + } + + GraphControl::DX::RenderMain::RenderContext& Context; + }; + DeviceNotify notify{ context }; + context.DxRes = std::make_unique(panel, ¬ify); + std::unique_lock lck{ context.Mtx }; + while (!context.Quit) + { + if (!context.Tasks.empty()) + { + auto task = std::move(context.Tasks.front()); + context.Tasks.pop(); + lck.unlock(); + task(context); + lck.lock(); + } + else + { + context.Cv.wait(lck); + } + } + } }; + } + + static void QuitWorker(GraphControl::DX::RenderMain& self) + { + { + std::scoped_lock lck{ self.m_renderContext.Mtx }; + self.m_renderContext.Quit = true; + } + self.m_renderContext.Cv.notify_one(); + } + + template + static void SubmitTask(const GraphControl::DX::RenderMain& self, F&& task) + { + { + std::scoped_lock lck{ self.m_renderContext.Mtx }; + self.m_renderContext.Tasks.push(std::forward(task)); + } + self.m_renderContext.Cv.notify_one(); + } + }; + using Helper = RenderMainHelper; } namespace GraphControl::DX { - RenderMain::RenderMain(SwapChainPanel ^ panel) - : m_deviceResources{ panel } - , m_nearestPointRenderer{ &m_deviceResources } - , m_backgroundColor{ {} } - , m_swapChainPanel{ panel } - , m_TraceLocation(Point(0, 0)) - , m_Tracing(false) + RenderMain::RenderMain(ctrls::SwapChainPanel ^ panel, Graphing::IGraph* graph) + : m_renderWorker(Helper::CreateRenderWorker(m_renderContext, panel, graph)) { - // Register to be notified if the Device is lost or recreated - m_deviceResources.RegisterDeviceNotify(this); + } - RegisterEventHandlers(); + void RenderMain::FireRenderPass() const + { + Helper::SubmitTask( + *this, + [](RenderContext& renderContext) + { + auto dx = renderContext.DxRes.get(); + auto d3dctx = dx->GetD3DDeviceContext(); + d3dctx->ClearRenderTargetView(dx->GetBackBufferRenderTargetView(), renderContext.BkgColor); + }); + } - m_drawActiveTracing = false; + concurrency::task RenderMain::RunRenderPassAsync() const + { + co_return true; } RenderMain::~RenderMain() { - UnregisterEventHandlers(); - } - - void RenderMain::Graph::set(shared_ptr graph) - { - m_graph = move(graph); - - if (m_graph) - { - if (auto renderer = m_graph->GetRenderer()) - { - float dpi = m_deviceResources.GetDpi(); - renderer->SetDpi(dpi, dpi); - - renderer->SetGraphSize(static_cast(m_swapChainPanel->ActualWidth), static_cast(m_swapChainPanel->ActualHeight)); - } - } - } - - void RenderMain::BackgroundColor::set(Windows::UI::Color backgroundColor) - { - m_backgroundColor[s_RedChannelIndex] = static_cast(backgroundColor.R) / s_MaxChannelValue; - m_backgroundColor[s_GreenChannelIndex] = static_cast(backgroundColor.G) / s_MaxChannelValue; - m_backgroundColor[s_BlueChannelIndex] = static_cast(backgroundColor.B) / s_MaxChannelValue; - m_backgroundColor[s_AlphaChannelIndex] = static_cast(backgroundColor.A) / s_MaxChannelValue; - - RunRenderPass(); - } - - void RenderMain::DrawNearestPoint::set(bool value) - { - if (m_drawNearestPoint != value) - { - m_drawNearestPoint = value; - if (!m_drawNearestPoint) - { - m_Tracing = false; - } - } - } - - void RenderMain::PointerLocation::set(Point location) - { - if (m_pointerLocation != location) - { - m_pointerLocation = location; - - bool wasPointRendered = m_Tracing; - if (CanRenderPoint() || wasPointRendered) - { - RunRenderPassAsync(); - } - } - } - - void RenderMain::ActiveTracing::set(bool value) - { - if (m_drawActiveTracing != value) - { - m_drawActiveTracing = value; - - bool wasPointRendered = m_Tracing; - if (CanRenderPoint() || wasPointRendered) - { - RunRenderPassAsync(); - } - } - } - - bool RenderMain::ActiveTracing::get() - { - return m_drawActiveTracing; - } - - // Updates application state when the window size changes (e.g. device orientation change) - void RenderMain::CreateWindowSizeDependentResources() - { - // TODO: Replace this with the sizedependent initialization of your app's content. - RunRenderPass(); - - if (m_swapChainPanel != nullptr) - { - // Initialize the active tracing location to just above and to the right of the center of the graph area - m_activeTracingPointerLocation.X = m_swapChainPanel->ActualWidth / 2 + 40; - m_activeTracingPointerLocation.Y = m_swapChainPanel->ActualHeight / 2 - 40; - } - } - - bool RenderMain::CanRenderPoint() - { - if (m_graph && (m_drawNearestPoint || m_drawActiveTracing)) - { - Point trackPoint = m_pointerLocation; - - if (m_drawActiveTracing) - { - trackPoint = m_activeTracingPointerLocation; - } - - if (!m_criticalSection.try_lock()) - { - return false; - } - - m_criticalSection.unlock(); - - critical_section::scoped_lock lock(m_criticalSection); - - int formulaId = -1; - double outNearestPointValueX, outNearestPointValueY; - float outNearestPointLocationX, outNearestPointLocationY; - double rhoValueOut, thetaValueOut, tValueOut; - - double xAxisMin, xAxisMax, yAxisMin, yAxisMax; - m_graph->GetRenderer()->GetDisplayRanges(xAxisMin, xAxisMax, yAxisMin, yAxisMax); - double precision = this->GetPrecision(xAxisMax, xAxisMin); - - m_Tracing = m_graph->GetRenderer()->GetClosePointData( - trackPoint.X, - trackPoint.Y, - precision, - formulaId, - outNearestPointLocationX, - outNearestPointLocationY, - outNearestPointValueX, - outNearestPointValueY, - rhoValueOut, - thetaValueOut, - tValueOut) - == S_OK; - m_Tracing = m_Tracing && !isnan(outNearestPointLocationX) && !isnan(outNearestPointLocationY); - } - else - { - m_Tracing = false; - } - - return m_Tracing; - } - - /// - /// Gets the precision value by computing the max and min - /// through this formula: - /// 10^(floor(log(max-min))-3) - /// https://github.com/microsoft/calculator/issues/998 - /// - /// max axis - /// min axis - /// the precision value - double RenderMain::GetPrecision(const double maxAxis, const double minAxis) - { - double exponent = static_cast(floor(log10(maxAxis - minAxis)) - 3); - double precision = pow(10, exponent); - return precision; - } - - void RenderMain::SetPointRadius(float radius) - { - m_nearestPointRenderer.SetRadius(radius); - } - - bool RenderMain::RunRenderPass() - { - // Non async render passes cancel if they can't obtain the lock immediatly - if (!m_criticalSection.try_lock()) - { - return false; - } - - m_criticalSection.unlock(); - - critical_section::scoped_lock lock(m_criticalSection); - - return RunRenderPassInternal(); - } - - IAsyncAction ^ RenderMain::RunRenderPassAsync(bool allowCancel) - { - // Try to cancel the renderPass that is in progress - if (m_renderPass != nullptr && m_renderPass->Status == ::AsyncStatus::Started) - { - m_renderPass->Cancel(); - } - - auto device = m_deviceResources; - auto workItemHandler = ref new WorkItemHandler([this, allowCancel](IAsyncAction ^ action) { - critical_section::scoped_lock lock(m_criticalSection); - - // allowCancel is passed as false when the grapher relies on the render pass to validate that an equation can be succesfully rendered. - // Passing false garauntees that another render pass doesn't cancel this one. - if (allowCancel && action->Status == ::AsyncStatus::Canceled) - { - return; - } - - RunRenderPassInternal(); - }); - - m_renderPass = ThreadPool::RunAsync(workItemHandler, WorkItemPriority::High, WorkItemOptions::None); - - return m_renderPass; - } - - bool RenderMain::RunRenderPassInternal() - { - // We are accessing Direct3D resources directly without Direct2D's knowledge, so we - // must manually acquire and apply the Direct2D factory lock. - ID2D1Multithread* m_D2DMultithread; - m_deviceResources.GetD2DFactory()->QueryInterface(IID_PPV_ARGS(&m_D2DMultithread)); - m_D2DMultithread->Enter(); - - bool succesful = Render(); - - if (succesful) - { - m_deviceResources.Present(); - } - - // It is absolutely critical that the factory lock be released upon - // exiting this function, or else any consequent Direct2D calls will be blocked. - m_D2DMultithread->Leave(); - - m_isRenderPassSuccesful = succesful; - return m_isRenderPassSuccesful; - } - - // Renders the current frame according to the current application state. - // Returns true if the frame was rendered and is ready to be displayed. - bool RenderMain::Render() - { - bool successful = true; - - // Must call BeginDraw before any draw commands. - ID2D1Factory3* pFactory = m_deviceResources.GetD2DFactory(); - ID2D1DeviceContext* pRenderTarget = m_deviceResources.GetD2DDeviceContext(); - - auto context = m_deviceResources.GetD3DDeviceContext(); - - // Clear the back buffer and set the background color. - context->ClearRenderTargetView(m_deviceResources.GetBackBufferRenderTargetView(), m_backgroundColor); - - if (m_graph) - { - if (auto renderer = m_graph->GetRenderer()) - { - pRenderTarget->BeginDraw(); - - bool hasMissingData = false; - m_HResult = renderer->DrawD2D1(pFactory, pRenderTarget, hasMissingData); - - successful = SUCCEEDED(m_HResult); - - // We ignore D2DERR_RECREATE_TARGET here. This error indicates that the device - // is lost. It will be handled during the next call to Present. - HRESULT endDraw = pRenderTarget->EndDraw(); - if (endDraw != D2DERR_RECREATE_TARGET) - { - DX::ThrowIfFailed(endDraw); - } - - if (successful) - { - if (m_drawNearestPoint || m_drawActiveTracing) - { - Point trackPoint = m_pointerLocation; - if (m_drawActiveTracing) - { - // Active tracing takes over for draw nearest point input from the mouse pointer. - trackPoint = m_activeTracingPointerLocation; - } - - int formulaId = -1; - double outNearestPointValueX, outNearestPointValueY; - double rhoValueOut, thetaValueOut, tValueOut; - float outNearestPointLocationX, outNearestPointLocationY; - double xAxisMin, xAxisMax, yAxisMin, yAxisMax; - renderer->GetDisplayRanges(xAxisMin, xAxisMax, yAxisMin, yAxisMax); - double precision = this->GetPrecision(xAxisMax, xAxisMin); - if (renderer->GetClosePointData( - trackPoint.X, - trackPoint.Y, - precision, - formulaId, - outNearestPointLocationX, - outNearestPointLocationY, - outNearestPointValueX, - outNearestPointValueY, - rhoValueOut, - thetaValueOut, - tValueOut) - == S_OK) - { - if (!isnan(outNearestPointLocationX) && !isnan(outNearestPointLocationY)) - { - auto lineColors = m_graph->GetOptions().GetGraphColors(); - - if (formulaId >= 0 && static_cast(formulaId) < lineColors.size()) - { - auto dotColor = lineColors[formulaId]; - m_nearestPointRenderer.SetColor(D2D1::ColorF(dotColor.R * 65536 + dotColor.G * 256 + dotColor.B, 1.0)); - } - - m_TraceLocation = Point(outNearestPointLocationX, outNearestPointLocationY); - m_nearestPointRenderer.Render(m_TraceLocation); - m_Tracing = true; - m_TraceLocation = Point(outNearestPointLocationX, outNearestPointLocationY); - m_XTraceValue = outNearestPointValueX; - m_YTraceValue = outNearestPointValueY; - } - else - { - m_Tracing = false; - } - } - else - { - m_Tracing = false; - } - } - } - } - } - - return successful; - } - - HRESULT RenderMain::GetRenderError() - { - return m_HResult; - } - - void RenderMain::OnLoaded(Object ^ sender, RoutedEventArgs ^ e) - { - RunRenderPass(); - } - - void RenderMain::RegisterEventHandlers() - { - UnregisterEventHandlers(); - - // Register event handlers for control lifecycle. - m_coreWindow = Agile(Window::Current->CoreWindow); - if (m_coreWindow != nullptr) - { - m_tokenVisibilityChanged = m_coreWindow->VisibilityChanged += - ref new TypedEventHandler(this, &RenderMain::OnVisibilityChanged); - } - - m_displayInformation = DisplayInformation::GetForCurrentView(); - if (m_displayInformation != nullptr) - { - m_tokenDpiChanged = m_displayInformation->DpiChanged += ref new TypedEventHandler(this, &RenderMain::OnDpiChanged); - - m_tokenOrientationChanged = m_displayInformation->OrientationChanged += - ref new TypedEventHandler(this, &RenderMain::OnOrientationChanged); - } - - m_tokenDisplayContentsInvalidated = DisplayInformation::DisplayContentsInvalidated += - ref new TypedEventHandler(this, &RenderMain::OnDisplayContentsInvalidated); - - if (m_swapChainPanel != nullptr) - { - m_tokenLoaded = m_swapChainPanel->Loaded += ref new RoutedEventHandler(this, &RenderMain::OnLoaded); - - m_tokenCompositionScaleChanged = m_swapChainPanel->CompositionScaleChanged += - ref new TypedEventHandler(this, &RenderMain::OnCompositionScaleChanged); - - m_tokenSizeChanged = m_swapChainPanel->SizeChanged += ref new SizeChangedEventHandler(this, &RenderMain::OnSizeChanged); - } - } - - void RenderMain::UnregisterEventHandlers() - { - if (m_coreWindow != nullptr) - { - if (m_tokenVisibilityChanged.Value != 0) - { - m_coreWindow->VisibilityChanged -= m_tokenVisibilityChanged; - m_tokenVisibilityChanged.Value = 0; - } - m_coreWindow = nullptr; - } - - if (m_displayInformation != nullptr) - { - if (m_tokenDpiChanged.Value != 0) - { - m_displayInformation->DpiChanged -= m_tokenDpiChanged; - m_tokenDpiChanged.Value = 0; - } - if (m_tokenOrientationChanged.Value != 0) - { - m_displayInformation->OrientationChanged -= m_tokenOrientationChanged; - m_tokenOrientationChanged.Value = 0; - } - m_displayInformation = nullptr; - } - - if (m_tokenDisplayContentsInvalidated.Value != 0) - { - DisplayInformation::DisplayContentsInvalidated -= m_tokenDisplayContentsInvalidated; - m_tokenDisplayContentsInvalidated.Value = 0; - } - - if (m_swapChainPanel != nullptr) - { - if (m_tokenLoaded.Value != 0) - { - m_swapChainPanel->Loaded -= m_tokenLoaded; - m_tokenLoaded.Value = 0; - } - if (m_tokenCompositionScaleChanged.Value != 0) - { - m_swapChainPanel->CompositionScaleChanged -= m_tokenCompositionScaleChanged; - m_tokenCompositionScaleChanged.Value = 0; - } - if (m_tokenSizeChanged.Value != 0) - { - m_swapChainPanel->SizeChanged -= m_tokenSizeChanged; - m_tokenSizeChanged.Value = 0; - } - } - } - - void RenderMain::OnVisibilityChanged(CoreWindow ^ sender, VisibilityChangedEventArgs ^ args) - { - if (args->Visible) - { - RunRenderPass(); - } - } - - void RenderMain::OnDpiChanged(DisplayInformation ^ sender, Object ^ args) - { - // Note: The value for LogicalDpi retrieved here may not match the effective DPI of the app - // if it is being scaled for high resolution devices. Once the DPI is set on DeviceResources, - // you should always retrieve it using the GetDpi method. - // See DeviceResources.cpp for more details. - m_deviceResources.SetDpi(sender->LogicalDpi); - - if (m_graph) - { - if (auto renderer = m_graph->GetRenderer()) - { - float dpi = m_deviceResources.GetDpi(); - renderer->SetDpi(dpi, dpi); - } - } - - CreateWindowSizeDependentResources(); - } - - void RenderMain::OnOrientationChanged(DisplayInformation ^ sender, Object ^ args) - { - m_deviceResources.SetCurrentOrientation(sender->CurrentOrientation); - CreateWindowSizeDependentResources(); - } - - void RenderMain::OnDisplayContentsInvalidated(DisplayInformation ^ sender, Object ^ args) - { - m_deviceResources.ValidateDevice(); - } - - void RenderMain::OnCompositionScaleChanged(SwapChainPanel ^ sender, Object ^ args) - { - m_deviceResources.SetCompositionScale(sender->CompositionScaleX, sender->CompositionScaleY); - CreateWindowSizeDependentResources(); - } - - void RenderMain::OnSizeChanged(Object ^ sender, SizeChangedEventArgs ^ e) - { - m_deviceResources.SetLogicalSize(e->NewSize); - - if (m_graph) - { - if (auto renderer = m_graph->GetRenderer()) - { - const auto& newSize = e->NewSize; - renderer->SetGraphSize(static_cast(newSize.Width), static_cast(newSize.Height)); - } - } - - CreateWindowSizeDependentResources(); - } - - // Notifies renderers that device resources need to be released. - void RenderMain::OnDeviceLost() - { - m_nearestPointRenderer.ReleaseDeviceDependentResources(); - } - - // Notifies renderers that device resources may now be recreated. - void RenderMain::OnDeviceRestored() - { - m_nearestPointRenderer.CreateDeviceDependentResources(); + Helper::QuitWorker(*this); + m_renderWorker.join(); } } diff --git a/src/GraphControl/DirectX/RenderMain.h b/src/GraphControl/DirectX/RenderMain.h index be021ba5..1ef66295 100644 --- a/src/GraphControl/DirectX/RenderMain.h +++ b/src/GraphControl/DirectX/RenderMain.h @@ -3,207 +3,72 @@ #pragma once -// Taken from the default template for Xaml and Direct3D 11 apps. +#include +#include +#include +#include +// Taken from the default template for Xaml and Direct3D 11 apps. #include "DeviceResources.h" #include "NearestPointRenderer.h" #include "IGraph.h" +namespace +{ + struct RenderMainHelper; +} + // Renders Direct2D and 3D content on the screen. namespace GraphControl::DX { - ref class RenderMain sealed : public IDeviceNotify + class RenderMain { + friend struct RenderMainHelper; + public: - virtual ~RenderMain(); + explicit RenderMain(Windows::UI::Xaml::Controls::SwapChainPanel ^ panel, Graphing::IGraph* graph); + ~RenderMain(); - // IDeviceNotify - virtual void OnDeviceLost(); - virtual void OnDeviceRestored(); + void FireRenderPass() const; + concurrency::task RunRenderPassAsync() const; - internal : RenderMain(Windows::UI::Xaml::Controls::SwapChainPanel ^ panel); - - property std::shared_ptr Graph + bool ActiveTracing() const noexcept { - void set(std::shared_ptr graph); + return m_renderContext.ActiveTracing; + } + Windows::Foundation::Point ActiveTraceCursorPosition() const noexcept + { + return m_renderContext.ActiveTraceCursorPosition; + } + Windows::Foundation::Point TraceLocation() const noexcept + { + std::scoped_lock lck{ m_renderContext.Mtx }; + return m_renderContext.TraceLocation; } - property Windows::UI::Color BackgroundColor - { - void set(Windows::UI::Color color); - } - - property bool DrawNearestPoint - { - void set(bool value); - } - - property Windows::Foundation::Point PointerLocation - { - void set(Windows::Foundation::Point location); - } - - void CreateWindowSizeDependentResources(); - - bool RenderMain::CanRenderPoint(); - - void SetPointRadius(float radius); - - bool RunRenderPass(); - - Windows::Foundation::IAsyncAction ^ RunRenderPassAsync(bool allowCancel = true); - - Concurrency::critical_section& GetCriticalSection() - { - return m_criticalSection; - } - - bool IsRenderPassSuccesful() - { - return m_isRenderPassSuccesful; - } - - HRESULT GetRenderError(); - - // Indicates if we are in active tracing mode (the tracing box is being used and controlled through keyboard input) - property bool ActiveTracing - { - bool get(); - void set(bool value); - } - - property Windows::Foundation::Point ActiveTraceCursorPosition - { - Windows::Foundation::Point get() - { - return m_activeTracingPointerLocation; - } - - void set(Windows::Foundation::Point newValue) - { - if (m_activeTracingPointerLocation != newValue) - { - m_activeTracingPointerLocation = newValue; - - bool wasPointRendered = m_Tracing; - if (CanRenderPoint() || wasPointRendered) - { - RunRenderPassAsync(); - } - } - } - } - - property double XTraceValue - { - double get() - { - return m_XTraceValue; - } - } - - property double YTraceValue - { - double get() - { - return m_YTraceValue; - } - } - - property Windows::Foundation::Point TraceLocation - { - Windows::Foundation::Point get() - { - return m_TraceLocation; - } - } - - // Any time we should be showing the tracing popup (either active or passive tracing) - property bool Tracing - { - bool get() - { - return m_Tracing; - } - } + void ActiveTracing(bool value) noexcept; + void ActiveTraceCursorPosition(const Windows::Foundation::Point& value) noexcept; + void BackgroundColor(const Windows::UI::Color& value) noexcept; + void PointerLocation(const Windows::Foundation::Point& value) noexcept; + void PointRadius(float radius) noexcept; private: - bool Render(); - - bool RunRenderPassInternal(); - - // Loaded/Unloaded - void OnLoaded(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e); - - // Dependent event registration - void RegisterEventHandlers(); - void UnregisterEventHandlers(); - - // Window event handlers. - void OnVisibilityChanged(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::VisibilityChangedEventArgs ^ args); - - // DisplayInformation event handlers. - void OnDpiChanged(Windows::Graphics::Display::DisplayInformation ^ sender, Platform::Object ^ args); - void OnOrientationChanged(Windows::Graphics::Display::DisplayInformation ^ sender, Platform::Object ^ args); - void OnDisplayContentsInvalidated(Windows::Graphics::Display::DisplayInformation ^ sender, Platform::Object ^ args); - - // Other event handlers. - void OnCompositionScaleChanged(Windows::UI::Xaml::Controls::SwapChainPanel ^ sender, Object ^ args); - void OnSizeChanged(Platform::Object ^ sender, Windows::UI::Xaml::SizeChangedEventArgs ^ e); - - double GetPrecision(const double maxAxis, const double minAxis); + struct RenderContext + { + std::unique_ptr DxRes; + Graphing::IGraph* Graph; + mutable std::mutex Mtx; + mutable std::condition_variable Cv; + mutable std::queue> Tasks; + Windows::Foundation::Point ActiveTraceCursorPosition = {}; + Windows::Foundation::Point TraceLocation = {}; + float BkgColor[4] = {}; + bool ActiveTracing = false; + bool Quit = false; + }; private: - DX::DeviceResources m_deviceResources; - NearestPointRenderer m_nearestPointRenderer; - - // Cached Graph object with Renderer property. - std::shared_ptr m_graph = nullptr; - - // Track current input pointer position. - bool m_drawNearestPoint = false; - Windows::Foundation::Point m_pointerLocation; - - // Track current active tracing pointer position. - bool m_drawActiveTracing = false; - Windows::Foundation::Point m_activeTracingPointerLocation; - - float m_backgroundColor[4]; - - // The SwapChainPanel^ surface. - Windows::UI::Xaml::Controls::SwapChainPanel ^ m_swapChainPanel = nullptr; - Windows::Foundation::EventRegistrationToken m_tokenLoaded; - Windows::Foundation::EventRegistrationToken m_tokenCompositionScaleChanged; - Windows::Foundation::EventRegistrationToken m_tokenSizeChanged; - - // Cached references to event notifiers. - Platform::Agile m_coreWindow = nullptr; - Windows::Foundation::EventRegistrationToken m_tokenVisibilityChanged; - - Windows::Graphics::Display::DisplayInformation ^ m_displayInformation = nullptr; - Windows::Foundation::EventRegistrationToken m_tokenDpiChanged; - Windows::Foundation::EventRegistrationToken m_tokenOrientationChanged; - Windows::Foundation::EventRegistrationToken m_tokenDisplayContentsInvalidated; - - // Track our independent input on a background worker thread. - Windows::Foundation::IAsyncAction ^ m_inputLoopWorker = nullptr; - Windows::UI::Core::CoreIndependentInputSource ^ m_coreInput = nullptr; - - double m_XTraceValue; - double m_YTraceValue; - - // And where is it located on screen - Windows::Foundation::Point m_TraceLocation; - - // Are we currently showing the tracing value - bool m_Tracing; - - Concurrency::critical_section m_criticalSection; - - Windows::Foundation::IAsyncAction ^ m_renderPass = nullptr; - - bool m_isRenderPassSuccesful; - - HRESULT m_HResult; + RenderContext m_renderContext; + std::thread m_renderWorker; }; }