refactor draft

This commit is contained in:
Tian Liao 2024-04-03 19:06:36 +08:00
commit 1de66142b5
6 changed files with 177 additions and 784 deletions

View file

@ -97,19 +97,11 @@ namespace GraphControl
{ {
if (auto renderer = m_graph->GetRenderer()) if (auto renderer = m_graph->GetRenderer())
{ {
m_renderMain->GetCriticalSection().lock();
if (SUCCEEDED(renderer->ScaleRange(centerX, centerY, scale))) if (SUCCEEDED(renderer->ScaleRange(centerX, centerY, scale)))
{ {
m_renderMain->GetCriticalSection().unlock(); m_renderMain->FireRenderPass();
m_renderMain->RunRenderPass();
GraphViewChangedEvent(this, GraphViewChangedReason::Manipulation); GraphViewChangedEvent(this, GraphViewChangedReason::Manipulation);
} }
else
{
m_renderMain->GetCriticalSection().unlock();
}
} }
} }
} }
@ -145,7 +137,7 @@ namespace GraphControl
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
m_renderMain->RunRenderPass(); m_renderMain->FireRenderPass();
GraphViewChangedEvent(this, GraphViewChangedReason::Reset); GraphViewChangedEvent(this, GraphViewChangedReason::Reset);
} }
} }
@ -158,8 +150,8 @@ namespace GraphControl
if (swapChainPanel) if (swapChainPanel)
{ {
swapChainPanel->AllowFocusOnInteraction = true; swapChainPanel->AllowFocusOnInteraction = true;
m_renderMain = ref new RenderMain(swapChainPanel); m_renderMain = std::make_unique<DX::RenderMain>(swapChainPanel, m_graph.get());
m_renderMain->BackgroundColor = GraphBackground; m_renderMain->BackgroundColor(GraphBackground);
} }
TryUpdateGraph(false); TryUpdateGraph(false);
@ -220,7 +212,7 @@ namespace GraphControl
if (m_renderMain) if (m_renderMain)
{ {
m_renderMain->RunRenderPass(); m_renderMain->FireRenderPass();
} }
} }
@ -404,11 +396,9 @@ namespace GraphControl
UpdateGraphOptions(m_graph->GetOptions(), validEqs); UpdateGraphOptions(m_graph->GetOptions(), validEqs);
SetGraphArgs(m_graph); SetGraphArgs(m_graph);
m_renderMain->Graph = m_graph;
// It is possible that the render fails, in that case fall through to explicit empty initialization // It is possible that the render fails, in that case fall through to explicit empty initialization
co_await m_renderMain->RunRenderPassAsync(false); auto succ = co_await m_renderMain->RunRenderPassAsync();
if (m_renderMain->IsRenderPassSuccesful()) if (succ)
{ {
UpdateVariables(); UpdateVariables();
successful = true; successful = true;
@ -438,7 +428,6 @@ namespace GraphControl
UpdateGraphOptions(m_graph->GetOptions(), vector<Equation ^>()); UpdateGraphOptions(m_graph->GetOptions(), vector<Equation ^>());
SetGraphArgs(m_graph); SetGraphArgs(m_graph);
m_renderMain->Graph = m_graph;
co_await m_renderMain->RunRenderPassAsync(); co_await m_renderMain->RunRenderPassAsync();
UpdateVariables(); UpdateVariables();
@ -479,8 +468,6 @@ namespace GraphControl
{ {
if (graph != nullptr && m_renderMain != nullptr) if (graph != nullptr && m_renderMain != nullptr)
{ {
critical_section::scoped_lock lock(m_renderMain->GetCriticalSection());
for (auto variablePair : Variables) for (auto variablePair : Variables)
{ {
graph->SetArgValue(variablePair->Key->Data(), variablePair->Value->Value); graph->SetArgValue(variablePair->Key->Data(), variablePair->Value->Value);
@ -557,15 +544,8 @@ namespace GraphControl
if (m_graph != nullptr && m_renderMain != nullptr) if (m_graph != nullptr && m_renderMain != nullptr)
{ {
auto workItemHandler = ref new WorkItemHandler([this, variableName, newValue](IAsyncAction ^ action) { m_graph->SetArgValue(variableName->Data(), newValue);
m_renderMain->GetCriticalSection().lock(); m_renderMain->FireRenderPass();
m_graph->SetArgValue(variableName->Data(), newValue);
m_renderMain->GetCriticalSection().unlock();
m_renderMain->RunRenderPass();
});
ThreadPool::RunAsync(workItemHandler, WorkItemPriority::High, WorkItemOptions::None);
} }
} }
@ -668,7 +648,7 @@ namespace GraphControl
m_renderMain->DrawNearestPoint = true; m_renderMain->DrawNearestPoint = true;
Point currPosition = e->GetCurrentPoint(/* relativeTo */ this)->Position; Point currPosition = e->GetCurrentPoint(/* relativeTo */ this)->Position;
if (m_renderMain->ActiveTracing) if (m_renderMain->ActiveTracing())
{ {
PointerValueChangedEvent(currPosition); PointerValueChangedEvent(currPosition);
ActiveTraceCursorPosition = currPosition; ActiveTraceCursorPosition = currPosition;
@ -681,7 +661,7 @@ namespace GraphControl
} }
else if (m_cachedCursor != nullptr) else if (m_cachedCursor != nullptr)
{ {
m_renderMain->PointerLocation = currPosition; m_renderMain->PointerLocation(currPosition);
::CoreWindow::GetForCurrentThread()->PointerCursor = m_cachedCursor; ::CoreWindow::GetForCurrentThread()->PointerCursor = m_cachedCursor;
m_cachedCursor = nullptr; m_cachedCursor = nullptr;
@ -690,7 +670,7 @@ namespace GraphControl
} }
else else
{ {
m_renderMain->PointerLocation = currPosition; m_renderMain->PointerLocation(currPosition);
UpdateTracingChanged(); UpdateTracingChanged();
} }
@ -783,15 +763,10 @@ namespace GraphControl
translationX /= -width; translationX /= -width;
translationY /= height; translationY /= height;
m_renderMain->GetCriticalSection().lock();
if (FAILED(renderer->MoveRangeByRatio(translationX, translationY))) if (FAILED(renderer->MoveRangeByRatio(translationX, translationY)))
{ {
m_renderMain->GetCriticalSection().unlock();
return; return;
} }
m_renderMain->GetCriticalSection().unlock();
needsRenderPass = true; needsRenderPass = true;
} }
@ -805,21 +780,16 @@ namespace GraphControl
const auto& pos = e->Position; const auto& pos = e->Position;
const auto [centerX, centerY] = PointerPositionToGraphPosition(pos.X, pos.Y, width, height); const auto [centerX, centerY] = PointerPositionToGraphPosition(pos.X, pos.Y, width, height);
m_renderMain->GetCriticalSection().lock();
if (FAILED(renderer->ScaleRange(centerX, centerY, scale))) if (FAILED(renderer->ScaleRange(centerX, centerY, scale)))
{ {
m_renderMain->GetCriticalSection().unlock();
return; return;
} }
m_renderMain->GetCriticalSection().unlock();
needsRenderPass = true; needsRenderPass = true;
} }
if (needsRenderPass) if (needsRenderPass)
{ {
m_renderMain->RunRenderPass(); m_renderMain->FireRenderPass();
GraphViewChangedEvent(this, GraphViewChangedReason::Manipulation); GraphViewChangedEvent(this, GraphViewChangedReason::Manipulation);
} }
} }
@ -1075,7 +1045,7 @@ void Grapher::OnGraphBackgroundPropertyChanged(Windows::UI::Color /*oldValue*/,
{ {
if (m_renderMain) if (m_renderMain)
{ {
m_renderMain->BackgroundColor = newValue; m_renderMain->BackgroundColor(newValue);
} }
if (m_graph) 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); auto gridLinesColor = Graphing::Color(newValue.R, newValue.G, newValue.B, newValue.A);
m_graph->GetOptions().SetGridColor(gridLinesColor); 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()); UpdateGraphOptions(m_graph->GetOptions(), GetGraphableEquations());
if (m_renderMain) if (m_renderMain)
{ {
m_renderMain->SetPointRadius(LineWidth + 1); m_renderMain->PointRadius(LineWidth + 1);
m_renderMain->RunRenderPass(); m_renderMain->FireRenderPass();
TraceLogger::GetInstance()->LogLineWidthChanged(); TraceLogger::GetInstance()->LogLineWidthChanged();
} }
@ -1112,7 +1082,6 @@ void Grapher::OnLineWidthPropertyChanged(double oldValue, double newValue)
optional<vector<shared_ptr<Graphing::IEquation>>> Grapher::TryInitializeGraph(bool keepCurrentView, const IExpression* graphingExp) optional<vector<shared_ptr<Graphing::IEquation>>> Grapher::TryInitializeGraph(bool keepCurrentView, const IExpression* graphingExp)
{ {
critical_section::scoped_lock lock(m_renderMain->GetCriticalSection());
if (keepCurrentView || IsKeepCurrentView) if (keepCurrentView || IsKeepCurrentView)
{ {
auto renderer = m_graph->GetRenderer(); auto renderer = m_graph->GetRenderer();

View file

@ -25,11 +25,12 @@ public
public public
delegate void PointerValueChangedEventHandler(Windows::Foundation::Point value); delegate void PointerValueChangedEventHandler(Windows::Foundation::Point value);
public enum class GraphViewChangedReason public
{ enum class GraphViewChangedReason
Manipulation, {
Reset Manipulation,
}; Reset
};
[Windows::UI::Xaml::Markup::ContentPropertyAttribute(Name = L"Equations")] public ref class Grapher sealed [Windows::UI::Xaml::Markup::ContentPropertyAttribute(Name = L"Equations")] public ref class Grapher sealed
: public Windows::UI::Xaml::Controls::Control, : public Windows::UI::Xaml::Controls::Control,
@ -65,14 +66,14 @@ public enum class GraphViewChangedReason
{ {
bool get() bool get()
{ {
return m_renderMain != nullptr && m_renderMain->ActiveTracing; return m_renderMain != nullptr && m_renderMain->ActiveTracing();
} }
void set(bool value) 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(); UpdateTracingChanged();
PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs(L"ActiveTracing")); PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs(L"ActiveTracing"));
} }
@ -86,7 +87,7 @@ public enum class GraphViewChangedReason
{ {
Windows::Foundation::Point get() Windows::Foundation::Point get()
{ {
return m_renderMain->TraceLocation; return m_renderMain->TraceLocation();
} }
} }
@ -94,14 +95,14 @@ public enum class GraphViewChangedReason
{ {
Windows::Foundation::Point get() Windows::Foundation::Point get()
{ {
return m_renderMain->ActiveTraceCursorPosition; return m_renderMain->ActiveTraceCursorPosition();
} }
void set(Windows::Foundation::Point newValue) 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(); UpdateTracingChanged();
} }
} }
@ -154,7 +155,7 @@ public enum class GraphViewChangedReason
m_graph->GetOptions().SetDefaultXRange(newValue); m_graph->GetOptions().SetDefaultXRange(newValue);
if (m_renderMain != nullptr) if (m_renderMain != nullptr)
{ {
m_renderMain->RunRenderPass(); m_renderMain->FireRenderPass();
} }
} }
} }
@ -174,7 +175,7 @@ public enum class GraphViewChangedReason
m_graph->GetOptions().SetDefaultXRange(newValue); m_graph->GetOptions().SetDefaultXRange(newValue);
if (m_renderMain != nullptr) if (m_renderMain != nullptr)
{ {
m_renderMain->RunRenderPass(); m_renderMain->FireRenderPass();
} }
} }
} }
@ -194,7 +195,7 @@ public enum class GraphViewChangedReason
m_graph->GetOptions().SetDefaultYRange(newValue); m_graph->GetOptions().SetDefaultYRange(newValue);
if (m_renderMain != nullptr) if (m_renderMain != nullptr)
{ {
m_renderMain->RunRenderPass(); m_renderMain->FireRenderPass();
} }
} }
} }
@ -214,7 +215,7 @@ public enum class GraphViewChangedReason
m_graph->GetOptions().SetDefaultYRange(newValue); m_graph->GetOptions().SetDefaultYRange(newValue);
if (m_renderMain != nullptr) if (m_renderMain != nullptr)
{ {
m_renderMain->RunRenderPass(); m_renderMain->FireRenderPass();
} }
} }
} }
@ -228,7 +229,6 @@ public enum class GraphViewChangedReason
{ {
if (auto render = m_graph->GetRenderer()) if (auto render = m_graph->GetRenderer())
{ {
Concurrency::critical_section::scoped_lock lock(m_renderMain->GetCriticalSection());
render->GetDisplayRanges(*xMin, *xMax, *yMin, *yMax); render->GetDisplayRanges(*xMin, *xMax, *yMin, *yMax);
} }
} }
@ -249,7 +249,7 @@ public enum class GraphViewChangedReason
m_rangeUpdatedBySettings = true; m_rangeUpdatedBySettings = true;
if (m_renderMain) if (m_renderMain)
{ {
m_renderMain->RunRenderPass(); m_renderMain->FireRenderPass();
GraphViewChangedEvent(this, GraphViewChangedReason::Manipulation); GraphViewChangedEvent(this, GraphViewChangedReason::Manipulation);
} }
} }
@ -304,12 +304,10 @@ public enum class GraphViewChangedReason
void SetEquationsAsValid(); void SetEquationsAsValid();
void SetEquationErrors(); void SetEquationErrors();
std::optional<std::vector<std::shared_ptr<Graphing::IEquation>>> TryInitializeGraph(bool keepCurrentView, _In_ const Graphing::IExpression* graphingExp = nullptr); std::optional<std::vector<std::shared_ptr<Graphing::IEquation>>>
TryInitializeGraph(bool keepCurrentView, _In_ const Graphing::IExpression* graphingExp = nullptr);
private: private:
DX::RenderMain ^ m_renderMain = nullptr;
static Windows::UI::Xaml::DependencyProperty ^ s_equationTemplateProperty; static Windows::UI::Xaml::DependencyProperty ^ s_equationTemplateProperty;
static Windows::UI::Xaml::DependencyProperty ^ s_equationsSourceProperty; static Windows::UI::Xaml::DependencyProperty ^ s_equationsSourceProperty;
@ -326,6 +324,7 @@ public enum class GraphViewChangedReason
const std::unique_ptr<Graphing::IMathSolver> m_solver; const std::unique_ptr<Graphing::IMathSolver> m_solver;
const std::shared_ptr<Graphing::IGraph> m_graph; const std::shared_ptr<Graphing::IGraph> m_graph;
std::unique_ptr<DX::RenderMain> m_renderMain;
bool m_calculatedForceProportional = false; bool m_calculatedForceProportional = false;
bool m_tracingTracking; bool m_tracingTracking;
bool m_trigUnitsChanged; bool m_trigUnitsChanged;

