feat: 实现叠加层 (p2)

This commit is contained in:
Xu 2026-04-27 14:16:11 +08:00
commit c148306a0c
17 changed files with 463 additions and 206 deletions

View file

@ -246,9 +246,7 @@ void CursorDrawer::PrepareForDraw(HCURSOR hCursor, POINT cursorPos, bool& needRe
needRedraw |= cursorPos != _curCursorPos;
}
if (needRedraw) {
_curCursorPos = cursorPos;
}
_curCursorPos = cursorPos;
}
HRESULT CursorDrawer::Draw(

View file

@ -37,34 +37,22 @@ public:
ID3D12Resource* backBuffer = nullptr
) noexcept;
void OnCursorVirtualizationStarted() noexcept {
_isCursorVirtualized = true;
void OnCursorVirtualizationChanged(bool value) noexcept {
_isCursorVirtualized = value;
}
void OnCursorVirtualizationEnded() noexcept {
_isCursorVirtualized = false;
}
void OnMoveStarted() noexcept {
_isMoving = true;
}
void OnMoveEnded() noexcept {
_isMoving = false;
}
void OnSrcMoveStarted() noexcept {
_isSrcMoving = true;
}
void OnSrcMoveEnded() noexcept {
_isSrcMoving = false;
void OnMovingChanged(bool value) noexcept {
_isMoving = value;
}
void OnMoved(const RECT& rendererRect, const RECT& destRect) noexcept;
void OnResized(const RECT& rendererRect, const RECT& destRect) noexcept;
void OnSrcMovingChanged(bool value) noexcept {
_isSrcMoving = value;
}
void OnColorInfoChanged(const ColorInfo& colorInfo) noexcept;
private:

View file

@ -40,18 +40,20 @@ CursorManager::~CursorManager() noexcept {
}
std::pair<HCURSOR, POINT> CursorManager::Update() noexcept {
bool wasCapturedOnForeground = _isCapturedOnForeground;
_UpdateCursorState();
_UpdateCursorPos();
if (_isCapturedOnForeground != wasCapturedOnForeground) {
ScalingWindow::Get().OnCursorCapturedOnForegroundChanged(_isCapturedOnForeground);
}
return { _hCursor, _cursorPos };
}
void CursorManager::OnResizeStarted() noexcept {
_isResizing = true;
}
void CursorManager::OnResizeEnded() noexcept {
_isResizing = false;
void CursorManager::OnResizingChanged(bool value) noexcept {
_isResizing = value;
}
void CursorManager::OnResized(const RECT& rendererRect, const RECT& destRect) noexcept {
@ -68,54 +70,47 @@ void CursorManager::OnResized(const RECT& rendererRect, const RECT& destRect) no
_lastCompletedHitTestResult = HTNOWHERE;
}
void CursorManager::OnMoveStarted() noexcept {
_isMoving = true;
void CursorManager::OnMovingChanged(bool value) noexcept {
_isMoving = value;
if (_isVirtualized) {
return;
if (value) {
if (_isVirtualized) {
return;
}
_localCursorPosOnMoving.x = _cursorPos.x - _rendererRect.left;
_localCursorPosOnMoving.y = _cursorPos.y - _rendererRect.top;
} else {
_localCursorPosOnMoving.x = std::numeric_limits<LONG>::max();
}
_localCursorPosOnMoving.x = _cursorPos.x - _rendererRect.left;
_localCursorPosOnMoving.y = _cursorPos.y - _rendererRect.top;
}
void CursorManager::OnMoveEnded() noexcept {
_isMoving = false;
_localCursorPosOnMoving.x = std::numeric_limits<LONG>::max();
}
void CursorManager::OnMoved(const RECT& rendererRect, const RECT& destRect) noexcept {
OnResized(rendererRect, destRect);
}
void CursorManager::OnSrcMoveStarted() noexcept {
_isSrcMoving = true;
void CursorManager::OnSrcMovingChanged(bool value) noexcept {
_isSrcMoving = value;
if (!_isVirtualized) {
return;
}
// 以防 _UpdateCursorState 错过时机没有设置 _localCursorPosOnMoving。
// 源窗口自己实现拖拽逻辑或标题栏上右键然后立刻左键可能遇到这种情况。
if (_localCursorPosOnMoving.x == std::numeric_limits<LONG>::max()) {
_localCursorPosOnMoving.x = _cursorPos.x - _rendererRect.left;
_localCursorPosOnMoving.y = _cursorPos.y - _rendererRect.top;
if (value) {
// 以防 _UpdateCursorState 错过时机没有设置 _localCursorPosOnMoving。
// 源窗口自己实现拖拽逻辑或标题栏上右键然后立刻左键可能遇到这种情况。
if (_localCursorPosOnMoving.x == std::numeric_limits<LONG>::max()) {
_localCursorPosOnMoving.x = _cursorPos.x - _rendererRect.left;
_localCursorPosOnMoving.y = _cursorPos.y - _rendererRect.top;
}
// 源窗口移动时临时还原光标移动速度
_RestoreCursorSpeed();
} else {
_localCursorPosOnMoving.x = std::numeric_limits<LONG>::max();
_AdjustCursorSpeed();
}
// 源窗口移动时临时还原光标移动速度
_RestoreCursorSpeed();
}
void CursorManager::OnSrcMoveEnded() noexcept {
_isSrcMoving = false;
if (!_isVirtualized) {
return;
}
_localCursorPosOnMoving.x = std::numeric_limits<LONG>::max();
_AdjustCursorSpeed();
}
void CursorManager::OnSrcMoved(const RECT& srcRect) noexcept {
@ -128,12 +123,8 @@ void CursorManager::OnSrcFocusChanged(bool focused) noexcept {
_isSrcFocused = focused;
}
void CursorManager::IsCursorOnOverlay(bool value) noexcept {
if (_isOnOverlay == value) {
return;
}
void CursorManager::OnCursorOnOverlayChanged(bool value) noexcept {
_isOnOverlay = value;
Update();
}
@ -1127,7 +1118,7 @@ void CursorManager::_StartVirtualization(POINT& cursorPos) noexcept {
});
_isVirtualized = true;
ScalingWindow::Get().OnCursorVirtualizationStarted();
ScalingWindow::Get().OnCursorVirtualizationChanged(true);
}
bool CursorManager::_StopVirtualization(POINT& cursorPos, bool onDestroy) noexcept {
@ -1159,7 +1150,7 @@ bool CursorManager::_StopVirtualization(POINT& cursorPos, bool onDestroy) noexce
_RestoreCursorSpeed();
_isVirtualized = false;
ScalingWindow::Get().OnCursorVirtualizationEnded();
ScalingWindow::Get().OnCursorVirtualizationChanged(false);
return true;
}

View file

@ -20,55 +20,28 @@ public:
std::pair<HCURSOR, POINT> Update() noexcept;
void OnResizeStarted() noexcept;
void OnResizeEnded() noexcept;
void OnResizingChanged(bool value) noexcept;
void OnResized(const RECT& rendererRect, const RECT& destRect) noexcept;
void OnMoveStarted() noexcept;
void OnMoveEnded() noexcept;
void OnMovingChanged(bool value) noexcept;
void OnMoved(const RECT& rendererRect, const RECT& destRect) noexcept;
void OnSrcMoveStarted() noexcept;
void OnSrcMoveEnded() noexcept;
void OnSrcMovingChanged(bool value) noexcept;
void OnSrcMoved(const RECT& srcRect) noexcept;
void OnSrcFocusChanged(bool focused) noexcept;
// 光标不在缩放窗口上或隐藏时为 NULL
HCURSOR CursorHandle() const noexcept {
return NULL;
}
// 屏幕坐标
POINT CursorPos() const noexcept {
return {};
}
bool IsCursorCaptured() const noexcept {
return _isVirtualized;
}
bool IsCursorCapturedOnForeground() const noexcept {
return _isCapturedOnForeground;
}
bool IsCursorOnOverlay() const noexcept {
return _isOnOverlay;
}
void IsCursorOnOverlay(bool value) noexcept;
void OnCursorOnOverlayChanged(bool value) noexcept;
bool IsCursorCapturedOnOverlay() const noexcept {
return _isCapturedOnOverlay;
}
void IsCursorCapturedOnOverlay(bool value) noexcept;
int16_t SrcHitTest() const noexcept {
int16_t GetSrcHitTest() const noexcept {
return _lastCompletedHitTestResult;
}

View file

@ -70,6 +70,10 @@ uint64_t FrameProducer::GetLatestFrameNumber() const noexcept {
return _frameRingBuffer.GetLatestFrameNumber();
}
uint32_t FrameProducer::GetFPS() const noexcept {
return _stepTimer.GetFPS();
}
bool FrameProducer::ConsumerBeginFrame(
ID3D12Resource*& frame,
uint32_t& frameSrvOffset,

View file

@ -32,6 +32,8 @@ public:
uint64_t GetLatestFrameNumber() const noexcept;
uint32_t GetFPS() const noexcept;
bool ConsumerBeginFrame(
ID3D12Resource*& frame,
uint32_t& frameSrvOffset,

View file

@ -1,7 +1,9 @@
#include "pch.h"
#include "ImGuiImpl.h"
#include "Logger.h"
#include "ScalingWindow.h"
#include <imgui.h>
#include <imgui_internal.h>
namespace Magpie {
@ -13,6 +15,14 @@ static bool operator==(const ImVec4& l, const ImVec4& r) noexcept {
return l.x == r.x && l.y == r.y && l.z == r.z && l.w == r.w;
}
static std::string_view GetWindowIDFromName(std::string_view name) noexcept {
size_t idPos = name.find("##");
if (idPos != std::string_view::npos) {
name.remove_prefix(idPos + 2);
}
return name;
}
ImGuiImpl::~ImGuiImpl() noexcept {
if (ImGui::GetCurrentContext()) {
ImGui::DestroyContext();
@ -49,4 +59,195 @@ bool ImGuiImpl::Initialize(D3D12Context& d3d12Context) noexcept {
return true;
}
void ImGuiImpl::NewFrame(
POINT cursorPos,
phmap::flat_hash_map<std::string, OverlayWindowOption>& windowOptions,
float fittsLawAdjustment,
float dpiScale
) noexcept {
ImGuiIO& io = ImGui::GetIO();
{
ImVec2 newDisplaySize(
float(_destRect.right - _destRect.left),
float(_destRect.bottom - _destRect.top)
);
if (io.DisplaySize != newDisplaySize) {
io.DisplaySize = newDisplaySize;
// 调整缩放窗口尺寸时强制调整叠加层窗口位置
_windowRects.clear();
}
}
_UpdateMousePos(cursorPos, fittsLawAdjustment);
// 不接受键盘输入
if (io.WantCaptureKeyboard) {
io.AddKeyEvent(ImGuiKey_Enter, true);
io.AddKeyEvent(ImGuiKey_Enter, false);
}
ImGui::NewFrame();
for (ImGuiWindow* window : ImGui::GetCurrentContext()->Windows) {
if (window->Flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoMove)) {
continue;
}
// 排除 Debug##Default 窗口和尚未初始化完成的窗口
if (window->IsFallbackWindow || window->Appearing) {
continue;
}
ImVec2 pos = window->Pos;
// 将窗口限制在视口内
if (io.DisplaySize.x > window->Size.x) {
pos.x = std::clamp(pos.x, 0.0f, io.DisplaySize.x - window->Size.x);
} else {
pos.x = 0;
}
if (io.DisplaySize.y > window->Size.y) {
pos.y = std::clamp(pos.y, 0.0f, io.DisplaySize.y - window->Size.y);
} else {
pos.y = 0;
}
std::string_view windowId = GetWindowIDFromName(window->Name);
if (auto it = windowOptions.find(windowId); it != windowOptions.end()) {
OverlayWindowOption& option = it->second;
auto it1 = _windowRects.find(windowId);
if (it1 == _windowRects.end()) {
// 第一次显示或调整缩放窗口大小时叠加层窗口应根据规则调整位置
if (option.hArea == 0) {
pos.x = option.hPos * dpiScale;
} else if (option.hArea == 1) {
pos.x = io.DisplaySize.x * option.hPos - window->Size.x / 2;
} else if (option.hArea == 2) {
pos.x = io.DisplaySize.x - option.hPos * dpiScale - window->Size.x;
} else {
assert(false);
}
if (option.vArea == 0) {
pos.y = option.vPos * dpiScale;
} else if (option.vArea == 1) {
pos.y = io.DisplaySize.y * option.vPos - window->Size.y / 2;
} else if (option.vArea == 2) {
pos.y = io.DisplaySize.y - option.vPos * dpiScale - window->Size.y;
} else {
assert(false);
}
// 再次将窗口限制在视口内
if (io.DisplaySize.x > window->Size.x) {
pos.x = std::clamp(pos.x, 0.0f, io.DisplaySize.x - window->Size.x);
} else {
pos.x = 0;
}
if (io.DisplaySize.y > window->Size.y) {
pos.y = std::clamp(pos.y, 0.0f, io.DisplaySize.y - window->Size.y);
} else {
pos.y = 0;
}
} else if (it1->second != ImVec4(pos.x, pos.y, window->Size.x, window->Size.y)) {
// 当且仅当用户移动窗口或调整窗口大小后后重新计算贴靠的边,调整缩放窗口大小时应保持
// 贴靠的边不变。我们根据两侧边距的比例决定贴靠哪边或者都不贴靠。
// 这些阈值决定是否贴靠在某一边上,它们不是定值,而是窗口尺寸和画面尺寸的比例。这个
// 算法的效果出乎意料的好,因为窗口两侧边距较大时人对比例更敏感,较小时则对差值更敏
// 感。
const float thresholdX = std::max(window->Size.x / io.DisplaySize.x, 0.2f);
const float thresholdY = std::max(window->Size.y / io.DisplaySize.y, 0.2f);
// 根据左右边距比例决定贴靠
float ratio = pos.x / (io.DisplaySize.x - pos.x - window->Size.x);
if (ratio < thresholdX) {
option.hArea = 0;
option.hPos = pos.x / dpiScale;
} else if (ratio <= 1 / thresholdX) {
option.hArea = 1;
option.hPos = (pos.x + window->Size.x / 2) / io.DisplaySize.x;
} else {
option.hArea = 2;
option.hPos = (io.DisplaySize.x - pos.x - window->Size.x) / dpiScale;
}
// 根据上下边距比例决定贴靠
ratio = pos.y / (io.DisplaySize.y - pos.y - window->Size.y);
if (ratio < thresholdY) {
option.vArea = 0;
option.vPos = pos.y / dpiScale;
} else if (ratio <= 1 / thresholdY) {
option.vArea = 1;
option.vPos = (pos.y + window->Size.y / 2) / io.DisplaySize.y;
} else {
option.vArea = 2;
option.vPos = (io.DisplaySize.y - pos.y - window->Size.y) / dpiScale;
}
}
ImGui::SetWindowPos(window, pos);
// 此时 window->Pos 已更新,记录新的窗口位置
_windowRects[windowId] = ImVec4(window->Pos.x, window->Pos.y, window->Size.x, window->Size.y);
} else {
ImGui::SetWindowPos(window, pos);
}
}
// 调整缩放窗口大小或鼠标被前台窗口捕获时避免鼠标跳跃
if (!_isResizing && !_isMoving && !_isCursorCapturedOnForeground) {
ScalingWindow::Get().OnCursorOnOverlayChanged(io.WantCaptureMouse);
}
}
void ImGuiImpl::Draw() noexcept {
}
void ImGuiImpl::OnResizingChanged(bool value) noexcept {
_isResizing = value;
}
void ImGuiImpl::OnResized(const RECT& rendererRect, const RECT& destRect) noexcept {
_rendererRect = rendererRect;
_destRect = destRect;
}
void ImGuiImpl::OnMovingChanged(bool value) noexcept {
_isMoving = value;
}
void ImGuiImpl::OnMoved(const RECT& rendererRect, const RECT& destRect) noexcept {
OnResized(rendererRect, destRect);
}
void ImGuiImpl::OnCursorCapturedOnForegroundChanged(bool value) noexcept {
_isCursorCapturedOnForeground = value;
}
void ImGuiImpl::_UpdateMousePos(POINT cursorPos, float fittsLawAdjustment) const noexcept {
ImGuiIO& io = ImGui::GetIO();
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
// 调整缩放窗口大小或鼠标被前台窗口捕获时不应和叠加层交互
if (_isResizing || _isMoving || _isCursorCapturedOnForeground) {
return;
}
// 转换为目标矩形局部坐标
io.MousePos.x = float(cursorPos.x - _destRect.left);
io.MousePos.y = float(cursorPos.y - _destRect.top);
// 下移鼠标的逻辑位置使得在上边缘可以选中工具栏按钮
if (io.MousePos.y >= 0 && io.MousePos.y < fittsLawAdjustment) {
io.MousePos.y = fittsLawAdjustment;
}
}
}

View file

@ -1,5 +1,8 @@
#pragma once
#include "ImGuiBackend.h"
#include "ScalingOptions.h"
#include <parallel_hashmap/phmap.h>
#include <imgui.h>
namespace Magpie {
@ -13,8 +16,37 @@ public:
bool Initialize(D3D12Context& d3d12Context) noexcept;
void NewFrame(
POINT cursorPos,
phmap::flat_hash_map<std::string, OverlayWindowOption>& windowOptions,
float fittsLawAdjustment,
float dpiScale
) noexcept;
void Draw() noexcept;
void OnResizingChanged(bool value) noexcept;
void OnResized(const RECT& rendererRect, const RECT& destRect) noexcept;
void OnMovingChanged(bool value) noexcept;
void OnMoved(const RECT& rendererRect, const RECT& destRect) noexcept;
void OnCursorCapturedOnForegroundChanged(bool value) noexcept;
private:
void _UpdateMousePos(POINT cursorPos, float fittsLawAdjustment) const noexcept;
ImGuiBackend _backend;
phmap::flat_hash_map<std::string, ImVec4> _windowRects;
RECT _rendererRect{};
RECT _destRect{};
bool _isMoving = false;
bool _isResizing = false;
bool _isCursorCapturedOnForeground = false;
};
}

View file

@ -10,12 +10,12 @@
namespace Magpie {
static const char* COLOR_INDICATOR = "";
static const wchar_t COLOR_INDICATOR_W = L'';
//static const char* COLOR_INDICATOR = "■";
//static const wchar_t COLOR_INDICATOR_W = L'■';
static const float CORNER_ROUNDING = 6;
static const char* TOOLBAR_WINDOW_ID = "toolbar";
//static const char* TOOLBAR_WINDOW_ID = "toolbar";
static const char* PROFILER_WINDOW_ID = "profiler";
static void SetDefaultWindowOptions(
@ -69,4 +69,58 @@ bool OverlayDrawer::Initialize(D3D12Context& d3d12Context, OverlayOptions& overl
return true;
}
void OverlayDrawer::OnResizingChanged(bool value) noexcept {
_imguiImpl.OnResizingChanged(value);
}
void OverlayDrawer::OnResized(const RECT& rendererRect, const RECT& destRect) noexcept {
_imguiImpl.OnResized(rendererRect, destRect);
}
void OverlayDrawer::OnMovingChanged(bool value) noexcept {
_imguiImpl.OnMovingChanged(value);
}
void OverlayDrawer::OnMoved(const RECT& rendererRect, const RECT& destRect) noexcept {
_imguiImpl.OnMoved(rendererRect, destRect);
}
void OverlayDrawer::OnCursorCapturedOnForegroundChanged(bool value) noexcept {
_imguiImpl.OnCursorCapturedOnForegroundChanged(value);
}
HRESULT OverlayDrawer::Draw(GraphicsContext& /*graphicsContext*/, POINT cursorPos, uint32_t /*fps*/) noexcept {
// 所有窗口都不可见则跳过 ImGui 绘制
/*if (!_AnyVisibleWindow()) {
return;
}*/
// 为了符合 Fitts 法则,鼠标在工具栏上时稍微下移逻辑位置使得在上边缘可以选中工具栏按钮
float fittsLawAdjustment = 0;
/*const char* hoveredWindowId = _imguiImpl.GetHoveredWindowId();
if (hoveredWindowId && hoveredWindowId == std::string_view(TOOLBAR_WINDOW_ID)) {
fittsLawAdjustment = 4 * _dpiScale;
}*/
_imguiImpl.NewFrame(cursorPos, _overlayOptions->windows, fittsLawAdjustment, _dpiScale);
#ifdef _DEBUG
ImGui::ShowDemoWindow(&_isDemoWindowVisible);
#endif
ImGui::EndFrame();
_imguiImpl.Draw();
return S_OK;
}
bool OverlayDrawer::_AnyVisibleWindow() const noexcept {
bool result = _isToolbarVisible || _isProfilerVisible;
#ifdef _DEBUG
result = result || _isDemoWindowVisible;
#endif
return result;
}
}

View file

@ -4,6 +4,7 @@
namespace Magpie {
struct OverlayOptions;
class GraphicsContext;
class OverlayDrawer {
public:
@ -13,7 +14,21 @@ public:
bool Initialize(D3D12Context& d3d12Context, OverlayOptions& overlayOptions) noexcept;
void OnResizingChanged(bool value) noexcept;
void OnResized(const RECT& rendererRect, const RECT& destRect) noexcept;
void OnMovingChanged(bool value) noexcept;
void OnMoved(const RECT& rendererRect, const RECT& destRect) noexcept;
void OnCursorCapturedOnForegroundChanged(bool value) noexcept;
HRESULT Draw(GraphicsContext& graphicsContext, POINT cursorPos, uint32_t fps) noexcept;
private:
bool _AnyVisibleWindow() const noexcept;
D3D12Context* _d3d12Context = nullptr;
OverlayOptions* _overlayOptions = nullptr;
@ -24,6 +39,12 @@ private:
struct {
std::string gpuName;
} _hardwareInfo;
bool _isToolbarVisible = false;
bool _isProfilerVisible = false;
#ifdef _DEBUG
bool _isDemoWindowVisible = false;
#endif
};
}

View file

@ -204,7 +204,7 @@ ComponentState Renderer::Render(
}
if (needRedraw) {
_CheckResult(_RenderImpl(waitForGpu), "_RenderImpl 失败");
_CheckResult(_RenderImpl(cursorPos, waitForGpu), "_RenderImpl 失败");
}
return _state;
@ -223,16 +223,16 @@ void Renderer::OnMonitorChanged(HMONITOR hMonitor) noexcept {
_CheckResult(_UpdateColorSpace(), "_UpdateColorSpace 失败");
}
void Renderer::OnResizeStarted() noexcept {
if (_state == ComponentState::NoError) {
_presenter->OnResizeStarted();
void Renderer::OnResizingChanged(bool value) noexcept {
if (_state != ComponentState::NoError) {
return;
}
}
void Renderer::OnResizeEnded() noexcept {
if (_state == ComponentState::NoError) {
_CheckResult(_presenter->OnResizeEnded(), "SwapChainPresenter::OnResizeEnded 失败");
if (!_CheckResult(_presenter->OnResizingChanged(value), "SwapChainPresenter::OnResizingChanged 失败")) {
return;
}
_overlayDrawer.OnResizingChanged(value);
}
void Renderer::OnResized(const RECT& rendererRect, RECT& destRect) noexcept {
@ -270,15 +270,13 @@ void Renderer::OnResized(const RECT& rendererRect, RECT& destRect) noexcept {
destRect.right = rendererRect.left + (LONG)_outputRect.right;
destRect.bottom = rendererRect.top + (LONG)_outputRect.bottom;
_overlayDrawer.OnResized(rendererRect, destRect);
_cursorDrawer.OnResized(rendererRect, destRect);
}
void Renderer::OnMoveStarted() noexcept {
_cursorDrawer.OnMoveStarted();
}
void Renderer::OnMoveEnded() noexcept {
_cursorDrawer.OnMoveEnded();
void Renderer::OnMovingChanged(bool value) noexcept {
_overlayDrawer.OnMovingChanged(value);
_cursorDrawer.OnMovingChanged(value);
}
void Renderer::OnMoved(const RECT& rendererRect, RECT& destRect) noexcept {
@ -287,23 +285,20 @@ void Renderer::OnMoved(const RECT& rendererRect, RECT& destRect) noexcept {
destRect.right = rendererRect.left + (LONG)_outputRect.right;
destRect.bottom = rendererRect.top + (LONG)_outputRect.bottom;
_overlayDrawer.OnMoved(rendererRect, destRect);
_cursorDrawer.OnMoved(rendererRect, destRect);
}
void Renderer::OnCursorVirtualizationStarted() noexcept {
_cursorDrawer.OnCursorVirtualizationStarted();
void Renderer::OnCursorVirtualizationChanged(bool value) noexcept {
_cursorDrawer.OnCursorVirtualizationChanged(value);
}
void Renderer::OnCursorVirtualizationEnded() noexcept {
_cursorDrawer.OnCursorVirtualizationEnded();
void Renderer::OnCursorCapturedOnForegroundChanged(bool value) noexcept {
_overlayDrawer.OnCursorCapturedOnForegroundChanged(value);
}
void Renderer::OnSrcMoveStarted() noexcept {
_cursorDrawer.OnSrcMoveStarted();
}
void Renderer::OnSrcMoveEnded() noexcept {
_cursorDrawer.OnSrcMoveEnded();
void Renderer::OnSrcMovingChanged(bool value) noexcept {
_cursorDrawer.OnSrcMovingChanged(value);
}
void Renderer::OnMsgDisplayChanged() noexcept {
@ -483,7 +478,7 @@ HRESULT Renderer::_UpdateColorSpace() noexcept {
return S_OK;
}
HRESULT Renderer::_RenderImpl(bool waitForGpu) noexcept {
HRESULT Renderer::_RenderImpl(POINT cursorPos, bool waitForGpu) noexcept {
// 处于 COMMON 状态,依赖隐式状态转换
ID3D12Resource* curFrame;
uint32_t curFrameSrvOffset;
@ -554,7 +549,9 @@ HRESULT Renderer::_RenderImpl(bool waitForGpu) noexcept {
_graphicsContext.Draw(3);
// 为了和 OS 保持一致SDR 下在 sRGB 空间中混合
_overlayDrawer.Draw(_graphicsContext, cursorPos, _frameProducer.GetFPS());
// 为了和 OS 保持一致SDR 下绘制光标时在 sRGB 空间中混合
if (_colorInfo.kind == winrt::AdvancedColorKind::StandardDynamicRange) {
_graphicsContext.OMSetRenderTarget(rawRtvOffset);
}

View file

@ -41,25 +41,19 @@ public:
void OnMonitorChanged(HMONITOR hMonitor) noexcept;
void OnResizeStarted() noexcept;
void OnResizeEnded() noexcept;
void OnResizingChanged(bool value) noexcept;
void OnResized(const RECT& rendererRect, RECT& destRect) noexcept;
void OnMoveStarted() noexcept;
void OnMoveEnded() noexcept;
void OnMovingChanged(bool value) noexcept;
void OnMoved(const RECT& rendererRect, RECT& destRect) noexcept;
void OnCursorVirtualizationStarted() noexcept;
void OnCursorVirtualizationChanged(bool value) noexcept;
void OnCursorVirtualizationEnded() noexcept;
void OnCursorCapturedOnForegroundChanged(bool value) noexcept;
void OnSrcMoveStarted() noexcept;
void OnSrcMoveEnded() noexcept;
void OnSrcMovingChanged(bool value) noexcept;
void OnMsgDisplayChanged() noexcept;
@ -72,7 +66,7 @@ private:
HRESULT _UpdateColorSpace() noexcept;
HRESULT _RenderImpl(bool waitForGpu = false) noexcept;
HRESULT _RenderImpl(POINT cursorPos, bool waitForGpu = false) noexcept;
void _UpdateOutputRect(SizeU outputSize) noexcept;

View file

@ -1,11 +1,11 @@
#include "pch.h"
#include "ScalingWindow.h"
#include "CommonSharedConstants.h"
#include "CursorManager.h"
#include "DebugInfo.h"
#include "ExclModeHelper.h"
#include "Logger.h"
#include "Renderer.h"
#include "ScalingWindow.h"
#include "Win32Helper.h"
#include "WindowHelper.h"
#include <dwmapi.h>
@ -319,7 +319,7 @@ ScalingError ScalingWindow::_StartImpl(HWND hwndSrc) noexcept {
return error;
}
_cursorManager = std::make_unique<class CursorManager>();
_cursorManager.emplace();
_cursorManager->Initialize(_srcTracker.SrcRect(), _rendererRect, destRect,
_srcTracker.IsMoving(), _srcTracker.IsFocused());
@ -433,12 +433,16 @@ void ScalingWindow::OnCursorVisibilityChanged(bool isVisible, bool onDestory) no
_renderer->OnCursorVisibilityChanged(isVisible, onDestory);
}
void ScalingWindow::OnCursorVirtualizationStarted() noexcept {
_renderer->OnCursorVirtualizationStarted();
void ScalingWindow::OnCursorVirtualizationChanged(bool value) noexcept {
_renderer->OnCursorVirtualizationChanged(value);
}
void ScalingWindow::OnCursorVirtualizationEnded() noexcept {
_renderer->OnCursorVirtualizationEnded();
void ScalingWindow::OnCursorCapturedOnForegroundChanged(bool value) noexcept {
_renderer->OnCursorCapturedOnForegroundChanged(value);
}
void ScalingWindow::OnCursorOnOverlayChanged(bool value) noexcept {
_cursorManager->OnCursorOnOverlayChanged(value);
}
void ScalingWindow::RestartAfterSrcRepositioned() noexcept {
@ -537,11 +541,11 @@ LRESULT ScalingWindow::_MessageHandler(UINT msg, WPARAM wParam, LPARAM lParam) n
_isMoving = !_isPreparingForResizing;
if (_isResizing) {
_cursorManager->OnResizeStarted();
_renderer->OnResizeStarted();
_cursorManager->OnResizingChanged(true);
_renderer->OnResizingChanged(true);
} else {
_cursorManager->OnMoveStarted();
_renderer->OnMoveStarted();
_cursorManager->OnMovingChanged(true);
_renderer->OnMovingChanged(true);
}
if (_options.IsTouchSupportEnabled()) {
@ -559,11 +563,11 @@ LRESULT ScalingWindow::_MessageHandler(UINT msg, WPARAM wParam, LPARAM lParam) n
_isMoving = false;
if (oldIsResizing) {
_cursorManager->OnResizeEnded();
_renderer->OnResizeEnded();
_cursorManager->OnResizingChanged(false);
_renderer->OnResizingChanged(false);
} else {
_cursorManager->OnMoveEnded();
_renderer->OnMoveEnded();
_cursorManager->OnMovingChanged(false);
_renderer->OnMovingChanged(false);
}
if (!_srcTracker.MoveOnEndResizeMove()) {
@ -634,7 +638,7 @@ LRESULT ScalingWindow::_MessageHandler(UINT msg, WPARAM wParam, LPARAM lParam) n
return HTCAPTION;
}*/
const int16_t srcHitTest = _cursorManager->SrcHitTest();
const int16_t srcHitTest = _cursorManager->GetSrcHitTest();
if (srcHitTest != HTNOWHERE) {
return srcHitTest;
}
@ -1393,13 +1397,12 @@ bool ScalingWindow::_UpdateSrcState(bool& isSrcRepositioning) noexcept {
if (srcMovingChanged) {
assert(_options.IsWindowedMode());
if (_srcTracker.IsMoving()) {
_cursorManager->OnSrcMoveStarted();
_renderer->OnSrcMoveStarted();
} else {
_cursorManager->OnSrcMoveEnded();
_renderer->OnSrcMoveEnded();
bool isSrcMoving = _srcTracker.IsMoving();
_cursorManager->OnSrcMovingChanged(isSrcMoving);
_renderer->OnSrcMovingChanged(isSrcMoving);
if (!isSrcMoving) {
_EnsureCaptionVisibleOnScreen();
}
@ -1408,8 +1411,7 @@ bool ScalingWindow::_UpdateSrcState(bool& isSrcRepositioning) noexcept {
}
// 广播用户开始或结束移动缩放窗口
PostMessage(HWND_BROADCAST, WM_MAGPIE_SCALINGCHANGED,
_srcTracker.IsMoving() ? 3 : 2, (LPARAM)Handle());
PostMessage(HWND_BROADCAST, WM_MAGPIE_SCALINGCHANGED, isSrcMoving ? 3 : 2, (LPARAM)Handle());
}
if (srcRectChanged) {

View file

@ -1,11 +1,12 @@
#pragma once
#include "CursorManager.h"
#include "ScalingOptions.h"
#include "SrcTracker.h"
#include "WindowBase.h"
namespace Magpie {
class CursorManager;
class Renderer;
class ScalingWindow final : public WindowBaseT<ScalingWindow> {
using base_type = WindowBaseT<ScalingWindow>;
@ -54,9 +55,11 @@ public:
void OnCursorVisibilityChanged(bool isVisible, bool onDestory) noexcept;
void OnCursorVirtualizationStarted() noexcept;
void OnCursorVirtualizationChanged(bool value) noexcept;
void OnCursorVirtualizationEnded() noexcept;
void OnCursorCapturedOnForegroundChanged(bool value) noexcept;
void OnCursorOnOverlayChanged(bool value) noexcept;
bool IsSrcRepositioning() const noexcept {
return _isSrcRepositioning;
@ -155,10 +158,11 @@ private:
uint32_t _nonTopBorderThicknessInClient = 0;
ScalingOptions _options;
std::unique_ptr<class Renderer> _renderer;
std::unique_ptr<class CursorManager> _cursorManager;
// 避免包含 Renderer.h 以加快编译速度
std::unique_ptr<Renderer> _renderer;
std::optional<CursorManager> _cursorManager;
class SrcTracker _srcTracker;
SrcTracker _srcTracker;
wil::unique_mutex_nothrow _exclModeMutex;

View file

@ -25,8 +25,8 @@ public:
return _frameCount;
}
// 从前端线程调用
uint32_t FPS() const noexcept {
// 支持跨线程调用
uint32_t GetFPS() const noexcept {
return _framesPerSecond.load(std::memory_order_relaxed);
}

View file

@ -265,6 +265,34 @@ HRESULT SwapChainPresenter::EndFrame(bool waitForGpu) noexcept {
return S_OK;
}
HRESULT SwapChainPresenter::OnResizingChanged(bool value) noexcept {
// 开始调整尺寸时推迟到尺寸变化再重建交换链
_isResizing = value;
if (!value) {
// 恢复后备缓冲数量
const uint32_t oldBufferCount = _bufferCount;
_bufferCount = _d3d12Context->GetMaxInFlightFrameCount() + 1;
if (_bufferCount != oldBufferCount) {
// 调用此方法前没等待 GPU
HRESULT hr = _d3d12Context->WaitForGpu();
if (FAILED(hr)) {
Logger::Get().ComError("D3D12Context::WaitForGPU", hr);
return hr;
}
hr = _RecreateBuffers();
if (FAILED(hr)) {
Logger::Get().ComError("_RecreateBuffers 失败", hr);
return hr;
}
}
}
return S_OK;
}
HRESULT SwapChainPresenter::OnResized(SizeU size) noexcept {
assert(size.width > 0 && size.height > 0 && size != _size);
@ -280,36 +308,6 @@ HRESULT SwapChainPresenter::OnResized(SizeU size) noexcept {
return hr;
}
void SwapChainPresenter::OnResizeStarted() noexcept {
// 尺寸变化时再重建交换链
_isResizing = true;
}
HRESULT SwapChainPresenter::OnResizeEnded() noexcept {
_isResizing = false;
// 恢复后备缓冲数量
const uint32_t oldBufferCount = _bufferCount;
_bufferCount = _d3d12Context->GetMaxInFlightFrameCount() + 1;
if (_bufferCount != oldBufferCount) {
// 调用此方法前没等待 GPU
HRESULT hr = _d3d12Context->WaitForGpu();
if (FAILED(hr)) {
Logger::Get().ComError("D3D12Context::WaitForGPU", hr);
return hr;
}
hr = _RecreateBuffers();
if (FAILED(hr)) {
Logger::Get().ComError("_RecreateBuffers 失败", hr);
return hr;
}
}
return S_OK;
}
HRESULT SwapChainPresenter::OnColorInfoChanged(const ColorInfo& colorInfo) noexcept {
const bool wasScRGB = _isScRGB;
_isScRGB = colorInfo.kind != winrt::AdvancedColorKind::StandardDynamicRange;

View file

@ -30,12 +30,10 @@ public:
SizeU GetSize() const noexcept { return _size; }
HRESULT OnResizingChanged(bool value) noexcept;
HRESULT OnResized(SizeU size) noexcept;
void OnResizeStarted() noexcept;
HRESULT OnResizeEnded() noexcept;
HRESULT OnColorInfoChanged(const ColorInfo& colorInfo) noexcept;
private: