citra_qt: Prepare GUI for Vulkan support

This commit is contained in:
emufan4568
2022-09-06 19:05:43 +03:00
committed by GPUCode
parent b588d6181b
commit 0fe61ba040
22 changed files with 366 additions and 258 deletions

View File

@ -23,7 +23,6 @@
#include "core/frontend/applets/default_applets.h" #include "core/frontend/applets/default_applets.h"
#include "core/frontend/camera/factory.h" #include "core/frontend/camera/factory.h"
#include "core/frontend/mic.h" #include "core/frontend/mic.h"
#include "core/frontend/scope_acquire_context.h"
#include "core/hle/service/am/am.h" #include "core/hle/service/am/am.h"
#include "core/hle/service/nfc/nfc.h" #include "core/hle/service/nfc/nfc.h"
#include "core/savestate.h" #include "core/savestate.h"

View File

@ -29,7 +29,6 @@
#include "core/file_sys/cia_container.h" #include "core/file_sys/cia_container.h"
#include "core/frontend/applets/default_applets.h" #include "core/frontend/applets/default_applets.h"
#include "core/frontend/framebuffer_layout.h" #include "core/frontend/framebuffer_layout.h"
#include "core/frontend/scope_acquire_context.h"
#include "core/gdbstub/gdbstub.h" #include "core/gdbstub/gdbstub.h"
#include "core/hle/service/am/am.h" #include "core/hle/service/am/am.h"
#include "core/hle/service/cfg/cfg.h" #include "core/hle/service/cfg/cfg.h"
@ -368,7 +367,7 @@ int main(int argc, char** argv) {
const auto secondary_window = const auto secondary_window =
use_secondary_window ? std::make_unique<EmuWindow_SDL2>(false, true) : nullptr; use_secondary_window ? std::make_unique<EmuWindow_SDL2>(false, true) : nullptr;
Frontend::ScopeAcquireContext scope(*emu_window); const auto scope = emu_window->Acquire();
LOG_INFO(Frontend, "Citra Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, LOG_INFO(Frontend, "Citra Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch,
Common::g_scm_desc); Common::g_scm_desc);

View File

@ -109,7 +109,8 @@ void Config::ReadValues() {
sdl2_config->GetInteger("Core", "cpu_clock_percentage", 100); sdl2_config->GetInteger("Core", "cpu_clock_percentage", 100);
// Renderer // Renderer
Settings::values.use_gles = sdl2_config->GetBoolean("Renderer", "use_gles", false); Settings::values.graphics_api =
static_cast<Settings::GraphicsAPI>(sdl2_config->GetInteger("Renderer", "graphics_api", 0));
Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", true); Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", true);
Settings::values.use_hw_shader = sdl2_config->GetBoolean("Renderer", "use_hw_shader", true); Settings::values.use_hw_shader = sdl2_config->GetBoolean("Renderer", "use_hw_shader", true);
#ifdef __APPLE__ #ifdef __APPLE__

View File

@ -137,7 +137,8 @@ void EmuWindow_SDL2::Fullscreen() {
EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen, bool is_secondary) : EmuWindow(is_secondary) { EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen, bool is_secondary) : EmuWindow(is_secondary) {
// Initialize the window // Initialize the window
if (Settings::values.use_gles) { const bool is_opengles = Settings::values.graphics_api == Settings::GraphicsAPI::OpenGLES;
if (is_opengles) {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
@ -192,7 +193,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen, bool is_secondary) : EmuWindow(i
} }
render_window_id = SDL_GetWindowID(render_window); render_window_id = SDL_GetWindowID(render_window);
auto gl_load_func = Settings::values.use_gles ? gladLoadGLES2Loader : gladLoadGLLoader; auto gl_load_func = is_opengles ? gladLoadGLES2Loader : gladLoadGLLoader;
if (!gl_load_func(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) { if (!gl_load_func(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) {
LOG_CRITICAL(Frontend, "Failed to initialize GL functions: {}", SDL_GetError()); LOG_CRITICAL(Frontend, "Failed to initialize GL functions: {}", SDL_GetError());

View File

@ -6,10 +6,11 @@
#include <QDragEnterEvent> #include <QDragEnterEvent>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QKeyEvent> #include <QKeyEvent>
#include <QMessageBox>
#include <QOffscreenSurface> #include <QOffscreenSurface>
#include <QOpenGLContext> #include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QOpenGLFunctions_4_3_Core> #include <QOpenGLFunctions_4_3_Core>
#include <QOpenGLExtraFunctions>
#include <fmt/format.h> #include <fmt/format.h>
#include "citra_qt/bootmanager.h" #include "citra_qt/bootmanager.h"
#include "citra_qt/main.h" #include "citra_qt/main.h"
@ -18,12 +19,9 @@
#include "common/settings.h" #include "common/settings.h"
#include "core/3ds.h" #include "core/3ds.h"
#include "core/core.h" #include "core/core.h"
#include "core/frontend/scope_acquire_context.h"
#include "core/perf_stats.h"
#include "input_common/keyboard.h" #include "input_common/keyboard.h"
#include "input_common/main.h" #include "input_common/main.h"
#include "input_common/motion_emu.h" #include "input_common/motion_emu.h"
#include "network/network.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
#include "video_core/video_core.h" #include "video_core/video_core.h"
@ -44,7 +42,7 @@ static GMainWindow* GetMainWindow() {
void EmuThread::run() { void EmuThread::run() {
MicroProfileOnThreadCreate("EmuThread"); MicroProfileOnThreadCreate("EmuThread");
Frontend::ScopeAcquireContext scope(core_context); const auto scope = core_context.Acquire();
emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
@ -113,90 +111,198 @@ void EmuThread::run() {
#endif #endif
} }
OpenGLWindow::OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context, class OpenGLSharedContext : public Frontend::GraphicsContext {
bool is_secondary) public:
: QWindow(parent), context(std::make_unique<QOpenGLContext>(shared_context->parent())), /// Create the original context that should be shared from
event_handler(event_handler), is_secondary{is_secondary} { explicit OpenGLSharedContext(QSurface* surface) : surface(surface) {
QSurfaceFormat format;
format.setVersion(4, 3);
format.setProfile(QSurfaceFormat::CoreProfile);
// TODO: expose a setting for buffer value (ie default/single/double/triple)
format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
format.setSwapInterval(0);
// disable vsync for any shared contexts context = std::make_unique<QOpenGLContext>();
auto format = shared_context->format();
format.setSwapInterval(Settings::values.use_vsync_new ? 1 : 0);
this->setFormat(format);
context->setShareContext(shared_context);
context->setScreen(this->screen());
context->setFormat(format); context->setFormat(format);
context->create(); if (!context->create()) {
LOG_ERROR(Frontend, "Unable to create main openGL context");
setSurfaceType(QWindow::OpenGLSurface); }
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
// WA_DontShowOnScreen, WA_DeleteOnClose
} }
OpenGLWindow::~OpenGLWindow() { /// Create the shared contexts for rendering and presentation
explicit OpenGLSharedContext(QOpenGLContext* share_context, QSurface* main_surface = nullptr) {
// disable vsync for any shared contexts
auto format = share_context->format();
format.setSwapInterval(main_surface ? Settings::values.use_vsync_new : 0);
context = std::make_unique<QOpenGLContext>();
context->setShareContext(share_context);
context->setFormat(format);
if (!context->create()) {
LOG_ERROR(Frontend, "Unable to create shared openGL context");
}
if (!main_surface) {
offscreen_surface = std::make_unique<QOffscreenSurface>(nullptr);
offscreen_surface->setFormat(format);
offscreen_surface->create();
surface = offscreen_surface.get();
} else {
surface = main_surface;
}
}
~OpenGLSharedContext() {
context->doneCurrent(); context->doneCurrent();
} }
void OpenGLWindow::Present() { void SwapBuffers() override {
if (!isExposed()) context->swapBuffers(surface);
return;
context->makeCurrent(this);
if (VideoCore::g_renderer) {
VideoCore::g_renderer->TryPresent(100, is_secondary);
}
context->swapBuffers(this);
auto f = context->versionFunctions<QOpenGLFunctions_4_3_Core>();
f->glFinish();
QWindow::requestUpdate();
} }
bool OpenGLWindow::event(QEvent* event) { void MakeCurrent() override {
switch (event->type()) { // We can't track the current state of the underlying context in this wrapper class because
case QEvent::UpdateRequest: // Qt may make the underlying context not current for one reason or another. In particular,
// the WebBrowser uses GL, so it seems to conflict if we aren't careful.
// Instead of always just making the context current (which does not have any caching to
// check if the underlying context is already current) we can check for the current context
// in the thread local data by calling `currentContext()` and checking if its ours.
if (QOpenGLContext::currentContext() != context.get()) {
context->makeCurrent(surface);
}
}
void DoneCurrent() override {
context->doneCurrent();
}
QOpenGLContext* GetShareContext() const {
return context.get();
}
private:
// Avoid using Qt parent system here since we might move the QObjects to new threads
// As a note, this means we should avoid using slots/signals with the objects too
std::unique_ptr<QOpenGLContext> context;
std::unique_ptr<QOffscreenSurface> offscreen_surface{};
QSurface* surface;
};
class DummyContext : public Frontend::GraphicsContext {};
class RenderWidget : public QWidget {
public:
RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) {
setAttribute(Qt::WA_NativeWindow);
setAttribute(Qt::WA_PaintOnScreen);
}
virtual ~RenderWidget() = default;
virtual void Present() {}
void paintEvent(QPaintEvent* event) override {
Present(); Present();
return true; update();
case QEvent::MouseButtonPress: }
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick: void resizeEvent(QResizeEvent* ev) override {
case QEvent::MouseMove: render_window->resize(ev->size());
case QEvent::KeyPress: render_window->OnFramebufferSizeChanged();
case QEvent::KeyRelease: }
case QEvent::FocusIn:
case QEvent::FocusOut: void keyPressEvent(QKeyEvent* event) override {
case QEvent::FocusAboutToChange: InputCommon::GetKeyboard()->PressKey(event->key());
case QEvent::Enter: }
case QEvent::Leave:
case QEvent::Wheel: void keyReleaseEvent(QKeyEvent* event) override {
case QEvent::TabletMove: InputCommon::GetKeyboard()->ReleaseKey(event->key());
case QEvent::TabletPress: }
case QEvent::TabletRelease:
case QEvent::TabletEnterProximity: void mousePressEvent(QMouseEvent* event) override {
case QEvent::TabletLeaveProximity: if (event->source() == Qt::MouseEventSynthesizedBySystem)
case QEvent::TouchBegin: return; // touch input is handled in TouchBeginEvent
case QEvent::TouchUpdate:
case QEvent::TouchEnd: const auto pos{event->pos()};
case QEvent::InputMethodQuery: if (event->button() == Qt::LeftButton) {
case QEvent::TouchCancel: const auto [x, y] = render_window->ScaleTouch(pos);
return QCoreApplication::sendEvent(event_handler, event); render_window->TouchPressed(x, y);
case QEvent::Drop: } else if (event->button() == Qt::RightButton) {
GetMainWindow()->DropAction(static_cast<QDropEvent*>(event)); InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
return true;
case QEvent::DragEnter:
case QEvent::DragMove:
GetMainWindow()->AcceptDropEvent(static_cast<QDropEvent*>(event));
return true;
default:
return QWindow::event(event);
} }
} }
void OpenGLWindow::exposeEvent(QExposeEvent* event) { void mouseMoveEvent(QMouseEvent* event) override {
QWindow::requestUpdate(); if (event->source() == Qt::MouseEventSynthesizedBySystem)
QWindow::exposeEvent(event); return; // touch input is handled in TouchUpdateEvent
const auto pos{event->pos()};
const auto [x, y] = render_window->ScaleTouch(pos);
render_window->TouchMoved(x, y);
InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
} }
void mouseReleaseEvent(QMouseEvent* event) override {
if (event->source() == Qt::MouseEventSynthesizedBySystem)
return; // touch input is handled in TouchEndEvent
if (event->button() == Qt::LeftButton)
render_window->TouchReleased();
else if (event->button() == Qt::RightButton)
InputCommon::GetMotionEmu()->EndTilt();
}
std::pair<unsigned, unsigned> GetSize() const {
return std::make_pair(width(), height());
}
QPaintEngine* paintEngine() const override {
return nullptr;
}
private:
GRenderWindow* render_window;
};
class OpenGLRenderWidget : public RenderWidget {
public:
explicit OpenGLRenderWidget(GRenderWindow* parent, bool is_secondary) :
RenderWidget(parent), is_secondary(is_secondary) {
windowHandle()->setSurfaceType(QWindow::OpenGLSurface);
}
void SetContext(std::unique_ptr<OpenGLSharedContext>&& context_) {
context = std::move(context_);
}
void Present() override {
if (!isVisible()) {
return;
}
if (!Core::System::GetInstance().IsPoweredOn()) {
return;
}
context->MakeCurrent();
const auto f = context->GetShareContext()->extraFunctions();
f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
VideoCore::g_renderer->TryPresent(100, is_secondary);
context->SwapBuffers();
f->glFinish();
}
private:
std::unique_ptr<OpenGLSharedContext> context{};
bool is_secondary;
};
class VulkanRenderWidget : public RenderWidget {
public:
explicit VulkanRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {
windowHandle()->setSurfaceType(QWindow::VulkanSurface);
}
};
GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread, bool is_secondary_) GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread, bool is_secondary_)
: QWidget(parent_), EmuWindow(is_secondary_), emu_thread(emu_thread) { : QWidget(parent_), EmuWindow(is_secondary_), emu_thread(emu_thread) {
@ -218,11 +324,11 @@ GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread, bool is_se
GRenderWindow::~GRenderWindow() = default; GRenderWindow::~GRenderWindow() = default;
void GRenderWindow::MakeCurrent() { void GRenderWindow::MakeCurrent() {
core_context->MakeCurrent(); main_context->MakeCurrent();
} }
void GRenderWindow::DoneCurrent() { void GRenderWindow::DoneCurrent() {
core_context->DoneCurrent(); main_context->DoneCurrent();
} }
void GRenderWindow::PollEvents() { void GRenderWindow::PollEvents() {
@ -393,34 +499,67 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) {
OnFramebufferSizeChanged(); OnFramebufferSizeChanged();
} }
void GRenderWindow::InitRenderTarget() { std::unique_ptr<Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
const Settings::GraphicsAPI graphics_api = Settings::values.graphics_api;
if (graphics_api == Settings::GraphicsAPI::OpenGL ||
graphics_api == Settings::GraphicsAPI::OpenGLES) {
auto c = static_cast<OpenGLSharedContext*>(main_context.get());
// Bind the shared contexts to the main surface in case the backend wants to take over
// presentation
return std::make_unique<OpenGLSharedContext>(c->GetShareContext(),
child_widget->windowHandle());
}
return std::make_unique<DummyContext>();
}
bool GRenderWindow::InitRenderTarget() {
ReleaseRenderTarget(); ReleaseRenderTarget();
{
// Create a dummy render widget so that Qt
// places the render window at the correct position.
const RenderWidget dummy_widget{this};
}
first_frame = false; first_frame = false;
GMainWindow* parent = GetMainWindow(); const Settings::GraphicsAPI graphics_api = Settings::values.graphics_api;
QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr; switch (graphics_api) {
child_window = new OpenGLWindow(parent_win_handle, this, QOpenGLContext::globalShareContext(), case Settings::GraphicsAPI::OpenGL:
is_secondary); case Settings::GraphicsAPI::OpenGLES:
child_window->create(); if (!InitializeOpenGL()) {
child_widget = createWindowContainer(child_window, this); return false;
}
break;
case Settings::GraphicsAPI::Vulkan:
if (!InitializeVulkan()) {
return false;
}
break;
}
child_widget->resize(Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight); child_widget->resize(Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight);
layout()->addWidget(child_widget); layout()->addWidget(child_widget);
// Reset minimum required size to avoid resizing issues on the main window after restarting.
setMinimumSize(1, 1);
core_context = CreateSharedContext();
resize(Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight); resize(Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight);
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
OnFramebufferSizeChanged(); OnFramebufferSizeChanged();
BackupGeometry(); BackupGeometry();
return true;
} }
void GRenderWindow::ReleaseRenderTarget() { void GRenderWindow::ReleaseRenderTarget() {
if (child_widget) { if (child_widget) {
layout()->removeWidget(child_widget); layout()->removeWidget(child_widget);
delete child_widget; child_widget->deleteLater();
child_widget = nullptr; child_widget = nullptr;
} }
main_context.reset();
} }
void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) {
@ -445,6 +584,37 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal
setMinimumSize(minimal_size.first, minimal_size.second); setMinimumSize(minimal_size.first, minimal_size.second);
} }
bool GRenderWindow::InitializeOpenGL() {
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
// WA_DontShowOnScreen, WA_DeleteOnClose
auto child = new OpenGLRenderWidget(this, is_secondary);
child_widget = child;
child_widget->windowHandle()->create();
auto context = std::make_shared<OpenGLSharedContext>(child->windowHandle());
main_context = context;
child->SetContext(
std::make_unique<OpenGLSharedContext>(context->GetShareContext(), child->windowHandle()));
// Check for OpenGL 4.3 support
if (!QOpenGLContext::globalShareContext()->versionFunctions<QOpenGLFunctions_4_3_Core>()) {
QMessageBox::critical(this, tr("OpenGL 4.3 Unsupported"),
tr("Your GPU may not support OpenGL 4.3, or you do not "
"have the latest graphics driver."));
return false;
}
return true;
}
bool GRenderWindow::InitializeVulkan() {
auto child = new VulkanRenderWidget(this);
child_widget = child;
child_widget->windowHandle()->create();
main_context = std::make_unique<DummyContext>();
return true;
}
void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) {
this->emu_thread = emu_thread; this->emu_thread = emu_thread;
} }
@ -456,31 +626,3 @@ void GRenderWindow::OnEmulationStopping() {
void GRenderWindow::showEvent(QShowEvent* event) { void GRenderWindow::showEvent(QShowEvent* event) {
QWidget::showEvent(event); QWidget::showEvent(event);
} }
std::unique_ptr<Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
return std::make_unique<GLContext>(QOpenGLContext::globalShareContext());
}
GLContext::GLContext(QOpenGLContext* shared_context)
: context(std::make_unique<QOpenGLContext>(shared_context->parent())),
surface(std::make_unique<QOffscreenSurface>(nullptr)) {
// disable vsync for any shared contexts
auto format = shared_context->format();
format.setSwapInterval(0);
context->setShareContext(shared_context);
context->setFormat(format);
context->create();
surface->setParent(shared_context->parent());
surface->setFormat(format);
surface->create();
}
void GLContext::MakeCurrent() {
context->makeCurrent(surface.get());
}
void GLContext::DoneCurrent() {
context->doneCurrent();
}

View File

@ -27,19 +27,6 @@ namespace VideoCore {
enum class LoadCallbackStage; enum class LoadCallbackStage;
} }
class GLContext : public Frontend::GraphicsContext {
public:
explicit GLContext(QOpenGLContext* shared_context);
void MakeCurrent() override;
void DoneCurrent() override;
private:
std::unique_ptr<QOpenGLContext> context;
std::unique_ptr<QOffscreenSurface> surface;
};
class EmuThread final : public QThread { class EmuThread final : public QThread {
Q_OBJECT Q_OBJECT
@ -126,26 +113,6 @@ signals:
void HideLoadingScreen(); void HideLoadingScreen();
}; };
class OpenGLWindow : public QWindow {
Q_OBJECT
public:
explicit OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context,
bool is_secondary = false);
~OpenGLWindow();
void Present();
protected:
bool event(QEvent* event) override;
void exposeEvent(QExposeEvent* event) override;
private:
std::unique_ptr<QOpenGLContext> context;
QWidget* event_handler;
bool is_secondary;
};
class GRenderWindow : public QWidget, public Frontend::EmuWindow { class GRenderWindow : public QWidget, public Frontend::EmuWindow {
Q_OBJECT Q_OBJECT
@ -185,13 +152,15 @@ public:
return has_focus; return has_focus;
} }
void InitRenderTarget(); bool InitRenderTarget();
/// Destroy the previous run's child_widget which should also destroy the child_window /// Destroy the previous run's child_widget which should also destroy the child_window
void ReleaseRenderTarget(); void ReleaseRenderTarget();
void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); void CaptureScreenshot(u32 res_scale, const QString& screenshot_path);
std::pair<u32, u32> ScaleTouch(const QPointF pos) const;
public slots: public slots:
void OnEmulationStarting(EmuThread* emu_thread); void OnEmulationStarting(EmuThread* emu_thread);
@ -211,29 +180,29 @@ signals:
void MouseActivity(); void MouseActivity();
private: private:
std::pair<u32, u32> ScaleTouch(QPointF pos) const;
void TouchBeginEvent(const QTouchEvent* event); void TouchBeginEvent(const QTouchEvent* event);
void TouchUpdateEvent(const QTouchEvent* event); void TouchUpdateEvent(const QTouchEvent* event);
void TouchEndEvent(); void TouchEndEvent();
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override; void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
std::unique_ptr<GraphicsContext> core_context; bool InitializeOpenGL();
bool InitializeVulkan();
QByteArray geometry;
/// Native window handle that backs this presentation widget
QWindow* child_window = nullptr;
/// In order to embed the window into GRenderWindow, you need to use createWindowContainer to
/// put the child_window into a widget then add it to the layout. This child_widget can be
/// parented to GRenderWindow and use Qt's lifetime system
QWidget* child_widget = nullptr;
EmuThread* emu_thread; EmuThread* emu_thread;
// Main context that will be shared with all other contexts that are requested.
// If this is used in a shared context setting, then this should not be used directly, but
// should instead be shared from
std::shared_ptr<Frontend::GraphicsContext> main_context;
/// Temporary storage of the screenshot taken /// Temporary storage of the screenshot taken
QImage screenshot_image; QImage screenshot_image;
QByteArray geometry;
QWidget* child_widget = nullptr;
bool first_frame = false; bool first_frame = false;
bool has_focus = false; bool has_focus = false;

