GS: Uncap upscale multiplier subject to GPU limits

This commit is contained in:
Stenzek 2024-07-01 00:21:03 +10:00 committed by Connor McLaughlin
parent 46e30467de
commit 315d30fe4c
18 changed files with 316 additions and 200 deletions

View File

@ -140,13 +140,6 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
//////////////////////////////////////////////////////////////////////////
// HW Settings
//////////////////////////////////////////////////////////////////////////
static const char* upscale_entries[] = {"Native (PS2) (Default)", "1.25x Native", "1.5x Native", "1.75x Native", "2x Native (~720p)",
"2.25x Native", "2.5x Native", "2.75x Native", "3x Native (~1080p)", "3.5x Native", "4x Native (~1440p/2K)", "5x Native (~1620p)",
"6x Native (~2160p/4K)", "7x Native (~2520p)", "8x Native (~2880p/5K)", nullptr};
static const char* upscale_values[] = {
"1", "1.25", "1.5", "1.75", "2", "2.25", "2.5", "2.75", "3", "3.5", "4", "5", "6", "7", "8", nullptr};
SettingWidgetBinder::BindWidgetToEnumSetting(
sif, m_ui.upscaleMultiplier, "EmuCore/GS", "upscale_multiplier", upscale_entries, upscale_values, "1.0");
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.textureFiltering, "EmuCore/GS", "filter", static_cast<int>(BiFiltering::PS2));
SettingWidgetBinder::BindWidgetToIntSetting(
sif, m_ui.trilinearFiltering, "EmuCore/GS", "TriFilter", static_cast<int>(TriFiltering::Automatic), -1);
@ -158,6 +151,8 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
sif, m_ui.blending, "EmuCore/GS", "accurate_blending_unit", static_cast<int>(AccBlendLevel::Basic));
SettingWidgetBinder::BindWidgetToIntSetting(
sif, m_ui.texturePreloading, "EmuCore/GS", "texture_preloading", static_cast<int>(TexturePreloadingLevel::Off));
connect(m_ui.upscaleMultiplier, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&GraphicsSettingsWidget::onUpscaleMultiplierChanged);
connect(m_ui.trilinearFiltering, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&GraphicsSettingsWidget::onTrilinearFilteringChanged);
onTrilinearFilteringChanged();
@ -1036,10 +1031,9 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
m_ui.exclusiveFullscreenControl->setEnabled(is_auto || is_vk);
// populate adapters
std::vector<std::string> adapters;
std::vector<std::string> fullscreen_modes;
GSGetAdaptersAndFullscreenModes(type, &adapters, &fullscreen_modes);
std::vector<GSAdapterInfo> adapters = GSGetAdapterInfo(type);
const GSAdapterInfo* current_adapter_info = nullptr;
// fill+select adapters
{
QSignalBlocker sb(m_ui.adapterDropdown);
@ -1062,12 +1056,17 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
}
}
for (const std::string& adapter : adapters)
for (const GSAdapterInfo& adapter : adapters)
{
m_ui.adapterDropdown->addItem(QString::fromStdString(adapter));
if (current_adapter == adapter)
m_ui.adapterDropdown->addItem(QString::fromStdString(adapter.name));
if (current_adapter == adapter.name)
{
m_ui.adapterDropdown->setCurrentIndex(m_ui.adapterDropdown->count() - 1);
current_adapter_info = &adapter;
}
}
current_adapter_info = (current_adapter_info || adapters.empty()) ? current_adapter_info : &adapters.front();
}
// fill+select fullscreen modes
@ -1090,13 +1089,90 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
}
}
for (const std::string& fs_mode : fullscreen_modes)
if (current_adapter_info)
{
m_ui.fullscreenModes->addItem(QString::fromStdString(fs_mode));
if (current_mode == fs_mode)
m_ui.fullscreenModes->setCurrentIndex(m_ui.fullscreenModes->count() - 1);
for (const std::string& fs_mode : current_adapter_info->fullscreen_modes)
{
m_ui.fullscreenModes->addItem(QString::fromStdString(fs_mode));
if (current_mode == fs_mode)
m_ui.fullscreenModes->setCurrentIndex(m_ui.fullscreenModes->count() - 1);
}
}
}
// assume the GPU can do 10K textures.
const u32 max_upscale_multiplier = std::max(current_adapter_info ? current_adapter_info->max_upscale_multiplier : 0u, 10u);
populateUpscaleMultipliers(max_upscale_multiplier);
}
void GraphicsSettingsWidget::populateUpscaleMultipliers(u32 max_upscale_multiplier)
{
static constexpr std::pair<const char*, float> templates[] = {
{QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "Native (PS2) (Default)"), 1.0f},
{QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "1.25x Native"), 1.25f},
{QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "1.5x Native"), 1.5f},
{QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "1.75x Native"), 1.75f},
{QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "2x Native (~720p)"), 2.0f},
{QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "2.25x Native"), 2.25f},
{QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "2.5x Native"), 2.5f},
{QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "2.75x Native"), 2.75f},
{QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "3x Native (~1080p)"), 3.0f},
{QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "3.5x Native"), 3.5f},
{QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "4x Native (~1440p/2K)"), 4.0f},
{QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "5x Native (~1620p)"), 5.0f},
{QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "6x Native (~2160p/4K)"), 6.0f},
{QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "7x Native (~2520p)"), 7.0f},
{QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "8x Native (~2880p/5K)"), 8.0f},
};
static constexpr u32 max_template_multiplier = 8;
// Limit the dropdown to 12x if we're not showing advanced settings. Save the noobs.
static constexpr u32 max_non_advanced_multiplier = 12;
QSignalBlocker sb(m_ui.upscaleMultiplier);
m_ui.upscaleMultiplier->clear();
for (const auto& [name, value] : templates)
{
if (value > max_upscale_multiplier)
continue;
m_ui.upscaleMultiplier->addItem(tr(name), QVariant(value));
}
const u32 max_shown_multiplier = QtHost::ShouldShowAdvancedSettings() ?
max_upscale_multiplier :
std::min(max_upscale_multiplier, max_non_advanced_multiplier);
for (u32 i = max_template_multiplier + 1; i <= max_shown_multiplier; i++)
m_ui.upscaleMultiplier->addItem(tr("%1x Native ").arg(i), QVariant(static_cast<float>(i)));
const float global_value = Host::GetBaseFloatSettingValue("EmuCore/GS", "upscale_multiplier", 1.0f);
if (m_dialog->isPerGameSettings())
{
m_ui.upscaleMultiplier->addItem(tr("Use Global Setting [%1]").arg(QStringLiteral("%1x").arg(global_value)));
const std::optional<float> config_value = m_dialog->getFloatValue("EmuCore/GS", "upscale_multiplier", std::nullopt);
if (config_value.has_value())
{
if (int index = m_ui.upscaleMultiplier->findData(QVariant(config_value.value())); index > 0)
m_ui.upscaleMultiplier->setCurrentIndex(index);
}
else
{
m_ui.upscaleMultiplier->setCurrentIndex(0);
}
}
else
{
if (int index = m_ui.upscaleMultiplier->findData(QVariant(global_value)); index > 0)
m_ui.upscaleMultiplier->setCurrentIndex(index);
}
}
void GraphicsSettingsWidget::onUpscaleMultiplierChanged()
{
const QVariant data = m_ui.upscaleMultiplier->currentData();
m_dialog->setFloatSettingValue("EmuCore/GS", "upscale_multiplier",
data.isValid() ? std::optional<float>(data.toFloat()) : std::optional<float>());
}
void GraphicsSettingsWidget::resetManualHardwareFixes()

