Skip to content

Commit

Permalink
[WIP]feat: 优化 GPU 选择
Browse files Browse the repository at this point in the history
  • Loading branch information
Blinue committed Oct 19, 2024
1 parent f03921c commit 5698211
Show file tree
Hide file tree
Showing 17 changed files with 384 additions and 74 deletions.
149 changes: 149 additions & 0 deletions src/Magpie.App/AdaptersService.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
#include "pch.h"
#include "AdaptersService.h"
#include "Logger.h"
#include <d3d11.h>
#include "Win32Utils.h"

namespace winrt::Magpie::App {

bool AdaptersService::Initialize() noexcept {
if (!_InitializeDXGI()) {
return false;
}

[]()->fire_and_forget {
AdaptersService& that = AdaptersService::Get();
CoreDispatcher dispatcher = CoreWindow::GetForCurrentThread().Dispatcher();

while (true) {
co_await resume_background();

if (!that._adaptersChangedEvent.wait()) {
co_return;
}

// _adaptersChangedCookie 由 _adaptersChangedEvent 保护。
// _adaptersChangedCookie 为零表示正在退出,否则表示显卡变化。
if (that._adaptersChangedCookie == 0) {
co_return;
}

co_await dispatcher;

that._dxgiFactory->UnregisterAdaptersChangedEvent(that._adaptersChangedCookie);
if (!that._InitializeDXGI()) {
co_return;
}

that.AdaptersChanged.Invoke();
}
}();

return true;
}

void AdaptersService::Uninitialize() noexcept {
_dxgiFactory->UnregisterAdaptersChangedEvent(_adaptersChangedCookie);
_adaptersChangedCookie = 0;
_adaptersChangedEvent.SetEvent();
_adaptersChangedEvent.reset();
_dxgiFactory = nullptr;
}

bool AdaptersService::_InitializeDXGI() noexcept {
HRESULT hr = CreateDXGIFactory1(IID_PPV_ARGS(&_dxgiFactory));
if (FAILED(hr)) {
Logger::Get().ComError("CreateDXGIFactory1 失败", hr);
return false;
}

if (!_adaptersChangedEvent) {
hr = _adaptersChangedEvent.create();
if (FAILED(hr)) {
Logger::Get().ComError("创建 event 失败", hr);
return false;
}
}

hr = _dxgiFactory->RegisterAdaptersChangedEvent(
_adaptersChangedEvent.get(), &_adaptersChangedCookie);
if (FAILED(hr)) {
Logger::Get().ComError("RegisterAdaptersChangedEvent 失败", hr);
return false;
}

_adapterInfos.clear();
++_adapterInfosVersion;

SmallVector<com_ptr<IDXGIAdapter1>> adapters;

com_ptr<IDXGIAdapter1> curAdapter;
for (UINT adapterIdx = 0;
SUCCEEDED(_dxgiFactory->EnumAdapters1(adapterIdx, curAdapter.put()));
++adapterIdx
) {
DXGI_ADAPTER_DESC1 desc;
hr = curAdapter->GetDesc1(&desc);
if (FAILED(hr)) {
continue;
}

// 不包含 WARP
if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) {
continue;
}

_adapterInfos.push_back({
.idx = adapterIdx,
.vendorId = desc.VendorId,
.deviceId = desc.DeviceId,
.description = desc.Description
});

adapters.push_back(std::move(curAdapter));
}

if (!_adapterInfos.empty()) {
// 检查每个显卡的功能级别,由于过于耗时转到后台执行
[](SmallVector<com_ptr<IDXGIAdapter1>> adapters)->fire_and_forget {
AdaptersService& that = AdaptersService::Get();
CoreDispatcher dispatcher = CoreWindow::GetForCurrentThread().Dispatcher();

const uint32_t adapterInfosVersion = that._adapterInfosVersion;

co_await resume_background();

SmallVector<uint32_t> oldAdapters;
{
wil::srwlock writeLock;
Win32Utils::RunParallel([&](uint32_t i) {
D3D_FEATURE_LEVEL fl = D3D_FEATURE_LEVEL_11_0;
if (FAILED(D3D11CreateDevice(adapters[i].get(), D3D_DRIVER_TYPE_UNKNOWN,
NULL, 0, &fl, 1, D3D11_SDK_VERSION, nullptr, nullptr, nullptr))) {
auto lock = writeLock.lock_exclusive();
oldAdapters.push_back(i);
}
}, (uint32_t)adapters.size());
}

co_await dispatcher;

if (adapterInfosVersion != that._adapterInfosVersion) {
// 检查过程中显卡变化则放弃后续处理
co_return;
}

for (uint32_t i : oldAdapters) {
that._adapterInfos[i].isFL11Supported = false;
}

if (!oldAdapters.empty()) {
that.AdaptersChanged.Invoke();
}
}(std::move(adapters));
}

return true;
}

}
47 changes: 47 additions & 0 deletions src/Magpie.App/AdaptersService.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#pragma once
#include <dxgi1_6.h>
#include <WinRTUtils.h>
#include <SmallVector.h>

