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/camera/factory.h"
#include "core/frontend/mic.h"
#include "core/frontend/scope_acquire_context.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/nfc/nfc.h"
#include "core/savestate.h"

View File

@ -29,7 +29,6 @@
#include "core/file_sys/cia_container.h"
#include "core/frontend/applets/default_applets.h"
#include "core/frontend/framebuffer_layout.h"
#include "core/frontend/scope_acquire_context.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/cfg/cfg.h"
@ -368,7 +367,7 @@ int main(int argc, char** argv) {
const auto secondary_window =
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,
Common::g_scm_desc);

View File

@ -109,7 +109,8 @@ void Config::ReadValues() {
sdl2_config->GetInteger("Core", "cpu_clock_percentage", 100);
// 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_shader = sdl2_config->GetBoolean("Renderer", "use_hw_shader", true);
#ifdef __APPLE__

View File

@ -137,7 +137,8 @@ void EmuWindow_SDL2::Fullscreen() {
EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen, bool is_secondary) : EmuWindow(is_secondary) {
// 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_MINOR_VERSION, 2);
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);
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))) {
LOG_CRITICAL(Frontend, "Failed to initialize GL functions: {}", SDL_GetError());

View File

@ -6,10 +6,11 @@
#include <QDragEnterEvent>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QMessageBox>
#include <QOffscreenSurface>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QOpenGLFunctions_4_3_Core>
#include <QOpenGLExtraFunctions>
#include <fmt/format.h>
#include "citra_qt/bootmanager.h"
#include "citra_qt/main.h"
@ -18,12 +19,9 @@
#include "common/settings.h"
#include "core/3ds.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/main.h"
#include "input_common/motion_emu.h"
#include "network/network.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
@ -44,7 +42,7 @@ static GMainWindow* GetMainWindow() {
void EmuThread::run() {
MicroProfileOnThreadCreate("EmuThread");
Frontend::ScopeAcquireContext scope(core_context);
const auto scope = core_context.Acquire();
emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
@ -113,90 +111,198 @@ void EmuThread::run() {
#endif
}
OpenGLWindow::OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context,
bool is_secondary)
: QWindow(parent), context(std::make_unique<QOpenGLContext>(shared_context->parent())),
event_handler(event_handler), is_secondary{is_secondary} {
class OpenGLSharedContext : public Frontend::GraphicsContext {
public:
/// Create the original context that should be shared from
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
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 = std::make_unique<QOpenGLContext>();
context->setFormat(format);
context->create();
setSurfaceType(QWindow::OpenGLSurface);
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
// WA_DontShowOnScreen, WA_DeleteOnClose
if (!context->create()) {
LOG_ERROR(Frontend, "Unable to create main openGL context");
}
}
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();
}
void OpenGLWindow::Present() {
if (!isExposed())
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();
void SwapBuffers() override {
context->swapBuffers(surface);
}
bool OpenGLWindow::event(QEvent* event) {
switch (event->type()) {
case QEvent::UpdateRequest:
void MakeCurrent() override {
// We can't track the current state of the underlying context in this wrapper class because
// 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();
return true;
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick:
case QEvent::MouseMove:
case QEvent::KeyPress:
case QEvent::KeyRelease:
case QEvent::FocusIn:
case QEvent::FocusOut:
case QEvent::FocusAboutToChange:
case QEvent::Enter:
case QEvent::Leave:
case QEvent::Wheel:
case QEvent::TabletMove:
case QEvent::TabletPress:
case QEvent::TabletRelease:
case QEvent::TabletEnterProximity:
case QEvent::TabletLeaveProximity:
case QEvent::TouchBegin:
case QEvent::TouchUpdate:
case QEvent::TouchEnd:
case QEvent::InputMethodQuery:
case QEvent::TouchCancel:
return QCoreApplication::sendEvent(event_handler, event);
case QEvent::Drop:
GetMainWindow()->DropAction(static_cast<QDropEvent*>(event));
return true;
case QEvent::DragEnter:
case QEvent::DragMove:
GetMainWindow()->AcceptDropEvent(static_cast<QDropEvent*>(event));
return true;
default:
return QWindow::event(event);
update();
}
void resizeEvent(QResizeEvent* ev) override {
render_window->resize(ev->size());
render_window->OnFramebufferSizeChanged();
}
void keyPressEvent(QKeyEvent* event) override {
InputCommon::GetKeyboard()->PressKey(event->key());
}
void keyReleaseEvent(QKeyEvent* event) override {
InputCommon::GetKeyboard()->ReleaseKey(event->key());
}
void mousePressEvent(QMouseEvent* event) override {
if (event->source() == Qt::MouseEventSynthesizedBySystem)
return; // touch input is handled in TouchBeginEvent
const auto pos{event->pos()};
if (event->button() == Qt::LeftButton) {
const auto [x, y] = render_window->ScaleTouch(pos);
render_window->TouchPressed(x, y);
} else if (event->button() == Qt::RightButton) {
InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
}
}
void OpenGLWindow::exposeEvent(QExposeEvent* event) {
QWindow::requestUpdate();
QWindow::exposeEvent(event);
void mouseMoveEvent(QMouseEvent* event) override {
if (event->source() == Qt::MouseEventSynthesizedBySystem)
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_)
: 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;
void GRenderWindow::MakeCurrent() {
core_context->MakeCurrent();
main_context->MakeCurrent();
}
void GRenderWindow::DoneCurrent() {
core_context->DoneCurrent();
main_context->DoneCurrent();
}
void GRenderWindow::PollEvents() {
@ -393,34 +499,67 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) {
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();
{
// Create a dummy render widget so that Qt
// places the render window at the correct position.
const RenderWidget dummy_widget{this};
}
first_frame = false;
GMainWindow* parent = GetMainWindow();
QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr;
child_window = new OpenGLWindow(parent_win_handle, this, QOpenGLContext::globalShareContext(),
is_secondary);
child_window->create();
child_widget = createWindowContainer(child_window, this);
const Settings::GraphicsAPI graphics_api = Settings::values.graphics_api;
switch (graphics_api) {
case Settings::GraphicsAPI::OpenGL:
case Settings::GraphicsAPI::OpenGLES:
if (!InitializeOpenGL()) {
return false;
}
break;
case Settings::GraphicsAPI::Vulkan:
if (!InitializeVulkan()) {
return false;
}
break;
}
child_widget->resize(Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight);
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);
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
OnFramebufferSizeChanged();
BackupGeometry();
return true;
}
void GRenderWindow::ReleaseRenderTarget() {
if (child_widget) {
layout()->removeWidget(child_widget);
delete child_widget;
child_widget->deleteLater();
child_widget = nullptr;
}
main_context.reset();
}
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);
}
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) {
this->emu_thread = emu_thread;
}
@ -456,31 +626,3 @@ void GRenderWindow::OnEmulationStopping() {
void GRenderWindow::showEvent(QShowEvent* 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;
}
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 {
Q_OBJECT
@ -126,26 +113,6 @@ signals:
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 {
Q_OBJECT
@ -185,13 +152,15 @@ public:
return has_focus;
}
void InitRenderTarget();
bool InitRenderTarget();
/// Destroy the previous run's child_widget which should also destroy the child_window
void ReleaseRenderTarget();
void CaptureScreenshot(u32 res_scale, const QString& screenshot_path);
std::pair<u32, u32> ScaleTouch(const QPointF pos) const;
public slots:
void OnEmulationStarting(EmuThread* emu_thread);
@ -211,29 +180,29 @@ signals:
void MouseActivity();
private:
std::pair<u32, u32> ScaleTouch(QPointF pos) const;
void TouchBeginEvent(const QTouchEvent* event);
void TouchUpdateEvent(const QTouchEvent* event);
void TouchEndEvent();
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
std::unique_ptr<GraphicsContext> core_context;
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;
bool InitializeOpenGL();
bool InitializeVulkan();
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
QImage screenshot_image;
QByteArray geometry;
QWidget* child_widget = nullptr;
bool first_frame = false;
bool has_focus = false;

View File

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

View File

@ -26,6 +26,36 @@
<string>Renderer</string>
</property>
<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>
<widget class="QCheckBox" name="toggle_hw_renderer">
<property name="toolTip">

View File

@ -18,12 +18,6 @@
#include <QtGui>
#include <QtWidgets>
#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/applets/mii_selector.h"
#include "citra_qt/applets/swkbd.h"
@ -82,7 +76,6 @@
#include "core/file_sys/archive_extsavedata.h"
#include "core/file_sys/archive_source_sd_savedata.h"
#include "core/frontend/applets/default_applets.h"
#include "core/frontend/scope_acquire_context.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/service/fs/archive.h"
#include "core/hle/service/nfc/nfc.h"
@ -96,6 +89,13 @@
#include "video_core/renderer_base.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
#include "citra_qt/discord_impl.h"
#endif
@ -954,16 +954,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
render_window->InitRenderTarget();
secondary_window->InitRenderTarget();
Frontend::ScopeAcquireContext scope(*render_window);
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;
}
const auto scope = render_window->Acquire();
Core::System& system{Core::System::GetInstance()};
@ -1017,7 +1008,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
case Core::System::ResultStatus::ErrorVideoCore:
QMessageBox::critical(
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 "
"the "
"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."));
break;
case Core::System::ResultStatus::ErrorVideoCore_ErrorBelowGL43:
QMessageBox::critical(this, below_gl43_title, below_gl43_message);
break;
default:
QMessageBox::critical(
this, tr("Error while loading ROM!"),

View File

@ -8,7 +8,6 @@
#include "common/settings.h"
#include "core/core.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/shared_page.h"
#include "core/hle/service/cam/cam.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/ir/ir_rst.h"
@ -95,7 +94,7 @@ void LogSettings() {
LOG_INFO(Config, "Citra Configuration:");
log_setting("Core_UseCpuJit", values.use_cpu_jit.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_UseHwShader", values.use_hw_shader.GetValue());
log_setting("Renderer_SeparableShader", values.separable_shader.GetValue());

View File

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

View File

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

View File

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

View File

@ -88,8 +88,6 @@ public:
ErrorVideoCore, ///< Error in the video core
ErrorVideoCore_ErrorGenericDrivers, ///< Error in the video core due to the user having
/// 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
ShutdownRequested, ///< Emulated program requested a system shutdown
ErrorUnknown ///< Any other error

View File

@ -62,11 +62,33 @@ class GraphicsContext {
public:
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
virtual void MakeCurrent() = 0;
virtual void MakeCurrent(){};
/// 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 "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/renderer_opengl.h"
@ -39,7 +38,7 @@ void FrameDumperOpenGL::StopDumping() {
}
void FrameDumperOpenGL::PresentLoop() {
Frontend::ScopeAcquireContext scope{*context};
const auto scope = context->Acquire();
InitializeOpenGLObjects();
const auto& layout = GetLayout();

View File

@ -7,7 +7,6 @@
#include <thread>
#include <unordered_map>
#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_shader_disk_cache.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
const auto LoadRawSepareble = [&](Frontend::GraphicsContext* context, std::size_t begin,
std::size_t end) {
Frontend::ScopeAcquireContext scope(*context);
const auto scope = context->Acquire();
for (std::size_t i = begin; i < end; ++i) {
if (stop_loading || compilation_failed) {
return;

View File

@ -1259,7 +1259,7 @@ static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum
VideoCore::ResultStatus RendererOpenGL::Init() {
#ifndef ANDROID
if (!gladLoadGL()) {
return VideoCore::ResultStatus::ErrorBelowGL43;
return VideoCore::ResultStatus::ErrorRendererInit;
}
// 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)) {
return VideoCore::ResultStatus::ErrorBelowGL43;
return VideoCore::ResultStatus::ErrorRendererInit;
}
InitOpenGLObjects();

View File

@ -44,7 +44,7 @@ ResultStatus Init(Frontend::EmuWindow& emu_window, Frontend::EmuWindow* secondar
g_memory = &memory;
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);
ResultStatus result = g_renderer->Init();

View File

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