diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.java b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.java index d791931b3..9f73e1ff2 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.java +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.java @@ -359,6 +359,8 @@ public final class SettingsFragmentPresenter { SettingSection rendererSection = mSettings.getSection(Settings.SECTION_RENDERER); Setting graphicsApi = rendererSection.getSetting(SettingsFile.KEY_GRAPHICS_API); + Setting spirvShaderGen = rendererSection.getSetting(SettingsFile.KEY_SPIRV_SHADER_GEN); + Setting asyncShaders = rendererSection.getSetting(SettingsFile.KEY_ASYNC_SHADERS); Setting resolutionFactor = rendererSection.getSetting(SettingsFile.KEY_RESOLUTION_FACTOR); Setting filterMode = rendererSection.getSetting(SettingsFile.KEY_FILTER_MODE); Setting shadersAccurateMul = rendererSection.getSetting(SettingsFile.KEY_SHADERS_ACCURATE_MUL); @@ -377,6 +379,8 @@ public final class SettingsFragmentPresenter { sl.add(new HeaderSetting(null, null, R.string.renderer, 0)); sl.add(new SingleChoiceSetting(SettingsFile.KEY_GRAPHICS_API, Settings.SECTION_RENDERER, R.string.graphics_api, 0, R.array.graphicsApiNames, R.array.graphicsApiValues, 0, graphicsApi)); + sl.add(new CheckBoxSetting(SettingsFile.KEY_SPIRV_SHADER_GEN, Settings.SECTION_RENDERER, R.string.spirv_shader_gen, R.string.spirv_shader_gen_description, true, spirvShaderGen)); + sl.add(new CheckBoxSetting(SettingsFile.KEY_ASYNC_SHADERS, Settings.SECTION_RENDERER, R.string.async_shaders, R.string.async_shaders_description, false, asyncShaders)); sl.add(new SliderSetting(SettingsFile.KEY_RESOLUTION_FACTOR, Settings.SECTION_RENDERER, R.string.internal_resolution, R.string.internal_resolution_description, 1, 4, "x", 1, resolutionFactor)); sl.add(new CheckBoxSetting(SettingsFile.KEY_FILTER_MODE, Settings.SECTION_RENDERER, R.string.linear_filtering, R.string.linear_filtering_description, true, filterMode)); sl.add(new CheckBoxSetting(SettingsFile.KEY_SHADERS_ACCURATE_MUL, Settings.SECTION_RENDERER, R.string.shaders_accurate_mul, R.string.shaders_accurate_mul_description, false, shadersAccurateMul)); @@ -424,6 +428,6 @@ public final class SettingsFragmentPresenter { sl.add(new CheckBoxSetting(SettingsFile.KEY_CPU_JIT, Settings.SECTION_CORE, R.string.cpu_jit, R.string.cpu_jit_description, true, useCpuJit, true, mView)); sl.add(new CheckBoxSetting(SettingsFile.KEY_HW_SHADER, Settings.SECTION_RENDERER, R.string.hw_shaders, R.string.hw_shaders_description, true, hardwareShader, true, mView)); sl.add(new CheckBoxSetting(SettingsFile.KEY_USE_VSYNC, Settings.SECTION_RENDERER, R.string.vsync, R.string.vsync_description, true, vsyncEnable)); - sl.add(new CheckBoxSetting(SettingsFile.KEY_RENDERER_DEBUG, Settings.SECTION_RENDERER, R.string.renderer_debug, R.string.renderer_debug_description, false, rendererDebug)); + sl.add(new CheckBoxSetting(SettingsFile.KEY_RENDERER_DEBUG, Settings.SECTION_DEBUG, R.string.renderer_debug, R.string.renderer_debug_description, false, rendererDebug)); } } diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/utils/SettingsFile.java b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/utils/SettingsFile.java index f1adea163..fec8f3282 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/utils/SettingsFile.java +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/utils/SettingsFile.java @@ -45,6 +45,8 @@ public final class SettingsFile { public static final String KEY_PREMIUM = "premium"; public static final String KEY_GRAPHICS_API = "graphics_api"; + public static final String KEY_SPIRV_SHADER_GEN = "spirv_shader_gen"; + public static final String KEY_ASYNC_SHADERS = "async_shader_compilation"; public static final String KEY_RENDERER_DEBUG = "renderer_debug"; public static final String KEY_HW_SHADER = "use_hw_shader"; public static final String KEY_SHADERS_ACCURATE_MUL = "shaders_accurate_mul"; diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt index 531704490..7d1e917b2 100644 --- a/src/android/app/src/main/jni/CMakeLists.txt +++ b/src/android/app/src/main/jni/CMakeLists.txt @@ -19,6 +19,10 @@ add_library(citra-android SHARED default_ini.h emu_window/emu_window.cpp emu_window/emu_window.h + emu_window/emu_window_gl.cpp + emu_window/emu_window_gl.h + emu_window/emu_window_vk.cpp + emu_window/emu_window_vk.h game_info.cpp game_settings.cpp game_settings.h diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp index 7efdeefcd..51b169368 100644 --- a/src/android/app/src/main/jni/config.cpp +++ b/src/android/app/src/main/jni/config.cpp @@ -147,6 +147,8 @@ void Config::ReadValues() { Settings::values.shaders_accurate_mul = sdl2_config->GetBoolean("Renderer", "shaders_accurate_mul", false); ReadSetting("Renderer", Settings::values.graphics_api); + ReadSetting("Renderer", Settings::values.async_shader_compilation); + ReadSetting("Renderer", Settings::values.spirv_shader_gen); ReadSetting("Renderer", Settings::values.use_hw_shader); ReadSetting("Renderer", Settings::values.use_shader_jit); ReadSetting("Renderer", Settings::values.resolution_factor); diff --git a/src/android/app/src/main/jni/default_ini.h b/src/android/app/src/main/jni/default_ini.h index 68e356b6f..8718b264b 100644 --- a/src/android/app/src/main/jni/default_ini.h +++ b/src/android/app/src/main/jni/default_ini.h @@ -99,9 +99,17 @@ cpu_clock_percentage = [Renderer] # Whether to render using OpenGL -# 1: OpenGLES (default) +# 1: OpenGL ES (default), 2: Vulkan graphics_api = +# Whether to compile shaders on multiple worker threads (Vulkan only) +# 0: Off, 1: On (default) +async_shader_compilation = + +# Whether to emit PICA fragment shader using SPIRV or GLSL (Vulkan only) +# 0: GLSL, 1: SPIR-V (default) +spirv_shader_gen = + # Whether to use hardware shaders to emulate 3DS shaders # 0: Software, 1 (default): Hardware use_hw_shader = diff --git a/src/android/app/src/main/jni/emu_window/emu_window.cpp b/src/android/app/src/main/jni/emu_window/emu_window.cpp index c2cc91ef5..1ce0429f3 100644 --- a/src/android/app/src/main/jni/emu_window/emu_window.cpp +++ b/src/android/app/src/main/jni/emu_window/emu_window.cpp @@ -6,10 +6,7 @@ #include #include #include - #include -#include - #include "common/logging/log.h" #include "common/settings.h" #include "input_common/main.h" @@ -20,52 +17,6 @@ #include "video_core/renderer_base.h" #include "video_core/video_core.h" -static constexpr std::array egl_attribs{EGL_SURFACE_TYPE, - EGL_WINDOW_BIT, - EGL_RENDERABLE_TYPE, - EGL_OPENGL_ES3_BIT_KHR, - EGL_BLUE_SIZE, - 8, - EGL_GREEN_SIZE, - 8, - EGL_RED_SIZE, - 8, - EGL_DEPTH_SIZE, - 0, - EGL_STENCIL_SIZE, - 0, - EGL_NONE}; -static constexpr std::array egl_empty_attribs{EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE}; -static constexpr std::array egl_context_attribs{EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE}; - -SharedContext_Android::SharedContext_Android(EGLDisplay egl_display, EGLConfig egl_config, - EGLContext egl_share_context) - : egl_display{egl_display}, egl_surface{eglCreatePbufferSurface(egl_display, egl_config, - egl_empty_attribs.data())}, - egl_context{eglCreateContext(egl_display, egl_config, egl_share_context, - egl_context_attribs.data())} { - ASSERT_MSG(egl_surface, "eglCreatePbufferSurface() failed!"); - ASSERT_MSG(egl_context, "eglCreateContext() failed!"); -} - -SharedContext_Android::~SharedContext_Android() { - if (!eglDestroySurface(egl_display, egl_surface)) { - LOG_CRITICAL(Frontend, "eglDestroySurface() failed"); - } - - if (!eglDestroyContext(egl_display, egl_context)) { - LOG_CRITICAL(Frontend, "eglDestroySurface() failed"); - } -} - -void SharedContext_Android::MakeCurrent() { - eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context); -} - -void SharedContext_Android::DoneCurrent() { - eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); -} - static bool IsPortraitMode() { return JNI_FALSE != IDCache::GetEnvForThread()->CallStaticBooleanMethod( IDCache::GetNativeLibraryClass(), IDCache::GetIsPortraitMode()); @@ -79,6 +30,10 @@ static void UpdateLandscapeScreenLayout() { void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) { render_window = surface; + + window_info.type = Frontend::WindowSystemType::Android; + window_info.render_surface = surface; + StopPresenting(); } @@ -98,6 +53,7 @@ void EmuWindow_Android::OnTouchMoved(int x, int y) { void EmuWindow_Android::OnFramebufferSizeChanged() { UpdateLandscapeScreenLayout(); const bool is_portrait_mode{IsPortraitMode()}; + const int bigger{window_width > window_height ? window_width : window_height}; const int smaller{window_width < window_height ? window_width : window_height}; if (is_portrait_mode) { @@ -107,7 +63,7 @@ void EmuWindow_Android::OnFramebufferSizeChanged() { } } -EmuWindow_Android::EmuWindow_Android(ANativeWindow* surface) { +EmuWindow_Android::EmuWindow_Android(ANativeWindow* surface) : host_window{surface} { LOG_DEBUG(Frontend, "Initializing EmuWindow_Android"); if (!surface) { @@ -115,108 +71,10 @@ EmuWindow_Android::EmuWindow_Android(ANativeWindow* surface) { return; } + window_width = ANativeWindow_getWidth(surface); + window_height = ANativeWindow_getHeight(surface); + Network::Init(); - - host_window = surface; - - if (egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); egl_display == EGL_NO_DISPLAY) { - LOG_CRITICAL(Frontend, "eglGetDisplay() failed"); - return; - } - if (eglInitialize(egl_display, 0, 0) != EGL_TRUE) { - LOG_CRITICAL(Frontend, "eglInitialize() failed"); - return; - } - if (EGLint egl_num_configs{}; eglChooseConfig(egl_display, egl_attribs.data(), &egl_config, 1, - &egl_num_configs) != EGL_TRUE) { - LOG_CRITICAL(Frontend, "eglChooseConfig() failed"); - return; - } - - CreateWindowSurface(); - - if (eglQuerySurface(egl_display, egl_surface, EGL_WIDTH, &window_width) != EGL_TRUE) { - return; - } - if (eglQuerySurface(egl_display, egl_surface, EGL_HEIGHT, &window_height) != EGL_TRUE) { - return; - } - - if (egl_context = eglCreateContext(egl_display, egl_config, 0, egl_context_attribs.data()); - egl_context == EGL_NO_CONTEXT) { - LOG_CRITICAL(Frontend, "eglCreateContext() failed"); - return; - } - if (eglSurfaceAttrib(egl_display, egl_surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED) != - EGL_TRUE) { - LOG_CRITICAL(Frontend, "eglSurfaceAttrib() failed"); - return; - } - if (core_context = CreateSharedContext(); !core_context) { - LOG_CRITICAL(Frontend, "CreateSharedContext() failed"); - return; - } - if (eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context) != EGL_TRUE) { - LOG_CRITICAL(Frontend, "eglMakeCurrent() failed"); - return; - } - if (!gladLoadGLES2Loader((GLADloadproc)eglGetProcAddress)) { - LOG_CRITICAL(Frontend, "gladLoadGLES2Loader() failed"); - return; - } - if (!eglSwapInterval(egl_display, Settings::values.use_vsync_new ? 1 : 0)) { - LOG_CRITICAL(Frontend, "eglSwapInterval() failed"); - return; - } - - OnFramebufferSizeChanged(); -} - -bool EmuWindow_Android::CreateWindowSurface() { - if (!host_window) { - return true; - } - - EGLint format{}; - eglGetConfigAttrib(egl_display, egl_config, EGL_NATIVE_VISUAL_ID, &format); - ANativeWindow_setBuffersGeometry(host_window, 0, 0, format); - - if (egl_surface = eglCreateWindowSurface(egl_display, egl_config, host_window, 0); - egl_surface == EGL_NO_SURFACE) { - return {}; - } - - return !!egl_surface; -} - -void EmuWindow_Android::DestroyWindowSurface() { - if (!egl_surface) { - return; - } - if (eglGetCurrentSurface(EGL_DRAW) == egl_surface) { - eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - } - if (!eglDestroySurface(egl_display, egl_surface)) { - LOG_CRITICAL(Frontend, "eglDestroySurface() failed"); - } - egl_surface = EGL_NO_SURFACE; -} - -void EmuWindow_Android::DestroyContext() { - if (!egl_context) { - return; - } - if (eglGetCurrentContext() == egl_context) { - eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - } - if (!eglDestroyContext(egl_display, egl_context)) { - LOG_CRITICAL(Frontend, "eglDestroySurface() failed"); - } - if (!eglTerminate(egl_display)) { - LOG_CRITICAL(Frontend, "eglTerminate() failed"); - } - egl_context = EGL_NO_CONTEXT; - egl_display = EGL_NO_DISPLAY; } EmuWindow_Android::~EmuWindow_Android() { @@ -224,34 +82,6 @@ EmuWindow_Android::~EmuWindow_Android() { DestroyContext(); } -std::unique_ptr EmuWindow_Android::CreateSharedContext() const { - return std::make_unique(egl_display, egl_config, egl_context); -} - -void EmuWindow_Android::StopPresenting() { - if (presenting_state == PresentingState::Running) { - eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - } - presenting_state = PresentingState::Stopped; -} - -void EmuWindow_Android::TryPresenting() { - if (presenting_state != PresentingState::Running) { - if (presenting_state == PresentingState::Initial) { - eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - presenting_state = PresentingState::Running; - } else { - return; - } - } - eglSwapInterval(egl_display, Settings::values.use_vsync_new ? 1 : 0); - if (VideoCore::g_renderer) { - VideoCore::g_renderer->TryPresent(0); - eglSwapBuffers(egl_display, egl_surface); - } -} - void EmuWindow_Android::PollEvents() { if (!render_window) { return; diff --git a/src/android/app/src/main/jni/emu_window/emu_window.h b/src/android/app/src/main/jni/emu_window/emu_window.h index 10a293c96..dd43fa16f 100644 --- a/src/android/app/src/main/jni/emu_window/emu_window.h +++ b/src/android/app/src/main/jni/emu_window/emu_window.h @@ -5,38 +5,13 @@ #pragma once #include - -#include -#include - #include "core/frontend/emu_window.h" -struct ANativeWindow; - -class SharedContext_Android : public Frontend::GraphicsContext { -public: - SharedContext_Android(EGLDisplay egl_display, EGLConfig egl_config, - EGLContext egl_share_context); - - ~SharedContext_Android() override; - - void MakeCurrent() override; - - void DoneCurrent() override; - -private: - EGLDisplay egl_display{}; - EGLSurface egl_surface{}; - EGLContext egl_context{}; -}; - class EmuWindow_Android : public Frontend::EmuWindow { public: EmuWindow_Android(ANativeWindow* surface); ~EmuWindow_Android(); - void Present(); - /// Called by the onSurfaceChanges() method to change the surface void OnSurfaceChanged(ANativeWindow* surface); @@ -47,31 +22,36 @@ public: void OnTouchMoved(int x, int y); void PollEvents() override; + void MakeCurrent() override; + void DoneCurrent() override; - void TryPresenting(); - void StopPresenting(); + virtual void TryPresenting() = 0; - std::unique_ptr CreateSharedContext() const override; + virtual void StopPresenting() = 0; -private: +protected: void OnFramebufferSizeChanged(); - bool CreateWindowSurface(); - void DestroyWindowSurface(); - void DestroyContext(); + /// Creates the API specific window surface + virtual bool CreateWindowSurface() { + return false; + } + + /// Destroys the API specific window surface + virtual void DestroyWindowSurface() {} + + /// Destroys the graphics context + virtual void DestroyContext() {} + +protected: ANativeWindow* render_window{}; ANativeWindow* host_window{}; int window_width{}; int window_height{}; - EGLConfig egl_config; - EGLSurface egl_surface{}; - EGLContext egl_context{}; - EGLDisplay egl_display{}; - std::unique_ptr core_context; enum class PresentingState { diff --git a/src/android/app/src/main/jni/emu_window/emu_window_gl.cpp b/src/android/app/src/main/jni/emu_window/emu_window_gl.cpp new file mode 100644 index 000000000..f0de55483 --- /dev/null +++ b/src/android/app/src/main/jni/emu_window/emu_window_gl.cpp @@ -0,0 +1,201 @@ +// Copyright 2019 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include + +#include +#include + +#include "common/logging/log.h" +#include "common/settings.h" +#include "input_common/main.h" +#include "jni/emu_window/emu_window_gl.h" +#include "video_core/renderer_base.h" +#include "video_core/video_core.h" + +static constexpr std::array egl_attribs{EGL_SURFACE_TYPE, + EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, + EGL_OPENGL_ES3_BIT_KHR, + EGL_BLUE_SIZE, + 8, + EGL_GREEN_SIZE, + 8, + EGL_RED_SIZE, + 8, + EGL_DEPTH_SIZE, + 0, + EGL_STENCIL_SIZE, + 0, + EGL_NONE}; +static constexpr std::array egl_empty_attribs{EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE}; +static constexpr std::array egl_context_attribs{EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE}; + +class SharedContext_Android : public Frontend::GraphicsContext { +public: + SharedContext_Android(EGLDisplay egl_display, EGLConfig egl_config, + EGLContext egl_share_context) + : egl_display{egl_display}, egl_surface{eglCreatePbufferSurface(egl_display, egl_config, + egl_empty_attribs.data())}, + egl_context{eglCreateContext(egl_display, egl_config, egl_share_context, + egl_context_attribs.data())} { + ASSERT_MSG(egl_surface, "eglCreatePbufferSurface() failed!"); + ASSERT_MSG(egl_context, "eglCreateContext() failed!"); + } + + ~SharedContext_Android() override { + if (!eglDestroySurface(egl_display, egl_surface)) { + LOG_CRITICAL(Frontend, "eglDestroySurface() failed"); + } + + if (!eglDestroyContext(egl_display, egl_context)) { + LOG_CRITICAL(Frontend, "eglDestroySurface() failed"); + } + } + + void MakeCurrent() override { + eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context); + } + + void DoneCurrent() override { + eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + } + +private: + EGLDisplay egl_display{}; + EGLSurface egl_surface{}; + EGLContext egl_context{}; +}; + +EmuWindow_Android_OpenGL::EmuWindow_Android_OpenGL(ANativeWindow* surface) + : EmuWindow_Android{surface} { + if (egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); egl_display == EGL_NO_DISPLAY) { + LOG_CRITICAL(Frontend, "eglGetDisplay() failed"); + return; + } + if (eglInitialize(egl_display, 0, 0) != EGL_TRUE) { + LOG_CRITICAL(Frontend, "eglInitialize() failed"); + return; + } + if (EGLint egl_num_configs{}; eglChooseConfig(egl_display, egl_attribs.data(), &egl_config, 1, + &egl_num_configs) != EGL_TRUE) { + LOG_CRITICAL(Frontend, "eglChooseConfig() failed"); + return; + } + + CreateWindowSurface(); + + if (eglQuerySurface(egl_display, egl_surface, EGL_WIDTH, &window_width) != EGL_TRUE) { + return; + } + if (eglQuerySurface(egl_display, egl_surface, EGL_HEIGHT, &window_height) != EGL_TRUE) { + return; + } + + if (egl_context = eglCreateContext(egl_display, egl_config, 0, egl_context_attribs.data()); + egl_context == EGL_NO_CONTEXT) { + LOG_CRITICAL(Frontend, "eglCreateContext() failed"); + return; + } + if (eglSurfaceAttrib(egl_display, egl_surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED) != + EGL_TRUE) { + LOG_CRITICAL(Frontend, "eglSurfaceAttrib() failed"); + return; + } + if (core_context = CreateSharedContext(); !core_context) { + LOG_CRITICAL(Frontend, "CreateSharedContext() failed"); + return; + } + if (eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context) != EGL_TRUE) { + LOG_CRITICAL(Frontend, "eglMakeCurrent() failed"); + return; + } + if (!gladLoadGLES2Loader((GLADloadproc)eglGetProcAddress)) { + LOG_CRITICAL(Frontend, "gladLoadGLES2Loader() failed"); + return; + } + if (!eglSwapInterval(egl_display, Settings::values.use_vsync_new ? 1 : 0)) { + LOG_CRITICAL(Frontend, "eglSwapInterval() failed"); + return; + } + + OnFramebufferSizeChanged(); +} + +bool EmuWindow_Android_OpenGL::CreateWindowSurface() { + if (!host_window) { + return true; + } + + EGLint format{}; + eglGetConfigAttrib(egl_display, egl_config, EGL_NATIVE_VISUAL_ID, &format); + ANativeWindow_setBuffersGeometry(host_window, 0, 0, format); + + if (egl_surface = eglCreateWindowSurface(egl_display, egl_config, host_window, 0); + egl_surface == EGL_NO_SURFACE) { + return {}; + } + + return egl_surface; +} + +void EmuWindow_Android_OpenGL::DestroyWindowSurface() { + if (!egl_surface) { + return; + } + if (eglGetCurrentSurface(EGL_DRAW) == egl_surface) { + eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + } + if (!eglDestroySurface(egl_display, egl_surface)) { + LOG_CRITICAL(Frontend, "eglDestroySurface() failed"); + } + egl_surface = EGL_NO_SURFACE; +} + +void EmuWindow_Android_OpenGL::DestroyContext() { + if (!egl_context) { + return; + } + if (eglGetCurrentContext() == egl_context) { + eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + } + if (!eglDestroyContext(egl_display, egl_context)) { + LOG_CRITICAL(Frontend, "eglDestroySurface() failed"); + } + if (!eglTerminate(egl_display)) { + LOG_CRITICAL(Frontend, "eglTerminate() failed"); + } + egl_context = EGL_NO_CONTEXT; + egl_display = EGL_NO_DISPLAY; +} + +std::unique_ptr EmuWindow_Android_OpenGL::CreateSharedContext() const { + return std::make_unique(egl_display, egl_config, egl_context); +} + +void EmuWindow_Android_OpenGL::StopPresenting() { + if (presenting_state == PresentingState::Running) { + eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + } + presenting_state = PresentingState::Stopped; +} + +void EmuWindow_Android_OpenGL::TryPresenting() { + if (presenting_state == PresentingState::Initial) [[unlikely]] { + eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + presenting_state = PresentingState::Running; + } + if (presenting_state != PresentingState::Running) [[unlikely]] { + return; + } + eglSwapInterval(egl_display, Settings::values.use_vsync_new ? 1 : 0); + if (VideoCore::g_renderer) { + VideoCore::g_renderer->TryPresent(0); + eglSwapBuffers(egl_display, egl_surface); + } +} diff --git a/src/android/app/src/main/jni/emu_window/emu_window_gl.h b/src/android/app/src/main/jni/emu_window/emu_window_gl.h new file mode 100644 index 000000000..b1a22b31d --- /dev/null +++ b/src/android/app/src/main/jni/emu_window/emu_window_gl.h @@ -0,0 +1,36 @@ +// Copyright 2019 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include + +#include +#include + +#include "jni/emu_window/emu_window.h" + +struct ANativeWindow; + +class EmuWindow_Android_OpenGL : public EmuWindow_Android { +public: + EmuWindow_Android_OpenGL(ANativeWindow* surface); + ~EmuWindow_Android_OpenGL() override = default; + + void TryPresenting() override; + void StopPresenting() override; + + std::unique_ptr CreateSharedContext() const override; + +private: + bool CreateWindowSurface() override; + void DestroyWindowSurface() override; + void DestroyContext() override; + +private: + EGLConfig egl_config; + EGLSurface egl_surface{}; + EGLContext egl_context{}; + EGLDisplay egl_display{}; +}; diff --git a/src/android/app/src/main/jni/emu_window/emu_window_vk.cpp b/src/android/app/src/main/jni/emu_window/emu_window_vk.cpp new file mode 100644 index 000000000..52c95dcae --- /dev/null +++ b/src/android/app/src/main/jni/emu_window/emu_window_vk.cpp @@ -0,0 +1,49 @@ +// Copyright 2019 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include "common/logging/log.h" +#include "common/settings.h" +#include "jni/emu_window/emu_window_vk.h" +#include "video_core/video_core.h" + +class SharedContext_Android : public Frontend::GraphicsContext {}; + +EmuWindow_Android_Vulkan::EmuWindow_Android_Vulkan(ANativeWindow* surface) + : EmuWindow_Android{surface} { + CreateWindowSurface(); + + if (core_context = CreateSharedContext(); !core_context) { + LOG_CRITICAL(Frontend, "CreateSharedContext() failed"); + return; + } + + OnFramebufferSizeChanged(); +} + +bool EmuWindow_Android_Vulkan::CreateWindowSurface() { + if (!host_window) { + return true; + } + + window_info.type = Frontend::WindowSystemType::Android; + window_info.render_surface = host_window; + + return true; +} + +std::unique_ptr EmuWindow_Android_Vulkan::CreateSharedContext() const { + return std::make_unique(); +} + +void EmuWindow_Android_Vulkan::StopPresenting() { + presenting_state = PresentingState::Stopped; +} + +void EmuWindow_Android_Vulkan::TryPresenting() { + if (presenting_state == PresentingState::Initial) { + presenting_state = PresentingState::Running; + } +} \ No newline at end of file diff --git a/src/android/app/src/main/jni/emu_window/emu_window_vk.h b/src/android/app/src/main/jni/emu_window/emu_window_vk.h new file mode 100644 index 000000000..69f0f7c96 --- /dev/null +++ b/src/android/app/src/main/jni/emu_window/emu_window_vk.h @@ -0,0 +1,23 @@ +// Copyright 2022 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "jni/emu_window/emu_window.h" + +struct ANativeWindow; + +class EmuWindow_Android_Vulkan : public EmuWindow_Android { +public: + EmuWindow_Android_Vulkan(ANativeWindow* surface); + ~EmuWindow_Android_Vulkan() override = default; + + void TryPresenting() override; + void StopPresenting() override; + + std::unique_ptr CreateSharedContext() const override; + +private: + bool CreateWindowSurface() override; +}; diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 2a722f37d..72241baf2 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -33,7 +33,8 @@ #include "jni/camera/ndk_camera.h" #include "jni/camera/still_image_camera.h" #include "jni/config.h" -#include "jni/emu_window/emu_window.h" +#include "jni/emu_window/emu_window_gl.h" +#include "jni/emu_window/emu_window_vk.h" #include "jni/game_settings.h" #include "jni/id_cache.h" #include "jni/input_manager.h" @@ -123,11 +124,14 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) { const auto graphics_api = Settings::values.graphics_api.GetValue(); switch (graphics_api) { case Settings::GraphicsAPI::OpenGL: - window = std::make_unique(s_surf); + window = std::make_unique(s_surf); + break; + case Settings::GraphicsAPI::Vulkan: + window = std::make_unique(s_surf); break; default: - LOG_CRITICAL(Frontend, "Unknown graphics API {}, using OpenGL", graphics_api); - window = std::make_unique(s_surf); + LOG_CRITICAL(Frontend, "Unknown graphics API {}, using Vulkan", graphics_api); + window = std::make_unique(s_surf); } Core::System& system{Core::System::GetInstance()}; diff --git a/src/android/app/src/main/jniLibs/arm64-v8a/libVkLayer_khronos_validation.so b/src/android/app/src/main/jniLibs/arm64-v8a/libVkLayer_khronos_validation.so new file mode 100644 index 000000000..b999455c4 Binary files /dev/null and b/src/android/app/src/main/jniLibs/arm64-v8a/libVkLayer_khronos_validation.so differ diff --git a/src/android/app/src/main/jniLibs/arm64-v8a/libc++_shared.so b/src/android/app/src/main/jniLibs/arm64-v8a/libc++_shared.so new file mode 100644 index 000000000..65ac529d1 Binary files /dev/null and b/src/android/app/src/main/jniLibs/arm64-v8a/libc++_shared.so differ diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index 69ae0e0e9..444abd6cb 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -177,11 +177,13 @@ - OpenGLES + OpenGL ES + Vulkan 1 + 2 diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index d5def8d9f..79af0abfa 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -74,6 +74,10 @@ Renderer Graphics API + Enable SPIR-V shader generation + Emits the fragment shader used to emulate PICA using SPIR-V instead of GLSL + Enable asynchronous shader compilation + Compiles shaders in the background to reduce stuttering during gameplay. When enabled expect temporary graphical glitches Debug Renderer Log additional graphics related debug information. When enabled, game performance will be significantly reduced. Enable V-Sync diff --git a/src/citra/CMakeLists.txt b/src/citra/CMakeLists.txt index 4e20b14d4..80cfc46e4 100644 --- a/src/citra/CMakeLists.txt +++ b/src/citra/CMakeLists.txt @@ -12,6 +12,8 @@ add_executable(citra emu_window/emu_window_sdl2_gl.h emu_window/emu_window_sdl2_sw.cpp emu_window/emu_window_sdl2_sw.h + emu_window/emu_window_sdl2_vk.cpp + emu_window/emu_window_sdl2_vk.h precompiled_headers.h resource.h ) diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index 588f46403..4029dd1e3 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp @@ -15,6 +15,7 @@ #include "citra/emu_window/emu_window_sdl2.h" #include "citra/emu_window/emu_window_sdl2_gl.h" #include "citra/emu_window/emu_window_sdl2_sw.h" +#include "citra/emu_window/emu_window_sdl2_vk.h" #include "common/common_paths.h" #include "common/detached_tasks.h" #include "common/file_util.h" @@ -351,6 +352,8 @@ int main(int argc, char** argv) { switch (Settings::values.graphics_api.GetValue()) { case Settings::GraphicsAPI::OpenGL: return std::make_unique(system, fullscreen, is_secondary); + case Settings::GraphicsAPI::Vulkan: + return std::make_unique(system, fullscreen, is_secondary); case Settings::GraphicsAPI::Software: return std::make_unique(system, fullscreen, is_secondary); } diff --git a/src/citra/config.cpp b/src/citra/config.cpp index 5edaf42da..9520a8f6d 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -133,6 +133,10 @@ void Config::ReadValues() { // Renderer ReadSetting("Renderer", Settings::values.graphics_api); + ReadSetting("Renderer", Settings::values.physical_device); + ReadSetting("Renderer", Settings::values.spirv_shader_gen); + ReadSetting("Renderer", Settings::values.async_shader_compilation); + ReadSetting("Renderer", Settings::values.async_presentation); ReadSetting("Renderer", Settings::values.use_gles); ReadSetting("Renderer", Settings::values.use_hw_shader); ReadSetting("Renderer", Settings::values.shaders_accurate_mul); diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index 941e17733..6dd52b372 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h @@ -99,7 +99,7 @@ cpu_clock_percentage = [Renderer] # Whether to render using OpenGL or Software -# 0: Software, 1: OpenGL (default) +# 0: Software, 1: OpenGL (default), 2: Vulkan graphics_api = # Whether to render using GLES or OpenGL diff --git a/src/citra/emu_window/emu_window_sdl2_vk.cpp b/src/citra/emu_window/emu_window_sdl2_vk.cpp new file mode 100644 index 000000000..51dda19b9 --- /dev/null +++ b/src/citra/emu_window/emu_window_sdl2_vk.cpp @@ -0,0 +1,88 @@ +// Copyright 2023 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include +#include +#include "citra/emu_window/emu_window_sdl2_vk.h" +#include "common/logging/log.h" +#include "common/scm_rev.h" +#include "core/frontend/emu_window.h" + +class DummyContext : public Frontend::GraphicsContext {}; + +EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen, bool is_secondary) + : EmuWindow_SDL2{system, is_secondary} { + const 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); + SDL_SysWMinfo wm; + SDL_VERSION(&wm.version); + if (SDL_GetWindowWMInfo(render_window, &wm) == SDL_FALSE) { + LOG_CRITICAL(Frontend, "Failed to get information from the window manager"); + std::exit(EXIT_FAILURE); + } + + if (fullscreen) { + Fullscreen(); + SDL_ShowCursor(false); + } + + switch (wm.subsystem) { +#ifdef SDL_VIDEO_DRIVER_WINDOWS + case SDL_SYSWM_TYPE::SDL_SYSWM_WINDOWS: + window_info.type = Frontend::WindowSystemType::Windows; + window_info.render_surface = reinterpret_cast(wm.info.win.window); + break; +#endif +#ifdef SDL_VIDEO_DRIVER_X11 + case SDL_SYSWM_TYPE::SDL_SYSWM_X11: + window_info.type = Frontend::WindowSystemType::X11; + window_info.display_connection = wm.info.x11.display; + window_info.render_surface = reinterpret_cast(wm.info.x11.window); + break; +#endif +#ifdef SDL_VIDEO_DRIVER_WAYLAND + case SDL_SYSWM_TYPE::SDL_SYSWM_WAYLAND: + window_info.type = Frontend::WindowSystemType::Wayland; + window_info.display_connection = wm.info.wl.display; + window_info.render_surface = wm.info.wl.surface; + break; +#endif +#ifdef SDL_VIDEO_DRIVER_COCOA + case SDL_SYSWM_TYPE::SDL_SYSWM_COCOA: + window_info.type = Frontend::WindowSystemType::MacOS; + window_info.render_surface = SDL_Metal_CreateView(render_window); + break; +#endif +#ifdef SDL_VIDEO_DRIVER_ANDROID + case SDL_SYSWM_TYPE::SDL_SYSWM_ANDROID: + window_info.type = Frontend::WindowSystemType::Android; + window_info.render_surface = reinterpret_cast(wm.info.android.window); + break; +#endif + default: + LOG_CRITICAL(Frontend, "Window manager subsystem {} not implemented", wm.subsystem); + std::exit(EXIT_FAILURE); + break; + } + + OnResize(); + OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); + SDL_PumpEvents(); +} + +EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() = default; + +std::unique_ptr EmuWindow_SDL2_VK::CreateSharedContext() const { + return std::make_unique(); +} diff --git a/src/citra/emu_window/emu_window_sdl2_vk.h b/src/citra/emu_window/emu_window_sdl2_vk.h new file mode 100644 index 000000000..be1cd1352 --- /dev/null +++ b/src/citra/emu_window/emu_window_sdl2_vk.h @@ -0,0 +1,24 @@ +// Copyright 2023 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include "citra/emu_window/emu_window_sdl2.h" + +namespace Frontend { +class GraphicsContext; +} + +namespace Core { +class System; +} + +class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { +public: + explicit EmuWindow_SDL2_VK(Core::System& system_, bool fullscreen, bool is_secondary); + ~EmuWindow_SDL2_VK() override; + + std::unique_ptr CreateSharedContext() const override; +}; diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index 157ecea41..663992586 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -289,6 +289,22 @@ private: }; #endif +class VulkanRenderWidget : public RenderWidget { +public: + explicit VulkanRenderWidget(GRenderWindow* parent) : RenderWidget(parent) { + setAttribute(Qt::WA_NativeWindow); + setAttribute(Qt::WA_PaintOnScreen); + if (GetWindowSystemType() == Frontend::WindowSystemType::Wayland) { + setAttribute(Qt::WA_DontCreateNativeAncestors); + } + windowHandle()->setSurfaceType(QWindow::VulkanSurface); + } + + QPaintEngine* paintEngine() const override { + return nullptr; + } +}; + struct SoftwareRenderWidget : public RenderWidget { explicit SoftwareRenderWidget(GRenderWindow* parent, Core::System& system_) : RenderWidget(parent), system(system_) {} @@ -601,6 +617,9 @@ bool GRenderWindow::InitRenderTarget() { return false; } break; + case Settings::GraphicsAPI::Vulkan: + InitializeVulkan(); + break; } // Update the Window System information with the new render target @@ -686,6 +705,13 @@ bool GRenderWindow::InitializeOpenGL() { #endif } +void GRenderWindow::InitializeVulkan() { + auto child = new VulkanRenderWidget(this); + child_widget = child; + child_widget->windowHandle()->create(); + main_context = std::make_unique(); +} + void GRenderWindow::InitializeSoftware() { child_widget = new SoftwareRenderWidget(this, system); main_context = std::make_unique(); diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h index 1689eebb7..ce96b313f 100644 --- a/src/citra_qt/bootmanager.h +++ b/src/citra_qt/bootmanager.h @@ -187,6 +187,7 @@ private: void OnMinimalClientAreaChangeRequest(std::pair minimal_size) override; bool InitializeOpenGL(); + void InitializeVulkan(); void InitializeSoftware(); bool LoadOpenGL(); diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index 3da6114b8..487b28438 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -483,6 +483,7 @@ void Config::ReadDebuggingValues() { ReadBasicSetting(Settings::values.use_gdbstub); ReadBasicSetting(Settings::values.gdbstub_port); ReadBasicSetting(Settings::values.renderer_debug); + ReadBasicSetting(Settings::values.dump_command_buffers); qt_config->beginGroup(QStringLiteral("LLE")); for (const auto& service_module : Service::service_module_map) { @@ -627,6 +628,10 @@ void Config::ReadRendererValues() { qt_config->beginGroup(QStringLiteral("Renderer")); ReadGlobalSetting(Settings::values.graphics_api); + ReadGlobalSetting(Settings::values.physical_device); + ReadGlobalSetting(Settings::values.spirv_shader_gen); + ReadGlobalSetting(Settings::values.async_shader_compilation); + ReadGlobalSetting(Settings::values.async_presentation); ReadGlobalSetting(Settings::values.use_hw_shader); ReadGlobalSetting(Settings::values.shaders_accurate_mul); ReadGlobalSetting(Settings::values.use_disk_shader_cache); @@ -1107,6 +1112,10 @@ void Config::SaveRendererValues() { qt_config->beginGroup(QStringLiteral("Renderer")); WriteGlobalSetting(Settings::values.graphics_api); + WriteGlobalSetting(Settings::values.physical_device); + WriteGlobalSetting(Settings::values.spirv_shader_gen); + WriteGlobalSetting(Settings::values.async_shader_compilation); + WriteGlobalSetting(Settings::values.async_presentation); WriteGlobalSetting(Settings::values.use_hw_shader); WriteGlobalSetting(Settings::values.shaders_accurate_mul); WriteGlobalSetting(Settings::values.use_disk_shader_cache); diff --git a/src/citra_qt/configuration/configure_graphics.ui b/src/citra_qt/configuration/configure_graphics.ui index 4235759cb..22d799784 100644 --- a/src/citra_qt/configuration/configure_graphics.ui +++ b/src/citra_qt/configuration/configure_graphics.ui @@ -63,6 +63,11 @@ OpenGL + + + Vulkan + + diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 202be08c3..560cc23dd 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -2470,9 +2470,11 @@ void GMainWindow::ShowMouseCursor() { } void GMainWindow::UpdateAPIIndicator(bool update) { - static std::array graphics_apis = {QStringLiteral("SOFTWARE"), QStringLiteral("OPENGL")}; + static std::array graphics_apis = {QStringLiteral("SOFTWARE"), QStringLiteral("OPENGL"), + QStringLiteral("VULKAN")}; - static std::array graphics_api_colors = {QStringLiteral("#3ae400"), QStringLiteral("#00ccdd")}; + static std::array graphics_api_colors = {QStringLiteral("#3ae400"), QStringLiteral("#00ccdd"), + QStringLiteral("#91242a")}; u32 api_index = static_cast(Settings::values.graphics_api.GetValue()); if (update) { diff --git a/src/common/dynamic_library/dynamic_library.cpp b/src/common/dynamic_library/dynamic_library.cpp index f5b348537..013c0c91a 100644 --- a/src/common/dynamic_library/dynamic_library.cpp +++ b/src/common/dynamic_library/dynamic_library.cpp @@ -12,6 +12,8 @@ namespace Common { +DynamicLibrary::DynamicLibrary() = default; + DynamicLibrary::DynamicLibrary(std::string_view name, int major, int minor) { auto full_name = GetLibraryName(name, major, minor); void(Load(full_name)); diff --git a/src/common/hash.h b/src/common/hash.h index fde248de9..d0bb1413d 100644 --- a/src/common/hash.h +++ b/src/common/hash.h @@ -37,8 +37,8 @@ static inline u64 ComputeStructHash64(const T& data) noexcept { * Combines the seed parameter with the provided hash, producing a new unique hash * Implementation from: http://boost.sourceforge.net/doc/html/boost/hash_combine.html */ -inline u64 HashCombine(std::size_t& seed, const u64 hash) { - return seed ^= hash + 0x9e3779b9 + (seed << 6) + (seed >> 2); +inline u64 HashCombine(std::size_t seed, const u64 hash) { + return seed ^ (hash + 0x9e3779b9 + (seed << 6) + (seed >> 2)); } template diff --git a/src/common/settings.cpp b/src/common/settings.cpp index f0356b00a..bf9086e14 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -72,6 +72,9 @@ void LogSettings() { log_setting("Core_CPUClockPercentage", values.cpu_clock_percentage.GetValue()); log_setting("Renderer_UseGLES", values.use_gles.GetValue()); log_setting("Renderer_GraphicsAPI", GetGraphicsAPIName(values.graphics_api.GetValue())); + log_setting("Renderer_AsyncShaders", values.async_shader_compilation.GetValue()); + log_setting("Renderer_AsyncPresentation", values.async_presentation.GetValue()); + log_setting("Renderer_SpirvShaderGen", values.spirv_shader_gen.GetValue()); log_setting("Renderer_Debug", values.renderer_debug.GetValue()); log_setting("Renderer_UseHwShader", values.use_hw_shader.GetValue()); log_setting("Renderer_ShadersAccurateMul", values.shaders_accurate_mul.GetValue()); @@ -159,6 +162,10 @@ void RestoreGlobalState(bool is_powered_on) { // Renderer values.graphics_api.SetGlobal(true); + values.physical_device.SetGlobal(true); + values.spirv_shader_gen.SetGlobal(true); + values.async_shader_compilation.SetGlobal(true); + values.async_presentation.SetGlobal(true); values.use_hw_shader.SetGlobal(true); values.use_disk_shader_cache.SetGlobal(true); values.shaders_accurate_mul.SetGlobal(true); diff --git a/src/common/settings.h b/src/common/settings.h index 6d3cbd98a..fcc10aa24 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -17,10 +17,10 @@ namespace Settings { -constexpr u32 GraphicsAPICount = 2; enum class GraphicsAPI { Software = 0, OpenGL = 1, + Vulkan = 2, }; enum class InitClock : u32 { @@ -430,12 +430,15 @@ struct Values { Setting allow_plugin_loader{true, "allow_plugin_loader"}; // Renderer - SwitchableSetting graphics_api{ - GraphicsAPI::OpenGL, GraphicsAPI::Software, static_cast(GraphicsAPICount - 1), - "graphics_api"}; + SwitchableSetting graphics_api{GraphicsAPI::OpenGL, GraphicsAPI::Software, + GraphicsAPI::Vulkan, "graphics_api"}; + SwitchableSetting physical_device{0, "physical_device"}; Setting use_gles{false, "use_gles"}; Setting renderer_debug{false, "renderer_debug"}; Setting dump_command_buffers{false, "dump_command_buffers"}; + SwitchableSetting spirv_shader_gen{true, "spirv_shader_gen"}; + SwitchableSetting async_shader_compilation{false, "async_shader_compilation"}; + SwitchableSetting async_presentation{true, "async_presentation"}; SwitchableSetting use_hw_shader{true, "use_hw_shader"}; SwitchableSetting use_disk_shader_cache{true, "use_disk_shader_cache"}; SwitchableSetting shaders_accurate_mul{true, "shaders_accurate_mul"};