View file

@ -71,7 +71,7 @@ namespace ScreenRotation
namespace GraphControl::DX namespace GraphControl::DX
{ {
// Constructor for DeviceResources. // Constructor for DeviceResources.
DeviceResources::DeviceResources(SwapChainPanel^ panel) : DeviceResources::DeviceResources(SwapChainPanel^ panel, IDeviceNotify* deviceNotify) :
m_screenViewport(), m_screenViewport(),
m_d3dFeatureLevel(D3D_FEATURE_LEVEL_9_1), m_d3dFeatureLevel(D3D_FEATURE_LEVEL_9_1),
m_d3dRenderTargetSize(), m_d3dRenderTargetSize(),
@ -83,7 +83,7 @@ namespace GraphControl::DX
m_effectiveDpi(-1.0f), m_effectiveDpi(-1.0f),
m_compositionScaleX(1.0f), m_compositionScaleX(1.0f),
m_compositionScaleY(1.0f), m_compositionScaleY(1.0f),
m_deviceNotify(nullptr) m_deviceNotify(deviceNotify)
{ {
CreateDeviceIndependentResources(); CreateDeviceIndependentResources();
CreateDeviceResources(); 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 // 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. // is entering an idle state and that temporary buffers can be reclaimed for use by other apps.
void DeviceResources::Trim() void DeviceResources::Trim()

View file

@ -8,17 +8,17 @@
namespace GraphControl::DX namespace GraphControl::DX
{ {
// Provides an interface for an application that owns DeviceResources to be notified of the device being lost or created. // 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 OnDeviceLost() = 0;
virtual void OnDeviceRestored(); virtual void OnDeviceRestored() = 0;
}; };
// Controls all the DirectX device resources. // Controls all the DirectX device resources.
class DeviceResources class DeviceResources
{ {
public: 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 SetSwapChainPanel(Windows::UI::Xaml::Controls::SwapChainPanel^ panel);
void SetLogicalSize(Windows::Foundation::Size logicalSize); void SetLogicalSize(Windows::Foundation::Size logicalSize);
void SetCurrentOrientation(Windows::Graphics::Display::DisplayOrientations currentOrientation); void SetCurrentOrientation(Windows::Graphics::Display::DisplayOrientations currentOrientation);
@ -26,7 +26,6 @@ namespace GraphControl::DX
void SetCompositionScale(float compositionScaleX, float compositionScaleY); void SetCompositionScale(float compositionScaleX, float compositionScaleY);
void ValidateDevice(); void ValidateDevice();
void HandleDeviceLost(); void HandleDeviceLost();
void RegisterDeviceNotify(IDeviceNotify^ deviceNotify);
void Trim(); void Trim();
void Present(); void Present();
@ -106,6 +105,6 @@ namespace GraphControl::DX
DirectX::XMFLOAT4X4 m_orientationTransform3D; DirectX::XMFLOAT4X4 m_orientationTransform3D;
// The IDeviceNotify can be held directly as it owns the DeviceResources. // The IDeviceNotify can be held directly as it owns the DeviceResources.
IDeviceNotify^ m_deviceNotify; IDeviceNotify* m_deviceNotify;
}; };
} }

View file

@ -5,16 +5,7 @@
#include "RenderMain.h" #include "RenderMain.h"
#include "DirectXHelper.h" #include "DirectXHelper.h"
using namespace Concurrency; namespace ctrls = Windows::UI::Xaml::Controls;
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 namespace
{ {
@ -23,524 +14,100 @@ namespace
constexpr unsigned int s_BlueChannelIndex = 2; constexpr unsigned int s_BlueChannelIndex = 2;
constexpr unsigned int s_AlphaChannelIndex = 3; constexpr unsigned int s_AlphaChannelIndex = 3;
constexpr float s_MaxChannelValue = 255.0f; constexpr float s_MaxChannelValue = 255.0f;
constexpr float nearestPointRadius = 3; 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<GraphControl::DX::DeviceResources>(panel, &notify);
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 <class F>
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<F>(task));
}
self.m_renderContext.Cv.notify_one();
}
};
using Helper = RenderMainHelper;
} }
namespace GraphControl::DX namespace GraphControl::DX
{ {
RenderMain::RenderMain(SwapChainPanel ^ panel) RenderMain::RenderMain(ctrls::SwapChainPanel ^ panel, Graphing::IGraph* graph)
: m_deviceResources{ panel } : m_renderWorker(Helper::CreateRenderWorker(m_renderContext, panel, graph))
, m_nearestPointRenderer{ &m_deviceResources }
, m_backgroundColor{ {} }
, m_swapChainPanel{ panel }
, m_TraceLocation(Point(0, 0))
, m_Tracing(false)
{ {
// 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<bool> RenderMain::RunRenderPassAsync() const
{
co_return true;
} }
RenderMain::~RenderMain() RenderMain::~RenderMain()
{ {
UnregisterEventHandlers(); Helper::QuitWorker(*this);
} m_renderWorker.join();
void RenderMain::Graph::set(shared_ptr<IGraph> 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<unsigned int>(m_swapChainPanel->ActualWidth), static_cast<unsigned int>(m_swapChainPanel->ActualHeight));
}
}
}
void RenderMain::BackgroundColor::set(Windows::UI::Color backgroundColor)
{
m_backgroundColor[s_RedChannelIndex] = static_cast<float>(backgroundColor.R) / s_MaxChannelValue;
m_backgroundColor[s_GreenChannelIndex] = static_cast<float>(backgroundColor.G) / s_MaxChannelValue;
m_backgroundColor[s_BlueChannelIndex] = static_cast<float>(backgroundColor.B) / s_MaxChannelValue;
m_backgroundColor[s_AlphaChannelIndex] = static_cast<float>(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;
}
/// <summary>
/// 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
/// </summary>
/// <param name="maxAxis">max axis</param>
/// <param name="minAxis">min axis</param>
/// <returns>the precision value</returns>
double RenderMain::GetPrecision(const double maxAxis, const double minAxis)
{
double exponent = static_cast<double>(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<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(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<CoreWindow>(Window::Current->CoreWindow);
if (m_coreWindow != nullptr)
{
m_tokenVisibilityChanged = m_coreWindow->VisibilityChanged +=
ref new TypedEventHandler<CoreWindow ^, VisibilityChangedEventArgs ^>(this, &RenderMain::OnVisibilityChanged);
}
m_displayInformation = DisplayInformation::GetForCurrentView();
if (m_displayInformation != nullptr)
{
m_tokenDpiChanged = m_displayInformation->DpiChanged += ref new TypedEventHandler<DisplayInformation ^, Object ^>(this, &RenderMain::OnDpiChanged);
m_tokenOrientationChanged = m_displayInformation->OrientationChanged +=
ref new TypedEventHandler<DisplayInformation ^, Object ^>(this, &RenderMain::OnOrientationChanged);
}
m_tokenDisplayContentsInvalidated = DisplayInformation::DisplayContentsInvalidated +=
ref new TypedEventHandler<DisplayInformation ^, Object ^>(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<SwapChainPanel ^, Object ^>(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<unsigned int>(newSize.Width), static_cast<unsigned int>(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();
} }
} }

View file

@ -3,207 +3,72 @@
#pragma once #pragma once
// Taken from the default template for Xaml and Direct3D 11 apps. #include <condition_variable>
#include <functional>
#include <mutex>
#include <queue>
// Taken from the default template for Xaml and Direct3D 11 apps.
#include "DeviceResources.h" #include "DeviceResources.h"
#include "NearestPointRenderer.h" #include "NearestPointRenderer.h"
#include "IGraph.h" #include "IGraph.h"
namespace
{
struct RenderMainHelper;
}
// Renders Direct2D and 3D content on the screen. // Renders Direct2D and 3D content on the screen.
namespace GraphControl::DX namespace GraphControl::DX
{ {
ref class RenderMain sealed : public IDeviceNotify class RenderMain
{ {
friend struct RenderMainHelper;
public: public:
virtual ~RenderMain(); explicit RenderMain(Windows::UI::Xaml::Controls::SwapChainPanel ^ panel, Graphing::IGraph* graph);
~RenderMain();
// IDeviceNotify void FireRenderPass() const;
virtual void OnDeviceLost(); concurrency::task<bool> RunRenderPassAsync() const;
virtual void OnDeviceRestored();
internal : RenderMain(Windows::UI::Xaml::Controls::SwapChainPanel ^ panel); bool ActiveTracing() const noexcept
property std::shared_ptr<Graphing::IGraph> Graph
{ {
void set(std::shared_ptr<Graphing::IGraph> 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 ActiveTracing(bool value) noexcept;
{ void ActiveTraceCursorPosition(const Windows::Foundation::Point& value) noexcept;
void set(Windows::UI::Color color); void BackgroundColor(const Windows::UI::Color& value) noexcept;
} void PointerLocation(const Windows::Foundation::Point& value) noexcept;
void PointRadius(float radius) noexcept;
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;
}
}
private: private:
bool Render(); struct RenderContext
{
bool RunRenderPassInternal(); std::unique_ptr<DeviceResources> DxRes;
Graphing::IGraph* Graph;
// Loaded/Unloaded mutable std::mutex Mtx;
void OnLoaded(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e); mutable std::condition_variable Cv;
mutable std::queue<std::function<void(RenderContext& context)>> Tasks;
// Dependent event registration Windows::Foundation::Point ActiveTraceCursorPosition = {};
void RegisterEventHandlers(); Windows::Foundation::Point TraceLocation = {};
void UnregisterEventHandlers(); float BkgColor[4] = {};
bool ActiveTracing = false;
// Window event handlers. bool Quit = false;
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);
private: private:
DX::DeviceResources m_deviceResources; RenderContext m_renderContext;
NearestPointRenderer m_nearestPointRenderer; std::thread m_renderWorker;
// Cached Graph object with Renderer property.
std::shared_ptr<Graphing::IGraph> 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<Windows::UI::Core::CoreWindow> 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;
}; };
} }