Allow rendering the graph on a background thread (#1014)
* Render on background thread * More work * variable fix * Add comments
This commit is contained in:
parent
f4ab94ce1c
commit
f1482252ef
@ -47,11 +47,13 @@ public
|
||||
{
|
||||
if (value < Min)
|
||||
{
|
||||
value = Min;
|
||||
Min = value;
|
||||
RaisePropertyChanged(L"Min");
|
||||
}
|
||||
else if (value > Max)
|
||||
{
|
||||
value = Max;
|
||||
Max = value;
|
||||
RaisePropertyChanged(L"Max");
|
||||
}
|
||||
|
||||
if (Value != value)
|
||||
|
@ -319,26 +319,6 @@ void EquationTextBox::OnHasErrorPropertyChanged(bool, bool)
|
||||
UpdateCommonVisualState();
|
||||
}
|
||||
|
||||
Platform::String ^ EquationTextBox::GetEquationText()
|
||||
{
|
||||
String ^ text;
|
||||
if (m_richEditBox != nullptr)
|
||||
{
|
||||
// Clear formatting since the graph control doesn't work with bold/underlines
|
||||
ITextRange ^ range = m_richEditBox->TextDocument->GetRange(0, m_richEditBox->TextDocument->Selection->EndPosition);
|
||||
|
||||
if (range != nullptr)
|
||||
{
|
||||
range->CharacterFormat->Bold = FormatEffect::Off;
|
||||
range->CharacterFormat->Underline = UnderlineType::None;
|
||||
}
|
||||
|
||||
text = m_richEditBox->MathText;
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
void EquationTextBox::SetEquationText(Platform::String ^ equationText)
|
||||
{
|
||||
if (m_richEditBox != nullptr)
|
||||
|
@ -33,7 +33,6 @@ namespace CalculatorApp
|
||||
event Windows::Foundation::EventHandler<MathRichEditBoxFormatRequest ^> ^ EquationFormatRequested;
|
||||
event Windows::UI::Xaml::RoutedEventHandler ^ EquationButtonClicked;
|
||||
|
||||
Platform::String ^ GetEquationText();
|
||||
void SetEquationText(Platform::String ^ equationText);
|
||||
void FocusTextBox();
|
||||
|
||||
|
@ -12,6 +12,7 @@ using namespace std;
|
||||
using namespace Windows::ApplicationModel;
|
||||
using namespace Windows::UI::Xaml;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
using namespace Windows::UI::Text;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
using namespace Windows::System;
|
||||
|
||||
@ -178,6 +179,15 @@ void MathRichEditBox::BackSpace()
|
||||
|
||||
void MathRichEditBox::SubmitEquation(EquationSubmissionSource source)
|
||||
{
|
||||
// Clear formatting since the graph control doesn't work with bold/underlines
|
||||
auto range = this->TextDocument->GetRange(0, this->TextDocument->Selection->EndPosition);
|
||||
|
||||
if (range != nullptr)
|
||||
{
|
||||
range->CharacterFormat->Bold = FormatEffect::Off;
|
||||
range->CharacterFormat->Underline = UnderlineType::None;
|
||||
}
|
||||
|
||||
auto newVal = GetMathTextProperty();
|
||||
if (MathText != newVal)
|
||||
{
|
||||
|
@ -330,9 +330,6 @@ void EquationInputArea::SubmitTextbox(TextBox ^ sender)
|
||||
{
|
||||
val = validateDouble(sender->Text, variableViewModel->Value);
|
||||
variableViewModel->Value = val;
|
||||
|
||||
// Assign back to val in case it gets changed due to min/max
|
||||
val = variableViewModel->Value;
|
||||
}
|
||||
else if (sender->Name == "MinTextBox")
|
||||
{
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user