diff --git a/src/core/fullscreen_ui.cpp b/src/core/fullscreen_ui.cpp index 49e03d93e..c87efa5f2 100644 --- a/src/core/fullscreen_ui.cpp +++ b/src/core/fullscreen_ui.cpp @@ -404,7 +404,7 @@ static SettingsPage s_settings_page = SettingsPage::Interface; static std::unique_ptr s_game_settings_interface; static std::unique_ptr s_game_settings_entry; static std::vector> s_game_list_directories_cache; -static std::vector s_graphics_adapter_list_cache; +static GPUDevice::AdapterInfoList s_graphics_adapter_list_cache; static std::vector s_fullscreen_mode_list_cache; static std::vector s_postprocessing_stages; static std::vector s_hotkey_list_cache; @@ -2781,10 +2781,13 @@ void FullscreenUI::SwitchToGameSettings(const GameList::Entry* entry) void FullscreenUI::PopulateGraphicsAdapterList() { - GPUDevice::AdapterAndModeList ml(g_gpu_device->GetAdapterAndModeList()); - s_graphics_adapter_list_cache = std::move(ml.adapter_names); - s_fullscreen_mode_list_cache = std::move(ml.fullscreen_modes); - s_fullscreen_mode_list_cache.insert(s_fullscreen_mode_list_cache.begin(), FSUI_STR("Borderless Fullscreen")); + const GPURenderer renderer = + Settings::ParseRendererName(GetEffectiveTinyStringSetting(GetEditingSettingsInterface(false), "GPU", "Renderer", + Settings::GetRendererName(Settings::DEFAULT_GPU_RENDERER)) + .c_str()) + .value_or(Settings::DEFAULT_GPU_RENDERER); + + s_graphics_adapter_list_cache = GPUDevice::GetAdapterListForAPI(Settings::GetRenderAPIForRenderer(renderer)); } void FullscreenUI::PopulateGameListDirectoryCache(SettingsInterface* si) @@ -4160,24 +4163,22 @@ void FullscreenUI::DrawDisplaySettingsPage() .value_or(Settings::DEFAULT_GPU_RENDERER); const bool is_hardware = (renderer != GPURenderer::Software); - std::optional strvalue = + std::optional current_adapter = bsi->GetOptionalSmallStringValue("GPU", "Adapter", game_settings ? std::nullopt : std::optional("")); if (MenuButtonWithValue(FSUI_CSTR("GPU Adapter"), FSUI_CSTR("Selects the GPU to use for rendering."), - strvalue.has_value() ? (strvalue->empty() ? FSUI_CSTR("Default") : strvalue->c_str()) : + current_adapter.has_value() ? (current_adapter->empty() ? FSUI_CSTR("Default") : current_adapter->c_str()) : FSUI_CSTR("Use Global Setting"))) { - GPUDevice::AdapterAndModeList aml(g_gpu_device->GetAdapterAndModeList()); - ImGuiFullscreen::ChoiceDialogOptions options; - options.reserve(aml.adapter_names.size() + 2); + options.reserve(s_graphics_adapter_list_cache.size() + 2); if (game_settings) - options.emplace_back(FSUI_STR("Use Global Setting"), !strvalue.has_value()); - options.emplace_back(FSUI_STR("Default"), strvalue.has_value() && strvalue->empty()); - for (std::string& mode : aml.adapter_names) + options.emplace_back(FSUI_STR("Use Global Setting"), !current_adapter.has_value()); + options.emplace_back(FSUI_STR("Default"), current_adapter.has_value() && current_adapter->empty()); + for (const GPUDevice::AdapterInfo& adapter : s_graphics_adapter_list_cache) { - const bool checked = (strvalue.has_value() && strvalue.value() == mode); - options.emplace_back(std::move(mode), checked); + const bool checked = (current_adapter.has_value() && current_adapter.value() == adapter.name); + options.emplace_back(adapter.name, checked); } auto callback = [game_settings](s32 index, const std::string& title, bool checked) { @@ -4204,7 +4205,7 @@ void FullscreenUI::DrawDisplaySettingsPage() OpenChoiceDialog(FSUI_ICONSTR(ICON_FA_TV, "GPU Adapter"), false, std::move(options), std::move(callback)); } - strvalue = bsi->GetOptionalSmallStringValue("GPU", "FullscreenMode", + std::optional strvalue = bsi->GetOptionalSmallStringValue("GPU", "FullscreenMode", game_settings ? std::nullopt : std::optional("")); if (MenuButtonWithValue( @@ -4212,17 +4213,36 @@ void FullscreenUI::DrawDisplaySettingsPage() strvalue.has_value() ? (strvalue->empty() ? FSUI_CSTR("Borderless Fullscreen") : strvalue->c_str()) : FSUI_CSTR("Use Global Setting"))) { - GPUDevice::AdapterAndModeList aml(g_gpu_device->GetAdapterAndModeList()); + const GPUDevice::AdapterInfo* selected_adapter = nullptr; + if (current_adapter.has_value()) + { + for (const GPUDevice::AdapterInfo& ai : s_graphics_adapter_list_cache) + { + if (ai.name == current_adapter->view()) + { + selected_adapter = &ai; + break; + } + } + } + else + { + if (!s_graphics_adapter_list_cache.empty()) + selected_adapter = &s_graphics_adapter_list_cache.front(); + } ImGuiFullscreen::ChoiceDialogOptions options; - options.reserve(aml.fullscreen_modes.size() + 2); + options.reserve((selected_adapter ? selected_adapter->fullscreen_modes.size() : 0) + 2); if (game_settings) options.emplace_back(FSUI_STR("Use Global Setting"), !strvalue.has_value()); options.emplace_back(FSUI_STR("Borderless Fullscreen"), strvalue.has_value() && strvalue->empty()); - for (std::string& mode : aml.fullscreen_modes) + if (selected_adapter) { - const bool checked = (strvalue.has_value() && strvalue.value() == mode); - options.emplace_back(std::move(mode), checked); + for (const std::string& mode : selected_adapter->fullscreen_modes) + { + const bool checked = (strvalue.has_value() && strvalue.value() == mode); + options.emplace_back(mode, checked); + } } auto callback = [game_settings](s32 index, const std::string& title, bool checked) { diff --git a/src/duckstation-qt/graphicssettingswidget.cpp b/src/duckstation-qt/graphicssettingswidget.cpp index b33601844..83b116328 100644 --- a/src/duckstation-qt/graphicssettingswidget.cpp +++ b/src/duckstation-qt/graphicssettingswidget.cpp @@ -8,14 +8,7 @@ #include "settingswindow.h" #include "settingwidgetbinder.h" -// For enumerating adapters. -#ifdef _WIN32 -#include "util/d3d11_device.h" -#include "util/d3d12_device.h" -#endif -#ifdef ENABLE_VULKAN -#include "util/vulkan_device.h" -#endif +#include static QVariant GetMSAAModeValue(uint multisamples, bool ssaa) { @@ -51,7 +44,6 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget* SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.renderer, "GPU", "Renderer", &Settings::ParseRendererName, &Settings::GetRendererName, Settings::DEFAULT_GPU_RENDERER); - SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.resolutionScale, "GPU", "ResolutionScale", 1); SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.textureFiltering, "GPU", "TextureFilter", &Settings::ParseTextureFilterName, &Settings::GetTextureFilterName, Settings::DEFAULT_GPU_TEXTURE_FILTER); @@ -89,10 +81,6 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget* connect(m_ui.renderer, QOverload::of(&QComboBox::currentIndexChanged), this, &GraphicsSettingsWidget::updateRendererDependentOptions); - connect(m_ui.adapter, QOverload::of(&QComboBox::currentIndexChanged), this, - &GraphicsSettingsWidget::onAdapterChanged); - connect(m_ui.resolutionScale, QOverload::of(&QComboBox::currentIndexChanged), this, - &GraphicsSettingsWidget::updateResolutionDependentOptions); connect(m_ui.textureFiltering, QOverload::of(&QComboBox::currentIndexChanged), this, &GraphicsSettingsWidget::updateResolutionDependentOptions); connect(m_ui.displayAspectRatio, QOverload::of(&QComboBox::currentIndexChanged), this, @@ -102,23 +90,6 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget* connect(m_ui.trueColor, &QCheckBox::checkStateChanged, this, &GraphicsSettingsWidget::onTrueColorChanged); connect(m_ui.pgxpEnable, &QCheckBox::checkStateChanged, this, &GraphicsSettingsWidget::updatePGXPSettingsEnabled); - if (!dialog->isPerGameSettings() || - (dialog->containsSettingValue("GPU", "Multisamples") || dialog->containsSettingValue("GPU", "PerSampleShading"))) - { - const QVariant current_msaa_mode( - GetMSAAModeValue(static_cast(dialog->getEffectiveIntValue("GPU", "Multisamples", 1)), - dialog->getEffectiveBoolValue("GPU", "PerSampleShading", false))); - const int current_msaa_index = m_ui.msaaMode->findData(current_msaa_mode); - if (current_msaa_index >= 0) - m_ui.msaaMode->setCurrentIndex(current_msaa_index); - } - else - { - m_ui.msaaMode->setCurrentIndex(0); - } - connect(m_ui.msaaMode, QOverload::of(&QComboBox::currentIndexChanged), this, - &GraphicsSettingsWidget::onMSAAModeChanged); - // Advanced Tab SettingWidgetBinder::BindWidgetToEnumSetting( @@ -151,9 +122,6 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget* SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.forceRoundedTexcoords, "GPU", "ForceRoundTextureCoordinates", false); - connect(m_ui.fullscreenMode, QOverload::of(&QComboBox::currentIndexChanged), this, - &GraphicsSettingsWidget::onFullscreenModeChanged); - // PGXP Tab SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.pgxpGeometryTolerance, "GPU", "PGXPTolerance", -1.0f); @@ -582,16 +550,6 @@ void GraphicsSettingsWidget::setupAdditionalUi() QString::fromUtf8(Settings::GetDisplayAlignmentDisplayName(static_cast(i)))); } - { - if (m_dialog->isPerGameSettings()) - m_ui.msaaMode->addItem(tr("Use Global Setting")); - m_ui.msaaMode->addItem(tr("Disabled"), GetMSAAModeValue(1, false)); - for (uint i = 2; i <= 32; i *= 2) - m_ui.msaaMode->addItem(tr("%1x MSAA").arg(i), GetMSAAModeValue(i, false)); - for (uint i = 2; i <= 32; i *= 2) - m_ui.msaaMode->addItem(tr("%1x SSAA").arg(i), GetMSAAModeValue(i, true)); - } - for (u32 i = 0; i < static_cast(GPULineDetectMode::Count); i++) { m_ui.gpuLineDetectMode->addItem( @@ -694,75 +652,134 @@ void GraphicsSettingsWidget::updateRendererDependentOptions() void GraphicsSettingsWidget::populateGPUAdaptersAndResolutions(RenderAPI render_api) { - GPUDevice::AdapterAndModeList aml; - switch (render_api) + // Don't re-query, it's expensive. + if (m_adapters_render_api != render_api) { -#ifdef _WIN32 - case RenderAPI::D3D11: - aml = D3D11Device::StaticGetAdapterAndModeList(); - break; - - case RenderAPI::D3D12: - aml = D3D12Device::StaticGetAdapterAndModeList(); - break; -#endif -#ifdef __APPLE__ - case RenderAPI::Metal: - aml = GPUDevice::WrapGetMetalAdapterAndModeList(); - break; -#endif -#ifdef ENABLE_VULKAN - case RenderAPI::Vulkan: - aml = VulkanDevice::StaticGetAdapterAndModeList(); - break; -#endif - - default: - break; + m_adapters_render_api = render_api; + m_adapters = GPUDevice::GetAdapterListForAPI(render_api); } - { - const std::string current_adapter(m_dialog->getEffectiveStringValue("GPU", "Adapter", "")); - QSignalBlocker blocker(m_ui.adapter); + const GPUDevice::AdapterInfo* current_adapter = nullptr; + SettingsInterface* const sif = m_dialog->getSettingsInterface(); - // add the default entry - we'll fall back to this if the GPU no longer exists, or there's no options + { + m_ui.adapter->disconnect(); m_ui.adapter->clear(); - m_ui.adapter->addItem(tr("(Default)")); + m_ui.adapter->addItem(tr("(Default)"), QVariant(QString())); - // add the other adapters - for (const std::string& adapter_name : aml.adapter_names) + const std::string current_adapter_name = m_dialog->getEffectiveStringValue("GPU", "Adapter", ""); + for (const GPUDevice::AdapterInfo& adapter : m_adapters) { - m_ui.adapter->addItem(QString::fromStdString(adapter_name)); - - if (adapter_name == current_adapter) - m_ui.adapter->setCurrentIndex(m_ui.adapter->count() - 1); + const QString qadaptername = QString::fromStdString(adapter.name); + m_ui.adapter->addItem(qadaptername, QVariant(qadaptername)); + if (adapter.name == current_adapter_name) + current_adapter = &adapter; } + // default adapter + if (!m_adapters.empty() && current_adapter_name.empty()) + current_adapter = &m_adapters.front(); + // disable it if we don't have a choice - m_ui.adapter->setEnabled(!aml.adapter_names.empty()); + m_ui.adapter->setEnabled(!m_adapters.empty()); + SettingWidgetBinder::BindWidgetToStringSetting(sif, m_ui.adapter, "GPU", "Adapter"); + connect(m_ui.adapter, QOverload::of(&QComboBox::currentIndexChanged), this, + &GraphicsSettingsWidget::updateRendererDependentOptions); } { - const std::string current_mode(m_dialog->getEffectiveStringValue("GPU", "FullscreenMode", "")); - QSignalBlocker blocker(m_ui.fullscreenMode); - + m_ui.fullscreenMode->disconnect(); m_ui.fullscreenMode->clear(); - m_ui.fullscreenMode->addItem(tr("Borderless Fullscreen")); - m_ui.fullscreenMode->setCurrentIndex(0); - for (const std::string& mode_name : aml.fullscreen_modes) + m_ui.fullscreenMode->addItem(tr("Borderless Fullscreen"), QVariant(QString())); + if (current_adapter) { - m_ui.fullscreenMode->addItem(QString::fromStdString(mode_name)); - - if (mode_name == current_mode) - m_ui.fullscreenMode->setCurrentIndex(m_ui.fullscreenMode->count() - 1); + for (const std::string& mode_name : current_adapter->fullscreen_modes) + { + const QString qmodename = QString::fromStdString(mode_name); + m_ui.fullscreenMode->addItem(qmodename, QVariant(qmodename)); + } } // disable it if we don't have a choice - m_ui.fullscreenMode->setEnabled(!aml.fullscreen_modes.empty()); + m_ui.fullscreenMode->setEnabled(current_adapter && !current_adapter->fullscreen_modes.empty()); + SettingWidgetBinder::BindWidgetToStringSetting(sif, m_ui.fullscreenMode, "GPU", "FullscreenMode"); } - // TODO: MSAA modes + { + m_ui.resolutionScale->disconnect(); + m_ui.resolutionScale->clear(); + + static constexpr const std::pair templates[] = { + {0, QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "Automatic (Based on Window Size)")}, + {1, QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "1x Native (Default)")}, + {3, QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "3x Native (for 720p)")}, + {5, QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "5x Native (for 1080p)")}, + {6, QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "6x Native (for 1440p)")}, + {9, QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "9x Native (for 4K)")}, + }; + + const int max_scale = + static_cast(current_adapter ? std::max(current_adapter->max_texture_size / 1024, 1) : 16); + for (int scale = 0; scale <= max_scale; scale++) + { + const auto it = std::find_if(std::begin(templates), std::end(templates), + [&scale](const std::pair& it) { return scale == it.first; }); + m_ui.resolutionScale->addItem((it != std::end(templates)) ? + qApp->translate("GraphicsSettingsWidget", it->second) : + qApp->translate("GraphicsSettingsWidget", "%1x Native").arg(scale)); + } + + SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.resolutionScale, "GPU", "ResolutionScale", 1); + connect(m_ui.resolutionScale, QOverload::of(&QComboBox::currentIndexChanged), this, + &GraphicsSettingsWidget::updateResolutionDependentOptions); + } + + { + m_ui.msaaMode->disconnect(); + m_ui.msaaMode->clear(); + + if (m_dialog->isPerGameSettings()) + m_ui.msaaMode->addItem(tr("Use Global Setting")); + + const u32 max_multisamples = current_adapter ? current_adapter->max_multisamples : 8; + m_ui.msaaMode->addItem(tr("Disabled"), GetMSAAModeValue(1, false)); + for (uint i = 2; i <= max_multisamples; i *= 2) + m_ui.msaaMode->addItem(tr("%1x MSAA").arg(i), GetMSAAModeValue(i, false)); + for (uint i = 2; i <= max_multisamples; i *= 2) + m_ui.msaaMode->addItem(tr("%1x SSAA").arg(i), GetMSAAModeValue(i, true)); + + if (!m_dialog->isPerGameSettings() || (m_dialog->containsSettingValue("GPU", "Multisamples") || + m_dialog->containsSettingValue("GPU", "PerSampleShading"))) + { + const QVariant current_msaa_mode( + GetMSAAModeValue(static_cast(m_dialog->getEffectiveIntValue("GPU", "Multisamples", 1)), + m_dialog->getEffectiveBoolValue("GPU", "PerSampleShading", false))); + const int current_msaa_index = m_ui.msaaMode->findData(current_msaa_mode); + if (current_msaa_index >= 0) + m_ui.msaaMode->setCurrentIndex(current_msaa_index); + } + else + { + m_ui.msaaMode->setCurrentIndex(0); + } + connect(m_ui.msaaMode, QOverload::of(&QComboBox::currentIndexChanged), this, [this]() { + const int index = m_ui.msaaMode->currentIndex(); + if (m_dialog->isPerGameSettings() && index == 0) + { + m_dialog->removeSettingValue("GPU", "Multisamples"); + m_dialog->removeSettingValue("GPU", "PerSampleShading"); + } + else + { + uint multisamples; + bool ssaa; + DecodeMSAAModeValue(m_ui.msaaMode->itemData(index), &multisamples, &ssaa); + m_dialog->setIntSettingValue("GPU", "Multisamples", static_cast(multisamples)); + m_dialog->setBoolSettingValue("GPU", "PerSampleShading", ssaa); + } + }); + } } void GraphicsSettingsWidget::updatePGXPSettingsEnabled() @@ -785,18 +802,6 @@ void GraphicsSettingsWidget::updatePGXPSettingsEnabled() m_ui.pgxpDepthClearThresholdLabel->setEnabled(depth_enabled); } -void GraphicsSettingsWidget::onAdapterChanged() -{ - if (m_ui.adapter->currentIndex() == 0) - { - // default - m_dialog->removeSettingValue("GPU", "Adapter"); - return; - } - - m_dialog->setStringSettingValue("GPU", "Adapter", m_ui.adapter->currentText().toUtf8().constData()); -} - void GraphicsSettingsWidget::onAspectRatioChanged() { const DisplayAspectRatio ratio = @@ -828,24 +833,6 @@ void GraphicsSettingsWidget::updateResolutionDependentOptions() onTrueColorChanged(); } -void GraphicsSettingsWidget::onMSAAModeChanged() -{ - const int index = m_ui.msaaMode->currentIndex(); - if (m_dialog->isPerGameSettings() && index == 0) - { - m_dialog->removeSettingValue("GPU", "Multisamples"); - m_dialog->removeSettingValue("GPU", "PerSampleShading"); - } - else - { - uint multisamples; - bool ssaa; - DecodeMSAAModeValue(m_ui.msaaMode->itemData(index), &multisamples, &ssaa); - m_dialog->setIntSettingValue("GPU", "Multisamples", static_cast(multisamples)); - m_dialog->setBoolSettingValue("GPU", "PerSampleShading", ssaa); - } -} - void GraphicsSettingsWidget::onTrueColorChanged() { const int resolution_scale = m_dialog->getEffectiveIntValue("GPU", "ResolutionScale", 1); @@ -879,18 +866,6 @@ void GraphicsSettingsWidget::onDownsampleModeChanged() } } -void GraphicsSettingsWidget::onFullscreenModeChanged() -{ - if (m_ui.fullscreenMode->currentIndex() == 0) - { - // default - m_dialog->removeSettingValue("GPU", "FullscreenMode"); - return; - } - - m_dialog->setStringSettingValue("GPU", "FullscreenMode", m_ui.fullscreenMode->currentText().toUtf8().constData()); -} - void GraphicsSettingsWidget::onEnableAnyTextureReplacementsChanged() { const bool any_replacements_enabled = diff --git a/src/duckstation-qt/graphicssettingswidget.h b/src/duckstation-qt/graphicssettingswidget.h index f3083d90d..71d16d544 100644 --- a/src/duckstation-qt/graphicssettingswidget.h +++ b/src/duckstation-qt/graphicssettingswidget.h @@ -7,7 +7,8 @@ #include "ui_graphicssettingswidget.h" -enum class RenderAPI : u32; +#include "util/gpu_device.h" + enum class GPURenderer : u8; class SettingsWindow; @@ -27,13 +28,10 @@ private Q_SLOTS: void updateRendererDependentOptions(); void updatePGXPSettingsEnabled(); - void onAdapterChanged(); void onAspectRatioChanged(); void updateResolutionDependentOptions(); - void onMSAAModeChanged(); void onTrueColorChanged(); void onDownsampleModeChanged(); - void onFullscreenModeChanged(); void onEnableAnyTextureReplacementsChanged(); void onEnableVRAMWriteDumpingChanged(); @@ -57,4 +55,7 @@ private: Ui::GraphicsSettingsWidget m_ui; SettingsWindow* m_dialog; + + GPUDevice::AdapterInfoList m_adapters; + RenderAPI m_adapters_render_api = RenderAPI::None; }; diff --git a/src/duckstation-qt/graphicssettingswidget.ui b/src/duckstation-qt/graphicssettingswidget.ui index 2489db323..3326a07d2 100644 --- a/src/duckstation-qt/graphicssettingswidget.ui +++ b/src/duckstation-qt/graphicssettingswidget.ui @@ -94,93 +94,7 @@ - - - - Automatic (Based on Window Size) - - - - - 1x Native (Default) - - - - - 2x Native - - - - - 3x Native (for 720p) - - - - - 4x Native - - - - - 5x Native (for 1080p) - - - - - 6x Native (for 1440p) - - - - - 7x Native - - - - - 8x Native - - - - - 9x Native (for 4K) - - - - - 10x Native - - - - - 11x Native - - - - - 12x Native - - - - - 13x Native - - - - - 14x Native - - - - - 15x Native - - - - - 16x Native - - - + diff --git a/src/util/d3d11_device.cpp b/src/util/d3d11_device.cpp index 1fa7e4e2e..1a7f33b6f 100644 --- a/src/util/d3d11_device.cpp +++ b/src/util/d3d11_device.cpp @@ -685,39 +685,6 @@ void D3D11Device::SubmitPresent() Panic("Not supported by this API."); } -GPUDevice::AdapterAndModeList D3D11Device::StaticGetAdapterAndModeList() -{ - AdapterAndModeList ret; - std::unique_lock lock(s_instance_mutex); - - // Device shouldn't be torn down since we have the lock. - if (g_gpu_device && g_gpu_device->GetRenderAPI() == RenderAPI::D3D11) - { - GetAdapterAndModeList(&ret, D3D11Device::GetInstance().m_dxgi_factory.Get()); - } - else - { - ComPtr factory = D3DCommon::CreateFactory(false, nullptr); - if (factory) - GetAdapterAndModeList(&ret, factory.Get()); - } - - return ret; -} - -void D3D11Device::GetAdapterAndModeList(AdapterAndModeList* ret, IDXGIFactory5* factory) -{ - ret->adapter_names = D3DCommon::GetAdapterNames(factory); - ret->fullscreen_modes = D3DCommon::GetFullscreenModes(factory, {}); -} - -GPUDevice::AdapterAndModeList D3D11Device::GetAdapterAndModeList() -{ - AdapterAndModeList ret; - GetAdapterAndModeList(&ret, m_dxgi_factory.Get()); - return ret; -} - bool D3D11Device::CreateTimestampQueries() { for (u32 i = 0; i < NUM_TIMESTAMP_QUERIES; i++) diff --git a/src/util/d3d11_device.h b/src/util/d3d11_device.h index 396c8a726..23da3223a 100644 --- a/src/util/d3d11_device.h +++ b/src/util/d3d11_device.h @@ -43,7 +43,6 @@ public: bool UpdateWindow() override; void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override; bool SupportsExclusiveFullscreen() const override; - AdapterAndModeList GetAdapterAndModeList() override; void DestroySurface() override; std::string GetDriverInfo() const override; @@ -110,8 +109,6 @@ public: void UnbindPipeline(D3D11Pipeline* pl); void UnbindTexture(D3D11Texture* tex); - static AdapterAndModeList StaticGetAdapterAndModeList(); - protected: bool CreateDevice(std::string_view adapter, bool threaded_presentation, std::optional exclusive_fullscreen_control, FeatureMask disabled_features, @@ -133,8 +130,6 @@ private: static constexpr u32 UNIFORM_BUFFER_ALIGNMENT_DISCARD = 16; static constexpr u8 NUM_TIMESTAMP_QUERIES = 3; - static void GetAdapterAndModeList(AdapterAndModeList* ret, IDXGIFactory5* factory); - void SetFeatures(FeatureMask disabled_features); u32 GetSwapChainBufferCount() const; diff --git a/src/util/d3d12_device.cpp b/src/util/d3d12_device.cpp index baa5191b8..2774ccfee 100644 --- a/src/util/d3d12_device.cpp +++ b/src/util/d3d12_device.cpp @@ -754,39 +754,6 @@ void D3D12Device::DestroyDeferredObjects(u64 fence_value) } } -void D3D12Device::GetAdapterAndModeList(AdapterAndModeList* ret, IDXGIFactory5* factory) -{ - ret->adapter_names = D3DCommon::GetAdapterNames(factory); - ret->fullscreen_modes = D3DCommon::GetFullscreenModes(factory, {}); -} - -GPUDevice::AdapterAndModeList D3D12Device::StaticGetAdapterAndModeList() -{ - AdapterAndModeList ret; - std::unique_lock lock(s_instance_mutex); - - // Device shouldn't be torn down since we have the lock. - if (g_gpu_device && g_gpu_device->GetRenderAPI() == RenderAPI::D3D12) - { - GetAdapterAndModeList(&ret, D3D12Device::GetInstance().m_dxgi_factory.Get()); - } - else - { - ComPtr factory = D3DCommon::CreateFactory(false, nullptr); - if (factory) - GetAdapterAndModeList(&ret, factory.Get()); - } - - return ret; -} - -GPUDevice::AdapterAndModeList D3D12Device::GetAdapterAndModeList() -{ - AdapterAndModeList ret; - GetAdapterAndModeList(&ret, m_dxgi_factory.Get()); - return ret; -} - RenderAPI D3D12Device::GetRenderAPI() const { return RenderAPI::D3D12; diff --git a/src/util/d3d12_device.h b/src/util/d3d12_device.h index 56a7a7280..f33c265f4 100644 --- a/src/util/d3d12_device.h +++ b/src/util/d3d12_device.h @@ -64,8 +64,6 @@ public: bool UpdateWindow() override; void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override; - static AdapterAndModeList StaticGetAdapterAndModeList(); - AdapterAndModeList GetAdapterAndModeList() override; void DestroySurface() override; std::string GetDriverInfo() const override; @@ -220,8 +218,6 @@ private: using SamplerMap = std::unordered_map; - static void GetAdapterAndModeList(AdapterAndModeList* ret, IDXGIFactory5* factory); - void SetFeatures(FeatureMask disabled_features); u32 GetSwapChainBufferCount() const; diff --git a/src/util/d3d_common.cpp b/src/util/d3d_common.cpp index 50b3bf65b..212387ebf 100644 --- a/src/util/d3d_common.cpp +++ b/src/util/d3d_common.cpp @@ -92,10 +92,10 @@ Microsoft::WRL::ComPtr D3DCommon::CreateFactory(bool debug, Error return factory; } -static std::string FixupDuplicateAdapterNames(const std::vector& adapter_names, std::string adapter_name) +static std::string FixupDuplicateAdapterNames(const GPUDevice::AdapterInfoList& adapter_names, std::string adapter_name) { if (std::any_of(adapter_names.begin(), adapter_names.end(), - [&adapter_name](const std::string& other) { return (adapter_name == other); })) + [&adapter_name](const GPUDevice::AdapterInfo& other) { return (adapter_name == other.name); })) { std::string original_adapter_name = std::move(adapter_name); @@ -104,21 +104,26 @@ static std::string FixupDuplicateAdapterNames(const std::vector& ad { adapter_name = fmt::format("{} ({})", original_adapter_name.c_str(), current_extra); current_extra++; - } while (std::any_of(adapter_names.begin(), adapter_names.end(), - [&adapter_name](const std::string& other) { return (adapter_name == other); })); + } while ( + std::any_of(adapter_names.begin(), adapter_names.end(), + [&adapter_name](const GPUDevice::AdapterInfo& other) { return (adapter_name == other.name); })); } return adapter_name; } -std::vector D3DCommon::GetAdapterNames(IDXGIFactory5* factory) +GPUDevice::AdapterInfoList D3DCommon::GetAdapterInfoList() { - std::vector adapter_names; + GPUDevice::AdapterInfoList adapters; + + Microsoft::WRL::ComPtr factory = CreateFactory(false, nullptr); + if (!factory) + return adapters; Microsoft::WRL::ComPtr adapter; for (u32 index = 0;; index++) { - const HRESULT hr = factory->EnumAdapters1(index, adapter.ReleaseAndGetAddressOf()); + HRESULT hr = factory->EnumAdapters1(index, adapter.ReleaseAndGetAddressOf()); if (hr == DXGI_ERROR_NOT_FOUND) break; @@ -128,50 +133,51 @@ std::vector D3DCommon::GetAdapterNames(IDXGIFactory5* factory) continue; } - adapter_names.push_back(FixupDuplicateAdapterNames(adapter_names, GetAdapterName(adapter.Get()))); + // Unfortunately we can't get any properties such as feature level without creating the device. + // So just assume a max of the D3D11 max across the board. + GPUDevice::AdapterInfo ai; + ai.name = FixupDuplicateAdapterNames(adapters, GetAdapterName(adapter.Get())); + ai.max_texture_size = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION; + ai.max_multisamples = 8; + ai.supports_sample_shading = true; + + Microsoft::WRL::ComPtr output; + if (SUCCEEDED(hr = adapter->EnumOutputs(0, output.ReleaseAndGetAddressOf()))) + { + UINT num_modes = 0; + if (SUCCEEDED(hr = output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, nullptr))) + { + std::vector dmodes(num_modes); + if (SUCCEEDED(hr = output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, dmodes.data()))) + { + for (const DXGI_MODE_DESC& mode : dmodes) + { + ai.fullscreen_modes.push_back(GPUDevice::GetFullscreenModeString( + mode.Width, mode.Height, + static_cast(mode.RefreshRate.Numerator) / static_cast(mode.RefreshRate.Denominator))); + } + } + else + { + ERROR_LOG("GetDisplayModeList() (2) failed: {:08X}", static_cast(hr)); + } + } + else + { + ERROR_LOG("GetDisplayModeList() failed: {:08X}", static_cast(hr)); + } + } + else + { + // Adapter may not have any outputs, don't spam the error log in this case. + if (hr != DXGI_ERROR_NOT_FOUND) + ERROR_LOG("EnumOutputs() failed: {:08X}", static_cast(hr)); + } + + adapters.push_back(std::move(ai)); } - return adapter_names; -} - -std::vector D3DCommon::GetFullscreenModes(IDXGIFactory5* factory, std::string_view adapter_name) -{ - std::vector modes; - HRESULT hr; - - Microsoft::WRL::ComPtr adapter = GetChosenOrFirstAdapter(factory, adapter_name); - if (!adapter) - return modes; - - Microsoft::WRL::ComPtr output; - if (FAILED(hr = adapter->EnumOutputs(0, &output))) - { - ERROR_LOG("EnumOutputs() failed: {:08X}", static_cast(hr)); - return modes; - } - - UINT num_modes = 0; - if (FAILED(hr = output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, nullptr))) - { - ERROR_LOG("GetDisplayModeList() failed: {:08X}", static_cast(hr)); - return modes; - } - - std::vector dmodes(num_modes); - if (FAILED(hr = output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, dmodes.data()))) - { - ERROR_LOG("GetDisplayModeList() (2) failed: {:08X}", static_cast(hr)); - return modes; - } - - for (const DXGI_MODE_DESC& mode : dmodes) - { - modes.push_back(GPUDevice::GetFullscreenModeString(mode.Width, mode.Height, - static_cast(mode.RefreshRate.Numerator) / - static_cast(mode.RefreshRate.Denominator))); - } - - return modes; + return adapters; } bool D3DCommon::GetRequestedExclusiveFullscreenModeDesc(IDXGIFactory5* factory, const RECT& window_rect, u32 width, @@ -256,7 +262,7 @@ Microsoft::WRL::ComPtr D3DCommon::GetAdapterByName(IDXGIFactory5* // This might seem a bit odd to cache the names.. but there's a method to the madness. // We might have two GPUs with the same name... :) - std::vector adapter_names; + GPUDevice::AdapterInfoList adapters; Microsoft::WRL::ComPtr adapter; for (u32 index = 0;; index++) @@ -271,14 +277,16 @@ Microsoft::WRL::ComPtr D3DCommon::GetAdapterByName(IDXGIFactory5* continue; } - std::string adapter_name = FixupDuplicateAdapterNames(adapter_names, GetAdapterName(adapter.Get())); + std::string adapter_name = FixupDuplicateAdapterNames(adapters, GetAdapterName(adapter.Get())); if (adapter_name == name) { VERBOSE_LOG("Found adapter '{}'", adapter_name); return adapter; } - adapter_names.push_back(std::move(adapter_name)); + GPUDevice::AdapterInfo ai; + ai.name = std::move(adapter_name); + adapters.push_back(std::move(ai)); } ERROR_LOG("Adapter '{}' not found.", name); diff --git a/src/util/d3d_common.h b/src/util/d3d_common.h index b3c97f396..8916f4e11 100644 --- a/src/util/d3d_common.h +++ b/src/util/d3d_common.h @@ -35,10 +35,7 @@ D3D_FEATURE_LEVEL GetDeviceMaxFeatureLevel(IDXGIAdapter1* adapter); Microsoft::WRL::ComPtr CreateFactory(bool debug, Error* error); // returns a list of all adapter names -std::vector GetAdapterNames(IDXGIFactory5* factory); - -// returns a list of fullscreen modes for the specified adapter -std::vector GetFullscreenModes(IDXGIFactory5* factory, std::string_view adapter_name); +GPUDevice::AdapterInfoList GetAdapterInfoList(); // returns the fullscreen mode to use for the specified dimensions bool GetRequestedExclusiveFullscreenModeDesc(IDXGIFactory5* factory, const RECT& window_rect, u32 width, u32 height, diff --git a/src/util/gpu_device.cpp b/src/util/gpu_device.cpp index d49aad029..7c59318af 100644 --- a/src/util/gpu_device.cpp +++ b/src/util/gpu_device.cpp @@ -292,6 +292,45 @@ bool GPUDevice::IsSameRenderAPI(RenderAPI lhs, RenderAPI rhs) (rhs == RenderAPI::OpenGL || rhs == RenderAPI::OpenGLES))); } +GPUDevice::AdapterInfoList GPUDevice::GetAdapterListForAPI(RenderAPI api) +{ + AdapterInfoList ret; + + switch (api) + { +#ifdef ENABLE_VULKAN + case RenderAPI::Vulkan: + ret = VulkanDevice::GetAdapterList(); + break; +#endif + +#ifdef ENABLE_OPENGL + case RenderAPI::OpenGL: + case RenderAPI::OpenGLES: + // No way of querying. + break; +#endif + +#ifdef _WIN32 + case RenderAPI::D3D11: + case RenderAPI::D3D12: + ret = D3DCommon::GetAdapterInfoList(); + break; +#endif + +#ifdef __APPLE__ + case RenderAPI::Metal: + ret = WrapGetMetalAdapterList(); + break; +#endif + + default: + break; + } + + return ret; +} + bool GPUDevice::Create(std::string_view adapter, std::string_view shader_cache_path, u32 shader_cache_version, bool debug_device, GPUVSyncMode vsync, bool allow_present_throttle, bool threaded_presentation, std::optional exclusive_fullscreen_control, FeatureMask disabled_features, Error* error) diff --git a/src/util/gpu_device.h b/src/util/gpu_device.h index 5e4e94204..e545c820d 100644 --- a/src/util/gpu_device.h +++ b/src/util/gpu_device.h @@ -509,11 +509,15 @@ public: u32 num_uploads; }; - struct AdapterAndModeList + struct AdapterInfo { - std::vector adapter_names; + std::string name; std::vector fullscreen_modes; + u32 max_texture_size; + u32 max_multisamples; + bool supports_sample_shading; }; + using AdapterInfoList = std::vector; struct PooledTextureDeleter { @@ -543,6 +547,9 @@ public: /// Returns true if the render API is the same (e.g. GLES and GL). static bool IsSameRenderAPI(RenderAPI lhs, RenderAPI rhs); + /// Returns a list of adapters for the given API. + static AdapterInfoList GetAdapterListForAPI(RenderAPI api); + /// Parses a fullscreen mode into its components (width * height @ refresh hz) static bool GetRequestedExclusiveFullscreenMode(u32* width, u32* height, float* refresh_rate); @@ -572,12 +579,6 @@ public: return counts[static_cast(layout)]; } -#ifdef __APPLE__ - // We have to define these in the base class, because they're in Objective C++. - static std::unique_ptr WrapNewMetalDevice(); - static AdapterAndModeList WrapGetMetalAdapterAndModeList(); -#endif - ALWAYS_INLINE const Features& GetFeatures() const { return m_features; } ALWAYS_INLINE u32 GetMaxTextureSize() const { return m_max_texture_size; } ALWAYS_INLINE u32 GetMaxMultisamples() const { return m_max_multisamples; } @@ -605,7 +606,6 @@ public: virtual bool UpdateWindow() = 0; virtual bool SupportsExclusiveFullscreen() const; - virtual AdapterAndModeList GetAdapterAndModeList() = 0; /// Call when the window size changes externally to recreate any resources. virtual void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) = 0; @@ -801,6 +801,12 @@ private: using TexturePool = std::deque; +#ifdef __APPLE__ + // We have to define these in the base class, because they're in Objective C++. + static std::unique_ptr WrapNewMetalDevice(); + static AdapterInfoList WrapGetMetalAdapterList(); +#endif + void OpenShaderCache(std::string_view base_path, u32 version); void CloseShaderCache(); bool CreateResources(Error* error); diff --git a/src/util/metal_device.h b/src/util/metal_device.h index 5602523b1..6bffc27e0 100644 --- a/src/util/metal_device.h +++ b/src/util/metal_device.h @@ -204,8 +204,6 @@ public: bool UpdateWindow() override; void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override; - - AdapterAndModeList GetAdapterAndModeList() override; void DestroySurface() override; std::string GetDriverInfo() const override; @@ -286,8 +284,6 @@ public: static void DeferRelease(id obj); static void DeferRelease(u64 fence_counter, id obj); - static AdapterAndModeList StaticGetAdapterAndModeList(); - protected: bool CreateDevice(std::string_view adapter, bool threaded_presentation, std::optional exclusive_fullscreen_control, FeatureMask disabled_features, diff --git a/src/util/metal_device.mm b/src/util/metal_device.mm index 4d91f6e18..7c1ee9e1c 100644 --- a/src/util/metal_device.mm +++ b/src/util/metal_device.mm @@ -87,6 +87,32 @@ static GPUTexture::Format GetTextureFormatForMTLFormat(MTLPixelFormat fmt) return GPUTexture::Format::Unknown; } +static u32 GetMetalMaxTextureSize(id device) +{ + // https://gist.github.com/kylehowells/63d0723abc9588eb734cade4b7df660d + if ([device supportsFamily:MTLGPUFamilyMacCatalyst1] || [device supportsFamily:MTLGPUFamilyMac1] || + [device supportsFamily:MTLGPUFamilyApple3]) + { + return 16384; + } + else + { + return 8192; + } +} + +static u32 GetMetalMaxMultisamples(id device) +{ + u32 max_multisamples = 0; + for (u32 multisamples = 1; multisamples < 16; multisamples *= 2) + { + if (![device supportsTextureSampleCount:multisamples]) + break; + max_multisamples = multisamples; + } + return max_multisamples; +} + template static void RunOnMainThread(F&& f) { @@ -204,16 +230,8 @@ bool MetalDevice::CreateDevice(std::string_view adapter, bool threaded_presentat void MetalDevice::SetFeatures(FeatureMask disabled_features) { - // https://gist.github.com/kylehowells/63d0723abc9588eb734cade4b7df660d - if ([m_device supportsFamily:MTLGPUFamilyMacCatalyst1] || [m_device supportsFamily:MTLGPUFamilyMac1] || - [m_device supportsFamily:MTLGPUFamilyApple3]) - { - m_max_texture_size = 16384; - } - else - { - m_max_texture_size = 8192; - } + m_max_texture_size = GetMetalMaxTextureSize(m_device); + m_max_multisamples = GetMetalMaxMultisamples(m_device); // Framebuffer fetch requires MSL 2.3 and an Apple GPU family. const bool supports_fbfetch = [m_device supportsFamily:MTLGPUFamilyApple1]; @@ -222,14 +240,6 @@ void MetalDevice::SetFeatures(FeatureMask disabled_features) const bool supports_barriers = ([m_device supportsFamily:MTLGPUFamilyMac1] && ![m_device supportsFamily:MTLGPUFamilyApple3]); - m_max_multisamples = 0; - for (u32 multisamples = 1; multisamples < 16; multisamples *= 2) - { - if (![m_device supportsTextureSampleCount:multisamples]) - break; - m_max_multisamples = multisamples; - } - m_features.dual_source_blend = !(disabled_features & FEATURE_MASK_DUAL_SOURCE_BLEND); m_features.framebuffer_fetch = !(disabled_features & FEATURE_MASK_FRAMEBUFFER_FETCH) && supports_fbfetch; m_features.per_sample_shading = true; @@ -538,26 +548,6 @@ bool MetalDevice::IsRenderTargetBound(const GPUTexture* tex) const return false; } -GPUDevice::AdapterAndModeList MetalDevice::StaticGetAdapterAndModeList() -{ - AdapterAndModeList ret; - @autoreleasepool - { - NSArray>* devices = [MTLCopyAllDevices() autorelease]; - const u32 count = static_cast([devices count]); - ret.adapter_names.reserve(count); - for (u32 i = 0; i < count; i++) - ret.adapter_names.emplace_back([devices[i].name UTF8String]); - } - - return ret; -} - -GPUDevice::AdapterAndModeList MetalDevice::GetAdapterAndModeList() -{ - return StaticGetAdapterAndModeList(); -} - bool MetalDevice::SetGPUTimingEnabled(bool enabled) { if (m_gpu_timing_enabled == enabled) @@ -2505,7 +2495,24 @@ std::unique_ptr GPUDevice::WrapNewMetalDevice() return std::unique_ptr(new MetalDevice()); } -GPUDevice::AdapterAndModeList GPUDevice::WrapGetMetalAdapterAndModeList() +GPUDevice::AdapterInfoList GPUDevice::WrapGetMetalAdapterList() { - return MetalDevice::StaticGetAdapterAndModeList(); + AdapterInfoList ret; + @autoreleasepool + { + NSArray>* devices = [MTLCopyAllDevices() autorelease]; + const u32 count = static_cast([devices count]); + ret.reserve(count); + for (u32 i = 0; i < count; i++) + { + AdapterInfo ai; + ai.name = [devices[i].name UTF8String]; + ai.max_texture_size = GetMetalMaxTextureSize(devices[i]); + ai.max_multisamples = GetMetalMaxMultisamples(devices[i]); + ai.supports_sample_shading = true; + ret.push_back(std::move(ai)); + } + } + + return ret; } diff --git a/src/util/opengl_context.cpp b/src/util/opengl_context.cpp index 4aab851c8..c6e2323c5 100644 --- a/src/util/opengl_context.cpp +++ b/src/util/opengl_context.cpp @@ -111,11 +111,6 @@ OpenGLContext::OpenGLContext(const WindowInfo& wi) : m_wi(wi) OpenGLContext::~OpenGLContext() = default; -std::vector OpenGLContext::EnumerateFullscreenModes() -{ - return {}; -} - std::unique_ptr OpenGLContext::Create(const WindowInfo& wi, Error* error) { static constexpr std::array vlist = {{{Profile::Core, 4, 6}, diff --git a/src/util/opengl_context.h b/src/util/opengl_context.h index e9d92e159..f7e5cedb3 100644 --- a/src/util/opengl_context.h +++ b/src/util/opengl_context.h @@ -9,7 +9,6 @@ #include #include -#include class Error; @@ -33,13 +32,6 @@ public: int minor_version; }; - struct FullscreenModeInfo - { - u32 width; - u32 height; - float refresh_rate; - }; - ALWAYS_INLINE const WindowInfo& GetWindowInfo() const { return m_wi; } ALWAYS_INLINE bool IsGLES() const { return (m_version.profile == Profile::ES); } ALWAYS_INLINE u32 GetSurfaceWidth() const { return m_wi.surface_width; } @@ -57,8 +49,6 @@ public: virtual bool SetSwapInterval(s32 interval) = 0; virtual std::unique_ptr CreateSharedContext(const WindowInfo& wi, Error* error) = 0; - virtual std::vector EnumerateFullscreenModes(); - static std::unique_ptr Create(const WindowInfo& wi, Error* error); protected: diff --git a/src/util/opengl_device.cpp b/src/util/opengl_device.cpp index ee2ccf5ce..44061a55c 100644 --- a/src/util/opengl_device.cpp +++ b/src/util/opengl_device.cpp @@ -663,21 +663,6 @@ void OpenGLDevice::DestroyFramebuffer(GLuint fbo) glDeleteFramebuffers(1, &fbo); } -GPUDevice::AdapterAndModeList OpenGLDevice::GetAdapterAndModeList() -{ - AdapterAndModeList aml; - - if (m_gl_context) - { - for (const OpenGLContext::FullscreenModeInfo& fmi : m_gl_context->EnumerateFullscreenModes()) - { - aml.fullscreen_modes.push_back(GetFullscreenModeString(fmi.width, fmi.height, fmi.refresh_rate)); - } - } - - return aml; -} - void OpenGLDevice::DestroySurface() { if (!m_gl_context) diff --git a/src/util/opengl_device.h b/src/util/opengl_device.h index 63e6f195e..0532a3ffe 100644 --- a/src/util/opengl_device.h +++ b/src/util/opengl_device.h @@ -48,8 +48,6 @@ public: std::string GetDriverInfo() const override; - AdapterAndModeList GetAdapterAndModeList() override; - std::unique_ptr CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, GPUTexture::Type type, GPUTexture::Format format, const void* data = nullptr, u32 data_stride = 0) override; diff --git a/src/util/vulkan_device.cpp b/src/util/vulkan_device.cpp index f6434672f..a0ab879e2 100644 --- a/src/util/vulkan_device.cpp +++ b/src/util/vulkan_device.cpp @@ -315,28 +315,76 @@ VulkanDevice::GPUList VulkanDevice::EnumerateGPUs(VkInstance instance) VkPhysicalDeviceProperties props = {}; vkGetPhysicalDeviceProperties(device, &props); - std::string gpu_name = props.deviceName; + VkPhysicalDeviceFeatures available_features = {}; + vkGetPhysicalDeviceFeatures(device, &available_features); + + AdapterInfo ai; + ai.name = props.deviceName; + ai.max_texture_size = std::min(props.limits.maxFramebufferWidth, props.limits.maxImageDimension2D); + ai.max_multisamples = GetMaxMultisamples(device, props); + ai.supports_sample_shading = available_features.sampleRateShading; // handle duplicate adapter names - if (std::any_of(gpus.begin(), gpus.end(), [&gpu_name](const auto& other) { return (gpu_name == other.second); })) + if (std::any_of(gpus.begin(), gpus.end(), [&ai](const auto& other) { return (ai.name == other.second.name); })) { - std::string original_adapter_name = std::move(gpu_name); + std::string original_adapter_name = std::move(ai.name); u32 current_extra = 2; do { - gpu_name = fmt::format("{} ({})", original_adapter_name, current_extra); + ai.name = fmt::format("{} ({})", original_adapter_name, current_extra); current_extra++; } while ( - std::any_of(gpus.begin(), gpus.end(), [&gpu_name](const auto& other) { return (gpu_name == other.second); })); + std::any_of(gpus.begin(), gpus.end(), [&ai](const auto& other) { return (ai.name == other.second.name); })); } - gpus.emplace_back(device, std::move(gpu_name)); + gpus.emplace_back(device, std::move(ai)); } return gpus; } +VulkanDevice::GPUList VulkanDevice::EnumerateGPUs() +{ + GPUList ret; + std::unique_lock lock(s_instance_mutex); + + // Device shouldn't be torn down since we have the lock. + if (g_gpu_device && g_gpu_device->GetRenderAPI() == RenderAPI::Vulkan && Vulkan::IsVulkanLibraryLoaded()) + { + ret = EnumerateGPUs(VulkanDevice::GetInstance().m_instance); + } + else + { + if (Vulkan::LoadVulkanLibrary(nullptr)) + { + OptionalExtensions oe = {}; + const VkInstance instance = CreateVulkanInstance(WindowInfo(), &oe, false, false); + if (instance != VK_NULL_HANDLE) + { + if (Vulkan::LoadVulkanInstanceFunctions(instance)) + ret = EnumerateGPUs(instance); + + vkDestroyInstance(instance, nullptr); + } + + Vulkan::UnloadVulkanLibrary(); + } + } + + return ret; +} + +GPUDevice::AdapterInfoList VulkanDevice::GetAdapterList() +{ + AdapterInfoList ret; + GPUList gpus = EnumerateGPUs(); + ret.reserve(gpus.size()); + for (auto& [physical_device, adapter_info] : gpus) + ret.push_back(std::move(adapter_info)); + return ret; +} + bool VulkanDevice::SelectDeviceExtensions(ExtensionList* extension_list, bool enable_surface) { u32 extension_count = 0; @@ -1853,67 +1901,21 @@ void VulkanDevice::DestroyFramebuffer(VkFramebuffer fbo) VulkanDevice::GetInstance().DeferFramebufferDestruction(fbo); } -void VulkanDevice::GetAdapterAndModeList(AdapterAndModeList* ret, VkInstance instance) -{ - GPUList gpus = EnumerateGPUs(instance); - ret->adapter_names.clear(); - for (auto& [gpu, name] : gpus) - ret->adapter_names.push_back(std::move(name)); -} - -GPUDevice::AdapterAndModeList VulkanDevice::StaticGetAdapterAndModeList() -{ - AdapterAndModeList ret; - std::unique_lock lock(s_instance_mutex); - - // Device shouldn't be torn down since we have the lock. - if (g_gpu_device && g_gpu_device->GetRenderAPI() == RenderAPI::Vulkan && Vulkan::IsVulkanLibraryLoaded()) - { - GetAdapterAndModeList(&ret, VulkanDevice::GetInstance().m_instance); - } - else - { - if (Vulkan::LoadVulkanLibrary(nullptr)) - { - OptionalExtensions oe = {}; - const VkInstance instance = CreateVulkanInstance(WindowInfo(), &oe, false, false); - if (instance != VK_NULL_HANDLE) - { - if (Vulkan::LoadVulkanInstanceFunctions(instance)) - GetAdapterAndModeList(&ret, instance); - - vkDestroyInstance(instance, nullptr); - } - - Vulkan::UnloadVulkanLibrary(); - } - } - - return ret; -} - -GPUDevice::AdapterAndModeList VulkanDevice::GetAdapterAndModeList() -{ - AdapterAndModeList ret; - GetAdapterAndModeList(&ret, m_instance); - return ret; -} - bool VulkanDevice::IsSuitableDefaultRenderer() { #ifdef __ANDROID__ // No way in hell. return false; #else - AdapterAndModeList aml = StaticGetAdapterAndModeList(); - if (aml.adapter_names.empty()) + GPUList gpus = EnumerateGPUs(); + if (gpus.empty()) { // No adapters, not gonna be able to use VK. return false; } // Check the first GPU, should be enough. - const std::string& name = aml.adapter_names.front(); + const std::string& name = gpus.front().second.name; INFO_LOG("Using Vulkan GPU '{}' for automatic renderer check.", name); // Any software rendering (LLVMpipe, SwiftShader). @@ -2000,8 +2002,8 @@ bool VulkanDevice::CreateDevice(std::string_view adapter, bool threaded_presenta u32 gpu_index = 0; for (; gpu_index < static_cast(gpus.size()); gpu_index++) { - INFO_LOG("GPU {}: {}", gpu_index, gpus[gpu_index].second); - if (gpus[gpu_index].second == adapter) + INFO_LOG("GPU {}: {}", gpu_index, gpus[gpu_index].second.name); + if (gpus[gpu_index].second.name == adapter) { m_physical_device = gpus[gpu_index].first; break; @@ -2010,13 +2012,13 @@ bool VulkanDevice::CreateDevice(std::string_view adapter, bool threaded_presenta if (gpu_index == static_cast(gpus.size())) { - WARNING_LOG("Requested GPU '{}' not found, using first ({})", adapter, gpus[0].second); + WARNING_LOG("Requested GPU '{}' not found, using first ({})", adapter, gpus[0].second.name); m_physical_device = gpus[0].first; } } else { - INFO_LOG("No GPU requested, using first ({})", gpus[0].second); + INFO_LOG("No GPU requested, using first ({})", gpus[0].second.name); m_physical_device = gpus[0].first; } @@ -2542,35 +2544,40 @@ void VulkanDevice::InsertDebugMessage(const char* msg) #endif } -bool VulkanDevice::CheckFeatures(FeatureMask disabled_features) +u32 VulkanDevice::GetMaxMultisamples(VkPhysicalDevice physical_device, const VkPhysicalDeviceProperties& properties) { - m_max_texture_size = m_device_properties.limits.maxImageDimension2D; - VkImageFormatProperties color_properties = {}; - vkGetPhysicalDeviceImageFormatProperties(m_physical_device, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TYPE_2D, + vkGetPhysicalDeviceImageFormatProperties(physical_device, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 0, &color_properties); VkImageFormatProperties depth_properties = {}; - vkGetPhysicalDeviceImageFormatProperties(m_physical_device, VK_FORMAT_D32_SFLOAT, VK_IMAGE_TYPE_2D, + vkGetPhysicalDeviceImageFormatProperties(physical_device, VK_FORMAT_D32_SFLOAT, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, 0, &depth_properties); - const VkSampleCountFlags combined_properties = m_device_properties.limits.framebufferColorSampleCounts & - m_device_properties.limits.framebufferDepthSampleCounts & + const VkSampleCountFlags combined_properties = properties.limits.framebufferColorSampleCounts & + properties.limits.framebufferDepthSampleCounts & color_properties.sampleCounts & depth_properties.sampleCounts; if (combined_properties & VK_SAMPLE_COUNT_64_BIT) - m_max_multisamples = 64; + return 64; else if (combined_properties & VK_SAMPLE_COUNT_32_BIT) - m_max_multisamples = 32; + return 32; else if (combined_properties & VK_SAMPLE_COUNT_16_BIT) - m_max_multisamples = 16; + return 16; else if (combined_properties & VK_SAMPLE_COUNT_8_BIT) - m_max_multisamples = 8; + return 8; else if (combined_properties & VK_SAMPLE_COUNT_4_BIT) - m_max_multisamples = 4; + return 4; else if (combined_properties & VK_SAMPLE_COUNT_2_BIT) - m_max_multisamples = 2; + return 2; else - m_max_multisamples = 1; + return 1; +} + +bool VulkanDevice::CheckFeatures(FeatureMask disabled_features) +{ + m_max_texture_size = + std::min(m_device_properties.limits.maxImageDimension2D, m_device_properties.limits.maxFramebufferWidth); + m_max_multisamples = GetMaxMultisamples(m_physical_device, m_device_properties); m_features.dual_source_blend = !(disabled_features & FEATURE_MASK_DUAL_SOURCE_BLEND) && m_device_features.dualSrcBlend; diff --git a/src/util/vulkan_device.h b/src/util/vulkan_device.h index cdf5ea819..347e8177b 100644 --- a/src/util/vulkan_device.h +++ b/src/util/vulkan_device.h @@ -66,15 +66,18 @@ public: VulkanDevice(); ~VulkanDevice() override; + // Returns a list of Vulkan-compatible GPUs. + using GPUList = std::vector>; + static GPUList EnumerateGPUs(VkInstance instance); + static GPUList EnumerateGPUs(); + static AdapterInfoList GetAdapterList(); + RenderAPI GetRenderAPI() const override; bool HasSurface() const override; bool UpdateWindow() override; void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override; - - static AdapterAndModeList StaticGetAdapterAndModeList(); - AdapterAndModeList GetAdapterAndModeList() override; void DestroySurface() override; std::string GetDriverInfo() const override; @@ -287,16 +290,10 @@ private: using CleanupObjectFunction = void (*)(VulkanDevice& dev, void* obj); using SamplerMap = std::unordered_map; - static void GetAdapterAndModeList(AdapterAndModeList* ret, VkInstance instance); - // Helper method to create a Vulkan instance. static VkInstance CreateVulkanInstance(const WindowInfo& wi, OptionalExtensions* oe, bool enable_debug_utils, bool enable_validation_layer); - // Returns a list of Vulkan-compatible GPUs. - using GPUList = std::vector>; - static GPUList EnumerateGPUs(VkInstance instance); - bool ValidatePipelineCacheHeader(const VK_PIPELINE_CACHE_HEADER& header); void FillPipelineCacheHeader(VK_PIPELINE_CACHE_HEADER* header); @@ -331,6 +328,8 @@ private: bool CheckFeatures(FeatureMask disabled_features); + static u32 GetMaxMultisamples(VkPhysicalDevice physical_device, const VkPhysicalDeviceProperties& properties); + bool CreateAllocator(); void DestroyAllocator(); bool CreateCommandBuffers();