View File

@ -19,9 +19,10 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
SetupPerGameUI(); SetupPerGameUI();
SetConfiguration(); SetConfiguration();
ui->hw_renderer_group->setEnabled(ui->hw_renderer_group->isEnabled() && const bool not_running = !Core::System::GetInstance().IsPoweredOn();
ui->toggle_hw_renderer->isChecked()); ui->hw_renderer_group->setEnabled(ui->toggle_hw_renderer->isChecked());
ui->toggle_vsync_new->setEnabled(!Core::System::GetInstance().IsPoweredOn()); ui->toggle_vsync_new->setEnabled(not_running);
ui->graphics_api_combo->setEnabled(not_running);
connect(ui->toggle_hw_renderer, &QCheckBox::toggled, this, [this] { connect(ui->toggle_hw_renderer, &QCheckBox::toggled, this, [this] {
const bool checked = ui->toggle_hw_renderer->isChecked(); const bool checked = ui->toggle_hw_renderer->isChecked();

View File

@ -26,6 +26,36 @@
<string>Renderer</string> <string>Renderer</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_6"> <layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Graphics API</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="graphics_api_combo">
<item>
<property name="text">
<string>OpenGL</string>
</property>
</item>
<item>
<property name="text">
<string>OpenGLES</string>
</property>
</item>
<item>
<property name="text">
<string>Vulkan</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item> <item>
<widget class="QCheckBox" name="toggle_hw_renderer"> <widget class="QCheckBox" name="toggle_hw_renderer">
<property name="toolTip"> <property name="toolTip">

View File

@ -18,12 +18,6 @@
#include <QtGui> #include <QtGui>
#include <QtWidgets> #include <QtWidgets>
#include <fmt/format.h> #include <fmt/format.h>
#ifdef __APPLE__
#include <unistd.h> // for chdir
#endif
#ifdef _WIN32
#include <windows.h>
#endif
#include "citra_qt/aboutdialog.h" #include "citra_qt/aboutdialog.h"
#include "citra_qt/applets/mii_selector.h" #include "citra_qt/applets/mii_selector.h"
#include "citra_qt/applets/swkbd.h" #include "citra_qt/applets/swkbd.h"
@ -82,7 +76,6 @@
#include "core/file_sys/archive_extsavedata.h" #include "core/file_sys/archive_extsavedata.h"
#include "core/file_sys/archive_source_sd_savedata.h" #include "core/file_sys/archive_source_sd_savedata.h"
#include "core/frontend/applets/default_applets.h" #include "core/frontend/applets/default_applets.h"
#include "core/frontend/scope_acquire_context.h"
#include "core/gdbstub/gdbstub.h" #include "core/gdbstub/gdbstub.h"
#include "core/hle/service/fs/archive.h" #include "core/hle/service/fs/archive.h"
#include "core/hle/service/nfc/nfc.h" #include "core/hle/service/nfc/nfc.h"
@ -96,6 +89,13 @@
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
#include "video_core/video_core.h" #include "video_core/video_core.h"
#ifdef __APPLE__
#include <unistd.h> // for chdir
#endif
#ifdef _WIN32
#include <windows.h>
#endif
#ifdef USE_DISCORD_PRESENCE #ifdef USE_DISCORD_PRESENCE
#include "citra_qt/discord_impl.h" #include "citra_qt/discord_impl.h"
#endif #endif
@ -954,16 +954,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
render_window->InitRenderTarget(); render_window->InitRenderTarget();
secondary_window->InitRenderTarget(); secondary_window->InitRenderTarget();
Frontend::ScopeAcquireContext scope(*render_window); const auto scope = render_window->Acquire();
const QString below_gl43_title = tr("OpenGL 4.3 Unsupported");
const QString below_gl43_message = tr("Your GPU may not support OpenGL 4.3, or you do not "
"have the latest graphics driver.");
if (!QOpenGLContext::globalShareContext()->versionFunctions<QOpenGLFunctions_4_3_Core>()) {
QMessageBox::critical(this, below_gl43_title, below_gl43_message);
return false;
}
Core::System& system{Core::System::GetInstance()}; Core::System& system{Core::System::GetInstance()};
@ -1017,7 +1008,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
case Core::System::ResultStatus::ErrorVideoCore: case Core::System::ResultStatus::ErrorVideoCore:
QMessageBox::critical( QMessageBox::critical(
this, tr("Video Core Error"), this, tr("Video Core Error"),
tr("An error has occurred. Please <a " tr("An error has occurred during intialization of the video backend. Please <a "
"href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>see " "href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>see "
"the " "the "
"log</a> for more details. " "log</a> for more details. "
@ -1032,10 +1023,6 @@ bool GMainWindow::LoadROM(const QString& filename) {
"proper drivers for your graphics card from the manufacturer's website.")); "proper drivers for your graphics card from the manufacturer's website."));
break; break;
case Core::System::ResultStatus::ErrorVideoCore_ErrorBelowGL43:
QMessageBox::critical(this, below_gl43_title, below_gl43_message);
break;
default: default:
QMessageBox::critical( QMessageBox::critical(
this, tr("Error while loading ROM!"), this, tr("Error while loading ROM!"),

View File

@ -8,7 +8,6 @@
#include "common/settings.h" #include "common/settings.h"
#include "core/core.h" #include "core/core.h"
#include "core/gdbstub/gdbstub.h" #include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/shared_page.h"
#include "core/hle/service/cam/cam.h" #include "core/hle/service/cam/cam.h"
#include "core/hle/service/hid/hid.h" #include "core/hle/service/hid/hid.h"
#include "core/hle/service/ir/ir_rst.h" #include "core/hle/service/ir/ir_rst.h"
@ -95,7 +94,7 @@ void LogSettings() {
LOG_INFO(Config, "Citra Configuration:"); LOG_INFO(Config, "Citra Configuration:");
log_setting("Core_UseCpuJit", values.use_cpu_jit.GetValue()); log_setting("Core_UseCpuJit", values.use_cpu_jit.GetValue());
log_setting("Core_CPUClockPercentage", values.cpu_clock_percentage.GetValue()); log_setting("Core_CPUClockPercentage", values.cpu_clock_percentage.GetValue());
log_setting("Renderer_UseGLES", values.use_gles.GetValue()); log_setting("Renderer_GraphicsAPI", values.graphics_api.GetValue());
log_setting("Renderer_UseHwRenderer", values.use_hw_renderer.GetValue()); log_setting("Renderer_UseHwRenderer", values.use_hw_renderer.GetValue());
log_setting("Renderer_UseHwShader", values.use_hw_shader.GetValue()); log_setting("Renderer_UseHwShader", values.use_hw_shader.GetValue());
log_setting("Renderer_SeparableShader", values.separable_shader.GetValue()); log_setting("Renderer_SeparableShader", values.separable_shader.GetValue());

View File

@ -15,6 +15,12 @@
namespace Settings { namespace Settings {
enum class GraphicsAPI {
OpenGL = 0,
OpenGLES = 1,
Vulkan = 2,
};
enum class InitClock : u32 { enum class InitClock : u32 {
SystemTime = 0, SystemTime = 0,
FixedTime = 1, FixedTime = 1,
@ -440,7 +446,7 @@ struct Values {
Setting<bool> allow_plugin_loader{true, "allow_plugin_loader"}; Setting<bool> allow_plugin_loader{true, "allow_plugin_loader"};
// Renderer // Renderer
Setting<bool> use_gles{false, "use_gles"}; SwitchableSetting<GraphicsAPI> graphics_api{GraphicsAPI::OpenGL, "graphics_api"};
SwitchableSetting<bool> use_hw_renderer{true, "use_hw_renderer"}; SwitchableSetting<bool> use_hw_renderer{true, "use_hw_renderer"};
SwitchableSetting<bool> use_hw_shader{true, "use_hw_shader"}; SwitchableSetting<bool> use_hw_shader{true, "use_hw_shader"};
SwitchableSetting<bool> separable_shader{false, "use_separable_shader"}; SwitchableSetting<bool> separable_shader{false, "use_separable_shader"};

View File

@ -113,8 +113,6 @@ add_library(core STATIC
frontend/input.h frontend/input.h
frontend/mic.cpp frontend/mic.cpp
frontend/mic.h frontend/mic.h
frontend/scope_acquire_context.cpp
frontend/scope_acquire_context.h
gdbstub/gdbstub.cpp gdbstub/gdbstub.cpp
gdbstub/gdbstub.h gdbstub/gdbstub.h
hle/applets/applet.cpp hle/applets/applet.cpp

View File

@ -431,8 +431,6 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window,
switch (result) { switch (result) {
case VideoCore::ResultStatus::ErrorGenericDrivers: case VideoCore::ResultStatus::ErrorGenericDrivers:
return ResultStatus::ErrorVideoCore_ErrorGenericDrivers; return ResultStatus::ErrorVideoCore_ErrorGenericDrivers;
case VideoCore::ResultStatus::ErrorBelowGL43:
return ResultStatus::ErrorVideoCore_ErrorBelowGL43;
default: default:
return ResultStatus::ErrorVideoCore; return ResultStatus::ErrorVideoCore;
} }

View File

@ -88,8 +88,6 @@ public:
ErrorVideoCore, ///< Error in the video core ErrorVideoCore, ///< Error in the video core
ErrorVideoCore_ErrorGenericDrivers, ///< Error in the video core due to the user having ErrorVideoCore_ErrorGenericDrivers, ///< Error in the video core due to the user having
/// generic drivers installed /// generic drivers installed
ErrorVideoCore_ErrorBelowGL43, ///< Error in the video core due to the user not having
/// OpenGL 4.3 or higher
ErrorSavestate, ///< Error saving or loading ErrorSavestate, ///< Error saving or loading
ShutdownRequested, ///< Emulated program requested a system shutdown ShutdownRequested, ///< Emulated program requested a system shutdown
ErrorUnknown ///< Any other error ErrorUnknown ///< Any other error

View File

@ -62,11 +62,33 @@ class GraphicsContext {
public: public:
virtual ~GraphicsContext(); virtual ~GraphicsContext();
/// Inform the driver to swap the front/back buffers and present the current image
virtual void SwapBuffers(){};
/// Makes the graphics context current for the caller thread /// Makes the graphics context current for the caller thread
virtual void MakeCurrent() = 0; virtual void MakeCurrent(){};
/// Releases (dunno if this is the "right" word) the context from the caller thread /// Releases (dunno if this is the "right" word) the context from the caller thread
virtual void DoneCurrent() = 0; virtual void DoneCurrent(){};
class Scoped {
public:
explicit Scoped(GraphicsContext& context_) : context(context_) {
context.MakeCurrent();
}
~Scoped() {
context.DoneCurrent();
}
private:
GraphicsContext& context;
};
/// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value
/// ends
[[nodiscard]] Scoped Acquire() {
return Scoped{*this};
}
}; };
/** /**

View File

@ -1,17 +0,0 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/frontend/emu_window.h"
#include "core/frontend/scope_acquire_context.h"
namespace Frontend {
ScopeAcquireContext::ScopeAcquireContext(Frontend::GraphicsContext& context) : context{context} {
context.MakeCurrent();
}
ScopeAcquireContext::~ScopeAcquireContext() {
context.DoneCurrent();
}
} // namespace Frontend

View File

@ -1,23 +0,0 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
namespace Frontend {
class GraphicsContext;
/// Helper class to acquire/release window context within a given scope
class ScopeAcquireContext : NonCopyable {
public:
explicit ScopeAcquireContext(Frontend::GraphicsContext& context);
~ScopeAcquireContext();
private:
Frontend::GraphicsContext& context;
};
} // namespace Frontend

View File

@ -4,7 +4,6 @@
#include <glad/glad.h> #include <glad/glad.h>
#include "core/frontend/emu_window.h" #include "core/frontend/emu_window.h"
#include "core/frontend/scope_acquire_context.h"
#include "video_core/renderer_opengl/frame_dumper_opengl.h" #include "video_core/renderer_opengl/frame_dumper_opengl.h"
#include "video_core/renderer_opengl/renderer_opengl.h" #include "video_core/renderer_opengl/renderer_opengl.h"
@ -39,7 +38,7 @@ void FrameDumperOpenGL::StopDumping() {
} }
void FrameDumperOpenGL::PresentLoop() { void FrameDumperOpenGL::PresentLoop() {
Frontend::ScopeAcquireContext scope{*context}; const auto scope = context->Acquire();
InitializeOpenGLObjects(); InitializeOpenGLObjects();
const auto& layout = GetLayout(); const auto& layout = GetLayout();

View File

@ -7,7 +7,6 @@
#include <thread> #include <thread>
#include <unordered_map> #include <unordered_map>
#include <boost/variant.hpp> #include <boost/variant.hpp>
#include "core/frontend/scope_acquire_context.h"
#include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_shader_disk_cache.h" #include "video_core/renderer_opengl/gl_shader_disk_cache.h"
#include "video_core/renderer_opengl/gl_shader_manager.h" #include "video_core/renderer_opengl/gl_shader_manager.h"
@ -641,7 +640,7 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading,
std::size_t built_shaders = 0; // It doesn't have be atomic since it's used behind a mutex std::size_t built_shaders = 0; // It doesn't have be atomic since it's used behind a mutex
const auto LoadRawSepareble = [&](Frontend::GraphicsContext* context, std::size_t begin, const auto LoadRawSepareble = [&](Frontend::GraphicsContext* context, std::size_t begin,
std::size_t end) { std::size_t end) {
Frontend::ScopeAcquireContext scope(*context); const auto scope = context->Acquire();
for (std::size_t i = begin; i < end; ++i) { for (std::size_t i = begin; i < end; ++i) {
if (stop_loading || compilation_failed) { if (stop_loading || compilation_failed) {
return; return;

View File

@ -1259,7 +1259,7 @@ static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum
VideoCore::ResultStatus RendererOpenGL::Init() { VideoCore::ResultStatus RendererOpenGL::Init() {
#ifndef ANDROID #ifndef ANDROID
if (!gladLoadGL()) { if (!gladLoadGL()) {
return VideoCore::ResultStatus::ErrorBelowGL43; return VideoCore::ResultStatus::ErrorRendererInit;
} }
// Qualcomm has some spammy info messages that are marked as errors but not important // Qualcomm has some spammy info messages that are marked as errors but not important
@ -1289,7 +1289,7 @@ VideoCore::ResultStatus RendererOpenGL::Init() {
} }
if (!(GLAD_GL_VERSION_4_3 || GLAD_GL_ES_VERSION_3_1)) { if (!(GLAD_GL_VERSION_4_3 || GLAD_GL_ES_VERSION_3_1)) {
return VideoCore::ResultStatus::ErrorBelowGL43; return VideoCore::ResultStatus::ErrorRendererInit;
} }
InitOpenGLObjects(); InitOpenGLObjects();

View File

@ -44,7 +44,7 @@ ResultStatus Init(Frontend::EmuWindow& emu_window, Frontend::EmuWindow* secondar
g_memory = &memory; g_memory = &memory;
Pica::Init(); Pica::Init();
OpenGL::GLES = Settings::values.use_gles.GetValue(); OpenGL::GLES = Settings::values.graphics_api.GetValue() == Settings::GraphicsAPI::OpenGLES;
g_renderer = std::make_unique<OpenGL::RendererOpenGL>(emu_window, secondary_window); g_renderer = std::make_unique<OpenGL::RendererOpenGL>(emu_window, secondary_window);
ResultStatus result = g_renderer->Init(); ResultStatus result = g_renderer->Init();

View File

@ -49,8 +49,8 @@ extern Memory::MemorySystem* g_memory;
enum class ResultStatus { enum class ResultStatus {
Success, Success,
ErrorGenericDrivers, ErrorRendererInit,
ErrorBelowGL43, ErrorGenericDrivers
}; };
/// Initialize the video core /// Initialize the video core