calculator/src/GraphControl/DirectX/RenderMain.cpp

379 lines
13 KiB
C++

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#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
{
constexpr unsigned int s_RedChannelIndex = 0;
constexpr unsigned int s_GreenChannelIndex = 1;
constexpr unsigned int s_BlueChannelIndex = 2;
constexpr unsigned int s_AlphaChannelIndex = 3;
constexpr float s_MaxChannelValue = 255.0f;
constexpr float nearestPointRadius = 3;
}
namespace GraphControl::DX
{
RenderMain::RenderMain(SwapChainPanel ^ panel)
: m_deviceResources{ panel }
, m_nearestPointRenderer{ &m_deviceResources }
, m_backgroundColor{ {} }
, m_swapChainPanel{ panel }
, m_TraceValue(Point(0, 0))
, m_TraceLocation(Point(0,0))
, m_Tracing(false)
, m_ActiveTracingPointRenderer{ &m_deviceResources }
{
// Register to be notified if the Device is lost or recreated
m_deviceResources.RegisterDeviceNotify(this);
RegisterEventHandlers();
m_drawActiveTracing = false;
m_activeTracingPointerLocation.X = 50;
m_activeTracingPointerLocation.Y = 50;
}
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));
}
}
RunRenderPass();
}
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;
}
RunRenderPass();
}
}
void RenderMain::PointerLocation::set(Point location)
{
if (m_pointerLocation != location)
{
m_pointerLocation = location;
RunRenderPass();
}
}
void RenderMain::ActiveTracing::set(bool value)
{
if (m_drawActiveTracing != value)
{
m_drawActiveTracing = value;
RunRenderPass();
}
}
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();
}
void RenderMain::RunRenderPass()
{
if (Render())
{
m_deviceResources.Present();
}
}
// 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;
successful = SUCCEEDED(renderer->DrawD2D1(pFactory, pRenderTarget, hasMissingData));
// 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;
m_ActiveTracingPointRenderer.Render(m_activeTracingPointerLocation);
}
int formulaId;
Point nearestPointLocation;
pair<float, float> nearestPointValue;
renderer->GetClosePointData(
trackPoint.X,
trackPoint.Y,
formulaId,
nearestPointLocation.X,
nearestPointLocation.Y,
nearestPointValue.first,
nearestPointValue.second);
if (!isnan(nearestPointLocation.X) && !isnan(nearestPointLocation.Y))
{
m_nearestPointRenderer.Render(nearestPointLocation);
m_Tracing = true;
m_TraceLocation = Point(nearestPointLocation.X, nearestPointLocation.Y);
m_TraceValue = Point(nearestPointValue.first, nearestPointValue.second);
}
else
{
m_Tracing = false;
}
}
}
}
}
return successful;
}
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();
}
}