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())
{
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<DX::RenderMain>(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<Equation ^>());
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<vector<shared_ptr<Graphing::IEquation>>> Grapher::TryInitializeGraph(bool keepCurrentView, const IExpression* graphingExp)
{
critical_section::scoped_lock lock(m_renderMain->GetCriticalSection());
if (keepCurrentView || IsKeepCurrentView)
{
auto renderer = m_graph->GetRenderer();

View file

@ -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<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:
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<Graphing::IMathSolver> m_solver;
const std::shared_ptr<Graphing::IGraph> m_graph;
std::unique_ptr<DX::RenderMain> m_renderMain;
bool m_calculatedForceProportional = false;
bool m_tracingTracking;
bool m_trigUnitsChanged;

View file

@ -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()

View file

@ -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;
};
}

View file

@ -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<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
{
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<bool> RenderMain::RunRenderPassAsync() const
{
co_return true;
}
RenderMain::~RenderMain()
{
UnregisterEventHandlers();
}
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();
Helper::QuitWorker(*this);
m_renderWorker.join();
}
}

View file

@ -3,207 +3,72 @@
#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 "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<bool> RunRenderPassAsync() const;
internal : RenderMain(Windows::UI::Xaml::Controls::SwapChainPanel ^ panel);
property std::shared_ptr<Graphing::IGraph> Graph
bool ActiveTracing() const noexcept
{
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 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<DeviceResources> DxRes;
Graphing::IGraph* Graph;
mutable std::mutex Mtx;
mutable std::condition_variable Cv;
mutable std::queue<std::function<void(RenderContext& context)>> 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<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;
RenderContext m_renderContext;
std::thread m_renderWorker;
};
}