diff --git a/CMakeLists.txt b/CMakeLists.txt index d2bf7ae8a..eaa5ca118 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,6 +80,10 @@ option(ENABLE_OPENAL "Enables the OpenAL audio backend" ON) CMAKE_DEPENDENT_OPTION(ENABLE_LIBUSB "Enable libusb for GameCube Adapter support" ON "NOT IOS" OFF) +CMAKE_DEPENDENT_OPTION(ENABLE_SOFTWARE_RENDERER "Enables the software renderer" ON "NOT ANDROID" OFF) +CMAKE_DEPENDENT_OPTION(ENABLE_OPENGL "Enables the OpenGL renderer" ON "NOT APPLE" OFF) +option(ENABLE_VULKAN "Enables the Vulkan renderer" ON) + option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF) CMAKE_DEPENDENT_OPTION(CITRA_ENABLE_BUNDLE_TARGET "Enable the distribution bundling target." ON "NOT ANDROID AND NOT IOS" OFF) @@ -255,20 +259,22 @@ find_package(tsl-robin-map QUIET) # ====================================== if (APPLE) - if (NOT USE_SYSTEM_MOLTENVK) - download_moltenvk() - endif() - find_library(MOLTENVK_LIBRARY MoltenVK REQUIRED) - message(STATUS "Using MoltenVK at ${MOLTENVK_LIBRARY}.") - if (NOT IOS) # Umbrella framework for everything GUI-related find_library(COCOA_LIBRARY Cocoa REQUIRED) endif() - find_library(AVFOUNDATION_LIBRARY AVFoundation REQUIRED) find_library(IOSURFACE_LIBRARY IOSurface REQUIRED) set(PLATFORM_LIBRARIES ${COCOA_LIBRARY} ${AVFOUNDATION_LIBRARY} ${IOSURFACE_LIBRARY} ${MOLTENVK_LIBRARY}) + + if (ENABLE_VULKAN) + if (NOT USE_SYSTEM_MOLTENVK) + download_moltenvk() + endif() + find_library(MOLTENVK_LIBRARY MoltenVK REQUIRED) + message(STATUS "Using MoltenVK at ${MOLTENVK_LIBRARY}.") + set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} ${MOLTENVK_LIBRARY}) + endif() elseif (WIN32) set(PLATFORM_LIBRARIES winmm ws2_32) if (MINGW) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 29e0a73b5..2f7bdde71 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -120,29 +120,6 @@ if (MSVC) add_subdirectory(getopt) endif() -# Glad -add_subdirectory(glad) - -# glslang -if(USE_SYSTEM_GLSLANG) - find_package(glslang REQUIRED) - add_library(glslang INTERFACE) - add_library(SPIRV INTERFACE) - target_link_libraries(glslang INTERFACE glslang::glslang) - target_link_libraries(SPIRV INTERFACE glslang::SPIRV) - # System include path is different from submodule include path - get_target_property(GLSLANG_PREFIX glslang::SPIRV INTERFACE_INCLUDE_DIRECTORIES) - target_include_directories(SPIRV SYSTEM INTERFACE "${GLSLANG_PREFIX}/glslang") -else() - set(SKIP_GLSLANG_INSTALL ON CACHE BOOL "") - set(ENABLE_GLSLANG_BINARIES OFF CACHE BOOL "") - set(ENABLE_SPVREMAPPER OFF CACHE BOOL "") - set(ENABLE_CTEST OFF CACHE BOOL "") - set(ENABLE_HLSL OFF CACHE BOOL "") - set(BUILD_EXTERNAL OFF CACHE BOOL "") - add_subdirectory(glslang) -endif() - # inih if(USE_SYSTEM_INIH) find_package(inih REQUIRED COMPONENTS inih inir) @@ -197,9 +174,6 @@ if(NOT USE_SYSTEM_SOUNDTOUCH) target_compile_definitions(SoundTouch PUBLIC SOUNDTOUCH_INTEGER_SAMPLES) endif() -# sirit -add_subdirectory(sirit EXCLUDE_FROM_ALL) - # Teakra add_subdirectory(teakra EXCLUDE_FROM_ALL) @@ -371,32 +345,64 @@ if (ENABLE_OPENAL) endif() endif() -# VMA -if(USE_SYSTEM_VMA) - add_library(vma INTERFACE) - find_package(VulkanMemoryAllocator REQUIRED) - if(TARGET GPUOpen::VulkanMemoryAllocator) - message(STATUS "Found VulkanMemoryAllocator") - target_link_libraries(vma INTERFACE GPUOpen::VulkanMemoryAllocator) - endif() -else() - add_library(vma INTERFACE) - target_include_directories(vma SYSTEM INTERFACE ./vma/include) +# OpenGL dependencies +if (ENABLE_OPENGL) + # Glad + add_subdirectory(glad) endif() -# vulkan-headers -add_library(vulkan-headers INTERFACE) -if(USE_SYSTEM_VULKAN_HEADERS) - find_package(Vulkan REQUIRED) - if(TARGET Vulkan::Headers) - message(STATUS "Found Vulkan headers") - target_link_libraries(vulkan-headers INTERFACE Vulkan::Headers) +# Vulkan dependencies +if (ENABLE_VULKAN) + # glslang + if(USE_SYSTEM_GLSLANG) + find_package(glslang REQUIRED) + add_library(glslang INTERFACE) + add_library(SPIRV INTERFACE) + target_link_libraries(glslang INTERFACE glslang::glslang) + target_link_libraries(SPIRV INTERFACE glslang::SPIRV) + # System include path is different from submodule include path + get_target_property(GLSLANG_PREFIX glslang::SPIRV INTERFACE_INCLUDE_DIRECTORIES) + target_include_directories(SPIRV SYSTEM INTERFACE "${GLSLANG_PREFIX}/glslang") + else() + set(SKIP_GLSLANG_INSTALL ON CACHE BOOL "") + set(ENABLE_GLSLANG_BINARIES OFF CACHE BOOL "") + set(ENABLE_SPVREMAPPER OFF CACHE BOOL "") + set(ENABLE_CTEST OFF CACHE BOOL "") + set(ENABLE_HLSL OFF CACHE BOOL "") + set(BUILD_EXTERNAL OFF CACHE BOOL "") + add_subdirectory(glslang) endif() -else() - target_include_directories(vulkan-headers SYSTEM INTERFACE ./vulkan-headers/include) -endif() -# adrenotools -if (ANDROID AND "arm64" IN_LIST ARCHITECTURE) - add_subdirectory(libadrenotools) + # sirit + add_subdirectory(sirit EXCLUDE_FROM_ALL) + + # VMA + if(USE_SYSTEM_VMA) + add_library(vma INTERFACE) + find_package(VulkanMemoryAllocator REQUIRED) + if(TARGET GPUOpen::VulkanMemoryAllocator) + message(STATUS "Found VulkanMemoryAllocator") + target_link_libraries(vma INTERFACE GPUOpen::VulkanMemoryAllocator) + endif() + else() + add_library(vma INTERFACE) + target_include_directories(vma SYSTEM INTERFACE ./vma/include) + endif() + + # vulkan-headers + add_library(vulkan-headers INTERFACE) + if(USE_SYSTEM_VULKAN_HEADERS) + find_package(Vulkan REQUIRED) + if(TARGET Vulkan::Headers) + message(STATUS "Found Vulkan headers") + target_link_libraries(vulkan-headers INTERFACE Vulkan::Headers) + endif() + else() + target_include_directories(vulkan-headers SYSTEM INTERFACE ./vulkan-headers/include) + endif() + + # adrenotools + if (ANDROID AND "arm64" IN_LIST ARCHITECTURE) + add_subdirectory(libadrenotools) + endif() endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e94302d31..461cd57e6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -159,8 +159,14 @@ else() endif() endif() -if (NOT APPLE) - add_compile_definitions(HAS_OPENGL) +if(ENABLE_SOFTWARE_RENDERER) + add_compile_definitions(ENABLE_SOFTWARE_RENDERER) +endif() +if(ENABLE_OPENGL) + add_compile_definitions(ENABLE_OPENGL) +endif() +if(ENABLE_VULKAN) + add_compile_definitions(ENABLE_VULKAN) endif() add_subdirectory(common) diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt index 624826e0f..84a34441a 100644 --- a/src/android/app/src/main/jni/CMakeLists.txt +++ b/src/android/app/src/main/jni/CMakeLists.txt @@ -19,10 +19,6 @@ 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 @@ -35,10 +31,23 @@ add_library(citra-android SHARED ) target_link_libraries(citra-android PRIVATE audio_core citra_common citra_core input_common network) -target_link_libraries(citra-android PRIVATE android camera2ndk EGL glad inih jnigraphics log mediandk yuv) +target_link_libraries(citra-android PRIVATE android camera2ndk inih jnigraphics log mediandk yuv) -if ("arm64" IN_LIST ARCHITECTURE) - target_link_libraries(citra-android PRIVATE adrenotools) +if (ENABLE_OPENGL) + target_sources(citra-android PRIVATE + emu_window/emu_window_gl.cpp + emu_window/emu_window_gl.h + ) + target_link_libraries(citra-android PRIVATE EGL glad) +endif() +if (ENABLE_VULKAN) + target_sources(citra-android PRIVATE + emu_window/emu_window_vk.cpp + emu_window/emu_window_vk.h + ) + if ("arm64" IN_LIST ARCHITECTURE) + target_link_libraries(citra-android PRIVATE adrenotools) + endif() endif() set(CPACK_PACKAGE_EXECUTABLES ${CPACK_PACKAGE_EXECUTABLES} citra-android) diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 1b07847b9..c670f7d19 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -45,8 +45,12 @@ #include "jni/camera/ndk_camera.h" #include "jni/camera/still_image_camera.h" #include "jni/config.h" +#ifdef ENABLE_OPENGL #include "jni/emu_window/emu_window_gl.h" +#endif +#ifdef ENABLE_VULKAN #include "jni/emu_window/emu_window_vk.h" +#endif #include "jni/game_settings.h" #include "jni/id_cache.h" #include "jni/input_manager.h" @@ -55,7 +59,7 @@ #include "video_core/gpu.h" #include "video_core/renderer_base.h" -#if CITRA_ARCH(arm64) +#if defined(ENABLE_VULKAN) && CITRA_ARCH(arm64) #include #endif @@ -142,15 +146,29 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) { const auto graphics_api = Settings::values.graphics_api.GetValue(); switch (graphics_api) { +#ifdef ENABLE_OPENGL case Settings::GraphicsAPI::OpenGL: window = std::make_unique(system, s_surf); break; +#endif +#ifdef ENABLE_VULKAN case Settings::GraphicsAPI::Vulkan: window = std::make_unique(s_surf, vulkan_library); break; +#endif default: - LOG_CRITICAL(Frontend, "Unknown graphics API {}, using Vulkan", graphics_api); + LOG_CRITICAL(Frontend, + "Unknown or unsupported graphics API {}, falling back to available default", + graphics_api); +#ifdef ENABLE_OPENGL + window = std::make_unique(system, s_surf); +#elif ENABLE_VULKAN window = std::make_unique(s_surf, vulkan_library); +#else +// TODO: Add a null renderer backend for this, perhaps. +#error "At least one renderer must be enabled." +#endif + break; } // Forces a config reload on game boot, if the user changed settings in the UI @@ -239,7 +257,7 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) { void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir, const std::string& custom_driver_name, const std::string& file_redirect_dir) { -#if CITRA_ARCH(arm64) +#if defined(ENABLE_VULKAN) && CITRA_ARCH(arm64) void* handle{}; const char* file_redirect_dir_{}; int featureFlags{}; diff --git a/src/citra/CMakeLists.txt b/src/citra/CMakeLists.txt index 80cfc46e4..42de087c7 100644 --- a/src/citra/CMakeLists.txt +++ b/src/citra/CMakeLists.txt @@ -8,25 +8,42 @@ add_executable(citra default_ini.h emu_window/emu_window_sdl2.cpp emu_window/emu_window_sdl2.h - emu_window/emu_window_sdl2_gl.cpp - 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 ) +if (ENABLE_SOFTWARE_RENDERER) + target_sources(citra PRIVATE + emu_window/emu_window_sdl2_sw.cpp + emu_window/emu_window_sdl2_sw.h + ) +endif() +if (ENABLE_OPENGL) + target_sources(citra PRIVATE + emu_window/emu_window_sdl2_gl.cpp + emu_window/emu_window_sdl2_gl.h + ) +endif() +if (ENABLE_VULKAN) + target_sources(citra PRIVATE + emu_window/emu_window_sdl2_vk.cpp + emu_window/emu_window_sdl2_vk.h + ) +endif() + create_target_directory_groups(citra) target_link_libraries(citra PRIVATE citra_common citra_core input_common network) -target_link_libraries(citra PRIVATE inih glad) +target_link_libraries(citra PRIVATE inih) if (MSVC) target_link_libraries(citra PRIVATE getopt) endif() target_link_libraries(citra PRIVATE ${PLATFORM_LIBRARIES} SDL2::SDL2 Threads::Threads) +if (ENABLE_OPENGL) + target_link_libraries(citra PRIVATE glad) +endif() + if(UNIX AND NOT APPLE) install(TARGETS citra RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") endif() diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index 7288f3407..8488b64df 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp @@ -13,9 +13,15 @@ #include "citra/config.h" #include "citra/emu_window/emu_window_sdl2.h" +#ifdef ENABLE_OPENGL #include "citra/emu_window/emu_window_sdl2_gl.h" +#endif +#ifdef ENABLE_SOFTWARE_RENDERER #include "citra/emu_window/emu_window_sdl2_sw.h" +#endif +#ifdef ENABLE_VULKAN #include "citra/emu_window/emu_window_sdl2_vk.h" +#endif #include "common/common_paths.h" #include "common/detached_tasks.h" #include "common/file_util.h" @@ -354,16 +360,36 @@ int main(int argc, char** argv) { const auto create_emu_window = [&](bool fullscreen, bool is_secondary) -> std::unique_ptr { - switch (Settings::values.graphics_api.GetValue()) { + const auto graphics_api = Settings::values.graphics_api.GetValue(); + switch (graphics_api) { +#ifdef ENABLE_OPENGL case Settings::GraphicsAPI::OpenGL: return std::make_unique(system, fullscreen, is_secondary); +#endif +#ifdef ENABLE_VULKAN case Settings::GraphicsAPI::Vulkan: return std::make_unique(system, fullscreen, is_secondary); +#endif +#ifdef ENABLE_SOFTWARE_RENDERER case Settings::GraphicsAPI::Software: return std::make_unique(system, fullscreen, is_secondary); +#endif + default: + LOG_CRITICAL( + Frontend, + "Unknown or unsupported graphics API {}, falling back to available default", + graphics_api); +#ifdef ENABLE_OPENGL + return std::make_unique(system, fullscreen, is_secondary); +#elif ENABLE_VULKAN + return std::make_unique(system, fullscreen, is_secondary); +#elif ENABLE_SOFTWARE_RENDERER + return std::make_unique(system, fullscreen, is_secondary); +#else +// TODO: Add a null renderer backend for this, perhaps. +#error "At least one renderer must be enabled." +#endif } - LOG_ERROR(Frontend, "Invalid Graphics API, using OpenGL"); - return std::make_unique(system, fullscreen, is_secondary); }; const auto emu_window{create_emu_window(fullscreen, false)}; diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 8edba366b..ec05a18d5 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -185,10 +185,15 @@ add_executable(citra-qt util/spinbox.h util/util.cpp util/util.h - util/vk_device_info.cpp - util/vk_device_info.h ) +if (ENABLE_VULKAN) + target_sources(citra-qt PRIVATE + util/vk_device_info.cpp + util/vk_device_info.h + ) +endif() + file(GLOB COMPAT_LIST ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) @@ -300,8 +305,16 @@ endif() create_target_directory_groups(citra-qt) target_link_libraries(citra-qt PRIVATE audio_core citra_common citra_core input_common network video_core) -target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt6::Widgets Qt6::Multimedia Qt6::Concurrent) -target_link_libraries(citra-qt PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads vulkan-headers) +target_link_libraries(citra-qt PRIVATE Boost::boost nihstro-headers Qt6::Widgets Qt6::Multimedia Qt6::Concurrent) +target_link_libraries(citra-qt PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) + +if (ENABLE_OPENGL) + target_link_libraries(citra-qt PRIVATE glad) +endif() + +if (ENABLE_VULKAN) + target_link_libraries(citra-qt PRIVATE vulkan-headers) +endif() if (NOT WIN32) target_include_directories(citra-qt PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS}) diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index 17c1b30fd..8f63f6713 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -26,7 +26,7 @@ #include "video_core/renderer_base.h" #include "video_core/renderer_software/renderer_software.h" -#ifdef HAS_OPENGL +#ifdef ENABLE_OPENGL #include #include @@ -137,7 +137,7 @@ void EmuThread::run() { #endif } -#ifdef HAS_OPENGL +#ifdef ENABLE_OPENGL static std::unique_ptr CreateQOpenGLContext(bool gles) { QSurfaceFormat format; if (gles) { @@ -258,7 +258,7 @@ public: virtual ~RenderWidget() = default; }; -#ifdef HAS_OPENGL +#ifdef ENABLE_OPENGL class OpenGLRenderWidget : public RenderWidget { public: explicit OpenGLRenderWidget(GRenderWindow* parent, Core::System& system_, bool is_secondary) @@ -305,6 +305,7 @@ private: }; #endif +#ifdef ENABLE_VULKAN class VulkanRenderWidget : public RenderWidget { public: explicit VulkanRenderWidget(GRenderWindow* parent) : RenderWidget(parent) { @@ -324,7 +325,9 @@ public: return nullptr; } }; +#endif +#ifdef ENABLE_SOFTWARE_RENDERER struct SoftwareRenderWidget : public RenderWidget { explicit SoftwareRenderWidget(GRenderWindow* parent, Core::System& system_) : RenderWidget(parent), system(system_) {} @@ -377,6 +380,7 @@ struct SoftwareRenderWidget : public RenderWidget { private: Core::System& system; }; +#endif static Frontend::WindowSystemType GetWindowSystemType() { // Determine WSI type based on Qt platform. @@ -636,17 +640,40 @@ bool GRenderWindow::InitRenderTarget() { const auto graphics_api = Settings::values.graphics_api.GetValue(); switch (graphics_api) { +#ifdef ENABLE_SOFTWARE_RENDERER case Settings::GraphicsAPI::Software: InitializeSoftware(); break; +#endif +#ifdef ENABLE_OPENGL case Settings::GraphicsAPI::OpenGL: if (!InitializeOpenGL() || !LoadOpenGL()) { return false; } break; +#endif +#ifdef ENABLE_VULKAN case Settings::GraphicsAPI::Vulkan: InitializeVulkan(); break; +#endif + default: + LOG_CRITICAL(Frontend, + "Unknown or unsupported graphics API {}, falling back to available default", + graphics_api); +#ifdef ENABLE_OPENGL + if (!InitializeOpenGL() || !LoadOpenGL()) { + return false; + } +#elif ENABLE_VULKAN + InitializeVulkan(); +#elif ENABLE_SOFTWARE_RENDERER + InitializeSoftware(); +#else +// TODO: Add a null renderer backend for this, perhaps. +#error "At least one renderer must be enabled." +#endif + break; } // Update the Window System information with the new render target @@ -700,8 +727,8 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair minimal setMinimumSize(minimal_size.first, minimal_size.second); } +#ifdef ENABLE_OPENGL bool GRenderWindow::InitializeOpenGL() { -#ifdef HAS_OPENGL if (!QOpenGLContext::supportsThreadedOpenGL()) { QMessageBox::warning(this, tr("OpenGL not available!"), tr("OpenGL shared contexts are not supported.")); @@ -726,33 +753,13 @@ bool GRenderWindow::InitializeOpenGL() { child_widget->windowHandle()->setFormat(format); return true; -#else - QMessageBox::warning(this, tr("OpenGL not available!"), - tr("Citra has not been compiled with OpenGL support.")); - return false; -#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(); -} - -#ifdef HAS_OPENGL static void* GetProcAddressGL(const char* name) { return reinterpret_cast(QOpenGLContext::currentContext()->getProcAddress(name)); } -#endif bool GRenderWindow::LoadOpenGL() { -#ifdef HAS_OPENGL auto context = CreateSharedContext(); auto scope = context->Acquire(); const auto gles = context->IsGLES(); @@ -785,12 +792,24 @@ bool GRenderWindow::LoadOpenGL() { } return true; -#else - QMessageBox::warning(this, tr("OpenGL not available!"), - tr("Citra has not been compiled with OpenGL support.")); - return false; -#endif } +#endif + +#ifdef ENABLE_VULKAN +void GRenderWindow::InitializeVulkan() { + auto child = new VulkanRenderWidget(this); + child_widget = child; + child_widget->windowHandle()->create(); + main_context = std::make_unique(); +} +#endif + +#ifdef ENABLE_SOFTWARE_RENDERER +void GRenderWindow::InitializeSoftware() { + child_widget = new SoftwareRenderWidget(this, system); + main_context = std::make_unique(); +} +#endif void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { this->emu_thread = emu_thread; @@ -805,7 +824,7 @@ void GRenderWindow::showEvent(QShowEvent* event) { } std::unique_ptr GRenderWindow::CreateSharedContext() const { -#ifdef HAS_OPENGL +#ifdef ENABLE_OPENGL const auto graphics_api = Settings::values.graphics_api.GetValue(); if (graphics_api == Settings::GraphicsAPI::OpenGL) { auto gl_context = static_cast(main_context.get()); diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h index ce96b313f..24fdf45e3 100644 --- a/src/citra_qt/bootmanager.h +++ b/src/citra_qt/bootmanager.h @@ -186,10 +186,16 @@ private: void OnMinimalClientAreaChangeRequest(std::pair minimal_size) override; +#ifdef ENABLE_OPENGL bool InitializeOpenGL(); - void InitializeVulkan(); - void InitializeSoftware(); bool LoadOpenGL(); +#endif +#ifdef ENABLE_VULKAN + void InitializeVulkan(); +#endif +#ifdef ENABLE_SOFTWARE_RENDERER + void InitializeSoftware(); +#endif QWidget* child_widget = nullptr; diff --git a/src/citra_qt/configuration/configure_debug.cpp b/src/citra_qt/configuration/configure_debug.cpp index 9143959e3..2386e2049 100644 --- a/src/citra_qt/configuration/configure_debug.cpp +++ b/src/citra_qt/configuration/configure_debug.cpp @@ -13,7 +13,9 @@ #include "common/logging/backend.h" #include "common/settings.h" #include "ui_configure_debug.h" +#ifdef ENABLE_VULKAN #include "video_core/renderer_vulkan/vk_instance.h" +#endif // The QSlider doesn't have an easy way to set a custom step amount, // so we can just convert from the sliders range (0 - 79) to the expected @@ -36,6 +38,7 @@ ConfigureDebug::ConfigureDebug(bool is_powered_on_, QWidget* parent) QDesktopServices::openUrl(QUrl::fromLocalFile(path)); }); +#ifdef ENABLE_VULKAN connect(ui->toggle_renderer_debug, &QCheckBox::clicked, this, [this](bool checked) { if (checked && Settings::values.graphics_api.GetValue() == Settings::GraphicsAPI::Vulkan) { try { @@ -65,6 +68,7 @@ ConfigureDebug::ConfigureDebug(bool is_powered_on_, QWidget* parent) } } }); +#endif ui->toggle_cpu_jit->setEnabled(!is_powered_on); ui->toggle_renderer_debug->setEnabled(!is_powered_on); diff --git a/src/citra_qt/configuration/configure_enhancements.cpp b/src/citra_qt/configuration/configure_enhancements.cpp index 55b8a79eb..e1a831eee 100644 --- a/src/citra_qt/configuration/configure_enhancements.cpp +++ b/src/citra_qt/configuration/configure_enhancements.cpp @@ -7,7 +7,9 @@ #include "citra_qt/configuration/configure_enhancements.h" #include "common/settings.h" #include "ui_configure_enhancements.h" +#ifdef ENABLE_OPENGL #include "video_core/renderer_opengl/post_processing_opengl.h" +#endif ConfigureEnhancements::ConfigureEnhancements(QWidget* parent) : QWidget(parent), ui(std::make_unique()) { @@ -117,12 +119,14 @@ void ConfigureEnhancements::updateShaders(Settings::StereoRenderOption stereo_op ui->shader_combobox->setCurrentIndex(0); +#ifdef ENABLE_OPENGL for (const auto& shader : OpenGL::GetPostProcessingShaderList( stereo_option == Settings::StereoRenderOption::Anaglyph)) { ui->shader_combobox->addItem(QString::fromStdString(shader)); if (current_shader == shader) ui->shader_combobox->setCurrentIndex(ui->shader_combobox->count() - 1); } +#endif } void ConfigureEnhancements::RetranslateUI() { diff --git a/src/citra_qt/configuration/configure_graphics.cpp b/src/citra_qt/configuration/configure_graphics.cpp index 7e2fcaee1..1ed3f63b0 100644 --- a/src/citra_qt/configuration/configure_graphics.cpp +++ b/src/citra_qt/configuration/configure_graphics.cpp @@ -3,11 +3,14 @@ // Refer to the license.txt file included. #include +#include #include "citra_qt/configuration/configuration_shared.h" #include "citra_qt/configuration/configure_graphics.h" #include "common/settings.h" #include "ui_configure_graphics.h" +#ifdef ENABLE_VULKAN #include "video_core/renderer_vulkan/vk_instance.h" +#endif ConfigureGraphics::ConfigureGraphics(std::span physical_devices, bool is_powered_on, QWidget* parent) @@ -27,12 +30,32 @@ ConfigureGraphics::ConfigureGraphics(std::span physical_devices, // Set the index to -1 to ensure the below lambda is called with setCurrentIndex ui->graphics_api_combo->setCurrentIndex(-1); + auto graphics_api_combo_model = + qobject_cast(ui->graphics_api_combo->model()); +#ifndef ENABLE_SOFTWARE_RENDERER + const auto software_item = + graphics_api_combo_model->item(static_cast(Settings::GraphicsAPI::Software)); + software_item->setFlags(software_item->flags() & ~Qt::ItemIsEnabled); +#endif +#ifndef ENABLE_OPENGL + const auto opengl_item = + graphics_api_combo_model->item(static_cast(Settings::GraphicsAPI::OpenGL)); + opengl_item->setFlags(opengl_item->flags() & ~Qt::ItemIsEnabled); +#endif +#ifndef ENABLE_VULKAN + const auto vulkan_item = + graphics_api_combo_model->item(static_cast(Settings::GraphicsAPI::Vulkan)); + vulkan_item->setFlags(vulkan_item->flags() & ~Qt::ItemIsEnabled); +#else if (physical_devices.empty()) { - const u32 index = static_cast(Settings::GraphicsAPI::Vulkan); - ui->graphics_api_combo->removeItem(index); + const auto vulkan_item = + graphics_api_combo_model->item(static_cast(Settings::GraphicsAPI::Vulkan)); + vulkan_item->setFlags(vulkan_item->flags() & ~Qt::ItemIsEnabled); + ui->physical_device_combo->setVisible(false); ui->spirv_shader_gen->setVisible(false); } +#endif connect(ui->graphics_api_combo, qOverload(&QComboBox::currentIndexChanged), this, [this](int index) { diff --git a/src/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp index 996088df0..43fcbcee6 100644 --- a/src/citra_qt/game_list.cpp +++ b/src/citra_qt/game_list.cpp @@ -519,6 +519,7 @@ void GameList::UpdateColumnVisibility() { tree_view->setColumnHidden(COLUMN_SIZE, !UISettings::values.show_size_column); } +#ifdef ENABLE_OPENGL void ForEachOpenGLCacheFile(u64 program_id, auto func) { for (const std::string_view cache_type : {"separable", "conventional"}) { const std::string path = fmt::format("{}opengl/precompiled/{}/{:016X}.bin", @@ -528,6 +529,7 @@ void ForEachOpenGLCacheFile(u64 program_id, auto func) { func(file); } } +#endif void GameList::AddGamePopup(QMenu& context_menu, const QString& path, const QString& name, u64 program_id, u64 extdata_id, Service::FS::MediaType media_type) { @@ -545,8 +547,10 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, const QStr QMenu* shader_menu = context_menu.addMenu(tr("Disk Shader Cache")); QAction* open_shader_cache_location = shader_menu->addAction(tr("Open Shader Cache Location")); shader_menu->addSeparator(); +#ifdef ENABLE_OPENGL QAction* delete_opengl_disk_shader_cache = shader_menu->addAction(tr("Delete OpenGL Shader Cache")); +#endif QMenu* uninstall_menu = context_menu.addMenu(tr("Uninstall")); QAction* uninstall_all = uninstall_menu->addAction(tr("Everything")); @@ -564,9 +568,11 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, const QStr const bool is_application = program_id_high == 0x00040000 || program_id_high == 0x00040002 || program_id_high == 0x00040010; +#ifdef ENABLE_OPENGL bool opengl_cache_exists = false; ForEachOpenGLCacheFile( program_id, [&opengl_cache_exists](QFile& file) { opengl_cache_exists |= file.exists(); }); +#endif std::string sdmc_dir = FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir); open_save_location->setEnabled( @@ -603,7 +609,9 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, const QStr open_mods_location->setEnabled(is_application); dump_romfs->setEnabled(is_application); +#ifdef ENABLE_OPENGL delete_opengl_disk_shader_cache->setEnabled(opengl_cache_exists); +#endif uninstall_all->setEnabled(is_installed || has_update || has_dlc); uninstall_game->setEnabled(is_installed); @@ -669,9 +677,11 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, const QStr emit OpenFolderRequested(program_id, GameListOpenTarget::SHADER_CACHE); } }); +#ifdef ENABLE_OPENGL connect(delete_opengl_disk_shader_cache, &QAction::triggered, this, [program_id] { ForEachOpenGLCacheFile(program_id, [](QFile& file) { file.remove(); }); }); +#endif connect(uninstall_all, &QAction::triggered, this, [=, this] { QMessageBox::StandardButton answer = QMessageBox::question( this, tr("Citra"), diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index b129a0e5f..2798fb099 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -270,6 +270,7 @@ GMainWindow::GMainWindow(Core::System& system_) connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor); connect(ui->menubar, &QMenuBar::hovered, this, &GMainWindow::OnMouseActivity); +#ifdef ENABLE_VULKAN physical_devices = GetVulkanPhysicalDevices(); if (physical_devices.empty()) { QMessageBox::warning(this, tr("No Suitable Vulkan Devices Detected"), @@ -277,6 +278,7 @@ GMainWindow::GMainWindow(Core::System& system_) "Your GPU may not support Vulkan 1.1, or you do not " "have the latest graphics driver.")); } +#endif #if ENABLE_QT_UPDATER if (UISettings::values.check_for_update_on_start) { @@ -2689,6 +2691,22 @@ void GMainWindow::UpdateAPIIndicator(bool update) { u32 api_index = static_cast(Settings::values.graphics_api.GetValue()); if (update) { api_index = (api_index + 1) % graphics_apis.size(); + // Skip past any disabled renderers. +#ifndef ENABLE_SOFTWARE_RENDERER + if (api_index == static_cast(Settings::GraphicsAPI::Software)) { + api_index = (api_index + 1) % graphics_apis.size(); + } +#endif +#ifndef ENABLE_OPENGL + if (api_index == static_cast(Settings::GraphicsAPI::OpenGL)) { + api_index = (api_index + 1) % graphics_apis.size(); + } +#endif +#ifndef ENABLE_VULKAN + if (api_index == static_cast(Settings::GraphicsAPI::Vulkan)) { + api_index = (api_index + 1) % graphics_apis.size(); + } +#endif Settings::values.graphics_api = static_cast(api_index); } @@ -3164,8 +3182,11 @@ int main(int argc, char* argv[]) { chdir(bin_path.c_str()); #endif +#ifdef ENABLE_OPENGL QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); +#endif + QApplication app(argc, argv); // Qt changes the locale and causes issues in float conversion using std::to_string() when diff --git a/src/common/settings.h b/src/common/settings.h index a52e797ac..e463ba4d7 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -449,13 +449,19 @@ struct Values { Setting allow_plugin_loader{true, "allow_plugin_loader"}; // Renderer - SwitchableSetting graphics_api{ -#ifdef HAS_OPENGL + SwitchableSetting graphics_api { +#if defined(ENABLE_OPENGL) GraphicsAPI::OpenGL, -#else +#elif defined(ENABLE_VULKAN) GraphicsAPI::Vulkan, +#elif defined(ENABLE_SOFTWARE_RENDERER) + GraphicsAPI::Software, +#else +// TODO: Add a null renderer backend for this, perhaps. +#error "At least one renderer must be enabled." #endif - GraphicsAPI::Software, GraphicsAPI::Vulkan, "graphics_api"}; + GraphicsAPI::Software, GraphicsAPI::Vulkan, "graphics_api" + }; SwitchableSetting physical_device{0, "physical_device"}; Setting use_gles{false, "use_gles"}; Setting renderer_debug{false, "renderer_debug"}; diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index de0fdcf4a..f2c084017 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -60,94 +60,9 @@ add_library(video_core STATIC rasterizer_cache/texture_cube.h rasterizer_cache/utils.cpp rasterizer_cache/utils.h - renderer_opengl/frame_dumper_opengl.cpp - renderer_opengl/frame_dumper_opengl.h - renderer_opengl/gl_blit_helper.cpp - renderer_opengl/gl_blit_helper.h - renderer_opengl/gl_driver.cpp - renderer_opengl/gl_driver.h - renderer_opengl/gl_rasterizer.cpp - renderer_opengl/gl_rasterizer.h - renderer_opengl/gl_rasterizer_cache.cpp - renderer_opengl/gl_resource_manager.cpp - renderer_opengl/gl_resource_manager.h - renderer_opengl/gl_shader_disk_cache.cpp - renderer_opengl/gl_shader_disk_cache.h - renderer_opengl/gl_shader_manager.cpp - renderer_opengl/gl_shader_manager.h - renderer_opengl/gl_shader_util.cpp - renderer_opengl/gl_shader_util.h - renderer_opengl/gl_state.cpp - renderer_opengl/gl_state.h - renderer_opengl/gl_stream_buffer.cpp - renderer_opengl/gl_stream_buffer.h - renderer_opengl/gl_texture_mailbox.cpp - renderer_opengl/gl_texture_mailbox.h - renderer_opengl/gl_texture_runtime.cpp - renderer_opengl/gl_texture_runtime.h - renderer_opengl/gl_vars.cpp - renderer_opengl/gl_vars.h - renderer_opengl/pica_to_gl.h - renderer_opengl/post_processing_opengl.cpp - renderer_opengl/post_processing_opengl.h - renderer_opengl/renderer_opengl.cpp - renderer_opengl/renderer_opengl.h - renderer_software/renderer_software.cpp - renderer_software/renderer_software.h + # Needed as a fallback regardless of enabled renderers. renderer_software/sw_blitter.cpp renderer_software/sw_blitter.h - renderer_software/sw_clipper.cpp - renderer_software/sw_clipper.h - renderer_software/sw_framebuffer.cpp - renderer_software/sw_framebuffer.h - renderer_software/sw_lighting.cpp - renderer_software/sw_lighting.h - renderer_software/sw_proctex.cpp - renderer_software/sw_proctex.h - renderer_software/sw_rasterizer.cpp - renderer_software/sw_rasterizer.h - renderer_software/sw_texturing.cpp - renderer_software/sw_texturing.h - renderer_vulkan/pica_to_vk.h - renderer_vulkan/renderer_vulkan.cpp - renderer_vulkan/renderer_vulkan.h - renderer_vulkan/vk_blit_helper.cpp - renderer_vulkan/vk_blit_helper.h - renderer_vulkan/vk_common.cpp - renderer_vulkan/vk_common.h - renderer_vulkan/vk_descriptor_pool.cpp - renderer_vulkan/vk_descriptor_pool.h - renderer_vulkan/vk_graphics_pipeline.cpp - renderer_vulkan/vk_graphics_pipeline.h - renderer_vulkan/vk_master_semaphore.cpp - renderer_vulkan/vk_master_semaphore.h - renderer_vulkan/vk_memory_util.cpp - renderer_vulkan/vk_memory_util.h - renderer_vulkan/vk_rasterizer.cpp - renderer_vulkan/vk_rasterizer.h - renderer_vulkan/vk_rasterizer_cache.cpp - renderer_vulkan/vk_scheduler.cpp - renderer_vulkan/vk_scheduler.h - renderer_vulkan/vk_resource_pool.cpp - renderer_vulkan/vk_resource_pool.h - renderer_vulkan/vk_instance.cpp - renderer_vulkan/vk_instance.h - renderer_vulkan/vk_pipeline_cache.cpp - renderer_vulkan/vk_pipeline_cache.h - renderer_vulkan/vk_platform.cpp - renderer_vulkan/vk_platform.h - renderer_vulkan/vk_present_window.cpp - renderer_vulkan/vk_present_window.h - renderer_vulkan/vk_renderpass_cache.cpp - renderer_vulkan/vk_renderpass_cache.h - renderer_vulkan/vk_shader_util.cpp - renderer_vulkan/vk_shader_util.h - renderer_vulkan/vk_stream_buffer.cpp - renderer_vulkan/vk_stream_buffer.h - renderer_vulkan/vk_swapchain.cpp - renderer_vulkan/vk_swapchain.h - renderer_vulkan/vk_texture_runtime.cpp - renderer_vulkan/vk_texture_runtime.h shader/debug_data.h shader/generator/glsl_fs_shader_gen.cpp shader/generator/glsl_fs_shader_gen.h @@ -162,8 +77,6 @@ add_library(video_core STATIC shader/generator/shader_gen.h shader/generator/shader_uniforms.cpp shader/generator/shader_uniforms.h - shader/generator/spv_fs_shader_gen.cpp - shader/generator/spv_fs_shader_gen.h shader/shader.cpp shader/shader.h shader/shader_interpreter.cpp @@ -183,6 +96,109 @@ add_library(video_core STATIC video_core.h ) +if (ENABLE_SOFTWARE_RENDERER) + target_sources(video_core PRIVATE + renderer_software/renderer_software.cpp + renderer_software/renderer_software.h + renderer_software/sw_clipper.cpp + renderer_software/sw_clipper.h + renderer_software/sw_framebuffer.cpp + renderer_software/sw_framebuffer.h + renderer_software/sw_lighting.cpp + renderer_software/sw_lighting.h + renderer_software/sw_proctex.cpp + renderer_software/sw_proctex.h + renderer_software/sw_rasterizer.cpp + renderer_software/sw_rasterizer.h + renderer_software/sw_texturing.cpp + renderer_software/sw_texturing.h + ) +endif() +if (ENABLE_OPENGL) + target_sources(video_core PRIVATE + renderer_opengl/frame_dumper_opengl.cpp + renderer_opengl/frame_dumper_opengl.h + renderer_opengl/gl_blit_helper.cpp + renderer_opengl/gl_blit_helper.h + renderer_opengl/gl_driver.cpp + renderer_opengl/gl_driver.h + renderer_opengl/gl_rasterizer.cpp + renderer_opengl/gl_rasterizer.h + renderer_opengl/gl_rasterizer_cache.cpp + renderer_opengl/gl_resource_manager.cpp + renderer_opengl/gl_resource_manager.h + renderer_opengl/gl_shader_disk_cache.cpp + renderer_opengl/gl_shader_disk_cache.h + renderer_opengl/gl_shader_manager.cpp + renderer_opengl/gl_shader_manager.h + renderer_opengl/gl_shader_util.cpp + renderer_opengl/gl_shader_util.h + renderer_opengl/gl_state.cpp + renderer_opengl/gl_state.h + renderer_opengl/gl_stream_buffer.cpp + renderer_opengl/gl_stream_buffer.h + renderer_opengl/gl_texture_mailbox.cpp + renderer_opengl/gl_texture_mailbox.h + renderer_opengl/gl_texture_runtime.cpp + renderer_opengl/gl_texture_runtime.h + renderer_opengl/gl_vars.cpp + renderer_opengl/gl_vars.h + renderer_opengl/pica_to_gl.h + renderer_opengl/post_processing_opengl.cpp + renderer_opengl/post_processing_opengl.h + renderer_opengl/renderer_opengl.cpp + renderer_opengl/renderer_opengl.h + ) + target_link_libraries(video_core PRIVATE glad) +endif() +if (ENABLE_VULKAN) + target_sources(video_core PRIVATE + renderer_vulkan/pica_to_vk.h + renderer_vulkan/renderer_vulkan.cpp + renderer_vulkan/renderer_vulkan.h + renderer_vulkan/vk_blit_helper.cpp + renderer_vulkan/vk_blit_helper.h + renderer_vulkan/vk_common.cpp + renderer_vulkan/vk_common.h + renderer_vulkan/vk_descriptor_pool.cpp + renderer_vulkan/vk_descriptor_pool.h + renderer_vulkan/vk_graphics_pipeline.cpp + renderer_vulkan/vk_graphics_pipeline.h + renderer_vulkan/vk_master_semaphore.cpp + renderer_vulkan/vk_master_semaphore.h + renderer_vulkan/vk_memory_util.cpp + renderer_vulkan/vk_memory_util.h + renderer_vulkan/vk_rasterizer.cpp + renderer_vulkan/vk_rasterizer.h + renderer_vulkan/vk_rasterizer_cache.cpp + renderer_vulkan/vk_scheduler.cpp + renderer_vulkan/vk_scheduler.h + renderer_vulkan/vk_resource_pool.cpp + renderer_vulkan/vk_resource_pool.h + renderer_vulkan/vk_instance.cpp + renderer_vulkan/vk_instance.h + renderer_vulkan/vk_pipeline_cache.cpp + renderer_vulkan/vk_pipeline_cache.h + renderer_vulkan/vk_platform.cpp + renderer_vulkan/vk_platform.h + renderer_vulkan/vk_present_window.cpp + renderer_vulkan/vk_present_window.h + renderer_vulkan/vk_renderpass_cache.cpp + renderer_vulkan/vk_renderpass_cache.h + renderer_vulkan/vk_shader_util.cpp + renderer_vulkan/vk_shader_util.h + renderer_vulkan/vk_stream_buffer.cpp + renderer_vulkan/vk_stream_buffer.h + renderer_vulkan/vk_swapchain.cpp + renderer_vulkan/vk_swapchain.h + renderer_vulkan/vk_texture_runtime.cpp + renderer_vulkan/vk_texture_runtime.h + shader/generator/spv_fs_shader_gen.cpp + shader/generator/spv_fs_shader_gen.h + ) + target_link_libraries(video_core PRIVATE vulkan-headers vma sirit SPIRV glslang) +endif() + add_dependencies(video_core host_shaders) target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE}) @@ -190,7 +206,6 @@ create_target_directory_groups(video_core) target_link_libraries(video_core PUBLIC citra_common citra_core) target_link_libraries(video_core PRIVATE Boost::serialization dds-ktx json-headers nihstro-headers tsl::robin_map) -target_link_libraries(video_core PRIVATE vulkan-headers vma glad sirit SPIRV glslang) if ("x86_64" IN_LIST ARCHITECTURE) target_link_libraries(video_core PUBLIC xbyak) diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index 51aa62282..59b9d140a 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp @@ -5,9 +5,15 @@ #include "common/logging/log.h" #include "common/settings.h" #include "video_core/gpu.h" +#ifdef ENABLE_OPENGL #include "video_core/renderer_opengl/renderer_opengl.h" +#endif +#ifdef ENABLE_SOFTWARE_RENDERER #include "video_core/renderer_software/renderer_software.h" +#endif +#ifdef ENABLE_VULKAN #include "video_core/renderer_vulkan/renderer_vulkan.h" +#endif #include "video_core/video_core.h" namespace VideoCore { @@ -17,15 +23,32 @@ std::unique_ptr CreateRenderer(Frontend::EmuWindow& emu_window, Pica::PicaCore& pica, Core::System& system) { const Settings::GraphicsAPI graphics_api = Settings::values.graphics_api.GetValue(); switch (graphics_api) { +#ifdef ENABLE_SOFTWARE_RENDERER case Settings::GraphicsAPI::Software: return std::make_unique(system, pica, emu_window); +#endif +#ifdef ENABLE_VULKAN case Settings::GraphicsAPI::Vulkan: return std::make_unique(system, pica, emu_window, secondary_window); +#endif +#ifdef ENABLE_OPENGL case Settings::GraphicsAPI::OpenGL: return std::make_unique(system, pica, emu_window, secondary_window); +#endif default: - LOG_CRITICAL(Render, "Unknown graphics API {}, using OpenGL", graphics_api); + LOG_CRITICAL(Render, + "Unknown or unsupported graphics API {}, falling back to available default", + graphics_api); +#ifdef ENABLE_OPENGL return std::make_unique(system, pica, emu_window, secondary_window); +#elif ENABLE_VULKAN + return std::make_unique(system, pica, emu_window, secondary_window); +#elif ENABLE_SOFTWARE_RENDERER + return std::make_unique(system, pica, emu_window); +#else +// TODO: Add a null renderer backend for this, perhaps. +#error "At least one renderer must be enabled." +#endif } }