From d30835094617a3ee7cffcc9befd8e9d08335de38 Mon Sep 17 00:00:00 2001 From: Bernhard Berger Date: Wed, 16 Apr 2025 00:45:12 +0200 Subject: [PATCH] feat: move logs and stats rendering to XAML textboxes which makes layouting a lot more flexible and removes complexity from DX rendering --- Pages/StreamPage.xaml | 6 +++ Pages/StreamPage.xaml.cpp | 69 ++++++++++++++++++++++++- Pages/StreamPage.xaml.h | 29 +++++++++++ Streaming/FFmpegDecoder.cpp | 1 - Streaming/LogRenderer.cpp | 80 ----------------------------- Streaming/LogRenderer.h | 27 ---------- Streaming/StatsRenderer.cpp | 79 ---------------------------- Streaming/StatsRenderer.h | 32 ------------ Streaming/moonlight_xbox_dxMain.cpp | 21 -------- Streaming/moonlight_xbox_dxMain.h | 8 +-- moonlight-xbox-dx.vcxproj | 4 -- moonlight-xbox-dx.vcxproj.filters | 12 ----- 12 files changed, 104 insertions(+), 264 deletions(-) delete mode 100644 Streaming/LogRenderer.cpp delete mode 100644 Streaming/LogRenderer.h delete mode 100644 Streaming/StatsRenderer.cpp delete mode 100644 Streaming/StatsRenderer.h diff --git a/Pages/StreamPage.xaml b/Pages/StreamPage.xaml index 2b21fe75..91e2c185 100644 --- a/Pages/StreamPage.xaml +++ b/Pages/StreamPage.xaml @@ -11,6 +11,12 @@ Loaded="Page_Loaded"> + + + + + + Initializing Moonlight... diff --git a/Pages/StreamPage.xaml.cpp b/Pages/StreamPage.xaml.cpp index a4e7b2cb..f9de5654 100644 --- a/Pages/StreamPage.xaml.cpp +++ b/Pages/StreamPage.xaml.cpp @@ -41,6 +41,14 @@ StreamPage::StreamPage(): this->Loaded += ref new Windows::UI::Xaml::RoutedEventHandler(this, &StreamPage::OnLoaded); this->Unloaded += ref new Windows::UI::Xaml::RoutedEventHandler(this, &StreamPage::OnUnloaded); + + m_logsTimer = ref new Windows::UI::Xaml::DispatcherTimer(); + m_logsTimer->Interval = Windows::Foundation::TimeSpan{ 10000000 }; // 1 second + m_logsTimer->Tick += ref new Windows::Foundation::EventHandler(this, &StreamPage::UpdateLogsText); + + m_statsTimer = ref new Windows::UI::Xaml::DispatcherTimer(); + m_statsTimer->Interval = Windows::Foundation::TimeSpan{ 10000000 }; // 1 second + m_statsTimer->Tick += ref new Windows::Foundation::EventHandler(this, &StreamPage::UpdateStatsText); } @@ -136,7 +144,14 @@ void StreamPage::toggleLogsButton_Click(Platform::Object^ sender, Windows::UI::X { Utils::showLogs = !Utils::showLogs; this->toggleLogsButton->Text = Utils::showLogs ? "Hide Logs" : "Show Logs"; - m_main->SetShowLogs(Utils::showLogs); + m_logsPanel->Visibility = Utils::showLogs ? Windows::UI::Xaml::Visibility::Visible : Windows::UI::Xaml::Visibility::Collapsed; + + if (Utils::showLogs) { + m_logsTimer->Start(); + UpdateLogsText(sender, e); + } else { + m_logsTimer->Stop(); + } } void StreamPage::OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) { @@ -149,7 +164,16 @@ void StreamPage::toggleStatsButton_Click(Platform::Object^ sender, Windows::UI:: { Utils::showStats = !Utils::showStats; this->toggleStatsButton->Text = Utils::showStats ? "Hide Stats" : "Show Stats"; - m_main->SetShowStats(Utils::showStats); + + m_statsPanel->Visibility = Utils::showStats ? Windows::UI::Xaml::Visibility::Visible : Windows::UI::Xaml::Visibility::Collapsed; + + if (Utils::showStats) { + m_statsTimer->Start(); + UpdateStatsText(sender, e); + } + else { + m_statsTimer->Stop(); + } } @@ -242,3 +266,44 @@ void StreamPage::OnUnloaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedE auto navigation = Windows::UI::Core::SystemNavigationManager::GetForCurrentView(); navigation->BackRequested -= m_back_cookie; } + +void StreamPage::UpdateLogsText(Platform::Object^ sender, Platform::Object^ args) +{ + Utils::logMutex.lock(); + std::vector lines = Utils::GetLogLines(); + std::wstring combinedText; + for (const auto& line : lines) { + combinedText += line; + } + Utils::logMutex.unlock(); + + m_logsTextBlock->Text = ref new Platform::String(combinedText.c_str()); +} + +void StreamPage::UpdateStatsText(Platform::Object^ sender, Platform::Object^ args) +{ + if (m_main != nullptr && m_main->GetStats() != nullptr) { + char statsOutput[1024]; + DX::StepTimer timer = m_main->GetTimer(); + + if (m_main->GetStats()->ShouldUpdateDisplay(timer, Utils::showStats, statsOutput, sizeof(statsOutput))) { + // Remove trailing newline if it exists + size_t len = strlen(statsOutput); + if (len > 0 && statsOutput[len - 1] == '\n') { + statsOutput[len - 1] = '\0'; + } + + // Convert from char* to Platform::String^ + size_t size = strlen(statsOutput) + 1; + wchar_t* wideText = new wchar_t[size]; + size_t convertedChars = 0; + mbstowcs_s(&convertedChars, wideText, size, statsOutput, _TRUNCATE); + Platform::String^ statsText = ref new Platform::String(wideText); + delete[] wideText; + + m_statsTextBlock->Text = statsText; + } + } + +} + diff --git a/Pages/StreamPage.xaml.h b/Pages/StreamPage.xaml.h index 75150602..1f2d16b0 100644 --- a/Pages/StreamPage.xaml.h +++ b/Pages/StreamPage.xaml.h @@ -67,6 +67,33 @@ namespace moonlight_xbox_dx } } + property TextBlock^ m_logsTextBlock { + TextBlock^ get() { + return this->LogsTextBlock; + } + } + + property StackPanel^ m_logsPanel { + StackPanel^ get() { + return this->LogsPanel; + } + } + + property TextBlock^ m_statsTextBlock { + TextBlock^ get() { + return this->StatsTextBlock; + } + } + + property StackPanel^ m_statsPanel { + StackPanel^ get() { + return this->StatsPanel; + } + } + + void UpdateLogsText(Platform::Object^ sender, Platform::Object^ args); + void UpdateStatsText(Platform::Object^ sender, Platform::Object^ args); + protected: virtual void OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) override; private: @@ -74,6 +101,8 @@ namespace moonlight_xbox_dx Windows::Foundation::IAsyncAction^ m_inputLoopWorker; Windows::UI::Core::CoreIndependentInputSource^ m_coreInput; Windows::Foundation::EventRegistrationToken m_back_cookie; + Windows::UI::Xaml::DispatcherTimer^ m_logsTimer; + Windows::UI::Xaml::DispatcherTimer^ m_statsTimer; // Resources used to render the DirectX content in the XAML page background. std::shared_ptr m_deviceResources; diff --git a/Streaming/FFmpegDecoder.cpp b/Streaming/FFmpegDecoder.cpp index c76151c3..07831ee9 100644 --- a/Streaming/FFmpegDecoder.cpp +++ b/Streaming/FFmpegDecoder.cpp @@ -1,6 +1,5 @@ #include "pch.h" #include "FFMpegDecoder.h" -#include "StatsRenderer.h" #include #include diff --git a/Streaming/LogRenderer.cpp b/Streaming/LogRenderer.cpp deleted file mode 100644 index f90fdb9c..00000000 --- a/Streaming/LogRenderer.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include "pch.h" -#include "LogRenderer.h" -#include "Utils.hpp" - -#include "Common/DirectXHelper.h" -#include - -using namespace DirectX; -using namespace moonlight_xbox_dx; -using namespace Microsoft::WRL; - -LogRenderer::LogRenderer(const std::shared_ptr& deviceResources) : - m_console(std::make_unique()), - m_deviceResources(deviceResources), - m_visible(false) -{ - m_console->SetForegroundColor(Colors::Yellow); - //m_console->SetDebugOutput(true); - - CreateDeviceDependentResources(); -} - -// Updates the text to be displayed. -void LogRenderer::Update(DX::StepTimer const& timer) -{ - // Only update the console once per second - static double lastUpdateSeconds = 0.0; - if (m_visible && timer.GetTotalSeconds() - lastUpdateSeconds >= 1.0) { - m_console->Clear(); - - Utils::logMutex.lock(); - std::vector lines = Utils::GetLogLines(); - for (std::wstring line : lines) { - m_console->Write(line.c_str()); - } - Utils::logMutex.unlock(); - - lastUpdateSeconds = timer.GetTotalSeconds(); - } -} - -// Renders a frame to the screen. -void LogRenderer::Render() -{ - if (m_visible) { - m_console->Render(); - } -} - -void LogRenderer::CreateDeviceDependentResources() -{ - m_deviceResources->GetUWPPixelDimensions(&m_displayWidth, &m_displayHeight); - - const wchar_t* font = L"Assets\\Font\\ModeSeven-24.spritefont"; // sized for 4K - if (m_displayHeight <= 1440) { - font = L"Assets\\Font\\ModeSeven-12.spritefont"; // for 1080p & 1440p - } - - m_console->RestoreDevice(m_deviceResources->GetD3DDeviceContext(), font); - - // use much faster font rendering - m_console->SetFixedWidthFont(true); -} - -void LogRenderer::CreateWindowSizeDependentResources() -{ - // The size of our text area (left, top, right, bottom) - RECT size = {m_displayWidth * 0.5, 0, m_displayWidth - 20, m_displayHeight - 20}; - - m_console->SetWindow(size); -} - -void LogRenderer::ReleaseDeviceDependentResources() -{ - m_console->ReleaseDevice(); -} - -void LogRenderer::SetVisible(bool visible) { - m_visible = visible; -} diff --git a/Streaming/LogRenderer.h b/Streaming/LogRenderer.h deleted file mode 100644 index 77d0c215..00000000 --- a/Streaming/LogRenderer.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include -#include "..\Common\StepTimer.h" -#include "..\Common\TextConsole.h" - -namespace moonlight_xbox_dx -{ - class LogRenderer - { - public: - LogRenderer(const std::shared_ptr& deviceResources); - void CreateDeviceDependentResources(); - void CreateWindowSizeDependentResources(); - void ReleaseDeviceDependentResources(); - void Update(DX::StepTimer const& timer); - void Render(); - void SetVisible(bool visible); - - private: - std::shared_ptr m_deviceResources; - std::unique_ptr m_console; - bool m_visible; - uint32_t m_displayWidth; - uint32_t m_displayHeight; - }; -} diff --git a/Streaming/StatsRenderer.cpp b/Streaming/StatsRenderer.cpp deleted file mode 100644 index 8700f4f9..00000000 --- a/Streaming/StatsRenderer.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include "pch.h" -#include "StatsRenderer.h" -#include "FFMpegDecoder.h" -#include "Utils.hpp" - -using namespace DirectX; -using namespace moonlight_xbox_dx; -using namespace Microsoft::WRL; -using namespace Windows::UI::Core; - -StatsRenderer::StatsRenderer(const std::shared_ptr& deviceResources, const std::shared_ptr& stats) : - m_console(std::make_unique()), - m_deviceResources(deviceResources), - m_visible(false), - m_stats(stats) -{ - m_console->SetForegroundColor(Colors::Yellow); - //m_console->SetDebugOutput(true); - - CreateDeviceDependentResources(); -} - -void StatsRenderer::Update(DX::StepTimer const& timer) -{ - // We let the Stats class always process even if not visible. Most of the time - // it will simply accumulate stats during its 1-second window period. Each second, - // when it determines the user-visible text should be updated, it will update outputStr and return true. - - char outputStr[1024]; // char is used so we can share more of the formatting code with moonlight-qt - wchar_t wideStr[2048]; - - if (m_stats->ShouldUpdateDisplay(timer, m_visible, outputStr, sizeof(outputStr))) { - size_t numChars = mbstowcs(wideStr, outputStr, 1024); - if (numChars != -1) { - m_console->Clear(); - m_console->Write(wideStr); - } - } -} - -// Renders a frame to the screen. -void StatsRenderer::Render() -{ - if (m_visible) { - m_console->Render(); - } -} - -void StatsRenderer::CreateDeviceDependentResources() -{ - m_deviceResources->GetUWPPixelDimensions(&m_displayWidth, &m_displayHeight); - - const wchar_t* font = L"Assets\\Font\\ModeSeven-24.spritefont"; // sized for 4K - if (m_displayHeight <= 1440) { - font = L"Assets\\Font\\ModeSeven-12.spritefont"; // for 1080p & 1440p - } - - m_console->RestoreDevice(m_deviceResources->GetD3DDeviceContext(), font); - - // use much faster font rendering - m_console->SetFixedWidthFont(true); -} - -void StatsRenderer::CreateWindowSizeDependentResources() -{ - // The size of our text area (left, top, right, bottom) - RECT size = {20, 0, m_displayWidth * 0.5, m_displayHeight * 0.2}; - - m_console->SetWindow(size); -} - -void StatsRenderer::ReleaseDeviceDependentResources() -{ - m_console->ReleaseDevice(); -} - -void StatsRenderer::SetVisible(bool visible) { - m_visible = visible; -} diff --git a/Streaming/StatsRenderer.h b/Streaming/StatsRenderer.h deleted file mode 100644 index b4e0cf7e..00000000 --- a/Streaming/StatsRenderer.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include -#include -#include -#include "..\Common\StepTimer.h" -#include "..\Common\TextConsole.h" -#include "..\State\Stats.h" - -namespace moonlight_xbox_dx -{ - class StatsRenderer - { - public: - StatsRenderer(const std::shared_ptr& deviceResources, const std::shared_ptr& stats); - void CreateDeviceDependentResources(); - void CreateWindowSizeDependentResources(); - void ReleaseDeviceDependentResources(); - void Update(DX::StepTimer const& timer); - void Render(); - void SetVisible(bool visible); - - private: - std::mutex m_mutex; - std::shared_ptr m_deviceResources; - std::unique_ptr m_console; - std::shared_ptr m_stats; - bool m_visible; - uint32_t m_displayWidth; - uint32_t m_displayHeight; - }; -} diff --git a/Streaming/moonlight_xbox_dxMain.cpp b/Streaming/moonlight_xbox_dxMain.cpp index c6cc72da..5aed8f09 100644 --- a/Streaming/moonlight_xbox_dxMain.cpp +++ b/Streaming/moonlight_xbox_dxMain.cpp @@ -44,12 +44,9 @@ moonlight_xbox_dxMain::moonlight_xbox_dxMain(const std::shared_ptr(m_deviceResources, moonlightClient, configuration); - m_LogRenderer = std::make_unique(m_deviceResources); - // Setup stats object. DeviceResources keeps a reference so that various components such as FFMpegDecoder can get to it m_stats = std::make_shared(); m_stats->SetDisplayStatus(configuration->enableVsync ? VSYNC_ON : VSYNC_OFF); - m_statsTextRenderer = std::make_unique(m_deviceResources, m_stats); m_deviceResources->SetStats(m_stats); streamPage->m_progressView->Visibility = Windows::UI::Xaml::Visibility::Visible; @@ -88,8 +85,6 @@ void moonlight_xbox_dxMain::CreateDeviceDependentResources() void moonlight_xbox_dxMain::CreateWindowSizeDependentResources() { m_sceneRenderer->CreateWindowSizeDependentResources(); - m_LogRenderer->CreateWindowSizeDependentResources(); - m_statsTextRenderer->CreateWindowSizeDependentResources(); } void moonlight_xbox_dxMain::StartRenderLoop() @@ -153,8 +148,6 @@ void moonlight_xbox_dxMain::Update() m_timer.Tick([&]() { m_sceneRenderer->Update(m_timer); - m_LogRenderer->Update(m_timer); - m_statsTextRenderer->Update(m_timer); }); } @@ -362,8 +355,6 @@ bool moonlight_xbox_dxMain::Render() // Render the scene objects. bool shouldPresent = m_sceneRenderer->Render(); - m_LogRenderer->Render(); - m_statsTextRenderer->Render(); return shouldPresent; } @@ -390,16 +381,12 @@ void moonlight_xbox_dxMain::Clear() void moonlight_xbox_dxMain::OnDeviceLost() { m_sceneRenderer->ReleaseDeviceDependentResources(); - m_LogRenderer->ReleaseDeviceDependentResources(); - m_statsTextRenderer->ReleaseDeviceDependentResources(); } // Notifies renderers that device resources may now be recreated. void moonlight_xbox_dxMain::OnDeviceRestored() { m_sceneRenderer->CreateDeviceDependentResources(); - m_LogRenderer->CreateDeviceDependentResources(); - m_statsTextRenderer->CreateDeviceDependentResources(); CreateDeviceDependentResources(); CreateWindowSizeDependentResources(); @@ -452,11 +439,3 @@ void moonlight_xbox_dxMain::SendWinAltB() { moonlightClient->KeyUp((unsigned short)Windows::System::VirtualKey::LeftWindows, 0); }); } - -void moonlight_xbox_dxMain::SetShowLogs(bool showLogs) { - m_LogRenderer->SetVisible(showLogs); -} - -void moonlight_xbox_dxMain::SetShowStats(bool showStats) { - m_statsTextRenderer->SetVisible(showStats); -} diff --git a/Streaming/moonlight_xbox_dxMain.h b/Streaming/moonlight_xbox_dxMain.h index da9aa940..7b5750e0 100644 --- a/Streaming/moonlight_xbox_dxMain.h +++ b/Streaming/moonlight_xbox_dxMain.h @@ -2,8 +2,6 @@ #include "Common\StepTimer.h" #include "Streaming\VideoRenderer.h" -#include "Streaming\LogRenderer.h" -#include "Streaming\StatsRenderer.h" #include "Pages\StreamPage.xaml.h" // Renders Direct2D and 3D content on the screen. @@ -20,6 +18,8 @@ namespace moonlight_xbox_dx void StartRenderLoop(); void StopRenderLoop(); void SetFlyoutOpened(bool value); + DX::StepTimer GetTimer() { return m_timer; } + std::shared_ptr GetStats() { return m_stats; } Concurrency::critical_section& GetCriticalSection() { return m_criticalSection; } bool mouseMode = false; bool keyboardMode = false; @@ -32,8 +32,6 @@ namespace moonlight_xbox_dx void CloseApp(); void SendGuideButton(int duration); void SendWinAltB(); - void SetShowLogs(bool showLogs); - void SetShowStats(bool showStats); private: void ProcessInput(); void Update(); @@ -44,8 +42,6 @@ namespace moonlight_xbox_dx std::shared_ptr m_deviceResources; std::unique_ptr m_sceneRenderer; - std::unique_ptr m_LogRenderer; - std::unique_ptr m_statsTextRenderer; std::shared_ptr m_stats; diff --git a/moonlight-xbox-dx.vcxproj b/moonlight-xbox-dx.vcxproj index 828633f6..a5adfc9e 100644 --- a/moonlight-xbox-dx.vcxproj +++ b/moonlight-xbox-dx.vcxproj @@ -342,7 +342,6 @@ - @@ -363,7 +362,6 @@ Pages\StreamPage.xaml - @@ -471,7 +469,6 @@ - @@ -501,7 +498,6 @@ Create Create - diff --git a/moonlight-xbox-dx.vcxproj.filters b/moonlight-xbox-dx.vcxproj.filters index 9ee1d991..5f97fa2e 100644 --- a/moonlight-xbox-dx.vcxproj.filters +++ b/moonlight-xbox-dx.vcxproj.filters @@ -50,9 +50,6 @@ Source Files - - Source Files - Source Files @@ -68,9 +65,6 @@ Source Files - - Source Files - Source Files @@ -370,9 +364,6 @@ Header Files - - Header Files - Header Files @@ -388,9 +379,6 @@ Header Files - - Header Files - Header Files