Allow rendering the graph on a background thread (#1014)
* Render on background thread * More work * variable fix * Add comments
This commit is contained in:
@@ -12,11 +12,13 @@ using namespace GraphControl::DX;
|
||||
using namespace Platform;
|
||||
using namespace Platform::Collections;
|
||||
using namespace std;
|
||||
using namespace Concurrency;
|
||||
using namespace Windows::Devices::Input;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
using namespace Windows::Storage::Streams;
|
||||
using namespace Windows::System;
|
||||
using namespace Windows::System::Threading;
|
||||
using namespace Windows::UI;
|
||||
using namespace Windows::UI::Core;
|
||||
using namespace Windows::UI::Input;
|
||||
@@ -228,9 +230,9 @@ namespace GraphControl
|
||||
TryPlotGraph(keepCurrentView, false);
|
||||
}
|
||||
|
||||
void Grapher::TryPlotGraph(bool keepCurrentView, bool shouldRetry)
|
||||
task<void> Grapher::TryPlotGraph(bool keepCurrentView, bool shouldRetry)
|
||||
{
|
||||
if (TryUpdateGraph(keepCurrentView))
|
||||
if (co_await TryUpdateGraph(keepCurrentView))
|
||||
{
|
||||
SetEquationsAsValid();
|
||||
}
|
||||
@@ -241,12 +243,12 @@ namespace GraphControl
|
||||
// If we failed to plot the graph, try again after the bad equations are flagged.
|
||||
if (shouldRetry)
|
||||
{
|
||||
TryUpdateGraph(keepCurrentView);
|
||||
co_await TryUpdateGraph(keepCurrentView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Grapher::TryUpdateGraph(bool keepCurrentView)
|
||||
task<bool> Grapher::TryUpdateGraph(bool keepCurrentView)
|
||||
{
|
||||
optional<vector<shared_ptr<IEquation>>> initResult = nullopt;
|
||||
bool successful = false;
|
||||
@@ -282,7 +284,7 @@ namespace GraphControl
|
||||
// If the equation request failed, then fail graphing.
|
||||
if (equationRequest == nullptr)
|
||||
{
|
||||
return false;
|
||||
co_return false;
|
||||
}
|
||||
|
||||
request += equationRequest;
|
||||
@@ -303,7 +305,8 @@ namespace GraphControl
|
||||
m_renderMain->Graph = m_graph;
|
||||
|
||||
// It is possible that the render fails, in that case fall through to explicit empty initialization
|
||||
if (m_renderMain->RunRenderPass())
|
||||
co_await m_renderMain->RunRenderPassAsync(false);
|
||||
if (m_renderMain->IsRenderPassSuccesful())
|
||||
{
|
||||
UpdateVariables();
|
||||
successful = true;
|
||||
@@ -329,7 +332,7 @@ namespace GraphControl
|
||||
SetGraphArgs();
|
||||
|
||||
m_renderMain->Graph = m_graph;
|
||||
m_renderMain->RunRenderPass();
|
||||
co_await m_renderMain->RunRenderPassAsync();
|
||||
|
||||
UpdateVariables();
|
||||
|
||||
@@ -341,7 +344,7 @@ namespace GraphControl
|
||||
}
|
||||
|
||||
// Return true if we were able to graph and render all graphable equations
|
||||
return successful;
|
||||
co_return successful;
|
||||
}
|
||||
|
||||
void Grapher::SetEquationsAsValid()
|
||||
@@ -365,8 +368,10 @@ namespace GraphControl
|
||||
|
||||
void Grapher::SetGraphArgs()
|
||||
{
|
||||
if (m_graph)
|
||||
if (m_graph != nullptr && m_renderMain != nullptr)
|
||||
{
|
||||
critical_section::scoped_lock lock(m_renderMain->GetCriticalSection());
|
||||
|
||||
for (auto variable : Variables)
|
||||
{
|
||||
m_graph->SetArgValue(variable->Key->Data(), variable->Value);
|
||||
@@ -436,14 +441,19 @@ namespace GraphControl
|
||||
|
||||
Variables->Insert(variableName, newValue);
|
||||
|
||||
if (m_graph)
|
||||
if (m_graph != nullptr && m_renderMain != nullptr)
|
||||
{
|
||||
m_graph->SetArgValue(variableName->Data(), newValue);
|
||||
|
||||
if (m_renderMain)
|
||||
{
|
||||
m_renderMain->RunRenderPass();
|
||||
}
|
||||
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->RunRenderPassAsync();
|
||||
});
|
||||
|
||||
ThreadPool::RunAsync(workItemHandler, WorkItemPriority::High, WorkItemOptions::None);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -272,8 +272,8 @@ public
|
||||
void OnEquationChanged(Equation ^ equation);
|
||||
void OnEquationStyleChanged(Equation ^ equation);
|
||||
void OnEquationLineEnabledChanged(Equation ^ equation);
|
||||
bool TryUpdateGraph(bool keepCurrentView);
|
||||
void TryPlotGraph(bool keepCurrentView, bool shouldRetry);
|
||||
concurrency::task<bool> TryUpdateGraph(bool keepCurrentView);
|
||||
concurrency::task<void> TryPlotGraph(bool keepCurrentView, bool shouldRetry);
|
||||
void UpdateGraphOptions(Graphing::IGraphingOptions& options, const std::vector<Equation ^>& validEqs);
|
||||
std::vector<Equation ^> GetGraphableEquations();
|
||||
void SetGraphArgs();
|
||||
|
@@ -63,7 +63,6 @@ namespace GraphControl::DX
|
||||
renderer->SetDpi(dpi, dpi);
|
||||
|
||||
renderer->SetGraphSize(static_cast<unsigned int>(m_swapChainPanel->ActualWidth), static_cast<unsigned int>(m_swapChainPanel->ActualHeight));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,6 +129,54 @@ namespace GraphControl::DX
|
||||
|
||||
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)
|
||||
@@ -137,7 +184,12 @@ namespace GraphControl::DX
|
||||
m_deviceResources.Present();
|
||||
}
|
||||
|
||||
return succesful;
|
||||
// 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.
|
||||
@@ -204,11 +256,11 @@ namespace GraphControl::DX
|
||||
{
|
||||
auto lineColors = m_graph->GetOptions().GetGraphColors();
|
||||
|
||||
if (formulaId >= 0 && static_cast<unsigned int>(formulaId) < lineColors.size())
|
||||
{
|
||||
auto dotColor = lineColors[formulaId];
|
||||
m_nearestPointRenderer.SetColor(D2D1::ColorF(dotColor.R * 65536 + dotColor.G * 256 + dotColor.B, 1.0));
|
||||
}
|
||||
if (formulaId >= 0 && static_cast<unsigned int>(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(nearestPointLocationX, nearestPointLocationY);
|
||||
m_nearestPointRenderer.Render(m_TraceLocation);
|
||||
|
@@ -47,6 +47,18 @@ namespace GraphControl::DX
|
||||
|
||||
bool RunRenderPass();
|
||||
|
||||
Windows::Foundation::IAsyncAction ^ RunRenderPassAsync(bool allowCancel = true);
|
||||
|
||||
Concurrency::critical_section& GetCriticalSection()
|
||||
{
|
||||
return m_criticalSection;
|
||||
}
|
||||
|
||||
bool IsRenderPassSuccesful()
|
||||
{
|
||||
return m_isRenderPassSuccesful;
|
||||
}
|
||||
|
||||
// Indicates if we are in active tracing mode (the tracing box is being used and controlled through keyboard input)
|
||||
property bool ActiveTracing
|
||||
{
|
||||
@@ -99,6 +111,8 @@ namespace GraphControl::DX
|
||||
private:
|
||||
bool Render();
|
||||
|
||||
bool RunRenderPassInternal();
|
||||
|
||||
// Loaded/Unloaded
|
||||
void OnLoaded(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);
|
||||
|
||||
@@ -162,5 +176,11 @@ namespace GraphControl::DX
|
||||
|
||||
// Are we currently showing the tracing value
|
||||
bool m_Tracing;
|
||||
|
||||
Concurrency::critical_section m_criticalSection;
|
||||
|
||||
Windows::Foundation::IAsyncAction ^ m_renderPass = nullptr;
|
||||
|
||||
bool m_isRenderPassSuccesful;
|
||||
};
|
||||
}
|
||||
|
Reference in New Issue
Block a user