From 843556d26d191074b9f5678aaeb7d5ded956fc10 Mon Sep 17 00:00:00 2001 From: Rys Sommefeldt Date: Fri, 15 Mar 2019 19:34:11 +0000 Subject: [PATCH] 1.4 * Add capture output folder to settings.ini to store information for next launch. * Add in-use API to overlay. * Only load either 32 or 64 bit vulkan layer. * Update Vulkan API version in layer descriptions. * Add known issue about gathering of system spec information. * Check if open sub registry key returns valid key for implicit layers. * Fix Win7 issue. * Intermediate builds increase 3rd component of version number instead of 4th. * Add 95th-percentile and 99.9-percentile frame time to performance summary output. * Add Application recovery for inactivating implicit Vulkan layers in case of OCAT crash. * Use ComPtr to temporary store context state when using immediate context for DX11 overlay. * Add missing comma to perf_summary.csv * Send capture time to overlay, so capture red dot disappears and capture end message appears on overlay when capture stops due to time out. * Add colored bar to overlay, changing colors each frame. * Change color sequence. * Add frame times graph to overlay. * Update frame time graph data also if graph is not displayed. * Fix FPS drop on certain games when overlay is enabled. * Change keyboard hook mechanism to send WM_HOTKEY to the message queue. Hotkey now exclusively owns the key. * Fix FPS drop on DX12. Skip presents on DX11 that are discarded. * Show error message when F12 is chosen as hotkey - F12 is a reserved key and cannot be used as hotkey anymore. * Skip discarded presents entirely, since it could cause FPS drop. * Update known issues section: RotTR flickering overlay issue. * Add audio cue at start and end of capture. * Fix sound copy cmd after building for 32bit * Fix linker issue. * Add scale indicator to frame graph. * Fix formatting issue with frame graph. Hide frame graph and colored bar when hook is disabled. * 1.4 * Update the changelog with 1.4 release notes * Drop Version.Revision from the printed UI version --- Commons/Config/Config.cpp | 6 +- Commons/Config/Config.h | 6 +- Commons/Overlay/OverlayMessage.h | 12 +- Commons/Overlay/VK_Environment.cpp | 9 +- Commons/Recording/OverlayThread.cpp | 19 +- Commons/Recording/PerformanceCounter.cpp | 1 + Commons/Recording/PerformanceCounter.hpp | 1 + Commons/Recording/RecordingState.cpp | 50 +- Commons/Recording/RecordingState.h | 12 +- Commons/Rendering/OverlayBitmap.cpp | 574 +++++++++++------- Commons/Rendering/OverlayBitmap.h | 34 +- Commons/Rendering/TextMessage.cpp | 8 + Commons/Rendering/TextMessage.h | 1 + Frontend/ConfigurationFile.cs | 49 +- Frontend/KeyCaptureMode.cs | 4 +- Frontend/KeyboardHook.cs | 93 +-- Frontend/MainWindow.xaml | 65 +- Frontend/MainWindow.xaml.cs | 183 +++++- Frontend/OverlayMessage.cs | 11 +- Frontend/OverlayTracker.cs | 16 + Frontend/RegistryUpdater.cs | 28 +- Frontend/UserInterfaceState.cs | 35 +- GameOverlay/d3d/source/d3d/d3d11_renderer.cpp | 70 ++- GameOverlay/d3d/source/d3d/d3d11_renderer.hpp | 4 +- GameOverlay/d3d/source/d3d/d3d12_renderer.cpp | 241 +++----- GameOverlay/d3d/source/d3d/d3d12_renderer.hpp | 4 +- GameOverlay/d3d/source/d3d/dxgi_swapchain.cpp | 17 +- GameOverlay/d3d/source/d3d/oculus.cpp | 4 +- GameOverlay/d3d/source/d3d/steamvr.cpp | 4 +- .../vulkan/VK_LAYER_OCAT_overlay32.json | 2 +- .../vulkan/VK_LAYER_OCAT_overlay64.json | 2 +- GameOverlay/vulkan/src/Rendering.cpp | 10 +- PresentMon/PresentMon/main.cpp | 90 --- PresentMonInterface/PresentMonInterface.cpp | 4 + .../PresentMonInterface.vcxproj | 20 +- PresentMonInterface/Recording.cpp | 21 +- appveyor.yml | 6 +- docs/source/changelog.rst | 34 ++ docs/source/license.rst | 39 +- docs/source/usage.rst | 2 + sounds/388046__paep3nguin__beep-up.wav | Bin 0 -> 44568 bytes sounds/388047__paep3nguin__beep-down.wav | Bin 0 -> 44556 bytes 42 files changed, 1094 insertions(+), 697 deletions(-) create mode 100644 sounds/388046__paep3nguin__beep-up.wav create mode 100644 sounds/388047__paep3nguin__beep-down.wav diff --git a/Commons/Config/Config.cpp b/Commons/Config/Config.cpp index 127cc5b..0c42798 100644 --- a/Commons/Config/Config.cpp +++ b/Commons/Config/Config.cpp @@ -39,10 +39,10 @@ bool Config::Load(const std::wstring& path) const auto fileName = (path + g_iniFile); if (FileExists(fileName)) { - hotkey_ = GetPrivateProfileInt(L"Recording", L"hotkey", hotkey_, fileName.c_str()); + hotkey_ = GetPrivateProfileInt(L"Recording", L"toggleCaptureHotkey", hotkey_, fileName.c_str()); toggleOverlayHotKey_ = GetPrivateProfileInt(L"Recording", L"toggleOverlayHotkey", toggleOverlayHotKey_, fileName.c_str()); - recordingTime_ = GetPrivateProfileInt(L"Recording", L"recordTime", recordingTime_, fileName.c_str()); - recordAllProcesses_ = ReadBoolFromIni(L"Recording", L"recordAllProcesses", recordAllProcesses_, fileName.c_str()); + recordingTime_ = GetPrivateProfileInt(L"Recording", L"captureTime", recordingTime_, fileName.c_str()); + recordAllProcesses_ = ReadBoolFromIni(L"Recording", L"captureAllProcesses", recordAllProcesses_, fileName.c_str()); overlayPosition_ = GetPrivateProfileInt(L"Recording", L"overlayPosition", overlayPosition_, fileName.c_str()); g_messageLog.LogInfo("Config", "file loaded"); diff --git a/Commons/Config/Config.h b/Commons/Config/Config.h index 8154b7e..bd09593 100644 --- a/Commons/Config/Config.h +++ b/Commons/Config/Config.h @@ -32,8 +32,10 @@ enum class Verbosity { }; struct Config { - unsigned int hotkey_ = 0x7A; - unsigned int toggleOverlayHotKey_ = 0x50; // P + unsigned int hotkey_ = 0x79; // F10 + unsigned int toggleOverlayHotKey_ = 0x78; // F9 + unsigned int toggleGraphOverlayHotKey_ = 0x76; // F7 + unsigned int toggleBarOverlayHotKey_ = 0x77; // F8 unsigned int recordingTime_ = 0; bool recordAllProcesses_ = true; unsigned int overlayPosition_ = 2; diff --git a/Commons/Overlay/OverlayMessage.h b/Commons/Overlay/OverlayMessage.h index f838e72..c5632c5 100644 --- a/Commons/Overlay/OverlayMessage.h +++ b/Commons/Overlay/OverlayMessage.h @@ -22,7 +22,7 @@ #pragma once -#include +#include // Keep in sync with OverlayMessageType in Frontend. enum class OverlayMessageType @@ -38,11 +38,19 @@ enum class OverlayMessageType // Visibility of the overlay while active ShowOverlay, HideOverlay, + // Visibility of the bar overlay while active + ShowGraphOverlay, + HideGraphOverlay, + // Visibility of the bar overlay while active + ShowBarOverlay, + HideBarOverlay, // Position of the overlay while active UpperLeft, UpperRight, LowerLeft, - LowerRight + LowerRight, + // Capture time + CaptureTime }; class OverlayMessage diff --git a/Commons/Overlay/VK_Environment.cpp b/Commons/Overlay/VK_Environment.cpp index 08fc846..3461ac8 100644 --- a/Commons/Overlay/VK_Environment.cpp +++ b/Commons/Overlay/VK_Environment.cpp @@ -28,7 +28,8 @@ namespace { const wchar_t g_vkEnvPath[] = L"VK_LAYER_PATH"; const wchar_t g_vkEnvLayers[] = L"VK_INSTANCE_LAYERS"; -const wchar_t g_vkLayerValue[] = L"VK_LAYER_OCAT_overlay32;VK_LAYER_OCAT_overlay64"; +const wchar_t g_vkLayerValue32[] = L"VK_LAYER_OCAT_overlay32"; +const wchar_t g_vkLayerValue64[] = L"VK_LAYER_OCAT_overlay64"; const wchar_t g_vkEnvOcat[] = L"OCAT_VULKAN_LAYER_ENABLED"; const wchar_t g_vkEnvOcatEnabled[] = L"1"; } @@ -37,7 +38,11 @@ void VK_Environment::SetVKEnvironment(const std::wstring& dllDirectory) { const auto dir = dllDirectory.substr(0, dllDirectory.find_last_of('\\')); originalEnvironment_.path = WriteEnvironmentVariable(g_vkEnvPath, dir, true); - originalEnvironment_.layers = WriteEnvironmentVariable(g_vkEnvLayers, g_vkLayerValue, true); +#if _WIN64 + originalEnvironment_.layers = WriteEnvironmentVariable(g_vkEnvLayers, g_vkLayerValue64, true); +#else + originalEnvironment_.layers = WriteEnvironmentVariable(g_vkEnvLayers, g_vkLayerValue32, true); +#endif originalEnvironment_.ocatVulkan = WriteEnvironmentVariable(g_vkEnvOcat, g_vkEnvOcatEnabled, true); changed_ = true; } diff --git a/Commons/Recording/OverlayThread.cpp b/Commons/Recording/OverlayThread.cpp index b21239f..28310bb 100644 --- a/Commons/Recording/OverlayThread.cpp +++ b/Commons/Recording/OverlayThread.cpp @@ -32,7 +32,7 @@ namespace GameOverlay { HWND g_windowHandle = NULL; OverlayThread::~OverlayThread() -{ +{ Stop(); } @@ -91,6 +91,18 @@ void OverlayThread::ThreadProc() case OverlayMessageType::HideOverlay: RecordingState::GetInstance().HideOverlay(); break; + case OverlayMessageType::ShowGraphOverlay: + RecordingState::GetInstance().ShowGraphOverlay(); + break; + case OverlayMessageType::HideGraphOverlay: + RecordingState::GetInstance().HideGraphOverlay(); + break; + case OverlayMessageType::ShowBarOverlay: + RecordingState::GetInstance().ShowBarOverlay(); + break; + case OverlayMessageType::HideBarOverlay: + RecordingState::GetInstance().HideBarOverlay(); + break; case OverlayMessageType::UpperLeft: case OverlayMessageType::UpperRight: case OverlayMessageType::LowerLeft: @@ -100,6 +112,9 @@ void OverlayThread::ThreadProc() RecordingState::GetInstance().SetOverlayPosition(overlayPosition); break; } + case OverlayMessageType::CaptureTime: + RecordingState::GetInstance().UpdateRecordingTime(); + break; default: break; } @@ -125,6 +140,8 @@ void OverlayThread::DisableOverlay() return; } RecordingState::GetInstance().HideOverlay(); + RecordingState::GetInstance().HideGraphOverlay(); + RecordingState::GetInstance().HideBarOverlay(); FreeLibraryAndExitThread(dll, 0); } diff --git a/Commons/Recording/PerformanceCounter.cpp b/Commons/Recording/PerformanceCounter.cpp index 05a0a8f..22eadee 100644 --- a/Commons/Recording/PerformanceCounter.cpp +++ b/Commons/Recording/PerformanceCounter.cpp @@ -48,6 +48,7 @@ const PerformanceCounter::FrameInfo& PerformanceCounter::NextFrame() const MilliSeconds frameDelta = currFrame - lastFrame_; deltaTime_ += frameDelta; frameTimes_.push_back(static_cast(frameDelta.count())); + currentFrameInfo_.frameTime = static_cast(frameDelta.count()); currentFrameCount_++; totalFrameCount_++; diff --git a/Commons/Recording/PerformanceCounter.hpp b/Commons/Recording/PerformanceCounter.hpp index 2c3f511..d3041a6 100644 --- a/Commons/Recording/PerformanceCounter.hpp +++ b/Commons/Recording/PerformanceCounter.hpp @@ -32,6 +32,7 @@ class PerformanceCounter { struct FrameInfo { std::int32_t fps = 0; float ms = 0.0f; + float frameTime = 0.0f; }; struct CaptureResults { diff --git a/Commons/Recording/RecordingState.cpp b/Commons/Recording/RecordingState.cpp index f476033..d1b1475 100644 --- a/Commons/Recording/RecordingState.cpp +++ b/Commons/Recording/RecordingState.cpp @@ -23,6 +23,9 @@ #include "RecordingState.h" #include "../Logging/MessageLog.h" +#include "../Config/Config.h" +#include "../Utility/FileDirectory.h" + using Clock = std::chrono::high_resolution_clock; using fSeconds = std::chrono::duration; @@ -33,7 +36,7 @@ RecordingState& RecordingState::GetInstance() } RecordingState::RecordingState() -{ +{ currentStateStart_ = Clock::now(); } @@ -56,10 +59,20 @@ bool RecordingState::Stopped() } bool RecordingState::IsOverlayShowing() -{ +{ return showOverlay_; } +bool RecordingState::IsGraphOverlayShowing() +{ + return showGraphOverlay_; +} + +bool RecordingState::IsBarOverlayShowing() +{ + return showBarOverlay_; +} + TextureState RecordingState::Update() { const fSeconds duration = Clock::now() - currentStateStart_; @@ -90,20 +103,47 @@ void RecordingState::SetDisplayTimes(float start, float end) } void RecordingState::SetRecordingTime(float time) -{ +{ recordingTime_ = time; } +void RecordingState::UpdateRecordingTime() +{ + Config config; + config.Load(g_fileDirectory.GetDirectory(DirectoryType::Config)); + SetRecordingTime(static_cast(config.recordingTime_)); +} + void RecordingState::ShowOverlay() -{ +{ showOverlay_ = true; } void RecordingState::HideOverlay() -{ +{ showOverlay_ = false; } +void RecordingState::ShowGraphOverlay() +{ + showGraphOverlay_ = true; +} + +void RecordingState::HideGraphOverlay() +{ + showGraphOverlay_ = false; +} + +void RecordingState::ShowBarOverlay() +{ + showBarOverlay_ = true; +} + +void RecordingState::HideBarOverlay() +{ + showBarOverlay_ = false; +} + OverlayPosition RecordingState::GetOverlayPosition() { return overlayPosition_; diff --git a/Commons/Recording/RecordingState.h b/Commons/Recording/RecordingState.h index 3a20599..6e50a34 100644 --- a/Commons/Recording/RecordingState.h +++ b/Commons/Recording/RecordingState.h @@ -26,7 +26,7 @@ #include "../Overlay/OverlayPosition.h" enum class TextureState -{ +{ Default, Start, Stop @@ -41,6 +41,8 @@ class RecordingState final { bool Started(); bool Stopped(); bool IsOverlayShowing(); + bool IsGraphOverlayShowing(); + bool IsBarOverlayShowing(); void Start(); void Stop(); @@ -49,16 +51,24 @@ class RecordingState final { void SetRecordingTime(float time); void HideOverlay(); void ShowOverlay(); + void HideGraphOverlay(); + void ShowGraphOverlay(); + void HideBarOverlay(); + void ShowBarOverlay(); void SetOverlayPosition(OverlayPosition overlayPosition); OverlayPosition GetOverlayPosition(); + void UpdateRecordingTime(); + private: RecordingState(); bool recording_ = false; bool stateChanged_ = false; bool showOverlay_ = true; + bool showGraphOverlay_ = true; + bool showBarOverlay_ = false; float startDisplayTime_ = 1.0f; float endDisplayTime_ = 1.0f; float recordingTime_ = 0.0f; diff --git a/Commons/Rendering/OverlayBitmap.cpp b/Commons/Rendering/OverlayBitmap.cpp index 3aebaed..ff92c9f 100644 --- a/Commons/Rendering/OverlayBitmap.cpp +++ b/Commons/Rendering/OverlayBitmap.cpp @@ -30,16 +30,35 @@ using namespace Microsoft::WRL; -const D2D1_COLOR_F OverlayBitmap::clearColor_ = { 0.0f, 0.0f, 0.0f, 0.01f }; -const D2D1_COLOR_F OverlayBitmap::fpsBackgroundColor_ = { 0.0f, 0.0f, 0.0f, 0.8f }; -const D2D1_COLOR_F OverlayBitmap::msBackgroundColor_ = { 0.0f, 0.0f, 0.0f, 0.7f }; -const D2D1_COLOR_F OverlayBitmap::messageBackgroundColor_ = { 0.0f, 0.0f, 0.0f, 0.5f }; -const D2D1_COLOR_F OverlayBitmap::fontColor_ = { 1.0f, 1.0f, 1.0f, 1.0f }; -const D2D1_COLOR_F OverlayBitmap::numberColor_ = { 1.0f, 162.0f / 255.0f, 26.0f / 255.0f, 1.0f }; -const D2D1_COLOR_F OverlayBitmap::recordingColor_ = { 1.0f, 0.0f, 0.0f, 1.0f }; - -OverlayBitmap::RawData::RawData() - : dataPtr{nullptr}, size{0} +const D2D1_COLOR_F OverlayBitmap::clearColor_ = {0.0f, 0.0f, 0.0f, 0.01f}; +const D2D1_COLOR_F OverlayBitmap::fpsBackgroundColor_ = {0.0f, 0.0f, 0.0f, 0.8f}; +const D2D1_COLOR_F OverlayBitmap::msBackgroundColor_ = {0.0f, 0.0f, 0.0f, 0.7f}; +const D2D1_COLOR_F OverlayBitmap::messageBackgroundColor_ = {0.0f, 0.0f, 0.0f, 0.5f}; +const D2D1_COLOR_F OverlayBitmap::graphBackgroundColor_ = {0.0f, 0.0f, 0.0f, 0.6f}; +const D2D1_COLOR_F OverlayBitmap::fontColor_ = {1.0f, 1.0f, 1.0f, 1.0f}; +const D2D1_COLOR_F OverlayBitmap::numberColor_ = {1.0f, 162.0f / 255.0f, 26.0f / 255.0f, 1.0f}; +const D2D1_COLOR_F OverlayBitmap::recordingColor_ = {1.0f, 0.0f, 0.0f, 1.0f}; + +const D2D1_COLOR_F OverlayBitmap::colorBarSequence_[] = { + {1.0f, 1.0f, 1.0f, 1.0f}, // White + {0.0f, 1.0f, 0.0f, 1.0f}, // Lime + {0.0f, 102.0f / 255.0f, 1.0f, 1.0f}, // Blue + {1.0f, 0.0f, 0.0f, 1.0f}, // Red + {204.0f / 255.0f, 204.0f / 255.0f, 1.0f, 1.0f}, // Teal + {51.0f / 255.0f, 204.0f / 255.0f, 1.0f, 1.0f}, // Navy + {0.0f, 176.0f / 255.0f, 0.0f, 1.0f}, // Green + {0.0f, 1.0f, 1.0f, 1.0f}, // Aqua + {138.0f / 255.0f, 135.0f / 255.0f, 0.0f, 1.0f}, // Dark green + {221.0f / 255.0f, 221.0f / 255.0f, 221.0f / 255.0f, 1.0f}, // Silver + {153.0f / 255.0f, 0.0f, 204.0f / 255.0f, 1.0f}, // Purple + {185.0f / 255.0f, 172.0f / 255.0f, 3.0f / 255.0f, 1.0f}, // Olive + {119.0f / 255.0f, 119.0f / 255.0f, 119.0f / 255.0f, 1.0f}, // Gray + {128.0f / 255.0f, 0.0f, 128.0f / 255.0f, 1.0f}, // Fuchsia + {1.0f, 255.0f / 255.0f, 0.0f, 1.0f}, // Yellow + {1.0f, 153.0f / 255.0f, 0.0f, 1.0f}, // Orange +}; + +OverlayBitmap::RawData::RawData() : dataPtr{nullptr}, size{0} { // Empty } @@ -49,46 +68,61 @@ OverlayBitmap::OverlayBitmap() // Empty } -bool OverlayBitmap::Init(int screenWidth, int screenHeight) +bool OverlayBitmap::Init(int screenWidth, int screenHeight, API api) { - if (!InitFactories()) - { + if (!InitFactories()) { return false; } CalcSize(screenWidth, screenHeight); - if (!InitBitmap()) - { + if (!InitBitmap()) { return false; } - if (!InitText()) - { + if (!InitText()) { return false; } + + switch (api) { + case API::DX11: + api_ = L"DX11"; + break; + case API::DX12: + api_ = L"DX12"; + break; + case API::Vulkan: + api_ = L"Vulkan"; + break; + default: + api_ = L"Unknown"; + } + return true; } OverlayBitmap::~OverlayBitmap() { - for (int i = 0; i < alignmentCount_; ++i) - { + for (int i = 0; i < alignmentCount_; ++i) { fpsMessage_[i].reset(); msMessage_[i].reset(); stateMessage_[i].reset(); stopValueMessage_[i].reset(); stopMessage_[i].reset(); recordingMessage_[i].reset(); + apiMessage_[i].reset(); + graphLabelMessage_[i].reset(); } - + renderTarget_.Reset(); textBrush_.Reset(); + helperLineBrush_.Reset(); textFormat_.Reset(); messageFormat_.Reset(); stopValueFormat_.Reset(); stopMessageFormat_.Reset(); recordingMessageFormat_.Reset(); + graphLabelMessagFormat_.Reset(); iwicFactory_.Reset(); bitmap_.Reset(); bitmapLock_.Reset(); @@ -104,7 +138,9 @@ OverlayBitmap::~OverlayBitmap() void OverlayBitmap::CalcSize(int screenWidth, int screenHeight) { fullWidth_ = 256; - fullHeight_ = lineHeight_ * 4; + fullHeight_ = lineHeight_ * 8; + messageHeight_ = lineHeight_ * 3; + barWidth_ = 24; Resize(screenWidth, screenHeight); @@ -112,69 +148,144 @@ void OverlayBitmap::CalcSize(int screenWidth, int screenHeight) const auto fullHeight = static_cast(fullHeight_); const auto lineHeight = static_cast(lineHeight_); const auto halfWidth = static_cast(fullWidth_ / 2); + const auto messageHeight = static_cast(messageHeight_); - // Areas depending on vertical alignment, as we switch FPS/ms with + const auto barHeight = static_cast(screenHeight); + const auto barWidth = static_cast(barWidth_); + + // Areas depending on vertical alignment, as we switch FPS/ms with // the start/stop message when displaying the overlay at the bottom of the page. - // ------------------------ - // | fpsArea | msArea | o | - // ------------------------ - // | messageArea | - // | + | - // | messageValueArea | - // ------------------------ + // |---|------------------------ + // |---|| apiArea | + // |---|------------------------ + // |---|| fpsArea | msArea | o | + // |---|------------------------ + // |---|| | | + // |---||L| graph | + // |---|| | | + // |---|------------------------ + // |---|| messageArea | + // |---|| + | + // |---|| messageValueArea | + // |---|------------------------ const int indexUpperLeft = static_cast(Alignment::UpperLeft); - fpsArea_[indexUpperLeft] = D2D1::RectF(0.0f, 0.0f, halfWidth - 10.0f, lineHeight); - msArea_[indexUpperLeft] = D2D1::RectF(halfWidth - 10.0f, 0.0f, fullWidth - 20.0f, lineHeight); - recordingArea_[indexUpperLeft] = D2D1::RectF(fullWidth - 20.0f, 0.0f, fullWidth, lineHeight); - messageArea_[indexUpperLeft] = D2D1::RectF(0.0f, lineHeight, fullWidth, fullHeight); - messageValueArea_[indexUpperLeft] = D2D1::RectF(0.0f, lineHeight, fullWidth * 0.35f, fullHeight); - - // ------------------------ - // | o | fpsArea | msArea | - // ------------------------ - // | messageArea | - // | + | - // | messageValueArea | - // ------------------------ + apiArea_[indexUpperLeft] = D2D1::RectF(barWidth, 0.0f, barWidth + fullWidth, lineHeight); + fpsArea_[indexUpperLeft] = + D2D1::RectF(barWidth, lineHeight, barWidth + halfWidth - 10.0f, lineHeight * 2); + msArea_[indexUpperLeft] = D2D1::RectF(barWidth + halfWidth - 10.0f, lineHeight, + barWidth + fullWidth - 20.0f, lineHeight * 2); + recordingArea_[indexUpperLeft] = + D2D1::RectF(barWidth + fullWidth - 20.0f, lineHeight, barWidth + fullWidth, lineHeight * 2); + graphLabelArea_[indexUpperLeft] = + D2D1::RectF(barWidth, lineHeight * 2, barWidth + 20.0f, lineHeight * 2 + messageHeight); + graphArea_[indexUpperLeft] = + D2D1::RectF(barWidth + 20.0f, lineHeight * 2, barWidth + fullWidth, + lineHeight * 2 + messageHeight); + messageArea_[indexUpperLeft] = + D2D1::RectF(barWidth, lineHeight * 2 + messageHeight, barWidth + fullWidth, fullHeight); + messageValueArea_[indexUpperLeft] = D2D1::RectF(barWidth, lineHeight * 2 + messageHeight, + barWidth + fullWidth * 0.35f, fullHeight); + colorBarArea_[indexUpperLeft] = D2D1::RectF(0.0f, 0.0f, barWidth, barHeight); + + // ------------------------|---| + // | apiArea ||---| + // ------------------------|---| + // | o | fpsArea | msArea ||---| + // ------------------------|---| + // | | ||---| + // |L| graph ||---| + // | | ||---| + // ------------------------|---| + // | messageArea ||---| + // | + ||---| + // | messageValueArea ||---| + // ------------------------|---| const int indexUpperRight = static_cast(Alignment::UpperRight); - recordingArea_[indexUpperRight] = D2D1::RectF(0.0f, 0.0f, 20.0f, lineHeight); - fpsArea_[indexUpperRight] = D2D1::RectF(20.0f, 0.0f, halfWidth + 10, lineHeight); - msArea_[indexUpperRight] = D2D1::RectF(halfWidth + 10, 0.0f, fullWidth, lineHeight); - messageArea_[indexUpperRight] = D2D1::RectF(0.0f, lineHeight, fullWidth, fullHeight); - messageValueArea_[indexUpperRight] = D2D1::RectF(0.0f, lineHeight, fullWidth * 0.35f, fullHeight); - - // ------------------------ - // | messageArea | - // | + | - // | messageValueArea | - // ------------------------ - // | fpsArea | msArea | o | - // ------------------------ + apiArea_[indexUpperRight] = D2D1::RectF(0.0f, 0.0f, fullWidth, lineHeight); + recordingArea_[indexUpperRight] = D2D1::RectF(0.0f, lineHeight, 20.0f, lineHeight * 2); + fpsArea_[indexUpperRight] = D2D1::RectF(20.0f, lineHeight, halfWidth + 10, lineHeight * 2); + msArea_[indexUpperRight] = D2D1::RectF(halfWidth + 10, lineHeight, fullWidth, lineHeight * 2); + graphLabelArea_[indexUpperRight] = + D2D1::RectF(0.0f, lineHeight * 2, 20.0f, lineHeight * 2 + messageHeight); + graphArea_[indexUpperRight] = + D2D1::RectF(20.0f, lineHeight * 2, fullWidth, lineHeight * 2 + messageHeight); + messageArea_[indexUpperRight] = + D2D1::RectF(0.0f, lineHeight * 2 + messageHeight, fullWidth, fullHeight); + messageValueArea_[indexUpperRight] = + D2D1::RectF(0.0f, lineHeight * 2 + messageHeight, fullWidth * 0.35f, fullHeight); + colorBarArea_[indexUpperRight] = D2D1::RectF(fullWidth, 0.0f, fullWidth + barWidth, barHeight); + + // |---|------------------------ + // |---|| messageArea | + // |---|| + | + // |---|| messageValueArea | + // |---|------------------------ + // |---|| | | + // |---||L| graph | + // |---|| | | + // |---|------------------------ + // |---|| fpsArea | msArea | o | + // |---|------------------------ + // |---|| apiArea | + // |---|------------------------ const int indexLowerLeft = static_cast(Alignment::LowerLeft); - messageArea_[indexLowerLeft] = D2D1::RectF(0.0f, 0.0f, fullWidth, fullHeight - lineHeight); - messageValueArea_[indexLowerLeft] = D2D1::RectF(0.0f, 0.0f, fullWidth * 0.35f, fullHeight - lineHeight); - fpsArea_[indexLowerLeft] = D2D1::RectF(0.0f, fullHeight - lineHeight, halfWidth - 10, fullHeight); - msArea_[indexLowerLeft] = D2D1::RectF(halfWidth - 10, fullHeight - lineHeight, fullWidth - 20.0f, fullHeight); - recordingArea_[indexLowerLeft] = D2D1::RectF(fullWidth - 20.0f, fullHeight - lineHeight, fullWidth, fullHeight); - - // ------------------------ - // | messageArea | - // | + | - // | messageValueArea | - // ------------------------ - // | o | fpsArea | msArea | - // ------------------------ + messageArea_[indexLowerLeft] = D2D1::RectF(barWidth, barHeight - fullHeight_, + barWidth + fullWidth, barHeight - lineHeight * 2); + messageValueArea_[indexLowerLeft] = + D2D1::RectF(barWidth, barHeight - fullHeight_, barWidth + fullWidth * 0.35f, + barHeight - lineHeight * 2 - messageHeight); + graphLabelArea_[indexLowerLeft] = + D2D1::RectF(barWidth, barHeight - lineHeight * 2 - messageHeight, barWidth + 20.0f, + barHeight - lineHeight * 2); + graphArea_[indexLowerLeft] = + D2D1::RectF(barWidth + 20.0f, barHeight - lineHeight * 2 - messageHeight, + barWidth + fullWidth, barHeight - lineHeight * 2); + fpsArea_[indexLowerLeft] = D2D1::RectF(barWidth, barHeight - lineHeight * 2, + barWidth + halfWidth - 10, barHeight - lineHeight); + msArea_[indexLowerLeft] = D2D1::RectF(barWidth + halfWidth - 10, barHeight - lineHeight * 2, + barWidth + fullWidth - 20.0f, barHeight - lineHeight); + recordingArea_[indexLowerLeft] = + D2D1::RectF(barWidth + fullWidth - 20.0f, barHeight - lineHeight * 2, barWidth + fullWidth, + barHeight - lineHeight); + apiArea_[indexLowerLeft] = + D2D1::RectF(barWidth + 0.0f, barHeight - lineHeight, barWidth + fullWidth, barHeight); + colorBarArea_[indexLowerLeft] = D2D1::RectF(0.0f, 0.0f, barWidth, barHeight); + + // ------------------------|---| + // | messageArea ||---| + // | + ||---| + // | messageValueArea ||---| + // ------------------------|---| + // | | ||---| + // |L| graph ||---| + // | | ||---| + // ------------------------|---| + // | o | fpsArea | msArea ||---| + // ------------------------|---| + // | apiArea ||---| + // ------------------------|---| const int indexLowerRight = static_cast(Alignment::LowerRight); - messageArea_[indexLowerRight] = D2D1::RectF(0.0f, 0.0f, fullWidth, fullHeight - lineHeight); - messageValueArea_[indexLowerRight] = D2D1::RectF(0.0f, 0.0f, fullWidth * 0.35f, fullHeight - lineHeight); - recordingArea_[indexLowerRight] = D2D1::RectF(0.0f, fullHeight - lineHeight, 20.0f, fullHeight); - fpsArea_[indexLowerRight] = D2D1::RectF(20.0f, fullHeight - lineHeight, halfWidth + 10, fullHeight); - msArea_[indexLowerRight] = D2D1::RectF(halfWidth + 10, fullHeight - lineHeight, fullWidth, fullHeight); + messageArea_[indexLowerRight] = + D2D1::RectF(0.0f, barHeight - fullHeight_, fullWidth, barHeight - lineHeight * 2); + messageValueArea_[indexLowerRight] = D2D1::RectF(0.0f, barHeight - fullHeight_, fullWidth * 0.35f, + barHeight - lineHeight * 2 - messageHeight); + graphLabelArea_[indexLowerRight] = D2D1::RectF(0.0f, barHeight - lineHeight * 2 - messageHeight, + 20.0f, barHeight - lineHeight * 2); + graphArea_[indexLowerRight] = D2D1::RectF(20.0f, barHeight - lineHeight * 2 - messageHeight, + fullWidth, barHeight - lineHeight * 2); + recordingArea_[indexLowerRight] = + D2D1::RectF(0.0f, barHeight - lineHeight * 2, 20.0f, barHeight - lineHeight); + fpsArea_[indexLowerRight] = + D2D1::RectF(20.0f, barHeight - lineHeight * 2, halfWidth + 10, barHeight - lineHeight); + msArea_[indexLowerRight] = + D2D1::RectF(halfWidth + 10, barHeight - lineHeight * 2, fullWidth, barHeight - lineHeight); + apiArea_[indexLowerRight] = D2D1::RectF(0.0f, barHeight - lineHeight, fullWidth, barHeight); + colorBarArea_[indexLowerRight] = D2D1::RectF(fullWidth, 0.0f, fullWidth + barWidth, barHeight); // Full area is not depending on vertical alignment. - fullArea_.d2d1 = D2D1::RectF(0.0f, 0.0f, fullWidth, fullHeight); - fullArea_.wic = { 0, 0, fullWidth_, fullHeight_ }; + fullArea_.d2d1 = D2D1::RectF(0.0f, 0.0f, fullWidth + barWidth, barHeight); + fullArea_.wic = {0, 0, fullWidth_ + barWidth_, screenHeight_}; } void OverlayBitmap::Resize(int screenWidth, int screenHeight) @@ -187,33 +298,27 @@ void OverlayBitmap::Resize(int screenWidth, int screenHeight) void OverlayBitmap::UpdateScreenPosition() { const auto overlayPosition = RecordingState::GetInstance().GetOverlayPosition(); - if (IsLowerOverlayPosition(overlayPosition)) - { - if (IsLeftOverlayPosition(overlayPosition)) - { - screenPosition_.x = offset_; - screenPosition_.y = screenHeight_ - fullHeight_ - offset_; + if (IsLowerOverlayPosition(overlayPosition)) { + if (IsLeftOverlayPosition(overlayPosition)) { + screenPosition_.x = 0; + screenPosition_.y = 0; currentAlignment_ = Alignment::LowerLeft; } - else - { - screenPosition_.x = screenWidth_ - fullWidth_ - offset_; - screenPosition_.y = screenHeight_ - fullHeight_ - offset_; + else { + screenPosition_.x = screenWidth_ - (fullWidth_ + barWidth_); + screenPosition_.y = 0; currentAlignment_ = Alignment::LowerRight; } } - else - { - if (IsLeftOverlayPosition(overlayPosition)) - { - screenPosition_.x = offset_; - screenPosition_.y = offset_; + else { + if (IsLeftOverlayPosition(overlayPosition)) { + screenPosition_.x = 0; + screenPosition_.y = 0; currentAlignment_ = Alignment::UpperLeft; } - else - { - screenPosition_.x = screenWidth_ - fullWidth_ - offset_; - screenPosition_.y = offset_; + else { + screenPosition_.x = screenWidth_ - (fullWidth_ + barWidth_); + screenPosition_.y = 0; currentAlignment_ = Alignment::UpperRight; } } @@ -235,42 +340,57 @@ void OverlayBitmap::StartRendering() void OverlayBitmap::Update() { const auto textureState = RecordingState::GetInstance().Update(); - if (RecordingState::GetInstance().Started()) - { + if (RecordingState::GetInstance().Started()) { performanceCounter_.Start(); } const auto frameInfo = performanceCounter_.NextFrame(); - if (RecordingState::GetInstance().Stopped()) - { + if (RecordingState::GetInstance().Stopped()) { performanceCounter_.Stop(); } + frameTimes_[currentFrame_] = frameInfo.frameTime; + UpdateScreenPosition(); - renderTarget_->Clear(clearColor_); // clear full bitmap - if (RecordingState::GetInstance().IsOverlayShowing()) - { + renderTarget_->Clear(clearColor_); // clear full bitmap + if (RecordingState::GetInstance().IsOverlayShowing()) { DrawFrameInfo(frameInfo); DrawMessages(textureState); } + if (RecordingState::GetInstance().IsGraphOverlayShowing()) { + DrawGraph(); + } + if (RecordingState::GetInstance().IsBarOverlayShowing()) { + DrawBar(); + } + + currentFrame_ = (currentFrame_ + 1) % 512; } void OverlayBitmap::DrawFrameInfo(const GameOverlay::PerformanceCounter::FrameInfo& frameInfo) { const int alignment = static_cast(currentAlignment_); + // api + renderTarget_->PushAxisAlignedClip(apiArea_[alignment], D2D1_ANTIALIAS_MODE_ALIASED); + renderTarget_->Clear(messageBackgroundColor_); + apiMessage_[alignment]->WriteMessage(api_, L" API"); + apiMessage_[alignment]->SetText(writeFactory_.Get(), textFormat_.Get()); + apiMessage_[alignment]->Draw(renderTarget_.Get()); - // recording dot - renderTarget_->PushAxisAlignedClip(recordingArea_[alignment], D2D1_ANTIALIAS_MODE_ALIASED); - renderTarget_->Clear(fpsBackgroundColor_); - if (recording_) { - recordingMessage_[alignment]->WriteMessage(L"\x2022"); - } - else { - recordingMessage_[alignment]->WriteMessage(L" "); - } - recordingMessage_[alignment]->SetText(writeFactory_.Get(), textFormat_.Get()); - recordingMessage_[alignment]->Draw(renderTarget_.Get()); + renderTarget_->PopAxisAlignedClip(); - renderTarget_->PopAxisAlignedClip(); + // recording dot + renderTarget_->PushAxisAlignedClip(recordingArea_[alignment], D2D1_ANTIALIAS_MODE_ALIASED); + renderTarget_->Clear(fpsBackgroundColor_); + if (recording_) { + recordingMessage_[alignment]->WriteMessage(L"\x2022"); + } + else { + recordingMessage_[alignment]->WriteMessage(L" "); + } + recordingMessage_[alignment]->SetText(writeFactory_.Get(), textFormat_.Get()); + recordingMessage_[alignment]->Draw(renderTarget_.Get()); + + renderTarget_->PopAxisAlignedClip(); // fps counter renderTarget_->PushAxisAlignedClip(fpsArea_[alignment], D2D1_ANTIALIAS_MODE_ALIASED); @@ -284,7 +404,6 @@ void OverlayBitmap::DrawFrameInfo(const GameOverlay::PerformanceCounter::FrameIn // ms counter renderTarget_->PushAxisAlignedClip(msArea_[alignment], D2D1_ANTIALIAS_MODE_ALIASED); renderTarget_->Clear(msBackgroundColor_); - msMessage_[alignment]->WriteMessage(frameInfo.ms, L" ms", precision_); msMessage_[alignment]->SetText(writeFactory_.Get(), textFormat_.Get()); msMessage_[alignment]->Draw(renderTarget_.Get()); @@ -294,8 +413,7 @@ void OverlayBitmap::DrawFrameInfo(const GameOverlay::PerformanceCounter::FrameIn void OverlayBitmap::DrawMessages(TextureState textureState) { - if (textureState == TextureState::Default) - { + if (textureState == TextureState::Default) { const int alignment = static_cast(currentAlignment_); renderTarget_->PushAxisAlignedClip(messageArea_[alignment], D2D1_ANTIALIAS_MODE_ALIASED); renderTarget_->Clear(clearColor_); @@ -306,15 +424,13 @@ void OverlayBitmap::DrawMessages(TextureState textureState) const int alignment = static_cast(currentAlignment_); renderTarget_->PushAxisAlignedClip(messageArea_[alignment], D2D1_ANTIALIAS_MODE_ALIASED); renderTarget_->Clear(messageBackgroundColor_); - if (textureState == TextureState::Start) - { + if (textureState == TextureState::Start) { stateMessage_[alignment]->WriteMessage(L"Capture Started"); stateMessage_[alignment]->SetText(writeFactory_.Get(), messageFormat_.Get()); stateMessage_[alignment]->Draw(renderTarget_.Get()); recording_ = true; } - else if (textureState == TextureState::Stop) - { + else if (textureState == TextureState::Stop) { const auto capture = performanceCounter_.GetLastCaptureResults(); stateMessage_[alignment]->WriteMessage(L"Capture Ended\n"); stateMessage_[alignment]->SetText(writeFactory_.Get(), messageFormat_.Get()); @@ -336,19 +452,71 @@ void OverlayBitmap::DrawMessages(TextureState textureState) renderTarget_->PopAxisAlignedClip(); } +void OverlayBitmap::DrawGraph() +{ + const int alignment = static_cast(currentAlignment_); + + renderTarget_->PushAxisAlignedClip(graphArea_[alignment], D2D1_ANTIALIAS_MODE_ALIASED); + renderTarget_->Clear(graphBackgroundColor_); + + renderTarget_->DrawLine(D2D1::Point2F(0.0f, graphArea_[alignment].bottom), + D2D1::Point2F(fullWidth_ + barWidth_, graphArea_[alignment].bottom), + helperLineBrush_.Get()); + renderTarget_->DrawLine(D2D1::Point2F(0.0f, graphArea_[alignment].bottom - 33.33f), + D2D1::Point2F(fullWidth_ + barWidth_, graphArea_[alignment].bottom - 33.33f), + helperLineBrush_.Get()); + renderTarget_->DrawLine(D2D1::Point2F(0.0f, graphArea_[alignment].bottom - 66.66f), + D2D1::Point2F(fullWidth_ + barWidth_, graphArea_[alignment].bottom - 66.66f), + helperLineBrush_.Get()); + renderTarget_->DrawLine(D2D1::Point2F(0.0f, graphArea_[alignment].bottom - 99.99f), + D2D1::Point2F(fullWidth_ + barWidth_, graphArea_[alignment].bottom - 99.99f), + helperLineBrush_.Get()); + + points_[0] = D2D1::Point2F(0.0f, graphArea_[alignment].bottom - frameTimes_[currentFrame_ % 512]); + float time = frameTimes_[((currentFrame_ + 512) - 1) % 512] * 0.2f; + + for (int i = 1; i < 512; i++) { + points_[i] = D2D1::Point2F( + time, graphArea_[alignment].bottom - frameTimes_[((currentFrame_ + 512) - i) % 512]); + time = time + frameTimes_[((currentFrame_ + 512) - i - 1) % 512] * 0.2f; + + renderTarget_->DrawLine(points_[i - 1], points_[i], textBrush_.Get()); + } + + renderTarget_->PopAxisAlignedClip(); + + renderTarget_->PushAxisAlignedClip(graphLabelArea_[alignment], D2D1_ANTIALIAS_MODE_ALIASED); + renderTarget_->Clear(graphBackgroundColor_); + graphLabelMessage_[alignment]->WriteMessage(L"ms\n\n\n100\n\n\n66\n\n\n33\n\n\n0"); + graphLabelMessage_[alignment]->SetText(writeFactory_.Get(), graphLabelMessagFormat_.Get()); + graphLabelMessage_[alignment]->Draw(renderTarget_.Get()); + renderTarget_->PopAxisAlignedClip(); +} + +void OverlayBitmap::DrawBar() +{ + const int alignment = static_cast(currentAlignment_); + + // colored bar + renderTarget_->PushAxisAlignedClip(colorBarArea_[alignment], D2D1_ANTIALIAS_MODE_ALIASED); + renderTarget_->Clear(colorBarSequence_[colorSequenceIndex_]); + + colorSequenceIndex_ = (colorSequenceIndex_ + 1) % 16; + + renderTarget_->PopAxisAlignedClip(); +} + void OverlayBitmap::FinishRendering() { HRESULT hr = renderTarget_->EndDraw(); - if (FAILED(hr)) - { + if (FAILED(hr)) { g_messageLog.LogWarning("OverlayBitmap", "EndDraw failed, HRESULT", hr); } } OverlayBitmap::RawData OverlayBitmap::GetBitmapDataRead() { - if (bitmapLock_) - { + if (bitmapLock_) { g_messageLog.LogWarning("OverlayBitmap", "Bitmap lock was not released"); } bitmapLock_.Reset(); @@ -356,87 +524,58 @@ OverlayBitmap::RawData OverlayBitmap::GetBitmapDataRead() RawData rawData = {}; const auto& currBitmapArea = fullArea_.wic; HRESULT hr = bitmap_->Lock(&currBitmapArea, WICBitmapLockRead, &bitmapLock_); - if (FAILED(hr)) - { + if (FAILED(hr)) { g_messageLog.LogWarning("OverlayBitmap", "Bitmap lock failed, HRESULT", hr); return rawData; } hr = bitmapLock_->GetDataPointer(&rawData.size, &rawData.dataPtr); - if (FAILED(hr)) - { - g_messageLog.LogWarning("OverlayBitmap", - "Bitmap lock GetDataPointer failed, HRESULT", hr); + if (FAILED(hr)) { + g_messageLog.LogWarning("OverlayBitmap", "Bitmap lock GetDataPointer failed, HRESULT", hr); rawData = {}; } return rawData; } -void OverlayBitmap::UnlockBitmapData() -{ - bitmapLock_.Reset(); -} +void OverlayBitmap::UnlockBitmapData() { bitmapLock_.Reset(); } -int OverlayBitmap::GetFullWidth() const -{ - return fullWidth_; -} +int OverlayBitmap::GetFullWidth() const { return fullWidth_ * 2; } -int OverlayBitmap::GetFullHeight() const -{ - return fullHeight_; -} +int OverlayBitmap::GetFullHeight() const { return screenHeight_; } -OverlayBitmap::Position OverlayBitmap::GetScreenPos() const -{ - return screenPosition_; -} +OverlayBitmap::Position OverlayBitmap::GetScreenPos() const { return screenPosition_; } -const D2D1_RECT_F & OverlayBitmap::GetCopyArea() const -{ - return fullArea_.d2d1; -} +const D2D1_RECT_F& OverlayBitmap::GetCopyArea() const { return fullArea_.d2d1; } -VkFormat OverlayBitmap::GetVKFormat() const -{ - return VK_FORMAT_B8G8R8A8_UNORM; -} +VkFormat OverlayBitmap::GetVKFormat() const { return VK_FORMAT_B8G8R8A8_UNORM; } bool OverlayBitmap::InitFactories() { HRESULT hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, IID_PPV_ARGS(&d2dFactory_)); - if (FAILED(hr)) - { - g_messageLog.LogError("OverlayBitmap", - "D2D1CreateFactory failed, HRESULT", hr); + if (FAILED(hr)) { + g_messageLog.LogError("OverlayBitmap", "D2D1CreateFactory failed, HRESULT", hr); return false; } hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(writeFactory_), reinterpret_cast(writeFactory_.GetAddressOf())); - if (FAILED(hr)) - { - g_messageLog.LogError("OverlayBitmap", - "DWriteCreateFactory failed, HRESULT", hr); + if (FAILED(hr)) { + g_messageLog.LogError("OverlayBitmap", "DWriteCreateFactory failed, HRESULT", hr); return false; } hr = CoInitialize(NULL); - if (hr == S_OK || hr == S_FALSE) - { + if (hr == S_OK || hr == S_FALSE) { coInitialized_ = true; } - else - { + else { g_messageLog.LogWarning("OverlayBitmap", "CoInitialize failed, HRESULT", hr); } hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&iwicFactory_)); - if (FAILED(hr)) - { - g_messageLog.LogError("OverlayBitmap", - "CoCreateInstance failed, HRESULT", hr); + if (FAILED(hr)) { + g_messageLog.LogError("OverlayBitmap", "CoCreateInstance failed, HRESULT", hr); return false; } return true; @@ -444,10 +583,9 @@ bool OverlayBitmap::InitFactories() bool OverlayBitmap::InitBitmap() { - HRESULT hr = iwicFactory_->CreateBitmap(fullWidth_, fullHeight_, GUID_WICPixelFormat32bppPBGRA, - WICBitmapCacheOnLoad, &bitmap_); - if (FAILED(hr)) - { + HRESULT hr = iwicFactory_->CreateBitmap( + fullWidth_ * 2, screenHeight_, GUID_WICPixelFormat32bppPBGRA, WICBitmapCacheOnLoad, &bitmap_); + if (FAILED(hr)) { g_messageLog.LogError("OverlayBitmap", "CreateBitmap failed, HRESULT", hr); return false; } @@ -458,18 +596,20 @@ bool OverlayBitmap::InitBitmap() D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE); hr = d2dFactory_->CreateWicBitmapRenderTarget(bitmap_.Get(), rtProperties, &renderTarget_); - if (FAILED(hr)) - { - g_messageLog.LogError("OverlayBitmap", - "CreateWicBitmapRenderTarget failed, HRESULT", hr); + if (FAILED(hr)) { + g_messageLog.LogError("OverlayBitmap", "CreateWicBitmapRenderTarget failed, HRESULT", hr); return false; } hr = renderTarget_->CreateSolidColorBrush(fontColor_, &textBrush_); - if (FAILED(hr)) - { - g_messageLog.LogError("OverlayBitmap", - "CreateTextFormat failed, HRESULT", hr); + if (FAILED(hr)) { + g_messageLog.LogError("OverlayBitmap", "CreateTextFormat failed, HRESULT", hr); + return false; + } + + hr = renderTarget_->CreateSolidColorBrush(numberColor_, &helperLineBrush_); + if (FAILED(hr)) { + g_messageLog.LogError("OverlayBitmap", "CreateTextFormat failed, HRESULT", hr); return false; } @@ -486,8 +626,10 @@ bool OverlayBitmap::InitText() CreateTextFormat(20.0f, DWRITE_TEXT_ALIGNMENT_TRAILING, DWRITE_PARAGRAPH_ALIGNMENT_CENTER); stopMessageFormat_ = CreateTextFormat(20.0f, DWRITE_TEXT_ALIGNMENT_LEADING, DWRITE_PARAGRAPH_ALIGNMENT_CENTER); - recordingMessageFormat_ = + recordingMessageFormat_ = CreateTextFormat(25.0f, DWRITE_TEXT_ALIGNMENT_CENTER, DWRITE_PARAGRAPH_ALIGNMENT_CENTER); + graphLabelMessagFormat_ = + CreateTextFormat(8.0f, DWRITE_TEXT_ALIGNMENT_CENTER, DWRITE_PARAGRAPH_ALIGNMENT_FAR); InitTextForAlignment(Alignment::LowerLeft); InitTextForAlignment(Alignment::LowerRight); @@ -499,59 +641,65 @@ bool OverlayBitmap::InitText() void OverlayBitmap::InitTextForAlignment(Alignment alignment) { const int ialignment = static_cast(alignment); + auto apiArea = apiArea_[ialignment]; + apiMessage_[ialignment].reset(new TextMessage(renderTarget_.Get(), fontColor_, numberColor_)); + apiMessage_[ialignment]->SetArea(apiArea.left, apiArea.top, apiArea.right - apiArea.left, + apiArea.bottom - apiArea.top); + auto fpsArea = fpsArea_[ialignment]; fpsMessage_[ialignment].reset(new TextMessage(renderTarget_.Get(), fontColor_, numberColor_)); - fpsMessage_[ialignment]->SetArea( - fpsArea.left, fpsArea.top, - fpsArea.right - fpsArea.left, - fpsArea.bottom - fpsArea.top); + fpsMessage_[ialignment]->SetArea(fpsArea.left, fpsArea.top, fpsArea.right - fpsArea.left, + fpsArea.bottom - fpsArea.top); auto msArea = msArea_[ialignment]; msMessage_[ialignment].reset(new TextMessage(renderTarget_.Get(), fontColor_, numberColor_)); - msMessage_[ialignment]->SetArea( - msArea.left, msArea.top, - msArea.right - msArea.left, - msArea.bottom - msArea.top); + msMessage_[ialignment]->SetArea(msArea.left, msArea.top, msArea.right - msArea.left, + msArea.bottom - msArea.top); auto recordingArea = recordingArea_[ialignment]; - recordingMessage_[ialignment].reset(new TextMessage(renderTarget_.Get(), recordingColor_, recordingColor_)); - recordingMessage_[ialignment]->SetArea( - recordingArea.left, recordingArea.top, - recordingArea.right - recordingArea.left, - recordingArea.bottom - recordingArea.top); + recordingMessage_[ialignment].reset( + new TextMessage(renderTarget_.Get(), recordingColor_, recordingColor_)); + recordingMessage_[ialignment]->SetArea(recordingArea.left, recordingArea.top, + recordingArea.right - recordingArea.left, + recordingArea.bottom - recordingArea.top); + + auto graphLabelArea = graphLabelArea_[ialignment]; + graphLabelMessage_[ialignment].reset( + new TextMessage(renderTarget_.Get(), fontColor_, numberColor_)); + graphLabelMessage_[ialignment]->SetArea(graphLabelArea.left, graphLabelArea.top, + graphLabelArea.right - graphLabelArea.left, + graphLabelArea.bottom - graphLabelArea.top); const auto offset2 = offset_ * 2; auto messageArea = messageArea_[ialignment]; stateMessage_[ialignment].reset(new TextMessage(renderTarget_.Get(), fontColor_, numberColor_)); - stateMessage_[ialignment]->SetArea( - messageArea.left + offset2, messageArea.top + offset_, - messageArea.right - messageArea.left - offset2, - messageArea.bottom - messageArea.top - offset_); + stateMessage_[ialignment]->SetArea(messageArea.left + offset2, messageArea.top + offset_, + messageArea.right - messageArea.left - offset2, + messageArea.bottom - messageArea.top - offset_); auto messageValueArea = messageValueArea_[ialignment]; - stopValueMessage_[ialignment].reset(new TextMessage(renderTarget_.Get(), fontColor_, numberColor_)); - stopValueMessage_[ialignment]->SetArea( - messageValueArea.left + offset2, messageValueArea.top + offset_, - messageValueArea.right - messageValueArea.left - offset2, - messageValueArea.bottom - messageValueArea.top - offset_); + stopValueMessage_[ialignment].reset( + new TextMessage(renderTarget_.Get(), fontColor_, numberColor_)); + stopValueMessage_[ialignment]->SetArea(messageValueArea.left + offset2, + messageValueArea.top + offset_, + messageValueArea.right - messageValueArea.left - offset2, + messageValueArea.bottom - messageValueArea.top - offset_); stopMessage_[ialignment].reset(new TextMessage(renderTarget_.Get(), fontColor_, numberColor_)); - stopMessage_[ialignment]->SetArea( - messageValueArea.right + offset2, messageArea.top + offset_, - messageArea.right - messageArea.left - offset2, - messageArea.bottom - messageArea.top - offset_); + stopMessage_[ialignment]->SetArea(messageValueArea.right + offset2, messageArea.top + offset_, + messageArea.right - messageArea.left - offset2, + messageArea.bottom - messageArea.top - offset_); } IDWriteTextFormat* OverlayBitmap::CreateTextFormat(float size, DWRITE_TEXT_ALIGNMENT textAlignment, - DWRITE_PARAGRAPH_ALIGNMENT paragraphAlignment) + DWRITE_PARAGRAPH_ALIGNMENT paragraphAlignment) { IDWriteTextFormat* textFormat; HRESULT hr = writeFactory_->CreateTextFormat(L"Verdane", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, size, L"en-us", &textFormat); - if (FAILED(hr)) - { + if (FAILED(hr)) { g_messageLog.LogError("OverlayBitmap", "CreateTextFormat failed, HRESULT", hr); return false; } diff --git a/Commons/Rendering/OverlayBitmap.h b/Commons/Rendering/OverlayBitmap.h index 07fc8d5..8639675 100644 --- a/Commons/Rendering/OverlayBitmap.h +++ b/Commons/Rendering/OverlayBitmap.h @@ -54,10 +54,17 @@ class OverlayBitmap final int y; }; + enum class API + { + DX11, + DX12, + Vulkan + }; + OverlayBitmap(); ~OverlayBitmap(); - bool Init(int screenWidth, int screenHeight); + bool Init(int screenWidth, int screenHeight, API api); void Resize(int screenWidth, int screenHeight); void DrawOverlay(); @@ -82,9 +89,9 @@ class OverlayBitmap final enum class Alignment { UpperLeft, // = 0 - UpperRight, // = 1 + UpperRight, // = 1 LowerLeft, // = 2 - LowerRight // = 3 + LowerRight // = 3 }; void CalcSize(int screenWidth, int screenHeight); @@ -98,6 +105,8 @@ class OverlayBitmap final void StartRendering(); void DrawFrameInfo(const GameOverlay::PerformanceCounter::FrameInfo& frameInfo); void DrawMessages(TextureState textureState); + void DrawGraph(); + void DrawBar(); void FinishRendering(); IDWriteTextFormat* CreateTextFormat(float size, DWRITE_TEXT_ALIGNMENT textAlignment, @@ -107,15 +116,18 @@ class OverlayBitmap final static const D2D1_COLOR_F fpsBackgroundColor_; static const D2D1_COLOR_F msBackgroundColor_; static const D2D1_COLOR_F messageBackgroundColor_; + static const D2D1_COLOR_F graphBackgroundColor_; static const D2D1_COLOR_F fontColor_; static const D2D1_COLOR_F numberColor_; static const D2D1_COLOR_F recordingColor_; + static const D2D1_COLOR_F colorBarSequence_[]; GameOverlay::PerformanceCounter performanceCounter_; Microsoft::WRL::ComPtr d2dFactory_; Microsoft::WRL::ComPtr renderTarget_; Microsoft::WRL::ComPtr textBrush_; + Microsoft::WRL::ComPtr helperLineBrush_; Microsoft::WRL::ComPtr writeFactory_; Microsoft::WRL::ComPtr textFormat_; @@ -123,6 +135,7 @@ class OverlayBitmap final Microsoft::WRL::ComPtr stopValueFormat_; Microsoft::WRL::ComPtr stopMessageFormat_; Microsoft::WRL::ComPtr recordingMessageFormat_; + Microsoft::WRL::ComPtr graphLabelMessagFormat_; Microsoft::WRL::ComPtr iwicFactory_; Microsoft::WRL::ComPtr bitmap_; @@ -135,11 +148,15 @@ class OverlayBitmap final std::unique_ptr stopValueMessage_[alignmentCount_]; std::unique_ptr stopMessage_[alignmentCount_]; std::unique_ptr recordingMessage_[alignmentCount_]; + std::unique_ptr apiMessage_[alignmentCount_]; + std::unique_ptr graphLabelMessage_[alignmentCount_]; + int messageHeight_; int fullWidth_; int fullHeight_; int screenWidth_; int screenHeight_; + int barWidth_; // always the upper left corner of the full copy area on the screen. Position screenPosition_; @@ -151,12 +168,23 @@ class OverlayBitmap final D2D1_RECT_F fpsArea_[alignmentCount_]; D2D1_RECT_F msArea_[alignmentCount_]; D2D1_RECT_F recordingArea_[alignmentCount_]; + D2D1_RECT_F apiArea_[alignmentCount_]; + D2D1_RECT_F colorBarArea_[alignmentCount_]; + D2D1_RECT_F graphArea_[alignmentCount_]; + D2D1_RECT_F graphLabelArea_[alignmentCount_]; int lineHeight_ = 45; int offset_ = 5; + int colorSequenceIndex_ = 0; + + D2D1_POINT_2F points_[512]; + float frameTimes_[512] = {}; + int currentFrame_ = 0; + bool coInitialized_ = false; Alignment currentAlignment_ = Alignment::UpperLeft; bool recording_ = false; + std::wstring api_; }; diff --git a/Commons/Rendering/TextMessage.cpp b/Commons/Rendering/TextMessage.cpp index c01c116..33fad71 100644 --- a/Commons/Rendering/TextMessage.cpp +++ b/Commons/Rendering/TextMessage.cpp @@ -69,6 +69,14 @@ void TextMessage::WriteMessage(int value, const std::wstring& msg) message_ << msg; } +void TextMessage::WriteMessage(const std::wstring& msgA, const std::wstring& msgB) +{ + const auto start = static_cast(message_.tellp()); + message_ << msgA; + numberRanges_.push_back({start, static_cast (message_.tellp()) - start}); + message_ << msgB; +} + void TextMessage::SetText(IDWriteFactory* writeFactory, IDWriteTextFormat* textFormat) { textLayout_ = nullptr; diff --git a/Commons/Rendering/TextMessage.h b/Commons/Rendering/TextMessage.h index 42d291d..f358fb9 100644 --- a/Commons/Rendering/TextMessage.h +++ b/Commons/Rendering/TextMessage.h @@ -45,6 +45,7 @@ class TextMessage final { void WriteMessage(const std::wstring& msg); void WriteMessage(float value, const std::wstring& msg, int precision); void WriteMessage(int value, const std::wstring& msg); + void WriteMessage(const std::wstring& msgA, const std::wstring& msgB); void SetText(IDWriteFactory* writeFactory, IDWriteTextFormat* textFormat); void Draw(ID2D1RenderTarget* renderTarget); diff --git a/Frontend/ConfigurationFile.cs b/Frontend/ConfigurationFile.cs index 3011eee..dcd423c 100644 --- a/Frontend/ConfigurationFile.cs +++ b/Frontend/ConfigurationFile.cs @@ -33,39 +33,49 @@ namespace Frontend { public class RecordingOptions { - public int toggleRecordingHotkey; + public int toggleCaptureHotkey; public int toggleOverlayHotkey; - public int recordTime; - public int recordDelay; - public bool recordAll; + public int toggleGraphOverlayHotkey; + public int toggleBarOverlayHotkey; + public int captureTime; + public int captureDelay; + public bool captureAll; public bool injectOnStart; public int overlayPosition; + public string captureOutputFolder; private const string section = "Recording"; public RecordingOptions() { - toggleRecordingHotkey = 123; // F12 - toggleOverlayHotkey = 0x50; // P - recordTime = 60; - recordDelay = 0; - recordAll = true; + toggleCaptureHotkey = 0x79; // F10 + toggleOverlayHotkey = 0x78; // F9 + toggleGraphOverlayHotkey = 0x76; // F7 + toggleBarOverlayHotkey = 0x77; // F8 + captureTime = 60; + captureDelay = 0; + captureAll = true; injectOnStart = true; overlayPosition = OverlayPosition.UpperRight.ToInt(); + const string outputFolderPath = ("\\OCAT\\Captures"); + captureOutputFolder = System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + outputFolderPath; } public void Save(string path) { - using (var iniFile = new StreamWriter(path, false)) + using (var iniFile = new StreamWriter(path, false, Encoding.Unicode)) { iniFile.WriteLine("[" + section + "]"); - iniFile.WriteLine("hotkey=" + toggleRecordingHotkey); + iniFile.WriteLine("toggleCaptureHotkey=" + toggleCaptureHotkey); iniFile.WriteLine("toggleOverlayHotkey=" + toggleOverlayHotkey); + iniFile.WriteLine("toggleFramegraphOverlayHotkey=" + toggleGraphOverlayHotkey); + iniFile.WriteLine("toggleColoredBarOverlayHotkey=" + toggleBarOverlayHotkey); iniFile.WriteLine("overlayPosition=" + overlayPosition); - iniFile.WriteLine("recordTime=" + recordTime); - iniFile.WriteLine("recordDelay=" + recordDelay); - iniFile.WriteLine("recordAllProcesses=" + Convert.ToInt32(recordAll)); + iniFile.WriteLine("captureTime=" + captureTime); + iniFile.WriteLine("captureDelay=" + captureDelay); + iniFile.WriteLine("captureAllProcesses=" + Convert.ToInt32(captureAll)); iniFile.WriteLine("injectOnStart=" + Convert.ToInt32(injectOnStart)); + iniFile.WriteLine("captureOutputFolder=" + captureOutputFolder); } } @@ -73,13 +83,16 @@ public void Load(string path) { if (ConfigurationFile.Exists()) { - toggleRecordingHotkey = ConfigurationFile.ReadInt(section, "hotkey", toggleRecordingHotkey, path); + toggleCaptureHotkey = ConfigurationFile.ReadInt(section, "toggleCaptureHotkey", toggleCaptureHotkey, path); toggleOverlayHotkey = ConfigurationFile.ReadInt(section, "toggleOverlayHotkey", toggleOverlayHotkey, path); + toggleGraphOverlayHotkey = ConfigurationFile.ReadInt(section, "toggleFramegraphOverlayHotkey", toggleGraphOverlayHotkey, path); + toggleBarOverlayHotkey = ConfigurationFile.ReadInt(section, "toggleColoredBarOverlayHotkey", toggleBarOverlayHotkey, path); overlayPosition = ConfigurationFile.ReadInt(section, "overlayPosition", overlayPosition, path); - recordTime = ConfigurationFile.ReadInt(section, "recordTime", recordTime, path); - recordDelay = ConfigurationFile.ReadInt(section, "recordDelay", recordDelay, path); - recordAll = ConfigurationFile.ReadBool(section, "recordAllProcesses", path); + captureTime = ConfigurationFile.ReadInt(section, "captureTime", captureTime, path); + captureDelay = ConfigurationFile.ReadInt(section, "captureDelay", captureDelay, path); + captureAll = ConfigurationFile.ReadBool(section, "captureAllProcesses", path); injectOnStart = ConfigurationFile.ReadBool(section, "injectOnStart", path); + captureOutputFolder = ConfigurationFile.ReadString(section, "captureOutputFolder", path); } } } diff --git a/Frontend/KeyCaptureMode.cs b/Frontend/KeyCaptureMode.cs index a825bdc..a572abb 100644 --- a/Frontend/KeyCaptureMode.cs +++ b/Frontend/KeyCaptureMode.cs @@ -11,6 +11,8 @@ public enum KeyCaptureMode { None, RecordingToggle, - VisibilityToggle + VisibilityToggle, + GraphVisibilityToggle, + BarVisibilityToggle } } diff --git a/Frontend/KeyboardHook.cs b/Frontend/KeyboardHook.cs index 37d77dc..11b7c9a 100644 --- a/Frontend/KeyboardHook.cs +++ b/Frontend/KeyboardHook.cs @@ -21,61 +21,24 @@ // using System; -using System.Collections.Generic; -using System.Linq; using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; using System.Windows; -using System.Windows.Input; namespace Frontend { class KeyboardHook { - public - struct tagKBDLLHOOKSTRUCT { - public - int vkCode; - public - int scanCode; - public - int flags; - public - int time; - public - int dwExtraInfo; - }; - - public - delegate int LowLevelKeyboardProc(int nCode, int wParam, ref tagKBDLLHOOKSTRUCT lParam); - - // C++ functions - [DllImport("kernel32.dll")] public static extern IntPtr LoadLibrary(string lpFileName); [DllImport("kernel32.dll")] static extern int GetLastError(); - - [DllImport("user32.dll")] static extern IntPtr SetWindowsHookEx(int idHook, - LowLevelKeyboardProc lpfn, - IntPtr hMod, uint dwThreadId); - [DllImport("user32.dll")] static extern int CallNextHookEx(IntPtr hhk, int nCode, int wParam, - tagKBDLLHOOKSTRUCT lParam); - - [DllImport("user32.dll")] static extern bool UnhookWindowsHookEx(IntPtr hhk); - - const int WH_KEYBOARD_LL = 13; - const int HC_ACTION = 0; - const int WM_KEYDOWN = 0x100; - const int WM_SYSKEYDOWN = 0x104; + [DllImport("user32.dll")] static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc); + [DllImport("user32.dll")] static extern bool UnregisterHotKey(IntPtr hWnd, int id); private int keyCode_; IntPtr globalHook_ = IntPtr.Zero; - private - LowLevelKeyboardProc keyBoardHookProcDelegate_; public - KeyboardHook() { keyBoardHookProcDelegate_ = KeyboardProc; } - ~KeyboardHook() { UnHook(); } + KeyboardHook() { } + ~KeyboardHook() { } public - bool ActivateHook(int newKeyCode) + bool ActivateHook(int newKeyCode, IntPtr handler) { if (globalHook_ != IntPtr.Zero) { // already set return true @@ -87,6 +50,9 @@ bool ActivateHook(int newKeyCode) UnHook(); } } + else { + globalHook_ = handler; + } return Hook(newKeyCode); } @@ -94,31 +60,35 @@ bool ActivateHook(int newKeyCode) public void UnHook() { - if (globalHook_ != IntPtr.Zero && !UnhookWindowsHookEx(globalHook_)) { + if (globalHook_ != IntPtr.Zero && !UnregisterHotKey(globalHook_, keyCode_)) + { int error = GetLastError(); - MessageBox.Show("UnhookWindowsHookEx failed " + error.ToString()); + MessageBox.Show("UnregisterHotKey failed " + error.ToString()); } + globalHook_ = IntPtr.Zero; } private bool Hook(int newKeyCode) { - IntPtr hMod = LoadLibrary("kernel32.dll"); - if (hMod == IntPtr.Zero) { - MessageBox.Show("Load Library failed"); - return false; - } - - globalHook_ = SetWindowsHookEx(WH_KEYBOARD_LL, keyBoardHookProcDelegate_, hMod, 0); - if (globalHook_ == IntPtr.Zero) { + if (!RegisterHotKey(globalHook_, newKeyCode, 0, newKeyCode)) + { int error = GetLastError(); - MessageBox.Show("SetWindowsHookEx failed " + error.ToString()); + if (newKeyCode == 0x7B) + { + MessageBox.Show( + "RegisterHotKey failed, F12 is a reserved key and cannot be used as hotkey " + error.ToString()); + } + else + { + MessageBox.Show("RegisterHotKey failed " + error.ToString()); + } + globalHook_ = IntPtr.Zero; return false; } keyCode_ = newKeyCode; - return true; } @@ -127,16 +97,13 @@ bool Hook(int newKeyCode) public event KeyboardDownEvent HotkeyDownEvent; - public - int KeyboardProc(int nCode, int wParam, ref tagKBDLLHOOKSTRUCT lParam) + public + void OnHotKeyEvent(long lParam) { - if (nCode == HC_ACTION) { - if (lParam.vkCode == keyCode_ && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) && - (HotkeyDownEvent != null)) { - HotkeyDownEvent(); - } + if (lParam == keyCode_ && (HotkeyDownEvent != null)) + { + HotkeyDownEvent(); } - return CallNextHookEx(globalHook_, nCode, wParam, lParam); } } -} +} \ No newline at end of file diff --git a/Frontend/MainWindow.xaml b/Frontend/MainWindow.xaml index 54ddd87..d7acec5 100644 --- a/Frontend/MainWindow.xaml +++ b/Frontend/MainWindow.xaml @@ -362,13 +362,14 @@ + - @@ -385,33 +386,47 @@ - FPS/ms + FPS/ms - FPS/ms + FPS/ms - FPS/ms + FPS/ms - FPS/ms + FPS/ms Overlay position - + - + @@ -432,21 +447,21 @@ - + - - + + + + + @@ -506,11 +521,11 @@ - - + + - +