diff --git a/src/citra/emu_window/emu_window_sdl2_gl.cpp b/src/citra/emu_window/emu_window_sdl2_gl.cpp index ec86f301a6..956dd6acf1 100644 --- a/src/citra/emu_window/emu_window_sdl2_gl.cpp +++ b/src/citra/emu_window/emu_window_sdl2_gl.cpp @@ -42,10 +42,8 @@ private: SDL_GLContext context; }; -EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(Core::System& system_, bool fullscreen, bool is_secondary) - : EmuWindow_SDL2{system_, is_secondary} { - // Initialize the window - if (Settings::values.use_gles) { +static SDL_Window* CreateGLWindow(const std::string& window_title, bool gles) { + if (gles) { 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); @@ -54,7 +52,16 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(Core::System& system_, bool fullscreen, boo SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); } + return SDL_CreateWindow(window_title.c_str(), + SDL_WINDOWPOS_UNDEFINED, // x position + SDL_WINDOWPOS_UNDEFINED, // y position + Core::kScreenTopWidth, + Core::kScreenTopHeight + Core::kScreenBottomHeight, + SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); +} +EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(Core::System& system_, bool fullscreen, bool is_secondary) + : EmuWindow_SDL2{system_, is_secondary} { SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); @@ -71,16 +78,16 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(Core::System& system_, bool fullscreen, boo std::string window_title = fmt::format("Citra {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc); - render_window = - SDL_CreateWindow(window_title.c_str(), - SDL_WINDOWPOS_UNDEFINED, // x position - SDL_WINDOWPOS_UNDEFINED, // y position - Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight, - SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); + // First, try to create a context with the requested type. + render_window = CreateGLWindow(window_title, Settings::values.use_gles.GetValue()); if (render_window == nullptr) { - LOG_CRITICAL(Frontend, "Failed to create SDL2 window: {}", SDL_GetError()); - exit(1); + // On failure, fall back to context with flipped type. + render_window = CreateGLWindow(window_title, !Settings::values.use_gles.GetValue()); + if (render_window == nullptr) { + LOG_CRITICAL(Frontend, "Failed to create SDL2 window: {}", SDL_GetError()); + exit(1); + } } strict_context_required = std::strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0; @@ -106,7 +113,11 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(Core::System& system_, bool fullscreen, boo } render_window_id = SDL_GetWindowID(render_window); - auto gl_load_func = Settings::values.use_gles ? gladLoadGLES2Loader : gladLoadGLLoader; + + int profile_mask = 0; + SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &profile_mask); + auto gl_load_func = + profile_mask == SDL_GL_CONTEXT_PROFILE_ES ? gladLoadGLES2Loader : gladLoadGLLoader; if (!gl_load_func(static_cast(SDL_GL_GetProcAddress))) { LOG_CRITICAL(Frontend, "Failed to initialize GL functions: {}", SDL_GetError()); diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index 9323667c58..ca2289b79a 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -138,37 +138,50 @@ void EmuThread::run() { } #ifdef HAS_OPENGL +static std::unique_ptr CreateQOpenGLContext(bool gles) { + QSurfaceFormat format; + if (gles) { + format.setRenderableType(QSurfaceFormat::RenderableType::OpenGLES); + format.setVersion(3, 2); + } else { + format.setRenderableType(QSurfaceFormat::RenderableType::OpenGL); + format.setVersion(4, 3); + } + format.setProfile(QSurfaceFormat::CoreProfile); + + if (Settings::values.renderer_debug) { + format.setOption(QSurfaceFormat::FormatOption::DebugContext); + } + + // TODO: expose a setting for buffer value (ie default/single/double/triple) + format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); + format.setSwapInterval(0); + + auto context = std::make_unique(); + context->setFormat(format); + if (!context->create()) { + LOG_ERROR(Frontend, "Unable to create OpenGL context with GLES = {}", gles); + return nullptr; + } + return context; +} + class OpenGLSharedContext : public Frontend::GraphicsContext { public: /// Create the original context that should be shared from explicit OpenGLSharedContext() { - QSurfaceFormat format; - - if (Settings::values.use_gles) { - format.setRenderableType(QSurfaceFormat::RenderableType::OpenGLES); - format.setVersion(3, 2); - } else { - format.setRenderableType(QSurfaceFormat::RenderableType::OpenGL); - format.setVersion(4, 3); - } - format.setProfile(QSurfaceFormat::CoreProfile); - - if (Settings::values.renderer_debug) { - format.setOption(QSurfaceFormat::FormatOption::DebugContext); - } - - // TODO: expose a setting for buffer value (ie default/single/double/triple) - format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); - format.setSwapInterval(0); - - context = std::make_unique(); - context->setFormat(format); - if (!context->create()) { - LOG_ERROR(Frontend, "Unable to create main openGL context"); + // First, try to create a context with the requested type. + context = CreateQOpenGLContext(Settings::values.use_gles.GetValue()); + if (context == nullptr) { + // On failure, fall back to context with flipped type. + context = CreateQOpenGLContext(!Settings::values.use_gles.GetValue()); + if (context == nullptr) { + LOG_ERROR(Frontend, "Unable to create any OpenGL context."); + } } offscreen_surface = std::make_unique(nullptr); - offscreen_surface->setFormat(format); + offscreen_surface->setFormat(context->format()); offscreen_surface->create(); surface = offscreen_surface.get(); } @@ -184,7 +197,7 @@ public: context->setShareContext(share_context); context->setFormat(format); if (!context->create()) { - LOG_ERROR(Frontend, "Unable to create shared openGL context"); + LOG_ERROR(Frontend, "Unable to create shared OpenGL context"); } surface = main_surface; @@ -194,6 +207,10 @@ public: OpenGLSharedContext::DoneCurrent(); } + bool IsGLES() override { + return context->format().renderableType() == QSurfaceFormat::RenderableType::OpenGLES; + } + void SwapBuffers() override { context->swapBuffers(surface); } @@ -739,8 +756,9 @@ bool GRenderWindow::LoadOpenGL() { #ifdef HAS_OPENGL auto context = CreateSharedContext(); auto scope = context->Acquire(); + const auto gles = context->IsGLES(); - auto gl_load_func = Settings::values.use_gles ? gladLoadGLES2Loader : gladLoadGLLoader; + auto gl_load_func = gles ? gladLoadGLES2Loader : gladLoadGLLoader; if (!gl_load_func(GetProcAddressGL)) { QMessageBox::warning( this, tr("Error while initializing OpenGL!"), @@ -751,14 +769,14 @@ bool GRenderWindow::LoadOpenGL() { const QString renderer = QString::fromUtf8(reinterpret_cast(glGetString(GL_RENDERER))); - if (!Settings::values.use_gles && !GLAD_GL_VERSION_4_3) { + if (!gles && !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.

GL Renderer:
%1") .arg(renderer)); return false; - } else if (Settings::values.use_gles && !GLAD_GL_ES_VERSION_3_2) { + } else if (gles && !GLAD_GL_ES_VERSION_3_2) { LOG_ERROR(Frontend, "GPU does not support OpenGL ES 3.2: {}", renderer.toStdString()); QMessageBox::warning(this, tr("Error while initializing OpenGL ES 3.2!"), tr("Your GPU may not support OpenGL ES 3.2, or you do not have the " diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 766449110d..2135ff2e85 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -77,6 +77,11 @@ class GraphicsContext { public: virtual ~GraphicsContext(); + /// Checks whether this context uses OpenGL ES. + virtual bool IsGLES() { + return false; + } + /// Inform the driver to swap the front/back buffers and present the current image virtual void SwapBuffers(){};