citra_qt: Add physical device selection dialog

This commit is contained in:
GPUCode
2022-10-05 18:03:38 +03:00
parent 20ccb995b1
commit 2da4c9ca90
18 changed files with 213 additions and 64 deletions

View File

@ -1,3 +1,31 @@
QPushButton#TogglableStatusBarButton {
color: #959595;
border: 1px solid transparent;
background-color: transparent;
padding: 0px 3px 0px 3px;
text-align: center;
}
QPushButton#TogglableStatusBarButton:checked {
color: palette(text);
}
QPushButton#TogglableStatusBarButton:hover {
border: 1px solid #76797C;
}
QPushButton#GraphicsAPIStatusBarButton {
color: #656565;
border: 1px solid transparent;
background-color: transparent;
padding: 0px 3px 0px 3px;
text-align: center;
}
QPushButton#GraphicsAPIStatusBarButton:hover {
border: 1px solid #76797C;
}
QToolTip {
border: 1px solid #76797C;
background-color: #5A7566;

View File

@ -161,3 +161,8 @@ if(ANDROID)
add_subdirectory(libyuv)
target_include_directories(yuv INTERFACE ./libyuv/include)
endif()
# VMA
add_library(vma INTERFACE)
target_include_directories(vma INTERFACE ./vma)

View File

@ -260,7 +260,7 @@ endif()
create_target_directory_groups(citra-qt)
target_link_libraries(citra-qt PRIVATE audio_core common core input_common network video_core)
target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt5::Widgets Qt5::Multimedia)
target_link_libraries(citra-qt PRIVATE Boost::boost glad vma nihstro-headers Qt5::Widgets Qt5::Multimedia)
target_link_libraries(citra-qt PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
if (NOT WIN32)

View File

@ -485,6 +485,7 @@ void Config::ReadRendererValues() {
Settings::values.graphics_api = static_cast<Settings::GraphicsAPI>(
ReadSetting(QStringLiteral("graphics_api"), static_cast<u32>(Settings::GraphicsAPI::OpenGL))
.toUInt());
Settings::values.physical_device = ReadSetting(QStringLiteral("physical_device"), 0).toUInt();
Settings::values.renderer_debug = ReadSetting(QStringLiteral("renderer_debug"), false).toBool();
Settings::values.use_hw_renderer =
ReadSetting(QStringLiteral("use_hw_renderer"), true).toBool();
@ -1000,6 +1001,7 @@ void Config::SaveRendererValues() {
WriteSetting(QStringLiteral("graphics_api"), static_cast<u32>(Settings::values.graphics_api),
static_cast<u32>(Settings::GraphicsAPI::OpenGL));
WriteSetting(QStringLiteral("physical_device"), Settings::values.physical_device, 0);
WriteSetting(QStringLiteral("renderer_debug"), Settings::values.renderer_debug, false);
WriteSetting(QStringLiteral("use_hw_renderer"), Settings::values.use_hw_renderer, true);
WriteSetting(QStringLiteral("use_hw_shader"), Settings::values.use_hw_shader, true);

View File

@ -10,10 +10,12 @@
#include "core/core.h"
#include "core/settings.h"
#include "ui_configure_graphics.h"
#include "video_core/renderer_vulkan/vk_instance.h"
ConfigureGraphics::ConfigureGraphics(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureGraphics>()) {
ui->setupUi(this);
DiscoverPhysicalDevices();
SetConfiguration();
const bool not_running = !Core::System::GetInstance().IsPoweredOn();
@ -24,6 +26,10 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
ui->graphics_api_combo->setEnabled(not_running);
ui->toggle_shader_jit->setEnabled(not_running);
ui->toggle_disk_shader_cache->setEnabled(hw_renderer_enabled && not_running);
SetPhysicalDeviceComboVisibility(ui->graphics_api_combo->currentIndex());
connect(ui->graphics_api_combo, qOverload<int>(&QComboBox::currentIndexChanged), this,
&ConfigureGraphics::SetPhysicalDeviceComboVisibility);
connect(ui->toggle_hw_renderer, &QCheckBox::toggled, this, [this] {
auto checked = ui->toggle_hw_renderer->isChecked();
@ -75,6 +81,7 @@ void ConfigureGraphics::SetConfiguration() {
ui->toggle_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache);
ui->toggle_vsync_new->setChecked(Settings::values.use_vsync_new);
ui->graphics_api_combo->setCurrentIndex(static_cast<int>(Settings::values.graphics_api));
ui->physical_device_combo->setCurrentIndex(static_cast<int>(Settings::values.physical_device));
}
void ConfigureGraphics::ApplyConfiguration() {
@ -86,8 +93,27 @@ void ConfigureGraphics::ApplyConfiguration() {
Settings::values.use_disk_shader_cache = ui->toggle_disk_shader_cache->isChecked();
Settings::values.use_vsync_new = ui->toggle_vsync_new->isChecked();
Settings::values.graphics_api = static_cast<Settings::GraphicsAPI>(ui->graphics_api_combo->currentIndex());
Settings::values.physical_device = static_cast<u16>(ui->physical_device_combo->currentIndex());
}
void ConfigureGraphics::RetranslateUI() {
ui->retranslateUi(this);
}
void ConfigureGraphics::DiscoverPhysicalDevices() {
Vulkan::Instance instance{};
const auto physical_devices = instance.GetPhysicalDevices();
ui->physical_device_combo->clear();
for (const vk::PhysicalDevice& physical_device : physical_devices) {
const QString name = QString::fromLocal8Bit(physical_device.getProperties().deviceName);
ui->physical_device_combo->addItem(name);
}
}
void ConfigureGraphics::SetPhysicalDeviceComboVisibility(int index) {
const auto graphics_api = static_cast<Settings::GraphicsAPI>(index);
const bool is_visible = graphics_api == Settings::GraphicsAPI::Vulkan;
ui->physical_device_label->setVisible(is_visible);
ui->physical_device_combo->setVisible(is_visible);
}

View File

@ -24,6 +24,11 @@ public:
void UpdateBackgroundColorButton(const QColor& color);
private:
void DiscoverPhysicalDevices();
void SetPhysicalDeviceComboVisibility(int index);
private:
std::unique_ptr<Ui::ConfigureGraphics> ui;
QColor bg_color;
};

View File

@ -21,15 +21,15 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="rendererBox">
<widget class="QGroupBox" name="apiBox">
<property name="title">
<string>Renderer</string>
<string>API Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<widget class="QLabel" name="graphics_api_label">
<property name="text">
<string>Graphics API</string>
</property>
@ -56,6 +56,29 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="physical_device_label">
<property name="text">
<string>Physical device</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="physical_device_combo"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="rendererBox">
<property name="title">
<string>Renderer</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QCheckBox" name="toggle_hw_renderer">
<property name="toolTip">

View File

@ -59,6 +59,7 @@
#include "common/microprofile.h"
#include "common/scm_rev.h"
#include "common/scope_exit.h"
#include "common/string_util.h"
#ifdef ARCHITECTURE_x86_64
#include "common/x64/cpu_detect.h"
#endif
@ -280,6 +281,23 @@ void GMainWindow::InitializeWidgets() {
message_label->setAlignment(Qt::AlignLeft);
statusBar()->addPermanentWidget(message_label, 1);
// Setup Graphics API button
graphics_api_button = new QPushButton();
graphics_api_button->setCheckable(true);
graphics_api_button->setObjectName(QStringLiteral("GraphicsAPIStatusBarButton"));
graphics_api_button->setFocusPolicy(Qt::NoFocus);
UpdateAPIIndicator(false);
connect(graphics_api_button, &QPushButton::clicked, this, [this] {
if (emulation_running) {
return;
}
UpdateAPIIndicator(true);
});
statusBar()->addPermanentWidget(graphics_api_button);
progress_bar = new QProgressBar();
progress_bar->hide();
statusBar()->addPermanentWidget(progress_bar);
@ -299,8 +317,9 @@ void GMainWindow::InitializeWidgets() {
label->setVisible(false);
label->setFrameStyle(QFrame::NoFrame);
label->setContentsMargins(4, 0, 4, 0);
statusBar()->addPermanentWidget(label, 0);
statusBar()->addPermanentWidget(label);
}
statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0);
statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0);
statusBar()->setVisible(true);
@ -1780,6 +1799,7 @@ void GMainWindow::OnConfigure() {
} else {
setMouseTracking(false);
}
UpdateAPIIndicator(false);
} else {
Settings::values.input_profiles = old_input_profiles;
Settings::values.touch_from_button_maps = old_touch_from_button_maps;
@ -2091,6 +2111,33 @@ void GMainWindow::ShowMouseCursor() {
}
}
void GMainWindow::UpdateAPIIndicator(bool override) {
static std::array graphics_apis = {
QStringLiteral("OPENGL"),
QStringLiteral("OPENGLES"),
QStringLiteral("VULKAN")
};
static std::array graphics_api_colors = {
QStringLiteral("#00ccdd"),
QStringLiteral("#ba2a8d"),
QStringLiteral("#91242a")
};
u32 api_index = static_cast<u32>(Settings::values.graphics_api);
if (override) {
api_index = (api_index + 1) % graphics_apis.size();
Settings::values.graphics_api =
static_cast<Settings::GraphicsAPI>(api_index);
}
const QString style_sheet =
QStringLiteral("QPushButton { font-weight: bold; color: %0; }").arg(graphics_api_colors[api_index]);
graphics_api_button->setText(graphics_apis[api_index]);
graphics_api_button->setStyleSheet(style_sheet);
}
void GMainWindow::OnMouseActivity() {
ShowMouseCursor();
}

View File

@ -9,6 +9,7 @@
#include <QMainWindow>
#include <QTimer>
#include <QTranslator>
#include <QPushButton>
#include "citra_qt/compatibility_list.h"
#include "citra_qt/hotkeys.h"
#include "common/announce_multiplayer_room.h"
@ -234,6 +235,7 @@ private:
void InstallCIA(QStringList filepaths);
void HideMouseCursor();
void ShowMouseCursor();
void UpdateAPIIndicator(bool override);
std::unique_ptr<Ui::MainWindow> ui;
@ -248,6 +250,7 @@ private:
QLabel* emu_speed_label = nullptr;
QLabel* game_fps_label = nullptr;
QLabel* emu_frametime_label = nullptr;
QPushButton* graphics_api_button = nullptr;
QTimer status_bar_update_timer;
bool message_label_used_for_movie = false;

View File

@ -169,6 +169,7 @@ struct Values {
// Renderer
GraphicsAPI graphics_api;
u16 physical_device;
bool renderer_debug;
bool use_hw_renderer;
bool use_hw_shader;

View File

@ -187,9 +187,8 @@ create_target_directory_groups(video_core)
# Include Vulkan headers
target_include_directories(video_core PRIVATE ../../externals/vulkan-headers/include)
target_include_directories(video_core PRIVATE ../../externals/vma)
target_link_libraries(video_core PUBLIC common core)
target_link_libraries(video_core PRIVATE glad glm::glm SPIRV glslang nihstro-headers Boost::serialization)
target_link_libraries(video_core PRIVATE glad vma glm::glm SPIRV glslang nihstro-headers Boost::serialization)
set_target_properties(video_core PROPERTIES INTERPROCEDURAL_OPTIMIZATION ${ENABLE_LTO})
if (ARCHITECTURE_x86_64)

View File

@ -1077,44 +1077,7 @@ bool RasterizerCache<T>::ValidateByReinterpretation(const Surface& surface, cons
auto src_rect = reinterpret_surface->GetScaledSubRect(reinterpret_params);
auto dest_rect = surface->GetScaledSubRect(reinterpret_params);
if (!texture_filterer->IsNull() && reinterpret_surface->res_scale == 1 &&
surface->res_scale == resolution_scale_factor) {
// The destination surface is either a framebuffer, or a filtered texture.
// Create an intermediate surface to convert to before blitting to the
// destination.
const u32 width = dest_rect.GetHeight() / resolution_scale_factor;
const u32 height = dest_rect.GetWidth() / resolution_scale_factor;
const Common::Rectangle<u32> tmp_rect{0, width, height, 0};
OGLTexture tmp_tex = AllocateSurfaceTexture(dst_format, height, width);
reinterpreter->Reinterpret(reinterpret_surface->texture, src_rect, tmp_tex,
tmp_rect);
if (!texture_filterer->Filter(tmp_tex, tmp_rect, surface->texture, dest_rect, type)) {
const TextureBlit texture_blit = {
.surface_type = type,
.src_level = 0,
.dst_level = 0,
.src_layer = 0,
.dst_layer = 0,
.src_region = Region2D{
.start = {0, 0},
.end = {width, height}
},
.dst_region = Region2D{
.start = {dest_rect.left, dest_rect.bottom},
.end = {dest_rect.right, dest_rect.top}
}
};
runtime.BlitTextures(tmp_tex, surface->texture, texture_blit);
}
} else {
reinterpreter->Reinterpret(reinterpret_surface->texture, src_rect, surface->texture,
dest_rect);
}
reinterpreter->Reinterpret(reinterpret_surface->texture, src_rect, surface->texture, dest_rect);
return true;
}
}*/

View File

@ -129,9 +129,9 @@ void TextureRuntime::FormatConvert(const Surface& surface, bool upload,
std::span<std::byte> source, std::span<std::byte> dest) {
const VideoCore::PixelFormat format = surface.pixel_format;
if (format == VideoCore::PixelFormat::RGBA8 && driver.IsOpenGLES()) {
Pica::Texture::ConvertABGRToRGBA(source, dest);
return Pica::Texture::ConvertABGRToRGBA(source, dest);
} else if (format == VideoCore::PixelFormat::RGB8 && driver.IsOpenGLES()) {
Pica::Texture::ConvertBGRToRGB(source, dest);
return Pica::Texture::ConvertBGRToRGB(source, dest);
} else {
ASSERT(dest.size() >= source.size());
std::memcpy(dest.data(), source.data(), source.size());

View File

@ -154,7 +154,8 @@ struct ScreenRectVertex {
constexpr u32 VERTEX_BUFFER_SIZE = sizeof(ScreenRectVertex) * 8192;
RendererVulkan::RendererVulkan(Frontend::EmuWindow& window)
: RendererBase{window}, instance{window, Settings::values.renderer_debug}, scheduler{instance, *this},
: RendererBase{window}, instance{window, Settings::values.physical_device, Settings::values.renderer_debug},
scheduler{instance, *this},
renderpass_cache{instance, scheduler}, runtime{instance, scheduler, renderpass_cache},
swapchain{instance, renderpass_cache},
vertex_buffer{instance, scheduler, VERTEX_BUFFER_SIZE, vk::BufferUsageFlagBits::eVertexBuffer, {}} {

View File

@ -38,7 +38,38 @@ vk::Format ToVkFormat(VideoCore::PixelFormat format) {
}
}
Instance::Instance(Frontend::EmuWindow& window, bool enable_validation) {
Instance::Instance() {
// Fetch instance independant function pointers
vk::DynamicLoader dl;
auto vkGetInstanceProcAddr = dl.getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);
const vk::ApplicationInfo application_info = {
.pApplicationName = "Citra",
.applicationVersion = VK_MAKE_VERSION(1, 0, 0),
.pEngineName = "Citra Vulkan",
.engineVersion = VK_MAKE_VERSION(1, 0, 0),
.apiVersion = VK_API_VERSION_1_0
};
const vk::InstanceCreateInfo instance_info = {
.pApplicationInfo = &application_info
};
instance = vk::createInstance(instance_info);
// Load required function pointers for querying the physical device
VULKAN_HPP_DEFAULT_DISPATCHER.vkEnumeratePhysicalDevices =
PFN_vkEnumeratePhysicalDevices(vkGetInstanceProcAddr(instance, "vkEnumeratePhysicalDevices"));
VULKAN_HPP_DEFAULT_DISPATCHER.vkGetPhysicalDeviceProperties =
PFN_vkGetPhysicalDeviceProperties(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceProperties"));
VULKAN_HPP_DEFAULT_DISPATCHER.vkDestroyInstance =
PFN_vkDestroyInstance(vkGetInstanceProcAddr(instance, "vkDestroyInstance"));
physical_devices = instance.enumeratePhysicalDevices();
}
Instance::Instance(Frontend::EmuWindow& window, u32 physical_device_index, bool enable_validation) {
auto window_info = window.GetWindowInfo();
// Fetch instance independant function pointers
@ -53,6 +84,7 @@ Instance::Instance(Frontend::EmuWindow& window, bool enable_validation) {
const u32 available_version = vk::enumerateInstanceVersion();
if (available_version < VK_API_VERSION_1_1) {
LOG_CRITICAL(Render_Vulkan, "Vulkan 1.0 is not supported, 1.1 is required!");
return;
}
const vk::ApplicationInfo application_info = {
@ -76,9 +108,16 @@ Instance::Instance(Frontend::EmuWindow& window, bool enable_validation) {
instance = vk::createInstance(instance_info);
surface = CreateSurface(instance, window);
// TODO: GPU select dialog
auto physical_devices = instance.enumeratePhysicalDevices();
physical_device = physical_devices[1];
// Pick physical device
physical_devices = instance.enumeratePhysicalDevices();
if (const u16 physical_device_count = static_cast<u16>(physical_devices.size());
physical_device_index >= physical_devices.size()) {
LOG_CRITICAL(Render_Vulkan, "Invalid physical device index {} provided when only {} devices exist",
physical_device_index, physical_device_count);
UNREACHABLE();
}
physical_device = physical_devices[physical_device_index];
device_properties = physical_device.getProperties();
CreateDevice();
@ -86,10 +125,12 @@ Instance::Instance(Frontend::EmuWindow& window, bool enable_validation) {
}
Instance::~Instance() {
device.waitIdle();
vmaDestroyAllocator(allocator);
device.destroy();
instance.destroySurfaceKHR(surface);
if (device) {
vmaDestroyAllocator(allocator);
device.destroy();
instance.destroySurfaceKHR(surface);
}
instance.destroy();
}

View File

@ -5,7 +5,8 @@
#pragma once
#include <array>
#include <unordered_map>
#include <span>
#include <vector>
#include "video_core/rasterizer_cache/pixel_format.h"
#include "video_core/renderer_vulkan/vk_common.h"
@ -27,7 +28,8 @@ struct FormatTraits {
/// The global Vulkan instance
class Instance {
public:
Instance(Frontend::EmuWindow& window, bool enable_validation);
Instance(); ///< Portable constructor used to query physical devices
Instance(Frontend::EmuWindow& window, u32 physical_device_index, bool enable_validation);
~Instance();
/// Returns the FormatTraits struct for the provided pixel format
@ -53,10 +55,16 @@ public:
return device;
}
/// Returns the VMA allocator handle
VmaAllocator GetAllocator() const {
return allocator;
}
/// Returns a list of the available physical devices
std::span<const vk::PhysicalDevice> GetPhysicalDevices() const {
return physical_devices;
}
/// Retrieve queue information
u32 GetGraphicsQueueFamilyIndex() const {
return graphics_queue_family_index;
@ -131,6 +139,7 @@ private:
VmaAllocator allocator;
vk::Queue present_queue;
vk::Queue graphics_queue;
std::vector<vk::PhysicalDevice> physical_devices;
std::array<FormatTraits, VideoCore::PIXEL_FORMAT_COUNT> format_table;
u32 present_queue_family_index = 0;
u32 graphics_queue_family_index = 0;

View File

@ -661,10 +661,6 @@ bool Surface::NeedsConvert() const {
}
u32 Surface::GetInternalBytesPerPixel() const {
if (alloc.format == vk::Format::eD32SfloatS8Uint) {
return 8;
}
return vk::blockSize(alloc.format);
}

View File

@ -227,7 +227,7 @@ void ConvertBGRToRGB(std::span<const std::byte> source, std::span<std::byte> des
u32 bgr{};
std::memcpy(&bgr, source.data() + i, 3);
const u32 rgb = Common::swap32(bgr << 8);
std::memcpy(dest.data(), &rgb, 3);
std::memcpy(dest.data() + i, &rgb, 3);
}
}