Prepare frontend for multiple graphics APIs (#6347)
* externals: Update dynarmic * settings: Introduce GraphicsAPI enum * For now it's OpenGL only but will be expanded upon later * citra_qt: Introduce backend agnostic context management * Mostly a direct port from yuzu * core: Simplify context acquire * settings: Add option to create debug contexts * renderer_opengl: Abstract initialization to Driver * This commit also updates glad and adds some useful extensions which we will use in part 2 * Rasterizer construction is moved to the specific renderer instead of RendererBase. Software rendering has been disable to achieve this but will be brought back in the next commit. * video_core: Remove Init/Shutdown methods from renderer * The constructor and destructor can do the same job * In addition move opengl function loading to Qt since SDL already does this. Also remove ErrorVideoCore which is never reached * citra_qt: Decouple software renderer from opengl part 1 * citra: Decouple software renderer from opengl part 2 * android: Decouple software renderer from opengl part 3 * swrasterizer: Decouple software renderer from opengl part 4 * This commit simply enforces the renderer naming conventions in the software renderer * video_core: Move RendererBase to VideoCore * video_core: De-globalize screenshot state * video_core: Pass system to the renderers * video_core: Commonize shader uniform data * video_core: Abstract backend agnostic rasterizer operations * bootmanager: Remove references to OpenGL for macOS OpenGL macOS headers definitions clash heavily with each other * citra_qt: Proper title for api settings * video_core: Reduce boost usage * bootmanager: Fix hide mouse option Remove event handlers from RenderWidget for events that are already handled by the parent GRenderWindow. Also enable mouse tracking on the RenderWidget. * android: Remove software from graphics api list * code: Address review comments * citra: Port per-game settings read * Having to update the default value for all backends is a pain so lets centralize it * android: Rename to OpenGLES --------- Co-authored-by: MerryMage <MerryMage@users.noreply.github.com> Co-authored-by: Vitor Kiguchi <vitor-kiguchi@hotmail.com>
This commit is contained in:
@@ -269,6 +269,10 @@ target_link_libraries(citra-qt PRIVATE audio_core common core input_common netwo
|
||||
target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt5::Widgets Qt5::Multimedia Qt5::Concurrent)
|
||||
target_link_libraries(citra-qt PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
||||
|
||||
if (NOT WIN32)
|
||||
target_include_directories(citra-qt PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
if (UNIX AND NOT APPLE)
|
||||
target_link_libraries(citra-qt PRIVATE Qt5::DBus)
|
||||
endif()
|
||||
@@ -325,6 +329,10 @@ if (MSVC)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (NOT APPLE)
|
||||
target_compile_definitions(citra-qt PRIVATE HAS_OPENGL)
|
||||
endif()
|
||||
|
||||
if (CITRA_USE_PRECOMPILED_HEADERS)
|
||||
target_precompile_headers(citra-qt PRIVATE precompiled_headers.h)
|
||||
endif()
|
||||
|
@@ -2,31 +2,45 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDragEnterEvent>
|
||||
#include <QHBoxLayout>
|
||||
#include <QKeyEvent>
|
||||
#include <QOffscreenSurface>
|
||||
#include <QOpenGLContext>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QOpenGLFunctions_4_3_Core>
|
||||
#include <QMessageBox>
|
||||
#include <QPainter>
|
||||
#include <fmt/format.h>
|
||||
#include "citra_qt/bootmanager.h"
|
||||
#include "citra_qt/main.h"
|
||||
#include "common/color.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/3ds.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/scope_acquire_context.h"
|
||||
#include "core/frontend/framebuffer_layout.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"
|
||||
|
||||
#ifdef HAS_OPENGL
|
||||
#include <QOffscreenSurface>
|
||||
#include <QOpenGLContext>
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <objc/message.h>
|
||||
#include <objc/objc.h>
|
||||
#endif
|
||||
|
||||
#if !defined(WIN32)
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
#endif
|
||||
|
||||
EmuThread::EmuThread(Frontend::GraphicsContext& core_context) : core_context(core_context) {}
|
||||
|
||||
EmuThread::~EmuThread() = default;
|
||||
@@ -44,7 +58,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);
|
||||
|
||||
@@ -55,6 +69,7 @@ void EmuThread::run() {
|
||||
});
|
||||
|
||||
emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
|
||||
emit HideLoadingScreen();
|
||||
|
||||
core_context.MakeCurrent();
|
||||
|
||||
@@ -113,90 +128,263 @@ 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} {
|
||||
#ifdef HAS_OPENGL
|
||||
class OpenGLSharedContext : public Frontend::GraphicsContext {
|
||||
public:
|
||||
/// Create the original context that should be shared from
|
||||
explicit OpenGLSharedContext() {
|
||||
QSurfaceFormat format;
|
||||
|
||||
// disable vsync for any shared contexts
|
||||
auto format = shared_context->format();
|
||||
format.setSwapInterval(Settings::values.use_vsync_new ? 1 : 0);
|
||||
this->setFormat(format);
|
||||
format.setVersion(4, 3);
|
||||
format.setProfile(QSurfaceFormat::CoreProfile);
|
||||
|
||||
context->setShareContext(shared_context);
|
||||
context->setScreen(this->screen());
|
||||
context->setFormat(format);
|
||||
context->create();
|
||||
if (Settings::values.renderer_debug) {
|
||||
format.setOption(QSurfaceFormat::FormatOption::DebugContext);
|
||||
}
|
||||
|
||||
setSurfaceType(QWindow::OpenGLSurface);
|
||||
// TODO: expose a setting for buffer value (ie default/single/double/triple)
|
||||
format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
|
||||
format.setSwapInterval(0);
|
||||
|
||||
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
|
||||
// WA_DontShowOnScreen, WA_DeleteOnClose
|
||||
}
|
||||
context = std::make_unique<QOpenGLContext>();
|
||||
context->setFormat(format);
|
||||
if (!context->create()) {
|
||||
LOG_ERROR(Frontend, "Unable to create main openGL context");
|
||||
}
|
||||
|
||||
OpenGLWindow::~OpenGLWindow() {
|
||||
context->doneCurrent();
|
||||
}
|
||||
|
||||
void OpenGLWindow::Present() {
|
||||
if (!isExposed())
|
||||
return;
|
||||
|
||||
context->makeCurrent(this);
|
||||
if (VideoCore::g_renderer) {
|
||||
VideoCore::g_renderer->TryPresent(100, is_secondary);
|
||||
offscreen_surface = std::make_unique<QOffscreenSurface>(nullptr);
|
||||
offscreen_surface->setFormat(format);
|
||||
offscreen_surface->create();
|
||||
surface = offscreen_surface.get();
|
||||
}
|
||||
context->swapBuffers(this);
|
||||
auto f = context->versionFunctions<QOpenGLFunctions_4_3_Core>();
|
||||
f->glFinish();
|
||||
QWindow::requestUpdate();
|
||||
}
|
||||
|
||||
bool OpenGLWindow::event(QEvent* event) {
|
||||
switch (event->type()) {
|
||||
case QEvent::UpdateRequest:
|
||||
/// Create the shared contexts for rendering and presentation
|
||||
explicit OpenGLSharedContext(QOpenGLContext* share_context, QSurface* main_surface) {
|
||||
|
||||
// disable vsync for any shared contexts
|
||||
auto format = share_context->format();
|
||||
format.setSwapInterval(main_surface ? Settings::values.use_vsync_new.GetValue() : 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");
|
||||
}
|
||||
|
||||
surface = main_surface;
|
||||
}
|
||||
|
||||
~OpenGLSharedContext() {
|
||||
context->doneCurrent();
|
||||
}
|
||||
|
||||
void SwapBuffers() override {
|
||||
context->swapBuffers(surface);
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
#endif
|
||||
|
||||
class DummyContext : public Frontend::GraphicsContext {};
|
||||
|
||||
class RenderWidget : public QWidget {
|
||||
public:
|
||||
RenderWidget(GRenderWindow* parent) : QWidget(parent) {
|
||||
setMouseTracking(true);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
std::pair<unsigned, unsigned> GetSize() const {
|
||||
return std::make_pair(width(), height());
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef HAS_OPENGL
|
||||
class OpenGLRenderWidget : public RenderWidget {
|
||||
public:
|
||||
explicit OpenGLRenderWidget(GRenderWindow* parent, bool is_secondary)
|
||||
: RenderWidget(parent), is_secondary(is_secondary) {
|
||||
setAttribute(Qt::WA_NativeWindow);
|
||||
setAttribute(Qt::WA_PaintOnScreen);
|
||||
windowHandle()->setSurfaceType(QWindow::OpenGLSurface);
|
||||
}
|
||||
|
||||
void SetContext(std::unique_ptr<Frontend::GraphicsContext>&& context_) {
|
||||
context = std::move(context_);
|
||||
}
|
||||
|
||||
void Present() override {
|
||||
if (!isVisible()) {
|
||||
return;
|
||||
}
|
||||
if (!Core::System::GetInstance().IsPoweredOn()) {
|
||||
return;
|
||||
}
|
||||
context->MakeCurrent();
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
VideoCore::g_renderer->TryPresent(100, is_secondary);
|
||||
context->SwapBuffers();
|
||||
glFinish();
|
||||
}
|
||||
|
||||
QPaintEngine* paintEngine() const override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<Frontend::GraphicsContext> context{};
|
||||
bool is_secondary;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct SoftwareRenderWidget : public RenderWidget {
|
||||
explicit SoftwareRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {}
|
||||
|
||||
void Present() override {
|
||||
if (!isVisible()) {
|
||||
return;
|
||||
}
|
||||
if (!Core::System::GetInstance().IsPoweredOn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto layout{Layout::DefaultFrameLayout(width(), height(), false, false)};
|
||||
QPainter painter(this);
|
||||
|
||||
const auto draw_screen = [&](int fb_id) {
|
||||
const auto rect = fb_id == 0 ? layout.top_screen : layout.bottom_screen;
|
||||
const QImage screen = LoadFramebuffer(fb_id);
|
||||
painter.drawImage(rect.left, rect.top, screen);
|
||||
};
|
||||
|
||||
painter.fillRect(rect(), qRgb(Settings::values.bg_red.GetValue() * 255,
|
||||
Settings::values.bg_green.GetValue() * 255,
|
||||
Settings::values.bg_blue.GetValue() * 255));
|
||||
draw_screen(0);
|
||||
draw_screen(1);
|
||||
|
||||
painter.end();
|
||||
}
|
||||
|
||||
QImage LoadFramebuffer(int fb_id) {
|
||||
const auto& framebuffer = GPU::g_regs.framebuffer_config[fb_id];
|
||||
const PAddr framebuffer_addr =
|
||||
framebuffer.active_fb == 0 ? framebuffer.address_left1 : framebuffer.address_left2;
|
||||
|
||||
Memory::RasterizerFlushRegion(framebuffer_addr, framebuffer.stride * framebuffer.height);
|
||||
const u8* framebuffer_data = VideoCore::g_memory->GetPhysicalPointer(framebuffer_addr);
|
||||
|
||||
const int width = framebuffer.height;
|
||||
const int height = framebuffer.width;
|
||||
const int bpp = GPU::Regs::BytesPerPixel(framebuffer.color_format);
|
||||
|
||||
QImage image{width, height, QImage::Format_RGBA8888};
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
const u8* pixel = framebuffer_data + (x * height + height - y) * bpp;
|
||||
const Common::Vec4 color = [&] {
|
||||
switch (framebuffer.color_format) {
|
||||
case GPU::Regs::PixelFormat::RGBA8:
|
||||
return Common::Color::DecodeRGBA8(pixel);
|
||||
case GPU::Regs::PixelFormat::RGB8:
|
||||
return Common::Color::DecodeRGB8(pixel);
|
||||
case GPU::Regs::PixelFormat::RGB565:
|
||||
return Common::Color::DecodeRGB565(pixel);
|
||||
case GPU::Regs::PixelFormat::RGB5A1:
|
||||
return Common::Color::DecodeRGB5A1(pixel);
|
||||
case GPU::Regs::PixelFormat::RGBA4:
|
||||
return Common::Color::DecodeRGBA4(pixel);
|
||||
}
|
||||
}();
|
||||
|
||||
image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a()));
|
||||
}
|
||||
}
|
||||
return image;
|
||||
}
|
||||
};
|
||||
|
||||
static Frontend::WindowSystemType GetWindowSystemType() {
|
||||
// Determine WSI type based on Qt platform.
|
||||
const QString platform_name = QGuiApplication::platformName();
|
||||
if (platform_name == QStringLiteral("windows"))
|
||||
return Frontend::WindowSystemType::Windows;
|
||||
else if (platform_name == QStringLiteral("xcb"))
|
||||
return Frontend::WindowSystemType::X11;
|
||||
else if (platform_name == QStringLiteral("wayland"))
|
||||
return Frontend::WindowSystemType::Wayland;
|
||||
else if (platform_name == QStringLiteral("cocoa"))
|
||||
return Frontend::WindowSystemType::MacOS;
|
||||
|
||||
LOG_CRITICAL(Frontend, "Unknown Qt platform!");
|
||||
return Frontend::WindowSystemType::Windows;
|
||||
}
|
||||
|
||||
void OpenGLWindow::exposeEvent(QExposeEvent* event) {
|
||||
QWindow::requestUpdate();
|
||||
QWindow::exposeEvent(event);
|
||||
static Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window) {
|
||||
Frontend::EmuWindow::WindowSystemInfo wsi;
|
||||
wsi.type = GetWindowSystemType();
|
||||
|
||||
if (window) {
|
||||
#if defined(WIN32)
|
||||
// Our Win32 Qt external doesn't have the private API.
|
||||
wsi.render_surface = reinterpret_cast<void*>(window->winId());
|
||||
#elif defined(__APPLE__)
|
||||
wsi.render_surface = reinterpret_cast<void* (*)(id, SEL)>(objc_msgSend)(
|
||||
reinterpret_cast<id>(window->winId()), sel_registerName("layer"));
|
||||
#else
|
||||
QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface();
|
||||
wsi.display_connection = pni->nativeResourceForWindow("display", window);
|
||||
if (wsi.type == Frontend::WindowSystemType::Wayland)
|
||||
wsi.render_surface = pni->nativeResourceForWindow("surface", window);
|
||||
else
|
||||
wsi.render_surface = reinterpret_cast<void*>(window->winId());
|
||||
#endif
|
||||
wsi.render_surface_scale = static_cast<float>(window->devicePixelRatio());
|
||||
} else {
|
||||
wsi.render_surface = nullptr;
|
||||
wsi.render_surface_scale = 1.0f;
|
||||
}
|
||||
|
||||
return wsi;
|
||||
}
|
||||
|
||||
std::shared_ptr<Frontend::GraphicsContext> GRenderWindow::main_context;
|
||||
|
||||
GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread, bool is_secondary_)
|
||||
: QWidget(parent_), EmuWindow(is_secondary_), emu_thread(emu_thread) {
|
||||
|
||||
@@ -218,11 +406,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() {
|
||||
@@ -295,8 +483,9 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
|
||||
}
|
||||
|
||||
void GRenderWindow::mousePressEvent(QMouseEvent* event) {
|
||||
if (event->source() == Qt::MouseEventSynthesizedBySystem)
|
||||
if (event->source() == Qt::MouseEventSynthesizedBySystem) {
|
||||
return; // touch input is handled in TouchBeginEvent
|
||||
}
|
||||
|
||||
auto pos = event->pos();
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
@@ -309,8 +498,9 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
|
||||
}
|
||||
|
||||
void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
|
||||
if (event->source() == Qt::MouseEventSynthesizedBySystem)
|
||||
if (event->source() == Qt::MouseEventSynthesizedBySystem) {
|
||||
return; // touch input is handled in TouchUpdateEvent
|
||||
}
|
||||
|
||||
auto pos = event->pos();
|
||||
const auto [x, y] = ScaleTouch(pos);
|
||||
@@ -320,8 +510,9 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
|
||||
}
|
||||
|
||||
void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
|
||||
if (event->source() == Qt::MouseEventSynthesizedBySystem)
|
||||
if (event->source() == Qt::MouseEventSynthesizedBySystem) {
|
||||
return; // touch input is handled in TouchEndEvent
|
||||
}
|
||||
|
||||
if (event->button() == Qt::LeftButton)
|
||||
this->TouchReleased();
|
||||
@@ -393,42 +584,61 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) {
|
||||
OnFramebufferSizeChanged();
|
||||
}
|
||||
|
||||
void GRenderWindow::InitRenderTarget() {
|
||||
ReleaseRenderTarget();
|
||||
bool GRenderWindow::InitRenderTarget() {
|
||||
{
|
||||
// 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 auto graphics_api = Settings::values.graphics_api.GetValue();
|
||||
switch (graphics_api) {
|
||||
case Settings::GraphicsAPI::Software:
|
||||
InitializeSoftware();
|
||||
break;
|
||||
case Settings::GraphicsAPI::OpenGL:
|
||||
if (!InitializeOpenGL() || !LoadOpenGL()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Update the Window System information with the new render target
|
||||
window_info = GetWindowSystemInfo(child_widget->windowHandle());
|
||||
|
||||
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) {
|
||||
if (res_scale == 0)
|
||||
if (res_scale == 0) {
|
||||
res_scale = VideoCore::GetResolutionScaleFactor();
|
||||
}
|
||||
|
||||
const auto layout{Layout::FrameLayoutFromResolutionScale(res_scale, is_secondary)};
|
||||
screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32);
|
||||
VideoCore::RequestScreenshot(
|
||||
VideoCore::g_renderer->RequestScreenshot(
|
||||
screenshot_image.bits(),
|
||||
[this, screenshot_path] {
|
||||
const std::string std_screenshot_path = screenshot_path.toStdString();
|
||||
@@ -445,6 +655,59 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal
|
||||
setMinimumSize(minimal_size.first, minimal_size.second);
|
||||
}
|
||||
|
||||
bool GRenderWindow::InitializeOpenGL() {
|
||||
#ifdef HAS_OPENGL
|
||||
// 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();
|
||||
|
||||
if (!main_context) {
|
||||
main_context = std::make_shared<OpenGLSharedContext>();
|
||||
}
|
||||
|
||||
auto child_context = CreateSharedContext();
|
||||
child->SetContext(std::move(child_context));
|
||||
|
||||
return true;
|
||||
#else
|
||||
QMessageBox::warning(this, tr("OpenGL not available!"),
|
||||
tr("Citra has not been compiled with OpenGL support."));
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void GRenderWindow::InitializeSoftware() {
|
||||
child_widget = new SoftwareRenderWidget(this);
|
||||
main_context = std::make_unique<DummyContext>();
|
||||
}
|
||||
|
||||
bool GRenderWindow::LoadOpenGL() {
|
||||
auto context = CreateSharedContext();
|
||||
auto scope = context->Acquire();
|
||||
if (!gladLoadGL()) {
|
||||
QMessageBox::warning(
|
||||
this, tr("Error while initializing OpenGL!"),
|
||||
tr("Your GPU may not support OpenGL, or you do not have the latest graphics driver."));
|
||||
return false;
|
||||
}
|
||||
|
||||
const QString renderer =
|
||||
QString::fromUtf8(reinterpret_cast<const char*>(glGetString(GL_RENDERER)));
|
||||
|
||||
if (!GLAD_GL_VERSION_4_3) {
|
||||
LOG_ERROR(Frontend, "GPU does not support OpenGL 4.3: {}", renderer.toStdString());
|
||||
QMessageBox::warning(this, tr("Error while initializing OpenGL 4.3!"),
|
||||
tr("Your GPU may not support OpenGL 4.3, or you do not have the "
|
||||
"latest graphics driver.<br><br>GL Renderer:<br>%1")
|
||||
.arg(renderer));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) {
|
||||
this->emu_thread = emu_thread;
|
||||
}
|
||||
@@ -458,29 +721,15 @@ void GRenderWindow::showEvent(QShowEvent* 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();
|
||||
#ifdef HAS_OPENGL
|
||||
const auto graphics_api = Settings::values.graphics_api.GetValue();
|
||||
if (graphics_api == Settings::GraphicsAPI::OpenGL) {
|
||||
auto gl_context = 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>(gl_context->GetShareContext(),
|
||||
child_widget->windowHandle());
|
||||
}
|
||||
#endif
|
||||
return std::make_unique<DummyContext>();
|
||||
}
|
||||
|
@@ -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,28 @@ 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;
|
||||
bool InitializeOpenGL();
|
||||
void InitializeSoftware();
|
||||
bool LoadOpenGL();
|
||||
|
||||
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;
|
||||
|
||||
/// 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
|
||||
static std::shared_ptr<Frontend::GraphicsContext> main_context;
|
||||
|
||||
/// Temporary storage of the screenshot taken
|
||||
QImage screenshot_image;
|
||||
QByteArray geometry;
|
||||
bool first_frame = false;
|
||||
bool has_focus = false;
|
||||
|
||||
|
@@ -482,6 +482,7 @@ void Config::ReadDebuggingValues() {
|
||||
qt_config->value(QStringLiteral("record_frame_times"), false).toBool();
|
||||
ReadBasicSetting(Settings::values.use_gdbstub);
|
||||
ReadBasicSetting(Settings::values.gdbstub_port);
|
||||
ReadBasicSetting(Settings::values.renderer_debug);
|
||||
|
||||
qt_config->beginGroup(QStringLiteral("LLE"));
|
||||
for (const auto& service_module : Service::service_module_map) {
|
||||
@@ -625,7 +626,7 @@ void Config::ReadPathValues() {
|
||||
void Config::ReadRendererValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Renderer"));
|
||||
|
||||
ReadGlobalSetting(Settings::values.use_hw_renderer);
|
||||
ReadGlobalSetting(Settings::values.graphics_api);
|
||||
ReadGlobalSetting(Settings::values.use_hw_shader);
|
||||
#ifdef __APPLE__
|
||||
// Hardware shader is broken on macos with Intel GPUs thanks to poor drivers.
|
||||
@@ -992,6 +993,7 @@ void Config::SaveDebuggingValues() {
|
||||
qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times);
|
||||
WriteBasicSetting(Settings::values.use_gdbstub);
|
||||
WriteBasicSetting(Settings::values.gdbstub_port);
|
||||
WriteBasicSetting(Settings::values.renderer_debug);
|
||||
|
||||
qt_config->beginGroup(QStringLiteral("LLE"));
|
||||
for (const auto& service_module : Settings::values.lle_modules) {
|
||||
@@ -1103,7 +1105,7 @@ void Config::SavePathValues() {
|
||||
void Config::SaveRendererValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Renderer"));
|
||||
|
||||
WriteGlobalSetting(Settings::values.use_hw_renderer);
|
||||
WriteGlobalSetting(Settings::values.graphics_api);
|
||||
WriteGlobalSetting(Settings::values.use_hw_shader);
|
||||
#ifdef __APPLE__
|
||||
// Hardware shader is broken on macos thanks to poor drivers.
|
||||
|
@@ -83,6 +83,21 @@ template <>
|
||||
void SetPerGameSetting(QComboBox* combobox,
|
||||
const Settings::SwitchableSetting<std::string>* setting);
|
||||
|
||||
/// Given an index of a combobox setting extracts the setting taking into
|
||||
/// account per-game status
|
||||
template <typename Type, bool ranged>
|
||||
Type GetComboboxSetting(int index, const Settings::SwitchableSetting<Type, ranged>* setting) {
|
||||
if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) {
|
||||
return static_cast<Type>(index);
|
||||
} else if (!Settings::IsConfiguringGlobal()) {
|
||||
if (index == 0) {
|
||||
return setting->GetValue();
|
||||
} else {
|
||||
return static_cast<Type>(index - ConfigurationShared::USE_GLOBAL_OFFSET);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a Qt widget sets the background color to indicate whether the setting
|
||||
/// is per-game overriden (highlighted) or global (non-highlighted)
|
||||
void SetHighlight(QWidget* widget, bool highlighted);
|
||||
|
@@ -37,6 +37,7 @@ ConfigureDebug::ConfigureDebug(QWidget* parent)
|
||||
|
||||
const bool is_powered_on = Core::System::GetInstance().IsPoweredOn();
|
||||
ui->toggle_cpu_jit->setEnabled(!is_powered_on);
|
||||
ui->toggle_renderer_debug->setEnabled(!is_powered_on);
|
||||
|
||||
// Set a minimum width for the label to prevent the slider from changing size.
|
||||
// This scales across DPIs. (This value should be enough for "xxx%")
|
||||
@@ -62,6 +63,7 @@ void ConfigureDebug::SetConfiguration() {
|
||||
ui->toggle_console->setChecked(UISettings::values.show_console.GetValue());
|
||||
ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter.GetValue()));
|
||||
ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit.GetValue());
|
||||
ui->toggle_renderer_debug->setChecked(Settings::values.renderer_debug.GetValue());
|
||||
|
||||
if (!Settings::IsConfiguringGlobal()) {
|
||||
if (Settings::values.cpu_clock_percentage.UsingGlobal()) {
|
||||
@@ -91,6 +93,7 @@ void ConfigureDebug::ApplyConfiguration() {
|
||||
filter.ParseFilterString(Settings::values.log_filter.GetValue());
|
||||
Log::SetGlobalFilter(filter);
|
||||
Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked();
|
||||
Settings::values.renderer_debug = ui->toggle_renderer_debug->isChecked();
|
||||
|
||||
ConfigurationShared::ApplyPerGameSetting(
|
||||
&Settings::values.cpu_clock_percentage, ui->clock_speed_combo,
|
||||
|
@@ -23,5 +23,6 @@ public:
|
||||
void SetConfiguration();
|
||||
void SetupPerGameUI();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::ConfigureDebug> ui;
|
||||
};
|
||||
|
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>443</width>
|
||||
<height>358</height>
|
||||
<width>523</width>
|
||||
<height>447</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -112,12 +112,34 @@
|
||||
<string>CPU</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="toggle_cpu_jit">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Enables the use of the ARM JIT compiler for emulating the 3DS CPUs. Don't disable unless for debugging purposes</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable CPU JIT</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QWidget" name="clock_speed_widget" native="true">
|
||||
<layout class="QHBoxLayout" name="clock_speed_layout">
|
||||
<property name="spacing">
|
||||
<number>7</number>
|
||||
</property>
|
||||
<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="QComboBox" name="clock_speed_combo">
|
||||
<item>
|
||||
@@ -180,13 +202,10 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="toggle_cpu_jit">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Enables the use of the ARM JIT compiler for emulating the 3DS CPUs. Don't disable unless for debugging purposes</p></body></html></string>
|
||||
</property>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="toggle_renderer_debug">
|
||||
<property name="text">
|
||||
<string>Enable CPU JIT</string>
|
||||
<string>Enable debug renderer</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@@ -22,7 +22,9 @@ ConfigureEnhancements::ConfigureEnhancements(QWidget* parent)
|
||||
|
||||
ui->layout_group->setEnabled(!Settings::values.custom_layout);
|
||||
|
||||
ui->resolution_factor_combobox->setEnabled(Settings::values.use_hw_renderer.GetValue());
|
||||
const auto graphics_api = Settings::values.graphics_api.GetValue();
|
||||
const bool res_scale_enabled = graphics_api != Settings::GraphicsAPI::Software;
|
||||
ui->resolution_factor_combobox->setEnabled(res_scale_enabled);
|
||||
|
||||
connect(ui->render_3d_combobox,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
|
||||
|
@@ -16,22 +16,20 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
|
||||
: QWidget(parent), ui(std::make_unique<Ui::ConfigureGraphics>()) {
|
||||
ui->setupUi(this);
|
||||
|
||||
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());
|
||||
// Set the index to -1 to ensure the below lambda is called with setCurrentIndex
|
||||
ui->graphics_api_combo->setCurrentIndex(-1);
|
||||
|
||||
connect(ui->toggle_hw_renderer, &QCheckBox::toggled, this, [this] {
|
||||
const bool checked = ui->toggle_hw_renderer->isChecked();
|
||||
ui->hw_renderer_group->setEnabled(checked);
|
||||
ui->toggle_disk_shader_cache->setEnabled(checked && ui->toggle_hw_shader->isChecked());
|
||||
});
|
||||
connect(ui->graphics_api_combo, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
[this](int index) {
|
||||
const auto graphics_api =
|
||||
ConfigurationShared::GetComboboxSetting(index, &Settings::values.graphics_api);
|
||||
const bool is_software = graphics_api == Settings::GraphicsAPI::Software;
|
||||
|
||||
ui->hw_shader_group->setEnabled(ui->toggle_hw_shader->isChecked());
|
||||
ui->toggle_disk_shader_cache->setEnabled(ui->toggle_hw_renderer->isChecked() &&
|
||||
ui->toggle_hw_shader->isChecked());
|
||||
ui->hw_renderer_group->setEnabled(!is_software);
|
||||
ui->toggle_disk_shader_cache->setEnabled(!is_software &&
|
||||
ui->toggle_hw_shader->isChecked());
|
||||
});
|
||||
|
||||
connect(ui->toggle_hw_shader, &QCheckBox::toggled, this, [this] {
|
||||
const bool checked = ui->toggle_hw_shader->isChecked();
|
||||
@@ -60,12 +58,24 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
|
||||
// TODO(B3N30): Hide this for macs with none Intel GPUs, too.
|
||||
ui->toggle_separable_shader->setVisible(false);
|
||||
#endif
|
||||
|
||||
SetupPerGameUI();
|
||||
SetConfiguration();
|
||||
}
|
||||
|
||||
ConfigureGraphics::~ConfigureGraphics() = default;
|
||||
|
||||
void ConfigureGraphics::SetConfiguration() {
|
||||
ui->toggle_hw_renderer->setChecked(Settings::values.use_hw_renderer.GetValue());
|
||||
if (!Settings::IsConfiguringGlobal()) {
|
||||
ConfigurationShared::SetHighlight(ui->graphics_api_group,
|
||||
!Settings::values.graphics_api.UsingGlobal());
|
||||
ConfigurationShared::SetPerGameSetting(ui->graphics_api_combo,
|
||||
&Settings::values.graphics_api);
|
||||
} else {
|
||||
ui->graphics_api_combo->setCurrentIndex(
|
||||
static_cast<int>(Settings::values.graphics_api.GetValue()));
|
||||
}
|
||||
|
||||
ui->toggle_hw_shader->setChecked(Settings::values.use_hw_shader.GetValue());
|
||||
ui->toggle_separable_shader->setChecked(Settings::values.separable_shader.GetValue());
|
||||
ui->toggle_accurate_mul->setChecked(Settings::values.shaders_accurate_mul.GetValue());
|
||||
@@ -78,8 +88,8 @@ void ConfigureGraphics::SetConfiguration() {
|
||||
}
|
||||
|
||||
void ConfigureGraphics::ApplyConfiguration() {
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_hw_renderer,
|
||||
ui->toggle_hw_renderer, use_hw_renderer);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.graphics_api,
|
||||
ui->graphics_api_combo);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_hw_shader, ui->toggle_hw_shader,
|
||||
use_hw_shader);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.separable_shader,
|
||||
@@ -103,7 +113,7 @@ void ConfigureGraphics::RetranslateUI() {
|
||||
void ConfigureGraphics::SetupPerGameUI() {
|
||||
// Block the global settings if a game is currently running that overrides them
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
ui->toggle_hw_renderer->setEnabled(Settings::values.use_hw_renderer.UsingGlobal());
|
||||
ui->graphics_api_group->setEnabled(Settings::values.graphics_api.UsingGlobal());
|
||||
ui->toggle_hw_shader->setEnabled(Settings::values.use_hw_shader.UsingGlobal());
|
||||
ui->toggle_separable_shader->setEnabled(Settings::values.separable_shader.UsingGlobal());
|
||||
ui->toggle_accurate_mul->setEnabled(Settings::values.shaders_accurate_mul.UsingGlobal());
|
||||
@@ -115,8 +125,10 @@ void ConfigureGraphics::SetupPerGameUI() {
|
||||
|
||||
ui->toggle_shader_jit->setVisible(false);
|
||||
|
||||
ConfigurationShared::SetColoredTristate(ui->toggle_hw_renderer,
|
||||
Settings::values.use_hw_renderer, use_hw_renderer);
|
||||
ConfigurationShared::SetColoredComboBox(
|
||||
ui->graphics_api_combo, ui->graphics_api_group,
|
||||
static_cast<u32>(Settings::values.graphics_api.GetValue(true)));
|
||||
|
||||
ConfigurationShared::SetColoredTristate(ui->toggle_hw_shader, Settings::values.use_hw_shader,
|
||||
use_hw_shader);
|
||||
ConfigurationShared::SetColoredTristate(ui->toggle_separable_shader,
|
||||
|
@@ -28,9 +28,9 @@ public:
|
||||
|
||||
void UpdateBackgroundColorButton(const QColor& color);
|
||||
|
||||
private:
|
||||
void SetupPerGameUI();
|
||||
|
||||
ConfigurationShared::CheckState use_hw_renderer;
|
||||
ConfigurationShared::CheckState use_hw_shader;
|
||||
ConfigurationShared::CheckState separable_shader;
|
||||
ConfigurationShared::CheckState shaders_accurate_mul;
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>430</height>
|
||||
<height>443</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
@@ -20,27 +20,65 @@
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="apiBox">
|
||||
<property name="title">
|
||||
<string>API Settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QWidget" name="graphics_api_group" native="true">
|
||||
<layout class="QHBoxLayout" name="graphics_api_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="graphics_api_label">
|
||||
<property name="text">
|
||||
<string>Graphics API</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="graphics_api_combo">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Software</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>OpenGL</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</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">
|
||||
<string><html><head/><body><p>Use OpenGL to accelerate rendering.</p><p>Disable to debug graphics-related problem.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable Hardware Renderer</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="hw_renderer_group" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<property name="leftMargin">
|
||||
<number>16</number>
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
@@ -157,8 +195,6 @@
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>toggle_hw_renderer</tabstop>
|
||||
<tabstop>toggle_hw_shader</tabstop>
|
||||
<tabstop>toggle_separable_shader</tabstop>
|
||||
<tabstop>toggle_accurate_mul</tabstop>
|
||||
<tabstop>toggle_shader_jit</tabstop>
|
||||
|
@@ -12,7 +12,6 @@
|
||||
#include <QFutureWatcher>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
#include <QOpenGLFunctions_4_3_Core>
|
||||
#include <QSysInfo>
|
||||
#include <QtConcurrent/QtConcurrentRun>
|
||||
#include <QtGui>
|
||||
@@ -88,7 +87,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/cfg/cfg.h"
|
||||
#include "core/hle/service/fs/archive.h"
|
||||
@@ -1026,16 +1024,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()};
|
||||
|
||||
@@ -1091,28 +1080,6 @@ bool GMainWindow::LoadROM(const QString& filename) {
|
||||
tr("GBA Virtual Console ROMs are not supported by Citra."));
|
||||
break;
|
||||
|
||||
case Core::System::ResultStatus::ErrorVideoCore:
|
||||
QMessageBox::critical(
|
||||
this, tr("Video Core Error"),
|
||||
tr("An error has occurred. Please <a "
|
||||
"href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>see "
|
||||
"the "
|
||||
"log</a> for more details. "
|
||||
"Ensure that you have the latest graphics drivers for your GPU."));
|
||||
break;
|
||||
|
||||
case Core::System::ResultStatus::ErrorVideoCore_ErrorGenericDrivers:
|
||||
QMessageBox::critical(
|
||||
this, tr("Video Core Error"),
|
||||
tr("You are running default Windows drivers "
|
||||
"for your GPU. You need to install the "
|
||||
"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!"),
|
||||
@@ -2786,14 +2753,6 @@ int main(int argc, char* argv[]) {
|
||||
QCoreApplication::setOrganizationName(QStringLiteral("Citra team"));
|
||||
QCoreApplication::setApplicationName(QStringLiteral("Citra"));
|
||||
|
||||
QSurfaceFormat format;
|
||||
format.setVersion(4, 3);
|
||||
format.setProfile(QSurfaceFormat::CoreProfile);
|
||||
format.setSwapInterval(0);
|
||||
// TODO: expose a setting for buffer value (ie default/single/double/triple)
|
||||
format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
|
||||
QSurfaceFormat::setDefaultFormat(format);
|
||||
|
||||
SetHighDPIAttributes();
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
Reference in New Issue
Block a user