View File

@ -29,6 +29,7 @@ private Q_SLOTS:
void onSWTextureFilteringChange();
void onRendererChanged(int index);
void onAdapterChanged(int index);
void onUpscaleMultiplierChanged();
void onTrilinearFilteringChanged();
void onGpuPaletteConversionChanged(int state);
void onCPUSpriteRenderBWChanged();
@ -46,6 +47,7 @@ private Q_SLOTS:
private:
GSRendererType getEffectiveRenderer() const;
void updateRendererDependentOptions();
void populateUpscaleMultipliers(u32 max_upscale_multiplier);
void resetManualHardwareFixes();
SettingsWindow* m_dialog;

View File

@ -48,6 +48,8 @@
#include "common/SmallString.h"
#include "common/StringUtil.h"
#include "IconsFontAwesome5.h"
#include "fmt/format.h"
#include <fstream>
@ -171,6 +173,24 @@ static void CloseGSDevice(bool clear_state)
g_gs_device.reset();
}
static void GSClampUpscaleMultiplier(Pcsx2Config::GSOptions& config)
{
const u32 max_upscale_multiplier = GSGetMaxUpscaleMultiplier(g_gs_device->GetMaxTextureSize());
if (config.UpscaleMultiplier <= static_cast<float>(max_upscale_multiplier))
{
// Shouldn't happen, but just in case.
if (config.UpscaleMultiplier < 1.0f)
config.UpscaleMultiplier = 1.0f;
return;
}
Host::AddIconOSDMessage("GSUpscaleMultiplierInvalid", ICON_FA_EXCLAMATION_TRIANGLE,
fmt::format(TRANSLATE_FS("GS", "Configured upscale multiplier {}x is above your GPU's supported multiplier of {}x."),
config.UpscaleMultiplier, max_upscale_multiplier),
Host::OSD_WARNING_DURATION);
config.UpscaleMultiplier = static_cast<float>(max_upscale_multiplier);
}
static bool OpenGSRenderer(GSRendererType renderer, u8* basemem)
{
// Must be done first, initialization routines in GSState use GSIsHardwareRenderer().
@ -184,6 +204,7 @@ static bool OpenGSRenderer(GSRendererType renderer, u8* basemem)
}
else if (renderer != GSRendererType::SW)
{
GSClampUpscaleMultiplier(GSConfig);
g_gs_renderer = std::make_unique<GSRendererHW>();
}
else
@ -548,9 +569,9 @@ std::optional<float> GSGetHostRefreshRate()
return surface_refresh_rate;
}
void GSGetAdaptersAndFullscreenModes(
GSRendererType renderer, std::vector<std::string>* adapters, std::vector<std::string>* fullscreen_modes)
std::vector<GSAdapterInfo> GSGetAdapterInfo(GSRendererType renderer)
{
std::vector<GSAdapterInfo> ret;
switch (renderer)
{
#ifdef _WIN32
@ -559,12 +580,7 @@ void GSGetAdaptersAndFullscreenModes(
{
auto factory = D3D::CreateFactory(false);
if (factory)
{
if (adapters)
*adapters = D3D::GetAdapterNames(factory.get());
if (fullscreen_modes)
*fullscreen_modes = D3D::GetFullscreenModes(factory.get(), EmuConfig.GS.Adapter);
}
ret = D3D::GetAdapterInfo(factory.get());
}
break;
#endif
@ -572,7 +588,7 @@ void GSGetAdaptersAndFullscreenModes(
#ifdef ENABLE_VULKAN
case GSRendererType::VK:
{
GSDeviceVK::GetAdaptersAndFullscreenModes(adapters, fullscreen_modes);
ret = GSDeviceVK::GetAdapterInfo();
}
break;
#endif
@ -580,8 +596,7 @@ void GSGetAdaptersAndFullscreenModes(
#ifdef __APPLE__
case GSRendererType::Metal:
{
if (adapters)
*adapters = GetMetalAdapterList();
ret = GetMetalAdapterList();
}
break;
#endif
@ -589,6 +604,14 @@ void GSGetAdaptersAndFullscreenModes(
default:
break;
}
return ret;
}
u32 GSGetMaxUpscaleMultiplier(u32 max_texture_size)
{
// Maximum GS target size is 1280x1280. Assume we want to upscale the max size target.
return std::max(max_texture_size / 1280, 1u);
}
GSVideoMode GSgetDisplayMode()
@ -712,6 +735,9 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
return;
}
// Ensure upscale multiplier is in range.
GSClampUpscaleMultiplier(GSConfig);
// Options which aren't using the global struct yet, so we need to recreate all GS objects.
if (GSConfig.SWExtraThreads != old_config.SWExtraThreads ||
GSConfig.SWExtraThreadsHeight != old_config.SWExtraThreadsHeight)

View File

@ -41,6 +41,14 @@ enum class GSDisplayAlignment
RightOrBottom
};
struct GSAdapterInfo
{
std::string name;
std::vector<std::string> fullscreen_modes;
u32 max_texture_size;
u32 max_upscale_multiplier;
};
class SmallStringBase;
// Returns the ID for the specified function, otherwise -1.
@ -83,8 +91,8 @@ GSRendererType GSGetCurrentRenderer();
bool GSIsHardwareRenderer();
bool GSWantsExclusiveFullscreen();
std::optional<float> GSGetHostRefreshRate();
void GSGetAdaptersAndFullscreenModes(
GSRendererType renderer, std::vector<std::string>* adapters, std::vector<std::string>* fullscreen_modes);
std::vector<GSAdapterInfo> GSGetAdapterInfo(GSRendererType renderer);
u32 GSGetMaxUpscaleMultiplier(u32 max_texture_size);
GSVideoMode GSgetDisplayMode();
void GSgetInternalResolution(int* width, int* height);
void GSgetStats(SmallStringBase& info);

View File

@ -435,7 +435,8 @@ void GSDevice::TextureRecycleDeleter::operator()(GSTexture* const tex)
GSTexture* GSDevice::FetchSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format, bool clear, bool prefer_unused_texture)
{
const GSVector2i size(width, height);
const GSVector2i size(std::clamp(width, 1, static_cast<int>(g_gs_device->GetMaxTextureSize())),
std::clamp(height, 1, static_cast<int>(g_gs_device->GetMaxTextureSize())));
FastList<GSTexture*>& pool = m_pool[type != GSTexture::Type::Texture];
GSTexture* t = nullptr;
@ -475,14 +476,14 @@ GSTexture* GSDevice::FetchSurface(GSTexture::Type type, int width, int height, i
}
else
{
t = CreateSurface(type, width, height, levels, format);
t = CreateSurface(type, size.x, size.y, levels, format);
if (!t)
{
Console.Error("GS: Memory allocation failure for %dx%d texture. Purging pool and retrying.", width, height);
ERROR_LOG("GS: Memory allocation failure for {}x{} texture. Purging pool and retrying.", size.x, size.y);
PurgePool();
if (!t)
{
Console.Error("GS: Memory allocation failure for %dx%d texture after purging pool.", width, height);
ERROR_LOG("GS: Memory allocation failure for {}x{} texture after purging pool.", size.x, size.y);
return nullptr;
}
}

View File

@ -804,6 +804,7 @@ public:
protected:
FeatureSupport m_features;
u32 m_max_texture_size = 0;
struct
{
@ -888,6 +889,7 @@ public:
__fi u64 GetPoolMemoryUsage() const { return m_pool_memory_usage; }
__fi FeatureSupport Features() const { return m_features; }
__fi u32 GetMaxTextureSize() const { return m_max_texture_size; }
__fi const WindowInfo& GetWindowInfo() const { return m_window_info; }
__fi s32 GetWindowWidth() const { return static_cast<s32>(m_window_info.surface_width); }

View File

@ -42,10 +42,10 @@ wil::com_ptr_nothrow<IDXGIFactory5> D3D::CreateFactory(bool debug)
return factory;
}
static std::string FixupDuplicateAdapterNames(const std::vector<std::string>& adapter_names, std::string adapter_name)
static std::string FixupDuplicateAdapterNames(const std::vector<GSAdapterInfo>& adapters, std::string adapter_name)
{
if (std::any_of(adapter_names.begin(), adapter_names.end(),
[&adapter_name](const std::string& other) { return (adapter_name == other); }))
if (std::any_of(adapters.begin(), adapters.end(),
[&adapter_name](const GSAdapterInfo& other) { return (adapter_name == other.name); }))
{
std::string original_adapter_name = std::move(adapter_name);
@ -54,73 +54,72 @@ 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(adapters.begin(), adapters.end(),
[&adapter_name](const GSAdapterInfo& other) { return (adapter_name == other.name); }));
}
return adapter_name;
}
std::vector<std::string> D3D::GetAdapterNames(IDXGIFactory5* factory)
std::vector<GSAdapterInfo> D3D::GetAdapterInfo(IDXGIFactory5* factory)
{
std::vector<std::string> adapter_names;
std::vector<GSAdapterInfo> adapters;
wil::com_ptr_nothrow<IDXGIAdapter1> adapter;
for (u32 index = 0;; index++)
{
const HRESULT hr = factory->EnumAdapters1(index, adapter.put());
HRESULT hr = factory->EnumAdapters1(index, adapter.put());
if (hr == DXGI_ERROR_NOT_FOUND)
break;
if (FAILED(hr))
{
Console.Error(fmt::format("IDXGIFactory2::EnumAdapters() returned %08X", hr));
ERROR_LOG("IDXGIFactory2::EnumAdapters() returned {:08X}", static_cast<unsigned>(hr));
continue;
}
adapter_names.push_back(FixupDuplicateAdapterNames(adapter_names, GetAdapterName(adapter.get())));
GSAdapterInfo ai;
ai.name = FixupDuplicateAdapterNames(adapters, 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.
ai.max_texture_size = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
ai.max_upscale_multiplier = GSGetMaxUpscaleMultiplier(ai.max_texture_size);
wil::com_ptr_nothrow<IDXGIOutput> output;
if (SUCCEEDED(hr = adapter->EnumOutputs(0, &output)))
{
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(GSDevice::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
{
ERROR_LOG("EnumOutputs() failed: {:08X}", static_cast<unsigned>(hr));
}
adapters.push_back(std::move(ai));
}
return adapter_names;
}
std::vector<std::string> D3D::GetFullscreenModes(IDXGIFactory5* factory, const std::string_view adapter_name)
{
std::vector<std::string> modes;
HRESULT hr;
wil::com_ptr_nothrow<IDXGIAdapter1> adapter = GetChosenOrFirstAdapter(factory, adapter_name);
if (!adapter)
return modes;
wil::com_ptr_nothrow<IDXGIOutput> output;
if (FAILED(hr = adapter->EnumOutputs(0, &output)))
{
Console.Error("EnumOutputs() failed: %08X", hr);
return modes;
}
UINT num_modes = 0;
if (FAILED(hr = output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, nullptr)))
{
Console.Error("GetDisplayModeList() failed: %08X", 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())))
{
Console.Error("GetDisplayModeList() (2) failed: %08X", hr);
return modes;
}
for (const DXGI_MODE_DESC& mode : dmodes)
{
modes.push_back(GSDevice::GetFullscreenModeString(mode.Width, mode.Height,
static_cast<float>(mode.RefreshRate.Numerator) / static_cast<float>(mode.RefreshRate.Denominator)));
}
return modes;
return adapters;
}
bool D3D::GetRequestedExclusiveFullscreenModeDesc(IDXGIFactory5* factory, const RECT& window_rect, u32 width,
@ -187,7 +186,7 @@ bool D3D::GetRequestedExclusiveFullscreenModeDesc(IDXGIFactory5* factory, const
if (FAILED(hr = intersecting_output->FindClosestMatchingMode(&request_mode, fullscreen_mode, nullptr)) ||
request_mode.Format != format)
{
Console.Error("Failed to find closest matching mode, hr=%08X", hr);
ERROR_LOG("Failed to find closest matching mode, hr={:08X}", static_cast<unsigned>(hr));
return false;
}
@ -203,7 +202,7 @@ wil::com_ptr_nothrow<IDXGIAdapter1> D3D::GetAdapterByName(IDXGIFactory5* factory
// 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;
std::vector<GSAdapterInfo> adapter_names;
wil::com_ptr_nothrow<IDXGIAdapter1> adapter;
for (u32 index = 0;; index++)
@ -214,18 +213,19 @@ wil::com_ptr_nothrow<IDXGIAdapter1> D3D::GetAdapterByName(IDXGIFactory5* factory
if (FAILED(hr))
{
Console.Error(fmt::format("IDXGIFactory2::EnumAdapters() returned %08X", hr));
ERROR_LOG("IDXGIFactory2::EnumAdapters() returned {:08X}", static_cast<unsigned>(hr));
continue;
}
std::string adapter_name = FixupDuplicateAdapterNames(adapter_names, GetAdapterName(adapter.get()));
if (adapter_name == name)
GSAdapterInfo ai;
ai.name = FixupDuplicateAdapterNames(adapter_names, GetAdapterName(adapter.get()));
if (ai.name == name)
{
Console.WriteLn(fmt::format("D3D: Found adapter '{}'", adapter_name));
INFO_LOG("D3D: Found adapter '{}'", ai.name);
return adapter;
}
adapter_names.push_back(std::move(adapter_name));
adapter_names.push_back(std::move(ai));
}
Console.Warning(fmt::format("Adapter '{}' not found.", name));
@ -404,9 +404,7 @@ GSRendererType D3D::GetPreferredRenderer()
if (check_for_mapping_layers())
return false;
std::vector<std::string> vk_adapter_names;
GSDeviceVK::GetAdaptersAndFullscreenModes(&vk_adapter_names, nullptr);
if (!vk_adapter_names.empty())
if (!GSDeviceVK::EnumerateGPUs().empty())
return true;
Host::AddIconOSDMessage("VKDriverUnsupported", ICON_FA_TV, TRANSLATE_STR("GS",

View File

@ -6,7 +6,8 @@
#include "common/RedtapeWindows.h"
#include "common/RedtapeWilCom.h"
#include "pcsx2/Config.h"
#include "Config.h"
#include "GS/GS.h"
#include <d3d11_1.h>
#include <dxgi1_5.h>
@ -19,11 +20,8 @@ namespace D3D
// create a dxgi factory
wil::com_ptr_nothrow<IDXGIFactory5> CreateFactory(bool debug);
// 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, const std::string_view adapter_name);
// returns a list of all adapter information
std::vector<GSAdapterInfo> GetAdapterInfo(IDXGIFactory5* factory);
// returns the fullscreen mode to use for the specified dimensions
bool GetRequestedExclusiveFullscreenModeDesc(IDXGIFactory5* factory, const RECT& window_rect, u32 width, u32 height,

View File

@ -595,13 +595,10 @@ void GSDevice11::SetFeatures(IDXGIAdapter1* adapter)
m_features.vs_expand = false;
}
}
}
int GSDevice11::GetMaxTextureSize() const
{
return (m_feature_level >= D3D_FEATURE_LEVEL_11_0) ?
D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION :
D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
m_max_texture_size = (m_feature_level >= D3D_FEATURE_LEVEL_11_0) ?
D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION :
D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION;
}
bool GSDevice11::HasSurface() const
@ -1185,10 +1182,8 @@ void GSDevice11::InsertDebugMessage(DebugMessageCategory category, const char* f
GSTexture* GSDevice11::CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format)
{
D3D11_TEXTURE2D_DESC desc = {};
// Texture limit for D3D10/11 min 1, max 8192 D3D10, max 16384 D3D11.
desc.Width = std::clamp(width, 1, GetMaxTextureSize());
desc.Height = std::clamp(height, 1, GetMaxTextureSize());
desc.Width = width;
desc.Height = height;
desc.Format = GSTexture11::GetDXGIFormat(format);
desc.MipLevels = levels;
desc.ArraySize = 1;

View File

@ -91,7 +91,6 @@ private:
};
void SetFeatures(IDXGIAdapter1* adapter);
int GetMaxTextureSize() const;
u32 GetSwapChainBufferCount() const;
bool CreateSwapChain();

View File

@ -1227,6 +1227,8 @@ bool GSDevice12::CheckFeatures()
SupportsTextureFormat(DXGI_FORMAT_BC3_UNORM);
m_features.bptc_textures = SupportsTextureFormat(DXGI_FORMAT_BC7_UNORM);
m_max_texture_size = D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION;
BOOL allow_tearing_supported = false;
HRESULT hr = m_dxgi_factory->CheckFeatureSupport(
DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported, sizeof(allow_tearing_supported));
@ -1289,22 +1291,19 @@ void GSDevice12::LookupNativeFormat(GSTexture::Format format, DXGI_FORMAT* d3d_f
GSTexture* GSDevice12::CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format)
{
const u32 clamped_width = static_cast<u32>(std::clamp<int>(width, 1, D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION));
const u32 clamped_height = static_cast<u32>(std::clamp<int>(height, 1, D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION));
DXGI_FORMAT dxgi_format, srv_format, rtv_format, dsv_format;
LookupNativeFormat(format, &dxgi_format, &srv_format, &rtv_format, &dsv_format);
const DXGI_FORMAT uav_format = (type == GSTexture::Type::RWTexture) ? dxgi_format : DXGI_FORMAT_UNKNOWN;
std::unique_ptr<GSTexture12> tex(GSTexture12::Create(type, format, clamped_width, clamped_height, levels,
std::unique_ptr<GSTexture12> tex(GSTexture12::Create(type, format, width, height, levels,
dxgi_format, srv_format, rtv_format, dsv_format, uav_format));
if (!tex)
{
// We're probably out of vram, try flushing the command buffer to release pending textures.
PurgePool();
ExecuteCommandListAndRestartRenderPass(true, "Couldn't allocate texture.");
tex = GSTexture12::Create(type, format, clamped_width, clamped_height, levels, dxgi_format, srv_format,
tex = GSTexture12::Create(type, format, width, height, levels, dxgi_format, srv_format,
rtv_format, dsv_format, uav_format);
}

View File

@ -25,12 +25,25 @@ GSDevice* MakeGSDeviceMTL()
return new GSDeviceMTL();
}
std::vector<std::string> GetMetalAdapterList()
std::vector<GSAdapterInfo> GetMetalAdapterList()
{ @autoreleasepool {
std::vector<std::string> list;
std::vector<GSAdapterInfo> list;
auto devs = MRCTransfer(MTLCopyAllDevices());
for (id<MTLDevice> dev in devs.Get())
list.push_back([[dev name] UTF8String]);
{
GSAdapterInfo ai;
ai.name = [[dev name] UTF8String];
ai.max_texture_size = 8192;
if ([dev supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily1_v1])
ai.max_texture_size = 16384;
if (@available(macOS 10.15, iOS 13.0, *))
if ([dev supportsFamily:MTLGPUFamilyApple3])
ai.max_texture_size = 16384;
ai.max_upscale_multiplier = GSGetMaxUpscaleMultiplier(ai.max_texture_size);
list.push_back(std::move(ai));
}
return list;
}}
@ -507,8 +520,8 @@ GSTexture* GSDeviceMTL::CreateSurface(GSTexture::Type type, int width, int heigh
MTLTextureDescriptor* desc = [MTLTextureDescriptor
texture2DDescriptorWithPixelFormat:fmt
width:std::max(1, std::min(width, m_dev.features.max_texsize))
height:std::max(1, std::min(height, m_dev.features.max_texsize))
width:width
height:height
mipmapped:levels > 1];
if (levels > 1)
@ -905,6 +918,7 @@ bool GSDeviceMTL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
m_features.stencil_buffer = true;
m_features.cas_sharpening = true;
m_features.test_and_sample_depth = true;
m_max_texture_size = m_dev.features.max_texsize;
// Init metal stuff
m_fn_constants = MRCTransfer([MTLFunctionConstantValues new]);

View File

@ -3,6 +3,8 @@
#pragma once
#include "GS/GS.h"
#include <string>
#include <vector>
@ -12,6 +14,6 @@
class GSDevice;
GSDevice* MakeGSDeviceMTL();
std::vector<std::string> GetMetalAdapterList();
std::vector<GSAdapterInfo> GetMetalAdapterList();
#endif

View File

@ -767,6 +767,10 @@ bool GSDeviceOGL::CheckFeatures(bool& buggy_pbo)
(point_range[0] <= GSConfig.UpscaleMultiplier && point_range[1] >= GSConfig.UpscaleMultiplier);
m_features.line_expand = false;
GLint max_texture_size = 1024;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
m_max_texture_size = std::max(1024u, static_cast<u32>(max_texture_size));
Console.WriteLn("Using %s for point expansion, %s for line expansion and %s for sprite expansion.",
m_features.point_expand ? "hardware" : (m_features.vs_expand ? "vertex expanding" : "UNSUPPORTED"),
m_features.line_expand ? "hardware" : (m_features.vs_expand ? "vertex expanding" : "UNSUPPORTED"),

View File

@ -291,24 +291,58 @@ GSDeviceVK::GPUList GSDeviceVK::EnumerateGPUs(VkInstance instance)
if (has_missing_extension)
continue;
std::string gpu_name = props.deviceName;
GSAdapterInfo ai;
ai.name = props.deviceName;
ai.max_texture_size = std::min(props.limits.maxFramebufferWidth, props.limits.maxImageDimension2D);
ai.max_upscale_multiplier = GSGetMaxUpscaleMultiplier(ai.max_texture_size);
// handle duplicate adapter names
if (std::any_of(
gpus.begin(), gpus.end(), [&gpu_name](const auto& other) { return (gpu_name == other.second); }))
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); }));
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;
}
GSDeviceVK::GPUList GSDeviceVK::EnumerateGPUs()
{
std::unique_lock lock(s_instance_mutex);
// Device shouldn't be torn down since we have the lock.
GPUList gpus;
if (g_gs_device && Vulkan::IsVulkanLibraryLoaded())
{
gpus = EnumerateGPUs(GSDeviceVK::GetInstance()->GetVulkanInstance());
}
else
{
if (Vulkan::LoadVulkanLibrary(nullptr))
{
OptionalExtensions oe = {};
const VkInstance instance = CreateVulkanInstance(WindowInfo(), &oe, false, false);
if (instance != VK_NULL_HANDLE)
{
if (Vulkan::LoadVulkanInstanceFunctions(instance))
gpus = EnumerateGPUs(instance);
vkDestroyInstance(instance, nullptr);
}
Vulkan::UnloadVulkanLibrary();
}
}
return gpus;
@ -1928,58 +1962,28 @@ bool GSDeviceVK::AllocatePreinitializedGPUBuffer(u32 size, VkBuffer* gpu_buffer,
}
void GSDeviceVK::GPUListToAdapterNames(std::vector<std::string>* dest, VkInstance instance)
std::vector<GSAdapterInfo> GSDeviceVK::GetAdapterInfo()
{
GPUList gpus = EnumerateGPUs(instance);
dest->clear();
dest->reserve(gpus.size());
for (auto& [gpu, name] : gpus)
dest->push_back(std::move(name));
}
void GSDeviceVK::GetAdaptersAndFullscreenModes(
std::vector<std::string>* adapters, std::vector<std::string>* fullscreen_modes)
{
std::unique_lock lock(s_instance_mutex);
// Device shouldn't be torn down since we have the lock.
if (g_gs_device && Vulkan::IsVulkanLibraryLoaded())
{
if (adapters)
GPUListToAdapterNames(adapters, GSDeviceVK::GetInstance()->GetVulkanInstance());
}
else
{
if (Vulkan::LoadVulkanLibrary(nullptr))
{
OptionalExtensions oe = {};
const VkInstance instance = CreateVulkanInstance(WindowInfo(), &oe, false, false);
if (instance != VK_NULL_HANDLE)
{
if (Vulkan::LoadVulkanInstanceFunctions(instance))
GPUListToAdapterNames(adapters, instance);
vkDestroyInstance(instance, nullptr);
}
Vulkan::UnloadVulkanLibrary();
}
}
GPUList gpus = EnumerateGPUs();
std::vector<GSAdapterInfo> ret;
ret.reserve(gpus.size());
for (auto& [physical_device, ai] : gpus)
ret.push_back(std::move(ai));
return ret;
}
bool GSDeviceVK::IsSuitableDefaultRenderer()
{
std::vector<std::string> adapters;
GetAdaptersAndFullscreenModes(&adapters, nullptr);
if (adapters.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 = adapters.front();
Console.WriteLn(fmt::format("Using Vulkan GPU '{}' for automatic renderer check.", name));
const std::string& name = gpus.front().second.name;
INFO_LOG("Using Vulkan GPU '{}' for automatic renderer check.", name);
// Any software rendering (LLVMpipe, SwiftShader).
if (StringUtil::StartsWithNoCase(name, "llvmpipe") || StringUtil::StartsWithNoCase(name, "SwiftShader"))
@ -2451,18 +2455,17 @@ bool GSDeviceVK::CreateDeviceAndSwapChain()
m_instance = CreateVulkanInstance(m_window_info, &m_optional_extensions, enable_debug_utils, enable_validation_layer);
if (m_instance == VK_NULL_HANDLE)
{
Host::ReportErrorAsync(
"Error", "Failed to create Vulkan instance. Does your GPU and/or driver support Vulkan?");
Host::ReportErrorAsync("Error", "Failed to create Vulkan instance. Does your GPU and/or driver support Vulkan?");
return false;
}
Console.Error("Vulkan validation/debug layers requested but are unavailable. Creating non-debug device.");
ERROR_LOG("Vulkan validation/debug layers requested but are unavailable. Creating non-debug device.");
}
}
if (!Vulkan::LoadVulkanInstanceFunctions(m_instance))
{
Console.Error("Failed to load Vulkan instance functions");
ERROR_LOG("Failed to load Vulkan instance functions");
return false;
}
@ -2478,8 +2481,8 @@ bool GSDeviceVK::CreateDeviceAndSwapChain()
u32 gpu_index = 0;
for (; gpu_index < static_cast<u32>(gpus.size()); gpu_index++)
{
Console.WriteLn(fmt::format("GPU {}: {}", gpu_index, gpus[gpu_index].second));
if (gpus[gpu_index].second == GSConfig.Adapter)
DEV_LOG("GPU {}: {}", gpu_index, gpus[gpu_index].second.name);
if (gpus[gpu_index].second.name == GSConfig.Adapter)
{
m_physical_device = gpus[gpu_index].first;
break;
@ -2488,14 +2491,13 @@ bool GSDeviceVK::CreateDeviceAndSwapChain()
if (gpu_index == static_cast<u32>(gpus.size()))
{
Console.Warning(
fmt::format("Requested GPU '{}' not found, using first ({})", GSConfig.Adapter, gpus[0].second));
WARNING_LOG("Requested GPU '{}' not found, using first ({})", GSConfig.Adapter, gpus[0].second.name);
m_physical_device = gpus[0].first;
}
}
else
{
Console.WriteLn(fmt::format("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;
}
@ -2647,6 +2649,8 @@ bool GSDeviceVK::CheckFeatures()
Host::OSD_WARNING_DURATION);
}
m_max_texture_size = m_device_properties.limits.maxImageDimension2D;
return true;
}
@ -2693,18 +2697,13 @@ VkFormat GSDeviceVK::LookupNativeFormat(GSTexture::Format format) const
GSTexture* GSDeviceVK::CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format)
{
const u32 clamped_width =
static_cast<u32>(std::clamp<int>(width, 1, m_device_properties.limits.maxImageDimension2D));
const u32 clamped_height =
static_cast<u32>(std::clamp<int>(height, 1, m_device_properties.limits.maxImageDimension2D));
std::unique_ptr<GSTexture> tex(GSTextureVK::Create(type, format, clamped_width, clamped_height, levels));
std::unique_ptr<GSTexture> tex = GSTextureVK::Create(type, format, width, height, levels);
if (!tex)
{
// We're probably out of vram, try flushing the command buffer to release pending textures.
PurgePool();
ExecuteCommandBufferAndRestartRenderPass(true, "Couldn't allocate texture.");
tex = GSTextureVK::Create(type, format, clamped_width, clamped_height, levels);
tex = GSTextureVK::Create(type, format, width, height, levels);
}
return tex.release();

View File

@ -129,11 +129,6 @@ private:
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);
static void GPUListToAdapterNames(std::vector<std::string>* dest, VkInstance instance);
// Enable/disable debug message runtime.
bool EnableDebugUtils();
void DisableDebugUtils();
@ -474,8 +469,11 @@ public:
__fi static GSDeviceVK* GetInstance() { return static_cast<GSDeviceVK*>(g_gs_device.get()); }
static void GetAdaptersAndFullscreenModes(
std::vector<std::string>* adapters, std::vector<std::string>* fullscreen_modes);
// Returns a list of Vulkan-compatible GPUs.
using GPUList = std::vector<std::pair<VkPhysicalDevice, GSAdapterInfo>>;
static GPUList EnumerateGPUs();
static GPUList EnumerateGPUs(VkInstance instance);
static std::vector<GSAdapterInfo> GetAdapterInfo();
/// Returns true if Vulkan is suitable as a default for the devices in the system.
static bool IsSuitableDefaultRenderer();

View File

@ -402,8 +402,7 @@ namespace FullscreenUI
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 std::vector<std::string> s_fullscreen_mode_list_cache;
static std::vector<GSAdapterInfo> s_graphics_adapter_list_cache;
static Patch::PatchInfoList s_game_patch_list;
static std::vector<std::string> s_enabled_game_patch_cache;
static Patch::PatchInfoList s_game_cheats_list;
@ -762,7 +761,6 @@ void FullscreenUI::Shutdown(bool clear_state)
s_game_cheats_list = {};
s_enabled_game_patch_cache = {};
s_game_patch_list = {};
s_fullscreen_mode_list_cache = {};
s_graphics_adapter_list_cache = {};
s_current_game_title = {};
s_current_game_subtitle = {};
@ -2736,7 +2734,7 @@ void FullscreenUI::SwitchToGameSettings(const GameList::Entry* entry)
void FullscreenUI::PopulateGraphicsAdapterList()
{
GSGetAdaptersAndFullscreenModes(GSConfig.Renderer, &s_graphics_adapter_list_cache, &s_fullscreen_mode_list_cache);
s_graphics_adapter_list_cache = GSGetAdapterInfo(GSConfig.Renderer);
}
void FullscreenUI::PopulateGameListDirectoryCache(SettingsInterface* si)

View File

@ -883,9 +883,6 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap)
SettingsWrapIntEnumEx(Renderer, "Renderer");
SettingsWrapEntryEx(UpscaleMultiplier, "upscale_multiplier");
// ~51x would the upper bound here for 32768x32768 textures, but you'll run out VRAM long before then.
UpscaleMultiplier = std::clamp(UpscaleMultiplier, 1.0f, 50.0f);
SettingsWrapBitBoolEx(HWMipmap, "hw_mipmap");
SettingsWrapIntEnumEx(AccurateBlendingUnit, "accurate_blending_unit");
SettingsWrapIntEnumEx(TextureFiltering, "filter");