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<INISettingsInterface> s_game_settings_interface;
static std::unique_ptr<GameList::Entry> s_game_settings_entry; 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::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<std::string> s_fullscreen_mode_list_cache;
static std::vector<PostProcessingStageInfo> s_postprocessing_stages; static std::vector<PostProcessingStageInfo> s_postprocessing_stages;
static std::vector<const HotkeyInfo*> s_hotkey_list_cache; static std::vector<const HotkeyInfo*> s_hotkey_list_cache;
@ -2781,10 +2781,13 @@ void FullscreenUI::SwitchToGameSettings(const GameList::Entry* entry)
void FullscreenUI::PopulateGraphicsAdapterList() void FullscreenUI::PopulateGraphicsAdapterList()
{ {
GPUDevice::AdapterAndModeList ml(g_gpu_device->GetAdapterAndModeList()); const GPURenderer renderer =
s_graphics_adapter_list_cache = std::move(ml.adapter_names); Settings::ParseRendererName(GetEffectiveTinyStringSetting(GetEditingSettingsInterface(false), "GPU", "Renderer",
s_fullscreen_mode_list_cache = std::move(ml.fullscreen_modes); Settings::GetRendererName(Settings::DEFAULT_GPU_RENDERER))
s_fullscreen_mode_list_cache.insert(s_fullscreen_mode_list_cache.begin(), FSUI_STR("Borderless Fullscreen")); .c_str())
.value_or(Settings::DEFAULT_GPU_RENDERER);
s_graphics_adapter_list_cache = GPUDevice::GetAdapterListForAPI(Settings::GetRenderAPIForRenderer(renderer));
} }
void FullscreenUI::PopulateGameListDirectoryCache(SettingsInterface* si) void FullscreenUI::PopulateGameListDirectoryCache(SettingsInterface* si)
@ -4160,24 +4163,22 @@ void FullscreenUI::DrawDisplaySettingsPage()
.value_or(Settings::DEFAULT_GPU_RENDERER); .value_or(Settings::DEFAULT_GPU_RENDERER);
const bool is_hardware = (renderer != GPURenderer::Software); 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*>("")); 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."), 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"))) FSUI_CSTR("Use Global Setting")))
{ {
GPUDevice::AdapterAndModeList aml(g_gpu_device->GetAdapterAndModeList());
ImGuiFullscreen::ChoiceDialogOptions options; ImGuiFullscreen::ChoiceDialogOptions options;
options.reserve(aml.adapter_names.size() + 2); options.reserve(s_graphics_adapter_list_cache.size() + 2);
if (game_settings) if (game_settings)
options.emplace_back(FSUI_STR("Use Global Setting"), !strvalue.has_value()); options.emplace_back(FSUI_STR("Use Global Setting"), !current_adapter.has_value());
options.emplace_back(FSUI_STR("Default"), strvalue.has_value() && strvalue->empty()); options.emplace_back(FSUI_STR("Default"), current_adapter.has_value() && current_adapter->empty());
for (std::string& mode : aml.adapter_names) for (const GPUDevice::AdapterInfo& adapter : s_graphics_adapter_list_cache)
{ {
const bool checked = (strvalue.has_value() && strvalue.value() == mode); const bool checked = (current_adapter.has_value() && current_adapter.value() == adapter.name);
options.emplace_back(std::move(mode), checked); options.emplace_back(adapter.name, checked);
} }
auto callback = [game_settings](s32 index, const std::string& title, bool 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)); 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*>("")); game_settings ? std::nullopt : std::optional<const char*>(""));
if (MenuButtonWithValue( if (MenuButtonWithValue(
@ -4212,17 +4213,36 @@ void FullscreenUI::DrawDisplaySettingsPage()
strvalue.has_value() ? (strvalue->empty() ? FSUI_CSTR("Borderless Fullscreen") : strvalue->c_str()) : strvalue.has_value() ? (strvalue->empty() ? FSUI_CSTR("Borderless Fullscreen") : strvalue->c_str()) :
FSUI_CSTR("Use Global Setting"))) 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; ImGuiFullscreen::ChoiceDialogOptions options;
options.reserve(aml.fullscreen_modes.size() + 2); options.reserve((selected_adapter ? selected_adapter->fullscreen_modes.size() : 0) + 2);
if (game_settings) if (game_settings)
options.emplace_back(FSUI_STR("Use Global Setting"), !strvalue.has_value()); options.emplace_back(FSUI_STR("Use Global Setting"), !strvalue.has_value());
options.emplace_back(FSUI_STR("Borderless Fullscreen"), strvalue.has_value() && strvalue->empty()); 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); for (const std::string& mode : selected_adapter->fullscreen_modes)
options.emplace_back(std::move(mode), checked); {
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) { auto callback = [game_settings](s32 index, const std::string& title, bool checked) {

View file

@ -8,14 +8,7 @@
#include "settingswindow.h" #include "settingswindow.h"
#include "settingwidgetbinder.h" #include "settingwidgetbinder.h"
// For enumerating adapters. #include <algorithm>
#ifdef _WIN32
#include "util/d3d11_device.h"
#include "util/d3d12_device.h"
#endif
#ifdef ENABLE_VULKAN
#include "util/vulkan_device.h"
#endif
static QVariant GetMSAAModeValue(uint multisamples, bool ssaa) 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, SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.renderer, "GPU", "Renderer", &Settings::ParseRendererName,
&Settings::GetRendererName, Settings::DEFAULT_GPU_RENDERER); &Settings::GetRendererName, Settings::DEFAULT_GPU_RENDERER);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.resolutionScale, "GPU", "ResolutionScale", 1);
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.textureFiltering, "GPU", "TextureFilter", SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.textureFiltering, "GPU", "TextureFilter",
&Settings::ParseTextureFilterName, &Settings::GetTextureFilterName, &Settings::ParseTextureFilterName, &Settings::GetTextureFilterName,
Settings::DEFAULT_GPU_TEXTURE_FILTER); Settings::DEFAULT_GPU_TEXTURE_FILTER);
@ -89,10 +81,6 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
connect(m_ui.renderer, QOverload<int>::of(&QComboBox::currentIndexChanged), this, connect(m_ui.renderer, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&GraphicsSettingsWidget::updateRendererDependentOptions); &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, connect(m_ui.textureFiltering, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&GraphicsSettingsWidget::updateResolutionDependentOptions); &GraphicsSettingsWidget::updateResolutionDependentOptions);
connect(m_ui.displayAspectRatio, QOverload<int>::of(&QComboBox::currentIndexChanged), this, 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.trueColor, &QCheckBox::checkStateChanged, this, &GraphicsSettingsWidget::onTrueColorChanged);
connect(m_ui.pgxpEnable, &QCheckBox::checkStateChanged, this, &GraphicsSettingsWidget::updatePGXPSettingsEnabled); 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 // Advanced Tab
SettingWidgetBinder::BindWidgetToEnumSetting( SettingWidgetBinder::BindWidgetToEnumSetting(
@ -151,9 +122,6 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.forceRoundedTexcoords, "GPU", "ForceRoundTextureCoordinates", SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.forceRoundedTexcoords, "GPU", "ForceRoundTextureCoordinates",
false); false);
connect(m_ui.fullscreenMode, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&GraphicsSettingsWidget::onFullscreenModeChanged);
// PGXP Tab // PGXP Tab
SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.pgxpGeometryTolerance, "GPU", "PGXPTolerance", -1.0f); 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)))); 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++) for (u32 i = 0; i < static_cast<u32>(GPULineDetectMode::Count); i++)
{ {
m_ui.gpuLineDetectMode->addItem( m_ui.gpuLineDetectMode->addItem(
@ -694,75 +652,134 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
void GraphicsSettingsWidget::populateGPUAdaptersAndResolutions(RenderAPI render_api) void GraphicsSettingsWidget::populateGPUAdaptersAndResolutions(RenderAPI render_api)
{ {
GPUDevice::AdapterAndModeList aml; // Don't re-query, it's expensive.
switch (render_api) if (m_adapters_render_api != render_api)
{ {
#ifdef _WIN32 m_adapters_render_api = render_api;
case RenderAPI::D3D11: m_adapters = GPUDevice::GetAdapterListForAPI(render_api);
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;
} }
{ const GPUDevice::AdapterInfo* current_adapter = nullptr;
const std::string current_adapter(m_dialog->getEffectiveStringValue("GPU", "Adapter", "")); SettingsInterface* const sif = m_dialog->getSettingsInterface();
QSignalBlocker blocker(m_ui.adapter);
// 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->clear();
m_ui.adapter->addItem(tr("(Default)")); m_ui.adapter->addItem(tr("(Default)"), QVariant(QString()));
// add the other adapters const std::string current_adapter_name = m_dialog->getEffectiveStringValue("GPU", "Adapter", "");
for (const std::string& adapter_name : aml.adapter_names) for (const GPUDevice::AdapterInfo& adapter : m_adapters)
{ {
m_ui.adapter->addItem(QString::fromStdString(adapter_name)); const QString qadaptername = QString::fromStdString(adapter.name);
m_ui.adapter->addItem(qadaptername, QVariant(qadaptername));
if (adapter_name == current_adapter) if (adapter.name == current_adapter_name)
m_ui.adapter->setCurrentIndex(m_ui.adapter->count() - 1); 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 // 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", "")); m_ui.fullscreenMode->disconnect();
QSignalBlocker blocker(m_ui.fullscreenMode);
m_ui.fullscreenMode->clear(); 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)); for (const std::string& mode_name : current_adapter->fullscreen_modes)
{
if (mode_name == current_mode) const QString qmodename = QString::fromStdString(mode_name);
m_ui.fullscreenMode->setCurrentIndex(m_ui.fullscreenMode->count() - 1); m_ui.fullscreenMode->addItem(qmodename, QVariant(qmodename));
}
} }
// disable it if we don't have a choice // 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() void GraphicsSettingsWidget::updatePGXPSettingsEnabled()
@ -785,18 +802,6 @@ void GraphicsSettingsWidget::updatePGXPSettingsEnabled()
m_ui.pgxpDepthClearThresholdLabel->setEnabled(depth_enabled); 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() void GraphicsSettingsWidget::onAspectRatioChanged()
{ {
const DisplayAspectRatio ratio = const DisplayAspectRatio ratio =
@ -828,24 +833,6 @@ void GraphicsSettingsWidget::updateResolutionDependentOptions()
onTrueColorChanged(); 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() void GraphicsSettingsWidget::onTrueColorChanged()
{ {
const int resolution_scale = m_dialog->getEffectiveIntValue("GPU", "ResolutionScale", 1); 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() void GraphicsSettingsWidget::onEnableAnyTextureReplacementsChanged()
{ {
const bool any_replacements_enabled = const bool any_replacements_enabled =

View file

@ -7,7 +7,8 @@
#include "ui_graphicssettingswidget.h" #include "ui_graphicssettingswidget.h"
enum class RenderAPI : u32; #include "util/gpu_device.h"
enum class GPURenderer : u8; enum class GPURenderer : u8;
class SettingsWindow; class SettingsWindow;
@ -27,13 +28,10 @@ private Q_SLOTS:
void updateRendererDependentOptions(); void updateRendererDependentOptions();
void updatePGXPSettingsEnabled(); void updatePGXPSettingsEnabled();
void onAdapterChanged();
void onAspectRatioChanged(); void onAspectRatioChanged();
void updateResolutionDependentOptions(); void updateResolutionDependentOptions();
void onMSAAModeChanged();
void onTrueColorChanged(); void onTrueColorChanged();
void onDownsampleModeChanged(); void onDownsampleModeChanged();
void onFullscreenModeChanged();
void onEnableAnyTextureReplacementsChanged(); void onEnableAnyTextureReplacementsChanged();
void onEnableVRAMWriteDumpingChanged(); void onEnableVRAMWriteDumpingChanged();
@ -57,4 +55,7 @@ private:
Ui::GraphicsSettingsWidget m_ui; Ui::GraphicsSettingsWidget m_ui;
SettingsWindow* m_dialog; SettingsWindow* m_dialog;
GPUDevice::AdapterInfoList m_adapters;
RenderAPI m_adapters_render_api = RenderAPI::None;
}; };

View file

@ -94,93 +94,7 @@
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item row="0" column="1">
<widget class="QComboBox" name="resolutionScale"> <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>
</item> </item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QLabel" name="gpuDownsampleLabel"> <widget class="QLabel" name="gpuDownsampleLabel">

View file

@ -685,39 +685,6 @@ void D3D11Device::SubmitPresent()
Panic("Not supported by this API."); 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() bool D3D11Device::CreateTimestampQueries()
{ {
for (u32 i = 0; i < NUM_TIMESTAMP_QUERIES; i++) for (u32 i = 0; i < NUM_TIMESTAMP_QUERIES; i++)

View file

@ -43,7 +43,6 @@ public:
bool UpdateWindow() override; bool UpdateWindow() override;
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override; void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
bool SupportsExclusiveFullscreen() const override; bool SupportsExclusiveFullscreen() const override;
AdapterAndModeList GetAdapterAndModeList() override;
void DestroySurface() override; void DestroySurface() override;
std::string GetDriverInfo() const override; std::string GetDriverInfo() const override;
@ -110,8 +109,6 @@ public:
void UnbindPipeline(D3D11Pipeline* pl); void UnbindPipeline(D3D11Pipeline* pl);
void UnbindTexture(D3D11Texture* tex); void UnbindTexture(D3D11Texture* tex);
static AdapterAndModeList StaticGetAdapterAndModeList();
protected: protected:
bool CreateDevice(std::string_view adapter, bool threaded_presentation, bool CreateDevice(std::string_view adapter, bool threaded_presentation,
std::optional<bool> exclusive_fullscreen_control, FeatureMask disabled_features, std::optional<bool> exclusive_fullscreen_control, FeatureMask disabled_features,
@ -133,8 +130,6 @@ private:
static constexpr u32 UNIFORM_BUFFER_ALIGNMENT_DISCARD = 16; static constexpr u32 UNIFORM_BUFFER_ALIGNMENT_DISCARD = 16;
static constexpr u8 NUM_TIMESTAMP_QUERIES = 3; static constexpr u8 NUM_TIMESTAMP_QUERIES = 3;
static void GetAdapterAndModeList(AdapterAndModeList* ret, IDXGIFactory5* factory);
void SetFeatures(FeatureMask disabled_features); void SetFeatures(FeatureMask disabled_features);
u32 GetSwapChainBufferCount() const; 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 RenderAPI D3D12Device::GetRenderAPI() const
{ {
return RenderAPI::D3D12; return RenderAPI::D3D12;

View file

@ -64,8 +64,6 @@ public:
bool UpdateWindow() override; bool UpdateWindow() override;
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) 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; void DestroySurface() override;
std::string GetDriverInfo() const override; std::string GetDriverInfo() const override;
@ -220,8 +218,6 @@ private:
using SamplerMap = std::unordered_map<u64, D3D12DescriptorHandle>; using SamplerMap = std::unordered_map<u64, D3D12DescriptorHandle>;
static void GetAdapterAndModeList(AdapterAndModeList* ret, IDXGIFactory5* factory);
void SetFeatures(FeatureMask disabled_features); void SetFeatures(FeatureMask disabled_features);
u32 GetSwapChainBufferCount() const; u32 GetSwapChainBufferCount() const;

View file

@ -92,10 +92,10 @@ Microsoft::WRL::ComPtr<IDXGIFactory5> D3DCommon::CreateFactory(bool debug, Error
return factory; 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(), 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); 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); adapter_name = fmt::format("{} ({})", original_adapter_name.c_str(), current_extra);
current_extra++; current_extra++;
} while (std::any_of(adapter_names.begin(), adapter_names.end(), } while (
[&adapter_name](const std::string& other) { return (adapter_name == other); })); std::any_of(adapter_names.begin(), adapter_names.end(),
[&adapter_name](const GPUDevice::AdapterInfo& other) { return (adapter_name == other.name); }));
} }
return adapter_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; Microsoft::WRL::ComPtr<IDXGIAdapter1> adapter;
for (u32 index = 0;; index++) 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) if (hr == DXGI_ERROR_NOT_FOUND)
break; break;
@ -128,50 +133,51 @@ std::vector<std::string> D3DCommon::GetAdapterNames(IDXGIFactory5* factory)
continue; 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; return adapters;
}
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;
} }
bool D3DCommon::GetRequestedExclusiveFullscreenModeDesc(IDXGIFactory5* factory, const RECT& window_rect, u32 width, 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. // 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... :) // We might have two GPUs with the same name... :)
std::vector<std::string> adapter_names; GPUDevice::AdapterInfoList adapters;
Microsoft::WRL::ComPtr<IDXGIAdapter1> adapter; Microsoft::WRL::ComPtr<IDXGIAdapter1> adapter;
for (u32 index = 0;; index++) for (u32 index = 0;; index++)
@ -271,14 +277,16 @@ Microsoft::WRL::ComPtr<IDXGIAdapter1> D3DCommon::GetAdapterByName(IDXGIFactory5*
continue; continue;
} }
std::string adapter_name = FixupDuplicateAdapterNames(adapter_names, GetAdapterName(adapter.Get())); std::string adapter_name = FixupDuplicateAdapterNames(adapters, GetAdapterName(adapter.Get()));
if (adapter_name == name) if (adapter_name == name)
{ {
VERBOSE_LOG("Found adapter '{}'", adapter_name); VERBOSE_LOG("Found adapter '{}'", adapter_name);
return adapter; 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); 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); Microsoft::WRL::ComPtr<IDXGIFactory5> CreateFactory(bool debug, Error* error);
// returns a list of all adapter names // returns a list of all adapter names
std::vector<std::string> GetAdapterNames(IDXGIFactory5* factory); GPUDevice::AdapterInfoList GetAdapterInfoList();
// returns a list of fullscreen modes for the specified adapter
std::vector<std::string> GetFullscreenModes(IDXGIFactory5* factory, std::string_view adapter_name);
// returns the fullscreen mode to use for the specified dimensions // returns the fullscreen mode to use for the specified dimensions
bool GetRequestedExclusiveFullscreenModeDesc(IDXGIFactory5* factory, const RECT& window_rect, u32 width, u32 height, 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))); (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 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, bool debug_device, GPUVSyncMode vsync, bool allow_present_throttle, bool threaded_presentation,
std::optional<bool> exclusive_fullscreen_control, FeatureMask disabled_features, Error* error) std::optional<bool> exclusive_fullscreen_control, FeatureMask disabled_features, Error* error)

View file

@ -509,11 +509,15 @@ public:
u32 num_uploads; u32 num_uploads;
}; };
struct AdapterAndModeList struct AdapterInfo
{ {
std::vector<std::string> adapter_names; std::string name;
std::vector<std::string> fullscreen_modes; std::vector<std::string> fullscreen_modes;
u32 max_texture_size;
u32 max_multisamples;
bool supports_sample_shading;
}; };
using AdapterInfoList = std::vector<AdapterInfo>;
struct PooledTextureDeleter struct PooledTextureDeleter
{ {
@ -543,6 +547,9 @@ public:
/// Returns true if the render API is the same (e.g. GLES and GL). /// Returns true if the render API is the same (e.g. GLES and GL).
static bool IsSameRenderAPI(RenderAPI lhs, RenderAPI rhs); 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) /// Parses a fullscreen mode into its components (width * height @ refresh hz)
static bool GetRequestedExclusiveFullscreenMode(u32* width, u32* height, float* refresh_rate); static bool GetRequestedExclusiveFullscreenMode(u32* width, u32* height, float* refresh_rate);
@ -572,12 +579,6 @@ public:
return counts[static_cast<u8>(layout)]; 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 const Features& GetFeatures() const { return m_features; }
ALWAYS_INLINE u32 GetMaxTextureSize() const { return m_max_texture_size; } ALWAYS_INLINE u32 GetMaxTextureSize() const { return m_max_texture_size; }
ALWAYS_INLINE u32 GetMaxMultisamples() const { return m_max_multisamples; } ALWAYS_INLINE u32 GetMaxMultisamples() const { return m_max_multisamples; }
@ -605,7 +606,6 @@ public:
virtual bool UpdateWindow() = 0; virtual bool UpdateWindow() = 0;
virtual bool SupportsExclusiveFullscreen() const; virtual bool SupportsExclusiveFullscreen() const;
virtual AdapterAndModeList GetAdapterAndModeList() = 0;
/// Call when the window size changes externally to recreate any resources. /// 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; 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>; 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 OpenShaderCache(std::string_view base_path, u32 version);
void CloseShaderCache(); void CloseShaderCache();
bool CreateResources(Error* error); bool CreateResources(Error* error);

View file

@ -204,8 +204,6 @@ public:
bool UpdateWindow() override; bool UpdateWindow() override;
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override; void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
AdapterAndModeList GetAdapterAndModeList() override;
void DestroySurface() override; void DestroySurface() override;
std::string GetDriverInfo() const override; std::string GetDriverInfo() const override;
@ -286,8 +284,6 @@ public:
static void DeferRelease(id obj); static void DeferRelease(id obj);
static void DeferRelease(u64 fence_counter, id obj); static void DeferRelease(u64 fence_counter, id obj);
static AdapterAndModeList StaticGetAdapterAndModeList();
protected: protected:
bool CreateDevice(std::string_view adapter, bool threaded_presentation, bool CreateDevice(std::string_view adapter, bool threaded_presentation,
std::optional<bool> exclusive_fullscreen_control, FeatureMask disabled_features, 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; 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> template<typename F>
static void RunOnMainThread(F&& 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) void MetalDevice::SetFeatures(FeatureMask disabled_features)
{ {
// https://gist.github.com/kylehowells/63d0723abc9588eb734cade4b7df660d m_max_texture_size = GetMetalMaxTextureSize(m_device);
if ([m_device supportsFamily:MTLGPUFamilyMacCatalyst1] || [m_device supportsFamily:MTLGPUFamilyMac1] || m_max_multisamples = GetMetalMaxMultisamples(m_device);
[m_device supportsFamily:MTLGPUFamilyApple3])
{
m_max_texture_size = 16384;
}
else
{
m_max_texture_size = 8192;
}
// Framebuffer fetch requires MSL 2.3 and an Apple GPU family. // Framebuffer fetch requires MSL 2.3 and an Apple GPU family.
const bool supports_fbfetch = [m_device supportsFamily:MTLGPUFamilyApple1]; const bool supports_fbfetch = [m_device supportsFamily:MTLGPUFamilyApple1];
@ -222,14 +240,6 @@ void MetalDevice::SetFeatures(FeatureMask disabled_features)
const bool supports_barriers = const bool supports_barriers =
([m_device supportsFamily:MTLGPUFamilyMac1] && ![m_device supportsFamily:MTLGPUFamilyApple3]); ([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.dual_source_blend = !(disabled_features & FEATURE_MASK_DUAL_SOURCE_BLEND);
m_features.framebuffer_fetch = !(disabled_features & FEATURE_MASK_FRAMEBUFFER_FETCH) && supports_fbfetch; m_features.framebuffer_fetch = !(disabled_features & FEATURE_MASK_FRAMEBUFFER_FETCH) && supports_fbfetch;
m_features.per_sample_shading = true; m_features.per_sample_shading = true;
@ -538,26 +548,6 @@ bool MetalDevice::IsRenderTargetBound(const GPUTexture* tex) const
return false; 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) bool MetalDevice::SetGPUTimingEnabled(bool enabled)
{ {
if (m_gpu_timing_enabled == enabled) if (m_gpu_timing_enabled == enabled)
@ -2505,7 +2495,24 @@ std::unique_ptr<GPUDevice> GPUDevice::WrapNewMetalDevice()
return std::unique_ptr<GPUDevice>(new MetalDevice()); 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; OpenGLContext::~OpenGLContext() = default;
std::vector<OpenGLContext::FullscreenModeInfo> OpenGLContext::EnumerateFullscreenModes()
{
return {};
}
std::unique_ptr<OpenGLContext> OpenGLContext::Create(const WindowInfo& wi, Error* error) std::unique_ptr<OpenGLContext> OpenGLContext::Create(const WindowInfo& wi, Error* error)
{ {
static constexpr std::array<Version, 14> vlist = {{{Profile::Core, 4, 6}, static constexpr std::array<Version, 14> vlist = {{{Profile::Core, 4, 6},

View file

@ -9,7 +9,6 @@
#include <memory> #include <memory>
#include <span> #include <span>
#include <vector>
class Error; class Error;
@ -33,13 +32,6 @@ public:
int minor_version; int minor_version;
}; };
struct FullscreenModeInfo
{
u32 width;
u32 height;
float refresh_rate;
};
ALWAYS_INLINE const WindowInfo& GetWindowInfo() const { return m_wi; } ALWAYS_INLINE const WindowInfo& GetWindowInfo() const { return m_wi; }
ALWAYS_INLINE bool IsGLES() const { return (m_version.profile == Profile::ES); } ALWAYS_INLINE bool IsGLES() const { return (m_version.profile == Profile::ES); }
ALWAYS_INLINE u32 GetSurfaceWidth() const { return m_wi.surface_width; } ALWAYS_INLINE u32 GetSurfaceWidth() const { return m_wi.surface_width; }
@ -57,8 +49,6 @@ public:
virtual bool SetSwapInterval(s32 interval) = 0; virtual bool SetSwapInterval(s32 interval) = 0;
virtual std::unique_ptr<OpenGLContext> CreateSharedContext(const WindowInfo& wi, Error* error) = 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); static std::unique_ptr<OpenGLContext> Create(const WindowInfo& wi, Error* error);
protected: protected:

View file

@ -663,21 +663,6 @@ void OpenGLDevice::DestroyFramebuffer(GLuint fbo)
glDeleteFramebuffers(1, &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() void OpenGLDevice::DestroySurface()
{ {
if (!m_gl_context) if (!m_gl_context)

View file

@ -48,8 +48,6 @@ public:
std::string GetDriverInfo() const override; std::string GetDriverInfo() const override;
AdapterAndModeList GetAdapterAndModeList() override;
std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
GPUTexture::Type type, GPUTexture::Format format, GPUTexture::Type type, GPUTexture::Format format,
const void* data = nullptr, u32 data_stride = 0) override; const void* data = nullptr, u32 data_stride = 0) override;

View file

@ -315,28 +315,76 @@ VulkanDevice::GPUList VulkanDevice::EnumerateGPUs(VkInstance instance)
VkPhysicalDeviceProperties props = {}; VkPhysicalDeviceProperties props = {};
vkGetPhysicalDeviceProperties(device, &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 // 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; u32 current_extra = 2;
do do
{ {
gpu_name = fmt::format("{} ({})", original_adapter_name, current_extra); ai.name = fmt::format("{} ({})", original_adapter_name, current_extra);
current_extra++; current_extra++;
} while ( } 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; 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) bool VulkanDevice::SelectDeviceExtensions(ExtensionList* extension_list, bool enable_surface)
{ {
u32 extension_count = 0; u32 extension_count = 0;
@ -1853,67 +1901,21 @@ void VulkanDevice::DestroyFramebuffer(VkFramebuffer fbo)
VulkanDevice::GetInstance().DeferFramebufferDestruction(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() bool VulkanDevice::IsSuitableDefaultRenderer()
{ {
#ifdef __ANDROID__ #ifdef __ANDROID__
// No way in hell. // No way in hell.
return false; return false;
#else #else
AdapterAndModeList aml = StaticGetAdapterAndModeList(); GPUList gpus = EnumerateGPUs();
if (aml.adapter_names.empty()) if (gpus.empty())
{ {
// No adapters, not gonna be able to use VK. // No adapters, not gonna be able to use VK.
return false; return false;
} }
// Check the first GPU, should be enough. // 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); INFO_LOG("Using Vulkan GPU '{}' for automatic renderer check.", name);
// Any software rendering (LLVMpipe, SwiftShader). // Any software rendering (LLVMpipe, SwiftShader).
@ -2000,8 +2002,8 @@ bool VulkanDevice::CreateDevice(std::string_view adapter, bool threaded_presenta
u32 gpu_index = 0; u32 gpu_index = 0;
for (; gpu_index < static_cast<u32>(gpus.size()); gpu_index++) for (; gpu_index < static_cast<u32>(gpus.size()); gpu_index++)
{ {
INFO_LOG("GPU {}: {}", gpu_index, gpus[gpu_index].second); INFO_LOG("GPU {}: {}", gpu_index, gpus[gpu_index].second.name);
if (gpus[gpu_index].second == adapter) if (gpus[gpu_index].second.name == adapter)
{ {
m_physical_device = gpus[gpu_index].first; m_physical_device = gpus[gpu_index].first;
break; break;
@ -2010,13 +2012,13 @@ bool VulkanDevice::CreateDevice(std::string_view adapter, bool threaded_presenta
if (gpu_index == static_cast<u32>(gpus.size())) 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; m_physical_device = gpus[0].first;
} }
} }
else 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; m_physical_device = gpus[0].first;
} }
@ -2542,35 +2544,40 @@ void VulkanDevice::InsertDebugMessage(const char* msg)
#endif #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 = {}; 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, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 0,
&color_properties); &color_properties);
VkImageFormatProperties depth_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, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, 0,
&depth_properties); &depth_properties);
const VkSampleCountFlags combined_properties = m_device_properties.limits.framebufferColorSampleCounts & const VkSampleCountFlags combined_properties = properties.limits.framebufferColorSampleCounts &
m_device_properties.limits.framebufferDepthSampleCounts & properties.limits.framebufferDepthSampleCounts &
color_properties.sampleCounts & depth_properties.sampleCounts; color_properties.sampleCounts & depth_properties.sampleCounts;
if (combined_properties & VK_SAMPLE_COUNT_64_BIT) if (combined_properties & VK_SAMPLE_COUNT_64_BIT)
m_max_multisamples = 64; return 64;
else if (combined_properties & VK_SAMPLE_COUNT_32_BIT) else if (combined_properties & VK_SAMPLE_COUNT_32_BIT)
m_max_multisamples = 32; return 32;
else if (combined_properties & VK_SAMPLE_COUNT_16_BIT) else if (combined_properties & VK_SAMPLE_COUNT_16_BIT)
m_max_multisamples = 16; return 16;
else if (combined_properties & VK_SAMPLE_COUNT_8_BIT) else if (combined_properties & VK_SAMPLE_COUNT_8_BIT)
m_max_multisamples = 8; return 8;
else if (combined_properties & VK_SAMPLE_COUNT_4_BIT) else if (combined_properties & VK_SAMPLE_COUNT_4_BIT)
m_max_multisamples = 4; return 4;
else if (combined_properties & VK_SAMPLE_COUNT_2_BIT) else if (combined_properties & VK_SAMPLE_COUNT_2_BIT)
m_max_multisamples = 2; return 2;
else 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 = m_features.dual_source_blend =
!(disabled_features & FEATURE_MASK_DUAL_SOURCE_BLEND) && m_device_features.dualSrcBlend; !(disabled_features & FEATURE_MASK_DUAL_SOURCE_BLEND) && m_device_features.dualSrcBlend;

View file

@ -66,15 +66,18 @@ public:
VulkanDevice(); VulkanDevice();
~VulkanDevice() override; ~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; RenderAPI GetRenderAPI() const override;
bool HasSurface() const override; bool HasSurface() const override;
bool UpdateWindow() override; bool UpdateWindow() override;
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) 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; void DestroySurface() override;
std::string GetDriverInfo() const override; std::string GetDriverInfo() const override;
@ -287,16 +290,10 @@ private:
using CleanupObjectFunction = void (*)(VulkanDevice& dev, void* obj); using CleanupObjectFunction = void (*)(VulkanDevice& dev, void* obj);
using SamplerMap = std::unordered_map<u64, VkSampler>; using SamplerMap = std::unordered_map<u64, VkSampler>;
static void GetAdapterAndModeList(AdapterAndModeList* ret, VkInstance instance);
// Helper method to create a Vulkan instance. // Helper method to create a Vulkan instance.
static VkInstance CreateVulkanInstance(const WindowInfo& wi, OptionalExtensions* oe, bool enable_debug_utils, static VkInstance CreateVulkanInstance(const WindowInfo& wi, OptionalExtensions* oe, bool enable_debug_utils,
bool enable_validation_layer); 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); bool ValidatePipelineCacheHeader(const VK_PIPELINE_CACHE_HEADER& header);
void FillPipelineCacheHeader(VK_PIPELINE_CACHE_HEADER* header); void FillPipelineCacheHeader(VK_PIPELINE_CACHE_HEADER* header);
@ -331,6 +328,8 @@ private:
bool CheckFeatures(FeatureMask disabled_features); bool CheckFeatures(FeatureMask disabled_features);
static u32 GetMaxMultisamples(VkPhysicalDevice physical_device, const VkPhysicalDeviceProperties& properties);
bool CreateAllocator(); bool CreateAllocator();
void DestroyAllocator(); void DestroyAllocator();
bool CreateCommandBuffers(); bool CreateCommandBuffers();