namespace winrt::Magpie::App {

struct AdapterInfo {
uint32_t idx = 0;
uint32_t vendorId = 0;
uint32_t deviceId = 0;
std::wstring description;
// 延迟检测
bool isFL11Supported = true;
};

class AdaptersService {
public:
static AdaptersService& Get() noexcept {
static AdaptersService instance;
return instance;
}

bool Initialize() noexcept;

void Uninitialize() noexcept;

const SmallVectorImpl<AdapterInfo>& AdapterInfos() const noexcept {
return _adapterInfos;
}

WinRTUtils::Event<delegate<>> AdaptersChanged;

private:
AdaptersService() = default;

bool _InitializeDXGI() noexcept;

com_ptr<IDXGIFactory7> _dxgiFactory;
wil::unique_event_nothrow _adaptersChangedEvent;
DWORD _adaptersChangedCookie = 0;

uint32_t _adapterInfosVersion = 0;
SmallVector<AdapterInfo, 2> _adapterInfos;
};

}
7 changes: 7 additions & 0 deletions src/Magpie.App/App.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "EffectsService.h"
#include "UpdateService.h"
#include "LocalizationService.h"
#include "AdaptersService.h"
#include "Logger.h"

namespace winrt::Magpie::App::implementation {
Expand Down Expand Up @@ -91,6 +92,11 @@ StartUpOptions App::Initialize(int) {
return result;
}

if (!AdaptersService::Get().Initialize()) {
result.IsError = true;
return result;
}

result.IsError = false;
result.MainWindowCenter = settings.MainWindowCenter();
result.MainWindowSizeInDips = settings.MainWindowSizeInDips();
Expand All @@ -110,6 +116,7 @@ void App::Uninitialize() {
// 不显示托盘图标的情况下关闭主窗口仍会在后台驻留数秒,推测和 XAML Islands 有关
// 这里提前取消热键注册,这样关闭 Magpie 后立即重新打开不会注册热键失败
ShortcutService::Get().Uninitialize();
AdaptersService::Get().Uninitialize();
}

bool App::IsShowNotifyIcon() const noexcept {
Expand Down
90 changes: 84 additions & 6 deletions src/Magpie.App/AppSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "ScalingMode.h"
#include "LocalizationService.h"
#include <ShellScalingApi.h>
#include <dxgi.h>

#pragma comment(lib, "Shcore.lib")

Expand Down Expand Up @@ -86,7 +87,14 @@ static void WriteProfile(rapidjson::PrettyWriter<rapidjson::StringBuffer>& write
writer.Uint((uint32_t)profile.multiMonitorUsage);

writer.Key("graphicsCard");
writer.Int(profile.graphicsCard);
writer.StartObject();
writer.Key("idx");
writer.Int(profile.graphicsCardId.idx);
writer.Key("vendorId");
writer.Uint(profile.graphicsCardId.vendorId);
writer.Key("deviceId");
writer.Uint(profile.graphicsCardId.deviceId);
writer.EndObject();
writer.Key("frameRateLimiterEnabled");
writer.Bool(profile.isFrameRateLimiterEnabled);
writer.Key("maxFrameRate");
Expand Down Expand Up @@ -737,6 +745,48 @@ void AppSettings::_LoadSettings(const rapidjson::GenericObject<true, rapidjson::
}
}

static GraphicsCardId GetGraphicsCardIdFromIdx(int idx) noexcept {
GraphicsCardId result;

if (idx < 0) {
// 使用默认显卡
return result;
}

com_ptr<IDXGIFactory1> dxgiFactory;
HRESULT hr = CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory));
if (FAILED(hr)) {
Logger::Get().ComError("CreateDXGIFactory1 失败", hr);
return result;
}

com_ptr<IDXGIAdapter1> adapter;
hr = dxgiFactory->EnumAdapters1(idx, adapter.put());
if (FAILED(hr)) {
// 可能因为该显卡已不存在
Logger::Get().ComError("EnumAdapters1 失败", hr);
return result;
}

DXGI_ADAPTER_DESC1 desc;
hr = adapter->GetDesc1(&desc);
if (FAILED(hr)) {
Logger::Get().ComError("GetDesc1 失败", hr);
return result;
}

if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) {
// 不使用 WARP
return result;
}

// 不检查 FL11,由 AdaptersService 检查
result.idx = idx;
result.vendorId = desc.VendorId;
result.deviceId = desc.DeviceId;
return result;
}

