Qt: Unrestrict resolution scale up to device limits

This commit is contained in:
Stenzek 2024-07-03 22:26:16 +10:00
parent 7927ec647f
commit 4b61a3cbf3
No known key found for this signature in database
20 changed files with 405 additions and 543 deletions

View file

@ -404,7 +404,7 @@ static SettingsPage s_settings_page = SettingsPage::Interface;
static std::unique_ptr<INISettingsInterface> s_game_settings_interface;
static std::unique_ptr<GameList::Entry> s_game_settings_entry;
static std::vector<std::pair<std::string, bool>> s_game_list_directories_cache;
static std::vector<std::string> s_graphics_adapter_list_cache;
static GPUDevice::AdapterInfoList s_graphics_adapter_list_cache;
static std::vector<std::string> s_fullscreen_mode_list_cache;
static std::vector<PostProcessingStageInfo> s_postprocessing_stages;
static std::vector<const HotkeyInfo*> 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<SmallString> strvalue =
std::optional<SmallString> current_adapter =
bsi->GetOptionalSmallStringValue("GPU", "Adapter", game_settings ? std::nullopt : std::optional<const char*>(""));
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<SmallString> strvalue = bsi->GetOptionalSmallStringValue("GPU", "FullscreenMode",
game_settings ? std::nullopt : std::optional<const char*>(""));
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) {

View file

@ -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 <algorithm>
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<int>::of(&QComboBox::currentIndexChanged), this,
&GraphicsSettingsWidget::updateRendererDependentOptions);
connect(m_ui.adapter, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&GraphicsSettingsWidget::onAdapterChanged);
connect(m_ui.resolutionScale, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&GraphicsSettingsWidget::updateResolutionDependentOptions);
connect(m_ui.textureFiltering, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&GraphicsSettingsWidget::updateResolutionDependentOptions);
connect(m_ui.displayAspectRatio, QOverload<int>::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<uint>(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<int>::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<int>::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<DisplayAlignment>(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<u32>(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<int>::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<int, const char*> 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<int>(current_adapter ? std::max<u32>(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<int, const char*>& 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<int>::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<uint>(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<int>::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<int>(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<int>(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 =

View file

@ -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;
};

View file

@ -94,93 +94,7 @@
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="resolutionScale">
<item>
<property name="text">
<string>Automatic (Based on Window Size)</string>
</property>
</item>
<item>
<property name="text">
<string>1x Native (Default)</string>
</property>
</item>
<item>
<property name="text">
<string>2x Native</string>
</property>
</item>
<item>
<property name="text">
<string>3x Native (for 720p)</string>
</property>
</item>
<item>
<property name="text">
<string>4x Native</string>
</property>
</item>
<item>
<property name="text">
<string>5x Native (for 1080p)</string>
</property>
</item>
<item>
<property name="text">
<string>6x Native (for 1440p)</string>
</property>
</item>
<item>
<property name="text">
<string>7x Native</string>
</property>
</item>
<item>
<property name="text">
<string>8x Native</string>
</property>
</item>
<item>
<property name="text">
<string>9x Native (for 4K)</string>
</property>
</item>
<item>
<property name="text">
<string>10x Native</string>
</property>
</item>
<item>
<property name="text">
<string>11x Native</string>
</property>
</item>
<item>
<property name="text">
<string>12x Native</string>
</property>
</item>
<item>
<property name="text">
<string>13x Native</string>
</property>
</item>
<item>
<property name="text">
<string>14x Native</string>
</property>
</item>
<item>
<property name="text">
<string>15x Native</string>
</property>
</item>
<item>
<property name="text">
<string>16x Native</string>
</property>
</item>
</widget>
<widget class="QComboBox" name="resolutionScale" />
</item>
<item row="1" column="0">
<widget class="QLabel" name="gpuDownsampleLabel">

View file

@ -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<IDXGIFactory5> 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++)

View file

@ -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<bool> 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;

View file

@ -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<IDXGIFactory5> 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;

View file

@ -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<u64, D3D12DescriptorHandle>;
static void GetAdapterAndModeList(AdapterAndModeList* ret, IDXGIFactory5* factory);
void SetFeatures(FeatureMask disabled_features);
u32 GetSwapChainBufferCount() const;

View file

@ -92,10 +92,10 @@ Microsoft::WRL::ComPtr<IDXGIFactory5> D3DCommon::CreateFactory(bool debug, Error
return factory;
}
static std::string FixupDuplicateAdapterNames(const std::vector<std::string>& 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<std::string>& 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<std::string> D3DCommon::GetAdapterNames(IDXGIFactory5* factory)
GPUDevice::AdapterInfoList D3DCommon::GetAdapterInfoList()
{
std::vector<std::string> adapter_names;
GPUDevice::AdapterInfoList adapters;
Microsoft::WRL::ComPtr<IDXGIFactory5> factory = CreateFactory(false, nullptr);
if (!factory)
return adapters;
Microsoft::WRL::ComPtr<IDXGIAdapter1> 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<std::string> 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<IDXGIOutput> 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<DXGI_MODE_DESC> 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<float>(mode.RefreshRate.Numerator) / static_cast<float>(mode.RefreshRate.Denominator)));
}
}
else
{
ERROR_LOG("GetDisplayModeList() (2) failed: {:08X}", static_cast<unsigned>(hr));
}
}
else
{
ERROR_LOG("GetDisplayModeList() failed: {:08X}", static_cast<unsigned>(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<unsigned>(hr));
}
adapters.push_back(std::move(ai));
}
return adapter_names;
}
std::vector<std::string> D3DCommon::GetFullscreenModes(IDXGIFactory5* factory, std::string_view adapter_name)
{
std::vector<std::string> modes;
HRESULT hr;
Microsoft::WRL::ComPtr<IDXGIAdapter1> adapter = GetChosenOrFirstAdapter(factory, adapter_name);
if (!adapter)
return modes;
Microsoft::WRL::ComPtr<IDXGIOutput> output;
if (FAILED(hr = adapter->EnumOutputs(0, &output)))
{
ERROR_LOG("EnumOutputs() failed: {:08X}", static_cast<unsigned>(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<unsigned>(hr));
return modes;
}
std::vector<DXGI_MODE_DESC> 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<unsigned>(hr));
return modes;
}
for (const DXGI_MODE_DESC& mode : dmodes)
{
modes.push_back(GPUDevice::GetFullscreenModeString(mode.Width, mode.Height,
static_cast<float>(mode.RefreshRate.Numerator) /
static_cast<float>(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<IDXGIAdapter1> 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<std::string> adapter_names;
GPUDevice::AdapterInfoList adapters;
Microsoft::WRL::ComPtr<IDXGIAdapter1> adapter;
for (u32 index = 0;; index++)
@ -271,14 +277,16 @@ Microsoft::WRL::ComPtr<IDXGIAdapter1> 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);

View file

@ -35,10 +35,7 @@ D3D_FEATURE_LEVEL GetDeviceMaxFeatureLevel(IDXGIAdapter1* adapter);
Microsoft::WRL::ComPtr<IDXGIFactory5> CreateFactory(bool debug, Error* error);
// returns a list of all adapter names
std::vector<std::string> GetAdapterNames(IDXGIFactory5* factory);
// returns a list of fullscreen modes for the specified adapter
std::vector<std::string> 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,

View file

@ -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<bool> exclusive_fullscreen_control, FeatureMask disabled_features, Error* error)

View file

@ -509,11 +509,15 @@ public:
u32 num_uploads;
};
struct AdapterAndModeList
struct AdapterInfo
{
std::vector<std::string> adapter_names;
std::string name;
std::vector<std::string> fullscreen_modes;
u32 max_texture_size;
u32 max_multisamples;
bool supports_sample_shading;
};
using AdapterInfoList = std::vector<AdapterInfo>;
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<u8>(layout)];
}
#ifdef __APPLE__
// We have to define these in the base class, because they're in Objective C++.
static std::unique_ptr<GPUDevice> 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<TexturePoolEntry>;
#ifdef __APPLE__
// We have to define these in the base class, because they're in Objective C++.
static std::unique_ptr<GPUDevice> WrapNewMetalDevice();
static AdapterInfoList WrapGetMetalAdapterList();
#endif
void OpenShaderCache(std::string_view base_path, u32 version);
void CloseShaderCache();
bool CreateResources(Error* error);

View file

@ -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<bool> exclusive_fullscreen_control, FeatureMask disabled_features,

View file

@ -87,6 +87,32 @@ static GPUTexture::Format GetTextureFormatForMTLFormat(MTLPixelFormat fmt)
return GPUTexture::Format::Unknown;
}
static u32 GetMetalMaxTextureSize(id<MTLDevice> 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<MTLDevice> 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<typename F>
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<id<MTLDevice>>* devices = [MTLCopyAllDevices() autorelease];
const u32 count = static_cast<u32>([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> GPUDevice::WrapNewMetalDevice()
return std::unique_ptr<GPUDevice>(new MetalDevice());
}
GPUDevice::AdapterAndModeList GPUDevice::WrapGetMetalAdapterAndModeList()
GPUDevice::AdapterInfoList GPUDevice::WrapGetMetalAdapterList()
{
return MetalDevice::StaticGetAdapterAndModeList();
AdapterInfoList ret;
@autoreleasepool
{
NSArray<id<MTLDevice>>* devices = [MTLCopyAllDevices() autorelease];
const u32 count = static_cast<u32>([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;
}

View file

@ -111,11 +111,6 @@ OpenGLContext::OpenGLContext(const WindowInfo& wi) : m_wi(wi)
OpenGLContext::~OpenGLContext() = default;
std::vector<OpenGLContext::FullscreenModeInfo> OpenGLContext::EnumerateFullscreenModes()
{
return {};
}
std::unique_ptr<OpenGLContext> OpenGLContext::Create(const WindowInfo& wi, Error* error)
{
static constexpr std::array<Version, 14> vlist = {{{Profile::Core, 4, 6},

View file

@ -9,7 +9,6 @@
#include <memory>
#include <span>
#include <vector>
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<OpenGLContext> CreateSharedContext(const WindowInfo& wi, Error* error) = 0;
virtual std::vector<FullscreenModeInfo> EnumerateFullscreenModes();
static std::unique_ptr<OpenGLContext> Create(const WindowInfo& wi, Error* error);
protected:

View file

@ -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)

View file

@ -48,8 +48,6 @@ public:
std::string GetDriverInfo() const override;
AdapterAndModeList GetAdapterAndModeList() override;
std::unique_ptr<GPUTexture> 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;

View file

@ -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<u32>(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<u32>(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;

View file

@ -66,15 +66,18 @@ public:
VulkanDevice();
~VulkanDevice() override;
// Returns a list of Vulkan-compatible GPUs.
using GPUList = std::vector<std::pair<VkPhysicalDevice, AdapterInfo>>;
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<u64, VkSampler>;
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<std::pair<VkPhysicalDevice, std::string>>;
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();