mirror of
https://github.com/citra-emu/citra.git
synced 2024-11-22 03:00:05 +00:00
qt: Display OpenGL renderer name and add Mesa override to support Windows OpenGLOn12. (#7395)
This commit is contained in:
parent
7a4854c519
commit
469f76b075
@ -179,6 +179,8 @@ add_executable(citra-qt
|
|||||||
qt_image_interface.h
|
qt_image_interface.h
|
||||||
util/clickable_label.cpp
|
util/clickable_label.cpp
|
||||||
util/clickable_label.h
|
util/clickable_label.h
|
||||||
|
util/graphics_device_info.cpp
|
||||||
|
util/graphics_device_info.h
|
||||||
util/sequence_dialog/sequence_dialog.cpp
|
util/sequence_dialog/sequence_dialog.cpp
|
||||||
util/sequence_dialog/sequence_dialog.h
|
util/sequence_dialog/sequence_dialog.h
|
||||||
util/spinbox.cpp
|
util/spinbox.cpp
|
||||||
@ -187,13 +189,6 @@ add_executable(citra-qt
|
|||||||
util/util.h
|
util/util.h
|
||||||
)
|
)
|
||||||
|
|
||||||
if (ENABLE_VULKAN)
|
|
||||||
target_sources(citra-qt PRIVATE
|
|
||||||
util/vk_device_info.cpp
|
|
||||||
util/vk_device_info.h
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
file(GLOB COMPAT_LIST
|
file(GLOB COMPAT_LIST
|
||||||
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
|
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||||
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
|
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
|
||||||
|
@ -23,14 +23,16 @@
|
|||||||
#include "ui_configure.h"
|
#include "ui_configure.h"
|
||||||
|
|
||||||
ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, Core::System& system_,
|
ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, Core::System& system_,
|
||||||
std::span<const QString> physical_devices, bool enable_web_config)
|
QString gl_renderer, std::span<const QString> physical_devices,
|
||||||
|
bool enable_web_config)
|
||||||
: QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()}, registry{registry_},
|
: QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()}, registry{registry_},
|
||||||
system{system_}, is_powered_on{system.IsPoweredOn()},
|
system{system_}, is_powered_on{system.IsPoweredOn()},
|
||||||
general_tab{std::make_unique<ConfigureGeneral>(this)},
|
general_tab{std::make_unique<ConfigureGeneral>(this)},
|
||||||
system_tab{std::make_unique<ConfigureSystem>(system, this)},
|
system_tab{std::make_unique<ConfigureSystem>(system, this)},
|
||||||
input_tab{std::make_unique<ConfigureInput>(this)},
|
input_tab{std::make_unique<ConfigureInput>(this)},
|
||||||
hotkeys_tab{std::make_unique<ConfigureHotkeys>(this)},
|
hotkeys_tab{std::make_unique<ConfigureHotkeys>(this)},
|
||||||
graphics_tab{std::make_unique<ConfigureGraphics>(physical_devices, is_powered_on, this)},
|
graphics_tab{
|
||||||
|
std::make_unique<ConfigureGraphics>(gl_renderer, physical_devices, is_powered_on, this)},
|
||||||
enhancements_tab{std::make_unique<ConfigureEnhancements>(this)},
|
enhancements_tab{std::make_unique<ConfigureEnhancements>(this)},
|
||||||
audio_tab{std::make_unique<ConfigureAudio>(is_powered_on, this)},
|
audio_tab{std::make_unique<ConfigureAudio>(is_powered_on, this)},
|
||||||
camera_tab{std::make_unique<ConfigureCamera>(this)},
|
camera_tab{std::make_unique<ConfigureCamera>(this)},
|
||||||
|
@ -37,7 +37,7 @@ class ConfigureDialog : public QDialog {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, Core::System& system,
|
explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, Core::System& system,
|
||||||
std::span<const QString> physical_devices,
|
QString gl_renderer, std::span<const QString> physical_devices,
|
||||||
bool enable_web_config = true);
|
bool enable_web_config = true);
|
||||||
~ConfigureDialog() override;
|
~ConfigureDialog() override;
|
||||||
|
|
||||||
|
@ -12,17 +12,13 @@
|
|||||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ConfigureGraphics::ConfigureGraphics(std::span<const QString> physical_devices, bool is_powered_on,
|
ConfigureGraphics::ConfigureGraphics(QString gl_renderer, std::span<const QString> physical_devices,
|
||||||
QWidget* parent)
|
bool is_powered_on, QWidget* parent)
|
||||||
: QWidget(parent), ui(std::make_unique<Ui::ConfigureGraphics>()) {
|
: QWidget(parent), ui(std::make_unique<Ui::ConfigureGraphics>()) {
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
SetupPerGameUI();
|
SetupPerGameUI();
|
||||||
|
|
||||||
for (const QString& name : physical_devices) {
|
|
||||||
ui->physical_device_combo->addItem(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
ui->graphics_api_combo->setEnabled(!is_powered_on);
|
ui->graphics_api_combo->setEnabled(!is_powered_on);
|
||||||
ui->physical_device_combo->setEnabled(!is_powered_on);
|
ui->physical_device_combo->setEnabled(!is_powered_on);
|
||||||
ui->toggle_async_shaders->setEnabled(!is_powered_on);
|
ui->toggle_async_shaders->setEnabled(!is_powered_on);
|
||||||
@ -37,11 +33,15 @@ ConfigureGraphics::ConfigureGraphics(std::span<const QString> physical_devices,
|
|||||||
graphics_api_combo_model->item(static_cast<u32>(Settings::GraphicsAPI::Software));
|
graphics_api_combo_model->item(static_cast<u32>(Settings::GraphicsAPI::Software));
|
||||||
software_item->setFlags(software_item->flags() & ~Qt::ItemIsEnabled);
|
software_item->setFlags(software_item->flags() & ~Qt::ItemIsEnabled);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef ENABLE_OPENGL
|
#ifndef ENABLE_OPENGL
|
||||||
const auto opengl_item =
|
const auto opengl_item =
|
||||||
graphics_api_combo_model->item(static_cast<u32>(Settings::GraphicsAPI::OpenGL));
|
graphics_api_combo_model->item(static_cast<u32>(Settings::GraphicsAPI::OpenGL));
|
||||||
opengl_item->setFlags(opengl_item->flags() & ~Qt::ItemIsEnabled);
|
opengl_item->setFlags(opengl_item->flags() & ~Qt::ItemIsEnabled);
|
||||||
|
#else
|
||||||
|
ui->opengl_renderer_name_label->setText(gl_renderer);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef ENABLE_VULKAN
|
#ifndef ENABLE_VULKAN
|
||||||
const auto vulkan_item =
|
const auto vulkan_item =
|
||||||
graphics_api_combo_model->item(static_cast<u32>(Settings::GraphicsAPI::Vulkan));
|
graphics_api_combo_model->item(static_cast<u32>(Settings::GraphicsAPI::Vulkan));
|
||||||
@ -54,6 +54,10 @@ ConfigureGraphics::ConfigureGraphics(std::span<const QString> physical_devices,
|
|||||||
|
|
||||||
ui->physical_device_combo->setVisible(false);
|
ui->physical_device_combo->setVisible(false);
|
||||||
ui->spirv_shader_gen->setVisible(false);
|
ui->spirv_shader_gen->setVisible(false);
|
||||||
|
} else {
|
||||||
|
for (const QString& name : physical_devices) {
|
||||||
|
ui->physical_device_combo->addItem(name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -202,24 +206,24 @@ void ConfigureGraphics::SetupPerGameUI() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureGraphics::SetPhysicalDeviceComboVisibility(int index) {
|
void ConfigureGraphics::SetPhysicalDeviceComboVisibility(int index) {
|
||||||
bool is_visible{};
|
Settings::GraphicsAPI effective_api{};
|
||||||
|
|
||||||
// When configuring per-game the physical device combo should be
|
// When configuring per-game the physical device combo should be
|
||||||
// shown either when the global api is used and that is Vulkan or
|
// shown either when the global api is used and that is Vulkan or
|
||||||
// Vulkan is set as the per-game api.
|
// Vulkan is set as the per-game api.
|
||||||
if (!Settings::IsConfiguringGlobal()) {
|
if (!Settings::IsConfiguringGlobal()) {
|
||||||
const auto global_graphics_api = Settings::values.graphics_api.GetValue(true);
|
|
||||||
const bool using_global = index == 0;
|
const bool using_global = index == 0;
|
||||||
if (!using_global) {
|
if (using_global) {
|
||||||
index -= ConfigurationShared::USE_GLOBAL_OFFSET;
|
effective_api = Settings::values.graphics_api.GetValue(true);
|
||||||
}
|
|
||||||
const auto graphics_api = static_cast<Settings::GraphicsAPI>(index);
|
|
||||||
is_visible = (using_global && global_graphics_api == Settings::GraphicsAPI::Vulkan) ||
|
|
||||||
graphics_api == Settings::GraphicsAPI::Vulkan;
|
|
||||||
} else {
|
} else {
|
||||||
const auto graphics_api = static_cast<Settings::GraphicsAPI>(index);
|
effective_api =
|
||||||
is_visible = graphics_api == Settings::GraphicsAPI::Vulkan;
|
static_cast<Settings::GraphicsAPI>(index - ConfigurationShared::USE_GLOBAL_OFFSET);
|
||||||
}
|
}
|
||||||
ui->physical_device_group->setVisible(is_visible);
|
} else {
|
||||||
ui->spirv_shader_gen->setVisible(is_visible);
|
effective_api = static_cast<Settings::GraphicsAPI>(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->physical_device_group->setVisible(effective_api == Settings::GraphicsAPI::Vulkan);
|
||||||
|
ui->spirv_shader_gen->setVisible(effective_api == Settings::GraphicsAPI::Vulkan);
|
||||||
|
ui->opengl_renderer_group->setVisible(effective_api == Settings::GraphicsAPI::OpenGL);
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,8 @@ class ConfigureGraphics : public QWidget {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ConfigureGraphics(std::span<const QString> physical_devices, bool is_powered_on,
|
explicit ConfigureGraphics(QString gl_renderer, std::span<const QString> physical_devices,
|
||||||
QWidget* parent = nullptr);
|
bool is_powered_on, QWidget* parent = nullptr);
|
||||||
~ConfigureGraphics() override;
|
~ConfigureGraphics() override;
|
||||||
|
|
||||||
void ApplyConfiguration();
|
void ApplyConfiguration();
|
||||||
|
@ -101,6 +101,34 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QWidget" name="opengl_renderer_group" native="true">
|
||||||
|
<layout class="QHBoxLayout" name="opengl_renderer_group_2">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="opengl_renderer_label">
|
||||||
|
<property name="text">
|
||||||
|
<string>OpenGL Renderer</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="opengl_renderer_name_label"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="spirv_shader_gen">
|
<widget class="QCheckBox" name="spirv_shader_gen">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -24,7 +24,8 @@
|
|||||||
#include "ui_configure_per_game.h"
|
#include "ui_configure_per_game.h"
|
||||||
|
|
||||||
ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const QString& file_name,
|
ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const QString& file_name,
|
||||||
std::span<const QString> physical_devices, Core::System& system_)
|
QString gl_renderer, std::span<const QString> physical_devices,
|
||||||
|
Core::System& system_)
|
||||||
: QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()),
|
: QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()),
|
||||||
filename{file_name.toStdString()}, title_id{title_id_}, system{system_} {
|
filename{file_name.toStdString()}, title_id{title_id_}, system{system_} {
|
||||||
const auto config_file_name = title_id == 0 ? std::string(FileUtil::GetFilename(filename))
|
const auto config_file_name = title_id == 0 ? std::string(FileUtil::GetFilename(filename))
|
||||||
@ -35,7 +36,8 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const QString
|
|||||||
audio_tab = std::make_unique<ConfigureAudio>(is_powered_on, this);
|
audio_tab = std::make_unique<ConfigureAudio>(is_powered_on, this);
|
||||||
general_tab = std::make_unique<ConfigureGeneral>(this);
|
general_tab = std::make_unique<ConfigureGeneral>(this);
|
||||||
enhancements_tab = std::make_unique<ConfigureEnhancements>(this);
|
enhancements_tab = std::make_unique<ConfigureEnhancements>(this);
|
||||||
graphics_tab = std::make_unique<ConfigureGraphics>(physical_devices, is_powered_on, this);
|
graphics_tab =
|
||||||
|
std::make_unique<ConfigureGraphics>(gl_renderer, physical_devices, is_powered_on, this);
|
||||||
system_tab = std::make_unique<ConfigureSystem>(system, this);
|
system_tab = std::make_unique<ConfigureSystem>(system, this);
|
||||||
debug_tab = std::make_unique<ConfigureDebug>(is_powered_on, this);
|
debug_tab = std::make_unique<ConfigureDebug>(is_powered_on, this);
|
||||||
cheat_tab = std::make_unique<ConfigureCheats>(system.CheatEngine(), title_id, this);
|
cheat_tab = std::make_unique<ConfigureCheats>(system.CheatEngine(), title_id, this);
|
||||||
|
@ -38,7 +38,8 @@ class ConfigurePerGame : public QDialog {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ConfigurePerGame(QWidget* parent, u64 title_id_, const QString& file_name,
|
explicit ConfigurePerGame(QWidget* parent, u64 title_id_, const QString& file_name,
|
||||||
std::span<const QString> physical_devices, Core::System& system_);
|
QString gl_renderer, std::span<const QString> physical_devices,
|
||||||
|
Core::System& system_);
|
||||||
~ConfigurePerGame() override;
|
~ConfigurePerGame() override;
|
||||||
|
|
||||||
/// Loads all button configurations to settings file
|
/// Loads all button configurations to settings file
|
||||||
|
@ -64,7 +64,7 @@
|
|||||||
#include "citra_qt/uisettings.h"
|
#include "citra_qt/uisettings.h"
|
||||||
#include "citra_qt/updater/updater.h"
|
#include "citra_qt/updater/updater.h"
|
||||||
#include "citra_qt/util/clickable_label.h"
|
#include "citra_qt/util/clickable_label.h"
|
||||||
#include "citra_qt/util/vk_device_info.h"
|
#include "citra_qt/util/graphics_device_info.h"
|
||||||
#include "common/arch.h"
|
#include "common/arch.h"
|
||||||
#include "common/common_paths.h"
|
#include "common/common_paths.h"
|
||||||
#include "common/detached_tasks.h"
|
#include "common/detached_tasks.h"
|
||||||
@ -270,6 +270,18 @@ GMainWindow::GMainWindow(Core::System& system_)
|
|||||||
connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor);
|
connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor);
|
||||||
connect(ui->menubar, &QMenuBar::hovered, this, &GMainWindow::OnMouseActivity);
|
connect(ui->menubar, &QMenuBar::hovered, this, &GMainWindow::OnMouseActivity);
|
||||||
|
|
||||||
|
#ifdef ENABLE_OPENGL
|
||||||
|
gl_renderer = GetOpenGLRenderer();
|
||||||
|
#if defined(_WIN32)
|
||||||
|
if (gl_renderer.startsWith(QStringLiteral("D3D12"))) {
|
||||||
|
// OpenGLOn12 supports but does not yet advertise OpenGL 4.0+
|
||||||
|
// We can override the version here to allow Citra to work.
|
||||||
|
// TODO: Remove this when OpenGL 4.0+ is advertised.
|
||||||
|
qputenv("MESA_GL_VERSION_OVERRIDE", "4.6");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef ENABLE_VULKAN
|
#ifdef ENABLE_VULKAN
|
||||||
physical_devices = GetVulkanPhysicalDevices();
|
physical_devices = GetVulkanPhysicalDevices();
|
||||||
if (physical_devices.empty()) {
|
if (physical_devices.empty()) {
|
||||||
@ -2158,7 +2170,7 @@ void GMainWindow::OnLoadState() {
|
|||||||
void GMainWindow::OnConfigure() {
|
void GMainWindow::OnConfigure() {
|
||||||
game_list->SetDirectoryWatcherEnabled(false);
|
game_list->SetDirectoryWatcherEnabled(false);
|
||||||
Settings::SetConfiguringGlobal(true);
|
Settings::SetConfiguringGlobal(true);
|
||||||
ConfigureDialog configureDialog(this, hotkey_registry, system, physical_devices,
|
ConfigureDialog configureDialog(this, hotkey_registry, system, gl_renderer, physical_devices,
|
||||||
!multiplayer_state->IsHostingPublicRoom());
|
!multiplayer_state->IsHostingPublicRoom());
|
||||||
connect(&configureDialog, &ConfigureDialog::LanguageChanged, this,
|
connect(&configureDialog, &ConfigureDialog::LanguageChanged, this,
|
||||||
&GMainWindow::OnLanguageChanged);
|
&GMainWindow::OnLanguageChanged);
|
||||||
@ -3006,7 +3018,7 @@ void GMainWindow::OnConfigurePerGame() {
|
|||||||
|
|
||||||
void GMainWindow::OpenPerGameConfiguration(u64 title_id, const QString& file_name) {
|
void GMainWindow::OpenPerGameConfiguration(u64 title_id, const QString& file_name) {
|
||||||
Settings::SetConfiguringGlobal(false);
|
Settings::SetConfiguringGlobal(false);
|
||||||
ConfigurePerGame dialog(this, title_id, file_name, physical_devices, system);
|
ConfigurePerGame dialog(this, title_id, file_name, gl_renderer, physical_devices, system);
|
||||||
const auto result = dialog.exec();
|
const auto result = dialog.exec();
|
||||||
|
|
||||||
if (result != QDialog::Accepted) {
|
if (result != QDialog::Accepted) {
|
||||||
|
@ -344,6 +344,7 @@ private:
|
|||||||
// Whether game was paused due to stopping video dumping
|
// Whether game was paused due to stopping video dumping
|
||||||
bool game_paused_for_dumping = false;
|
bool game_paused_for_dumping = false;
|
||||||
|
|
||||||
|
QString gl_renderer;
|
||||||
std::vector<QString> physical_devices;
|
std::vector<QString> physical_devices;
|
||||||
|
|
||||||
// Debugger panes
|
// Debugger panes
|
||||||
|
@ -2,9 +2,34 @@
|
|||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include "citra_qt/util/vk_device_info.h"
|
#include "citra_qt/util/graphics_device_info.h"
|
||||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
|
||||||
|
|
||||||
|
#ifdef ENABLE_OPENGL
|
||||||
|
#include <QOffscreenSurface>
|
||||||
|
#include <QOpenGLContext>
|
||||||
|
#include <QOpenGLFunctions>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENABLE_VULKAN
|
||||||
|
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENABLE_OPENGL
|
||||||
|
QString GetOpenGLRenderer() {
|
||||||
|
QOffscreenSurface surface;
|
||||||
|
surface.create();
|
||||||
|
|
||||||
|
QOpenGLContext context;
|
||||||
|
if (context.create()) {
|
||||||
|
context.makeCurrent(&surface);
|
||||||
|
return QString::fromUtf8(context.functions()->glGetString(GL_RENDERER));
|
||||||
|
} else {
|
||||||
|
return QStringLiteral("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENABLE_VULKAN
|
||||||
std::vector<QString> GetVulkanPhysicalDevices() {
|
std::vector<QString> GetVulkanPhysicalDevices() {
|
||||||
std::vector<QString> result;
|
std::vector<QString> result;
|
||||||
try {
|
try {
|
||||||
@ -21,3 +46,4 @@ std::vector<QString> GetVulkanPhysicalDevices() {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
#endif
|
@ -7,5 +7,12 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
#ifdef ENABLE_OPENGL
|
||||||
|
/// Returns the name of the OpenGL renderer.
|
||||||
|
QString GetOpenGLRenderer();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENABLE_VULKAN
|
||||||
/// Returns a list of all available vulkan GPUs.
|
/// Returns a list of all available vulkan GPUs.
|
||||||
std::vector<QString> GetVulkanPhysicalDevices();
|
std::vector<QString> GetVulkanPhysicalDevices();
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user