bool AppSettings::_LoadProfile(
const rapidjson::GenericObject<true, rapidjson::Value>& profileObj,
Profile& profile,
Expand Down Expand Up @@ -822,12 +872,40 @@ bool AppSettings::_LoadProfile(
profile.multiMonitorUsage = (MultiMonitorUsage)multiMonitorUsage;
}

if (!JsonHelper::ReadInt(profileObj, "graphicsCard", profile.graphicsCard, true)) {
// v0.10.0-preview1 使用 graphicsAdapter
uint32_t graphicsAdater = 0;
JsonHelper::ReadUInt(profileObj, "graphicsAdapter", graphicsAdater);
profile.graphicsCard = (int)graphicsAdater - 1;
{
auto graphicsCardIdNode = profileObj.FindMember("graphicsCardId");
if (graphicsCardIdNode == profileObj.end()) {
// v0.10 和 v0.11 只使用索引
int graphicsCard = -1;
if (!JsonHelper::ReadInt(profileObj, "graphicsCard", graphicsCard, true)) {
// v0.10.0-preview1 使用 graphicsAdapter
uint32_t graphicsAdater = 0;
JsonHelper::ReadUInt(profileObj, "graphicsAdapter", graphicsAdater);
graphicsCard = (int)graphicsAdater - 1;
}

profile.graphicsCardId = GetGraphicsCardIdFromIdx(graphicsCard);
} else if (graphicsCardIdNode->value.IsObject()) {
auto graphicsCardIdObj = graphicsCardIdNode->value.GetObj();

auto idxNode = graphicsCardIdObj.FindMember("idx");
if (idxNode != graphicsCardIdObj.end() && idxNode->value.IsInt()) {
profile.graphicsCardId.idx = idxNode->value.GetInt();
}

auto vendorIdNode = graphicsCardIdObj.FindMember("vendorId");
if (vendorIdNode != graphicsCardIdObj.end() && vendorIdNode->value.IsUint()) {
profile.graphicsCardId.vendorId = vendorIdNode->value.GetUint();
}

auto deviceIdNode = graphicsCardIdObj.FindMember("deviceId");
if (deviceIdNode != graphicsCardIdObj.end() && deviceIdNode->value.IsUint()) {
profile.graphicsCardId.deviceId = deviceIdNode->value.GetUint();
}
}
}



JsonHelper::ReadBool(profileObj, "frameRateLimiterEnabled", profile.isFrameRateLimiterEnabled);
JsonHelper::ReadFloat(profileObj, "maxFrameRate", profile.maxFrameRate);
Expand Down
2 changes: 2 additions & 0 deletions src/Magpie.App/Magpie.App.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
<DependentUpon>ControlSizeTrigger.idl</DependentUpon>
<SubType>Code</SubType>
</ClInclude>
<ClInclude Include="AdaptersService.h" />
<ClInclude Include="EffectHelper.h" />
<ClInclude Include="EffectParametersViewModel.h">
<DependentUpon>EffectParametersViewModel.idl</DependentUpon>
Expand Down Expand Up @@ -273,6 +274,7 @@
<DependentUpon>ControlSizeTrigger.idl</DependentUpon>
<SubType>Code</SubType>
</ClCompile>
<ClCompile Include="AdaptersService.cpp" />
<ClCompile Include="EffectParametersViewModel.cpp">
<DependentUpon>EffectParametersViewModel.idl</DependentUpon>
<SubType>Code</SubType>
Expand Down
6 changes: 6 additions & 0 deletions src/Magpie.App/Magpie.App.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@
<ClCompile Include="TouchHelper.cpp">
<Filter>Helpers</Filter>
</ClCompile>
<ClCompile Include="AdaptersService.cpp">
<Filter>Services</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
Expand Down Expand Up @@ -123,6 +126,9 @@
<ClInclude Include="TouchHelper.h">
<Filter>Helpers</Filter>
</ClInclude>
<ClInclude Include="AdaptersService.h">
<Filter>Services</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="Pages">
Expand Down
5 changes: 2 additions & 3 deletions src/Magpie.App/Profile.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ struct Profile {
isCroppingEnabled = other.isCroppingEnabled;
cropping = other.cropping;
captureMethod = other.captureMethod;
graphicsCard = other.graphicsCard;
graphicsCardId = other.graphicsCardId;
isFrameRateLimiterEnabled = other.isFrameRateLimiterEnabled;
maxFrameRate = other.maxFrameRate;
multiMonitorUsage = other.multiMonitorUsage;
Expand Down Expand Up @@ -58,8 +58,7 @@ struct Profile {
// -1 表示原样
int scalingMode = -1;
::Magpie::Core::CaptureMethod captureMethod = ::Magpie::Core::CaptureMethod::GraphicsCapture;
// -1 表示默认,大于等于 0 为图形适配器的索引
int graphicsCard = -1;
::Magpie::Core::GraphicsCardId graphicsCardId;
::Magpie::Core::MultiMonitorUsage multiMonitorUsage = ::Magpie::Core::MultiMonitorUsage::Closest;
::Magpie::Core::CursorInterpolationMode cursorInterpolationMode = ::Magpie::Core::CursorInterpolationMode::NearestNeighbor;

Expand Down
Loading

0 comments on commit 5698211

Please sign in to comment.