perf: _cursorBaseSize 变化后避免等待 GPU

This commit is contained in:
Xu 2026-03-16 10:37:05 +08:00
commit a2dceaf57d
4 changed files with 77 additions and 20 deletions

View file

@ -82,7 +82,7 @@ def remove_file(file):
pass
for file in glob.glob("*.lib"):
for file in glob.glob("*.lib", ".exp"):
remove_file(file)
print("清理完毕", flush=True)

View file

@ -79,6 +79,10 @@ CursorDrawer::~CursorDrawer() noexcept {
pair.second.FreeDescriptors(csuDescriptorHeap, rtvDescriptorHeap);
}
for (const _CursorInfo& cursorInfo : _retiredCursorInfos) {
cursorInfo.FreeDescriptors(csuDescriptorHeap, rtvDescriptorHeap);
}
if (_tempOriginTextureSrvOffset != std::numeric_limits<uint32_t>::max()) {
csuDescriptorHeap.Free(_tempOriginTextureSrvOffset, 1);
}
@ -146,8 +150,25 @@ bool CursorDrawer::Initialize(
}
_cursorBaseSize = cursorBaseSize;
// 清空已解析的光标
_d3d12Context->WaitForGpu();
// _cursorBaseSize 改变后现有 _CursorInfo 失效
for (auto& pair : _cursorInfos) {
_retiredCursorInfos.push_back(std::move(pair.second));
// 所有权已经转移,避免重复释放
pair.second.textureSrvOffset = std::numeric_limits<uint32_t>::max();
pair.second.textureRtvOffset = std::numeric_limits<uint32_t>::max();
pair.second.originTextureSrvOffset = std::numeric_limits<uint32_t>::max();
}
// 将 fenceValue 按升序排列
std::sort(
_retiredCursorInfos.begin(),
_retiredCursorInfos.end(),
[](const _CursorInfo& l, const _CursorInfo& r) {
return l.lastUseFenceValue < r.lastUseFenceValue;
}
);
_ClearCursorInfos();
});
});
@ -156,7 +177,7 @@ bool CursorDrawer::Initialize(
return true;
}
bool CursorDrawer::CheckForRedraw(HCURSOR hCursor, POINT cursorPos) noexcept {
void CursorDrawer::PrepareForDraw(HCURSOR hCursor, POINT cursorPos, bool& needRedraw) noexcept {
const ScalingWindow& scalingWindow = ScalingWindow::Get();
const ScalingOptions& options = scalingWindow.Options();
@ -200,7 +221,7 @@ bool CursorDrawer::CheckForRedraw(HCURSOR hCursor, POINT cursorPos) noexcept {
if (hCursor) {
while (true) {
_curCursorInfoKeyValue = _ResolveCursor(hCursor, cursorPos, false);
_curCursorInfoKeyValue = _ResolveCursor(hCursor, cursorPos, needRedraw);
if (_curCursorInfoKeyValue) {
const _CursorInfo& cursorInfo = _curCursorInfoKeyValue->second;
// 检查光标是否在视口内。hCursor 不为 NULL 说明光标已在缩放窗口内,因此这个检查很少失败
@ -235,13 +256,12 @@ bool CursorDrawer::CheckForRedraw(HCURSOR hCursor, POINT cursorPos) noexcept {
}
}
// 光标形状或位置变化时需要重新绘制
if (hCursor != _hCurCursor || (hCursor && cursorPos != _curCursorPos)) {
// 光标形状或位置变化时总是需要重新绘制;有时即使不变也需要重新绘制,比如色域
// 或 _cursorBaseSize 变化后。
needRedraw |= hCursor != _hCurCursor || (hCursor && cursorPos != _curCursorPos);
if (needRedraw) {
_hCurCursor = hCursor;
_curCursorPos = cursorPos;
return true;
} else {
return false;
}
}
@ -516,7 +536,7 @@ static bool GetCursorSizeFromBmps(HBITMAP hColorBmp, HBITMAP hMaskBmp, Size& siz
std::pair<const CursorDrawer::_CursorInfoKey, CursorDrawer::_CursorInfo>* CursorDrawer::_ResolveCursor(
HCURSOR hCursor,
POINT cursorPos,
bool isAni
bool& needRedraw
) noexcept {
assert(hCursor);
@ -548,7 +568,7 @@ std::pair<const CursorDrawer::_CursorInfoKey, CursorDrawer::_CursorInfo>* Cursor
_CursorInfo cursorInfo{};
// 如果不能确定光标是否随 DPI 缩放则假设为真,绝大多数情况下是对的
bool isCursorDpiAware = !isAni;
bool isCursorDpiAware = true;
ICONINFOEX iconInfoEx = { .cbSize = sizeof(iconInfoEx) };
if (!GetIconInfoEx(hCursor, &iconInfoEx)) {
@ -654,6 +674,8 @@ std::pair<const CursorDrawer::_CursorInfoKey, CursorDrawer::_CursorInfo>* Cursor
return nullptr;
}
needRedraw = true;
return &*_cursorInfos.emplace(_CursorInfoKey{ hCursor, isCursorDpiAware ? monitorDpi : 0 },
std::move(cursorInfo)).first;
}
@ -1536,6 +1558,25 @@ void CursorDrawer::_ClearRetiredResources(uint64_t completedFenceValue) noexcept
}
}
if (!_retiredCursorInfos.empty()) {
auto& csuDescriptorHeap = _d3d12Context->GetDescriptorHeap();
auto& rtvDescriptorHeap = _d3d12Context->GetDescriptorHeap(true);
// fenceValue 按升序排列
auto it = _retiredCursorInfos.begin();
for (; it != _retiredCursorInfos.end(); ++it) {
if (it->lastUseFenceValue > completedFenceValue) {
break;
}
it->FreeDescriptors(csuDescriptorHeap, rtvDescriptorHeap);
}
if (it != _retiredCursorInfos.begin()) {
_retiredCursorInfos.erase(_retiredCursorInfos.begin(), it);
}
}
if (!_retiredTempOriginTextures.empty()) {
auto& csuDescriptorHeap = _d3d12Context->GetDescriptorHeap();

View file

@ -26,7 +26,7 @@ public:
const ColorInfo& colorInfo
) noexcept;
bool CheckForRedraw(HCURSOR hCursor, POINT cursorPos) noexcept;
void PrepareForDraw(HCURSOR hCursor, POINT cursorPos, bool& needRedraw) noexcept;
// backBuffer 不为空表示掩码光标在叠加层上
HRESULT Draw(
@ -125,7 +125,11 @@ private:
) const noexcept;
};
std::pair<const _CursorInfoKey, _CursorInfo>* _ResolveCursor(HCURSOR hCursor, POINT cursorPos, bool isAni) noexcept;
std::pair<const _CursorInfoKey, _CursorInfo>* _ResolveCursor(
HCURSOR hCursor,
POINT cursorPos,
bool& needRedraw
) noexcept;
Size _CalcCursorSize(
Size cursorBmpSize,
@ -178,9 +182,12 @@ private:
phmap::flat_hash_map<_CursorInfoKey, _CursorInfo> _cursorInfos;
// 保存临时资源未被释放的 _CursorInfo。保存键而不是指针以防 _cursorInfos 扩容后失效
// 保存临时资源未被释放的 _CursorInfo。保存键而不是指针以防 _cursorInfos 扩容后失效
SmallVector<_CursorInfoKey, 1> _cursorInfosWithTempResources;
// 保存 _cursorBaseSize 改变后失效的 _CursorInfo
SmallVector<_CursorInfo, 0> _retiredCursorInfos;
// 保存解析失败的光标以避免重复尝试
phmap::flat_hash_set<HCURSOR> _unresolvableCursors;

View file

@ -174,6 +174,8 @@ ComponentState Renderer2::Render(
return _state;
}
bool needRedraw = false;
{
const uint64_t latestProducerFrameNumber = _frameProducer.GetLatestFrameNumber();
if (latestProducerFrameNumber == 0) {
@ -183,15 +185,22 @@ ComponentState Renderer2::Render(
return _state;
}
if (latestProducerFrameNumber == _lastProducerFrameNumber &&
!_cursorDrawer.CheckForRedraw(hCursor, cursorPos)) {
return _state;
if (latestProducerFrameNumber != _lastProducerFrameNumber) {
needRedraw = true;
_lastProducerFrameNumber = latestProducerFrameNumber;
}
_lastProducerFrameNumber = latestProducerFrameNumber;
}
_CheckResult(_RenderImpl(waitForGpu), "_RenderImpl 失败");
{
bool cursorNeedRedraw = false;
_cursorDrawer.PrepareForDraw(hCursor, cursorPos, cursorNeedRedraw);
needRedraw |= cursorNeedRedraw;
}
if (needRedraw) {
_CheckResult(_RenderImpl(waitForGpu), "_RenderImpl 失败");
}
return _state;
}