Merge branch 'master' into online
This commit is contained in:
@@ -5,6 +5,10 @@ include_directories(.)
|
||||
set_property(DIRECTORY APPEND PROPERTY
|
||||
COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>)
|
||||
|
||||
if (ENABLE_LTO)
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||
endif()
|
||||
|
||||
# Set compilation flags
|
||||
if (MSVC)
|
||||
set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING "" FORCE)
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
|
||||
<activity
|
||||
android:name="org.citra.citra_emu.activities.EmulationActivity"
|
||||
android:exported="true"
|
||||
android:resizeableActivity="false"
|
||||
android:theme="@style/Theme.Citra.Main"
|
||||
android:launchMode="singleTop"/>
|
||||
|
||||
+5
-1
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -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";
|
||||
|
||||
@@ -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
|
||||
@@ -30,7 +34,7 @@ add_library(citra-android SHARED
|
||||
ndk_motion.h
|
||||
)
|
||||
|
||||
target_link_libraries(citra-android PRIVATE audio_core citra_common citra_core input_common network)
|
||||
target_link_libraries(citra-android PRIVATE audio_core citra_common citra_core input_common network adrenotools)
|
||||
target_link_libraries(citra-android PRIVATE android camera2ndk EGL glad inih jnigraphics log mediandk yuv)
|
||||
|
||||
set(CPACK_PACKAGE_EXECUTABLES ${CPACK_PACKAGE_EXECUTABLES} citra-android)
|
||||
|
||||
@@ -42,9 +42,7 @@ struct CaptureSession final {
|
||||
|
||||
#define MEMBER(type, name, func) \
|
||||
struct type##Deleter { \
|
||||
void operator()(type* ptr) { \
|
||||
type##_##func(ptr); \
|
||||
} \
|
||||
void operator()(type* ptr) { type##_##func(ptr); } \
|
||||
}; \
|
||||
std::unique_ptr<type, type##Deleter> name
|
||||
|
||||
|
||||
@@ -147,6 +147,9 @@ 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_presentation);
|
||||
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);
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -6,10 +6,7 @@
|
||||
#include <array>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
#include <android/native_window_jni.h>
|
||||
#include <glad/glad.h>
|
||||
|
||||
#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<EGLint, 15> 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<EGLint, 5> egl_empty_attribs{EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
|
||||
static constexpr std::array<EGLint, 4> 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,7 +30,12 @@ static void UpdateLandscapeScreenLayout() {
|
||||
|
||||
void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
|
||||
render_window = surface;
|
||||
|
||||
window_info.type = Frontend::WindowSystemType::Android;
|
||||
window_info.render_surface = surface;
|
||||
|
||||
StopPresenting();
|
||||
OnFramebufferSizeChanged();
|
||||
}
|
||||
|
||||
bool EmuWindow_Android::OnTouchEvent(int x, int y, bool pressed) {
|
||||
@@ -98,6 +54,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 +64,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 +72,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,48 +83,6 @@ EmuWindow_Android::~EmuWindow_Android() {
|
||||
DestroyContext();
|
||||
}
|
||||
|
||||
std::unique_ptr<Frontend::GraphicsContext> EmuWindow_Android::CreateSharedContext() const {
|
||||
return std::make_unique<SharedContext_Android>(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;
|
||||
}
|
||||
|
||||
host_window = render_window;
|
||||
render_window = nullptr;
|
||||
|
||||
DestroyWindowSurface();
|
||||
CreateWindowSurface();
|
||||
OnFramebufferSizeChanged();
|
||||
presenting_state = PresentingState::Initial;
|
||||
}
|
||||
|
||||
void EmuWindow_Android::MakeCurrent() {
|
||||
core_context->MakeCurrent();
|
||||
}
|
||||
|
||||
@@ -5,38 +5,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
|
||||
#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);
|
||||
|
||||
@@ -46,38 +21,34 @@ public:
|
||||
/// Handles movement of touch pointer
|
||||
void OnTouchMoved(int x, int y);
|
||||
|
||||
void PollEvents() override;
|
||||
void MakeCurrent() override;
|
||||
|
||||
void DoneCurrent() override;
|
||||
|
||||
void TryPresenting();
|
||||
void StopPresenting();
|
||||
virtual void TryPresenting() {}
|
||||
|
||||
std::unique_ptr<GraphicsContext> CreateSharedContext() const override;
|
||||
virtual void StopPresenting() {}
|
||||
|
||||
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<Frontend::GraphicsContext> core_context;
|
||||
|
||||
enum class PresentingState {
|
||||
Initial,
|
||||
Running,
|
||||
Stopped,
|
||||
};
|
||||
PresentingState presenting_state{};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
// Copyright 2019 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
#include <android/native_window_jni.h>
|
||||
#include <glad/glad.h>
|
||||
|
||||
#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<EGLint, 15> 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<EGLint, 5> egl_empty_attribs{EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
|
||||
static constexpr std::array<EGLint, 4> 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<Frontend::GraphicsContext> EmuWindow_Android_OpenGL::CreateSharedContext() const {
|
||||
return std::make_unique<SharedContext_Android>(egl_display, egl_config, egl_context);
|
||||
}
|
||||
|
||||
void EmuWindow_Android_OpenGL::PollEvents() {
|
||||
if (!render_window) {
|
||||
return;
|
||||
}
|
||||
|
||||
host_window = render_window;
|
||||
render_window = nullptr;
|
||||
|
||||
DestroyWindowSurface();
|
||||
CreateWindowSurface();
|
||||
OnFramebufferSizeChanged();
|
||||
presenting_state = PresentingState::Initial;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright 2019 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
|
||||
#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;
|
||||
void PollEvents() override;
|
||||
|
||||
std::unique_ptr<GraphicsContext> 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{};
|
||||
|
||||
enum class PresentingState {
|
||||
Initial,
|
||||
Running,
|
||||
Stopped,
|
||||
};
|
||||
PresentingState presenting_state{};
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright 2019 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstdlib>
|
||||
#include <android/native_window_jni.h>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "jni/emu_window/emu_window_vk.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
class GraphicsContext_Android final : public Frontend::GraphicsContext {
|
||||
public:
|
||||
explicit GraphicsContext_Android(std::shared_ptr<Common::DynamicLibrary> driver_library_)
|
||||
: driver_library{driver_library_} {}
|
||||
|
||||
~GraphicsContext_Android() = default;
|
||||
|
||||
std::shared_ptr<Common::DynamicLibrary> GetDriverLibrary() override {
|
||||
return driver_library;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Common::DynamicLibrary> driver_library;
|
||||
};
|
||||
|
||||
EmuWindow_Android_Vulkan::EmuWindow_Android_Vulkan(
|
||||
ANativeWindow* surface, std::shared_ptr<Common::DynamicLibrary> driver_library_)
|
||||
: EmuWindow_Android{surface}, driver_library{driver_library_} {
|
||||
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<Frontend::GraphicsContext> EmuWindow_Android_Vulkan::CreateSharedContext() const {
|
||||
return std::make_unique<GraphicsContext_Android>(driver_library);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// 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,
|
||||
std::shared_ptr<Common::DynamicLibrary> driver_library);
|
||||
~EmuWindow_Android_Vulkan() override = default;
|
||||
|
||||
void PollEvents() override {}
|
||||
|
||||
std::unique_ptr<GraphicsContext> CreateSharedContext() const override;
|
||||
|
||||
private:
|
||||
bool CreateWindowSurface() override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Common::DynamicLibrary> driver_library;
|
||||
};
|
||||
@@ -4,13 +4,16 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include <android/api-level.h>
|
||||
#include <android/native_window_jni.h>
|
||||
|
||||
#include "audio_core/dsp_interface.h"
|
||||
#include "common/aarch64/cpu_detect.h"
|
||||
#include "common/arch.h"
|
||||
#include "common/common_paths.h"
|
||||
#include "common/dynamic_library/dynamic_library.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/log.h"
|
||||
@@ -33,7 +36,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"
|
||||
@@ -42,10 +46,15 @@
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
#if CITRA_ARCH(arm64)
|
||||
#include <adrenotools/driver.h>
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
ANativeWindow* s_surf;
|
||||
|
||||
std::shared_ptr<Common::DynamicLibrary> vulkan_library{};
|
||||
std::unique_ptr<EmuWindow_Android> window;
|
||||
|
||||
std::atomic<bool> stop_run{true};
|
||||
@@ -123,11 +132,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<EmuWindow_Android>(s_surf);
|
||||
window = std::make_unique<EmuWindow_Android_OpenGL>(s_surf);
|
||||
break;
|
||||
case Settings::GraphicsAPI::Vulkan:
|
||||
window = std::make_unique<EmuWindow_Android_Vulkan>(s_surf, vulkan_library);
|
||||
break;
|
||||
default:
|
||||
LOG_CRITICAL(Frontend, "Unknown graphics API {}, using OpenGL", graphics_api);
|
||||
window = std::make_unique<EmuWindow_Android>(s_surf);
|
||||
LOG_CRITICAL(Frontend, "Unknown graphics API {}, using Vulkan", graphics_api);
|
||||
window = std::make_unique<EmuWindow_Android_Vulkan>(s_surf, vulkan_library);
|
||||
}
|
||||
|
||||
Core::System& system{Core::System::GetInstance()};
|
||||
@@ -228,6 +240,37 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
|
||||
return Core::System::ResultStatus::Success;
|
||||
}
|
||||
|
||||
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)
|
||||
void* handle{};
|
||||
const char* file_redirect_dir_{};
|
||||
int featureFlags{};
|
||||
|
||||
// Enable driver file redirection when renderer debugging is enabled.
|
||||
if (Settings::values.renderer_debug && file_redirect_dir.size()) {
|
||||
featureFlags |= ADRENOTOOLS_DRIVER_FILE_REDIRECT;
|
||||
file_redirect_dir_ = file_redirect_dir.c_str();
|
||||
}
|
||||
|
||||
// Try to load a custom driver.
|
||||
if (custom_driver_name.size()) {
|
||||
handle = adrenotools_open_libvulkan(
|
||||
RTLD_NOW, featureFlags | ADRENOTOOLS_DRIVER_CUSTOM, nullptr, hook_lib_dir.c_str(),
|
||||
custom_driver_dir.c_str(), custom_driver_name.c_str(), file_redirect_dir_, nullptr);
|
||||
}
|
||||
|
||||
// Try to load the system driver.
|
||||
if (!handle) {
|
||||
handle = adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(),
|
||||
nullptr, nullptr, file_redirect_dir_, nullptr);
|
||||
}
|
||||
|
||||
vulkan_library = std::make_shared<Common::DynamicLibrary>(handle);
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
void Java_org_citra_citra_1emu_NativeLibrary_SurfaceChanged(JNIEnv* env,
|
||||
@@ -238,6 +281,9 @@ void Java_org_citra_citra_1emu_NativeLibrary_SurfaceChanged(JNIEnv* env,
|
||||
if (window) {
|
||||
window->OnSurfaceChanged(s_surf);
|
||||
}
|
||||
if (VideoCore::g_renderer) {
|
||||
VideoCore::g_renderer->NotifySurfaceChanged();
|
||||
}
|
||||
|
||||
LOG_INFO(Frontend, "Surface changed");
|
||||
}
|
||||
@@ -258,6 +304,15 @@ void Java_org_citra_citra_1emu_NativeLibrary_DoFrame(JNIEnv* env, [[maybe_unused
|
||||
window->TryPresenting();
|
||||
}
|
||||
|
||||
void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver(JNIEnv* env, jclass clazz,
|
||||
jstring hook_lib_dir,
|
||||
jstring custom_driver_dir,
|
||||
jstring custom_driver_name,
|
||||
jstring file_redirect_dir) {
|
||||
InitializeGpuDriver(GetJString(env, hook_lib_dir), GetJString(env, custom_driver_dir),
|
||||
GetJString(env, custom_driver_name), GetJString(env, file_redirect_dir));
|
||||
}
|
||||
|
||||
void Java_org_citra_citra_1emu_NativeLibrary_NotifyOrientationChange(JNIEnv* env,
|
||||
[[maybe_unused]] jclass clazz,
|
||||
jint layout_option,
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -177,11 +177,13 @@
|
||||
</integer-array>
|
||||
|
||||
<string-array name="graphicsApiNames">
|
||||
<item>OpenGLES</item>
|
||||
<item>OpenGL ES</item>
|
||||
<item>Vulkan</item>
|
||||
</string-array>
|
||||
|
||||
<integer-array name="graphicsApiValues">
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
</integer-array>
|
||||
|
||||
<string-array name="textureFilterNames">
|
||||
|
||||
@@ -74,6 +74,10 @@
|
||||
<!-- Graphics settings strings -->
|
||||
<string name="renderer">Renderer</string>
|
||||
<string name="graphics_api">Graphics API</string>
|
||||
<string name="spirv_shader_gen">Enable SPIR-V shader generation</string>
|
||||
<string name="spirv_shader_gen_description">Emits the fragment shader used to emulate PICA using SPIR-V instead of GLSL</string>
|
||||
<string name="async_shaders">Enable asynchronous shader compilation</string>
|
||||
<string name="async_shaders_description">Compiles shaders in the background to reduce stuttering during gameplay. When enabled expect temporary graphical glitches</string>
|
||||
<string name="renderer_debug">Debug Renderer</string>
|
||||
<string name="renderer_debug_description">Log additional graphics related debug information. When enabled, game performance will be significantly reduced.</string>
|
||||
<string name="vsync">Enable V-Sync</string>
|
||||
|
||||
@@ -49,8 +49,6 @@ create_target_directory_groups(audio_core)
|
||||
|
||||
target_link_libraries(audio_core PUBLIC citra_common citra_core)
|
||||
target_link_libraries(audio_core PRIVATE SoundTouch teakra)
|
||||
set_target_properties(audio_core PROPERTIES INTERPROCEDURAL_OPTIMIZATION ${ENABLE_LTO})
|
||||
add_definitions(-DSOUNDTOUCH_INTEGER_SAMPLES)
|
||||
|
||||
if(ENABLE_MF)
|
||||
target_sources(audio_core PRIVATE
|
||||
|
||||
@@ -33,34 +33,34 @@ void Mixers::ParseConfig(DspConfiguration& config) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.mixer1_enabled_dirty) {
|
||||
config.mixer1_enabled_dirty.Assign(0);
|
||||
state.mixer1_enabled = config.mixer1_enabled != 0;
|
||||
LOG_TRACE(Audio_DSP, "mixers mixer1_enabled = {}", config.mixer1_enabled);
|
||||
if (config.aux_bus_enable[0]) {
|
||||
config.aux_bus_enable_0_dirty.Assign(0);
|
||||
state.aux_bus_enable[0] = config.aux_bus_enable[0] != 0;
|
||||
LOG_TRACE(Audio_DSP, "mixers aux_bus_enable[0] = {}", config.aux_bus_enable[0]);
|
||||
}
|
||||
|
||||
if (config.mixer2_enabled_dirty) {
|
||||
config.mixer2_enabled_dirty.Assign(0);
|
||||
state.mixer2_enabled = config.mixer2_enabled != 0;
|
||||
LOG_TRACE(Audio_DSP, "mixers mixer2_enabled = {}", config.mixer2_enabled);
|
||||
if (config.aux_bus_enable[1]) {
|
||||
config.aux_bus_enable_1_dirty.Assign(0);
|
||||
state.aux_bus_enable[1] = config.aux_bus_enable[1] != 0;
|
||||
LOG_TRACE(Audio_DSP, "mixers aux_bus_enable[1] = {}", config.aux_bus_enable[1]);
|
||||
}
|
||||
|
||||
if (config.volume_0_dirty) {
|
||||
config.volume_0_dirty.Assign(0);
|
||||
state.intermediate_mixer_volume[0] = config.volume[0];
|
||||
LOG_TRACE(Audio_DSP, "mixers volume[0] = {}", config.volume[0]);
|
||||
if (config.master_volume) {
|
||||
config.master_volume_dirty.Assign(0);
|
||||
state.intermediate_mixer_volume[0] = config.master_volume;
|
||||
LOG_TRACE(Audio_DSP, "mixers master_volume = {}", config.master_volume);
|
||||
}
|
||||
|
||||
if (config.volume_1_dirty) {
|
||||
config.volume_1_dirty.Assign(0);
|
||||
state.intermediate_mixer_volume[1] = config.volume[1];
|
||||
LOG_TRACE(Audio_DSP, "mixers volume[1] = {}", config.volume[1]);
|
||||
if (config.aux_return_volume[0]) {
|
||||
config.aux_return_volume_0_dirty.Assign(0);
|
||||
state.intermediate_mixer_volume[1] = config.aux_return_volume[0];
|
||||
LOG_TRACE(Audio_DSP, "mixers aux_return_volume[0] = {}", config.aux_return_volume[0]);
|
||||
}
|
||||
|
||||
if (config.volume_2_dirty) {
|
||||
config.volume_2_dirty.Assign(0);
|
||||
state.intermediate_mixer_volume[2] = config.volume[2];
|
||||
LOG_TRACE(Audio_DSP, "mixers volume[2] = {}", config.volume[2]);
|
||||
if (config.aux_return_volume[1]) {
|
||||
config.aux_return_volume_1_dirty.Assign(0);
|
||||
state.intermediate_mixer_volume[2] = config.aux_return_volume[1];
|
||||
LOG_TRACE(Audio_DSP, "mixers aux_return_volume[1] = {}", config.aux_return_volume[1]);
|
||||
}
|
||||
|
||||
if (config.output_format_dirty) {
|
||||
@@ -137,7 +137,7 @@ void Mixers::AuxReturn(const IntermediateMixSamples& read_samples) {
|
||||
// NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to
|
||||
// QuadFrame32.
|
||||
|
||||
if (state.mixer1_enabled) {
|
||||
if (state.aux_bus_enable[0]) {
|
||||
for (std::size_t sample = 0; sample < samples_per_frame; sample++) {
|
||||
for (std::size_t channel = 0; channel < 4; channel++) {
|
||||
state.intermediate_mix_buffer[1][sample][channel] =
|
||||
@@ -146,7 +146,7 @@ void Mixers::AuxReturn(const IntermediateMixSamples& read_samples) {
|
||||
}
|
||||
}
|
||||
|
||||
if (state.mixer2_enabled) {
|
||||
if (state.aux_bus_enable[1]) {
|
||||
for (std::size_t sample = 0; sample < samples_per_frame; sample++) {
|
||||
for (std::size_t channel = 0; channel < 4; channel++) {
|
||||
state.intermediate_mix_buffer[2][sample][channel] =
|
||||
@@ -163,7 +163,7 @@ void Mixers::AuxSend(IntermediateMixSamples& write_samples,
|
||||
|
||||
state.intermediate_mix_buffer[0] = input[0];
|
||||
|
||||
if (state.mixer1_enabled) {
|
||||
if (state.aux_bus_enable[0]) {
|
||||
for (std::size_t sample = 0; sample < samples_per_frame; sample++) {
|
||||
for (std::size_t channel = 0; channel < 4; channel++) {
|
||||
write_samples.mix1.pcm32[channel][sample] = input[1][sample][channel];
|
||||
@@ -173,7 +173,7 @@ void Mixers::AuxSend(IntermediateMixSamples& write_samples,
|
||||
state.intermediate_mix_buffer[1] = input[1];
|
||||
}
|
||||
|
||||
if (state.mixer2_enabled) {
|
||||
if (state.aux_bus_enable[1]) {
|
||||
for (std::size_t sample = 0; sample < samples_per_frame; sample++) {
|
||||
for (std::size_t channel = 0; channel < 4; channel++) {
|
||||
write_samples.mix2.pcm32[channel][sample] = input[2][sample][channel];
|
||||
@@ -187,6 +187,8 @@ void Mixers::AuxSend(IntermediateMixSamples& write_samples,
|
||||
void Mixers::MixCurrentFrame() {
|
||||
current_frame.fill({});
|
||||
|
||||
// TODO(SachinV): This is probably not accurate, based on symbols from FE:Fates,
|
||||
// state.intermediate_mixer_volume[0] represents the master volume
|
||||
for (std::size_t mix = 0; mix < 3; mix++) {
|
||||
DownmixAndMixIntoCurrentFrame(state.intermediate_mixer_volume[mix],
|
||||
state.intermediate_mix_buffer[mix]);
|
||||
|
||||
@@ -34,8 +34,7 @@ private:
|
||||
struct {
|
||||
std::array<float, 3> intermediate_mixer_volume = {};
|
||||
|
||||
bool mixer1_enabled = false;
|
||||
bool mixer2_enabled = false;
|
||||
std::array<bool, 2> aux_bus_enable = {};
|
||||
std::array<QuadFrame32, 3> intermediate_mix_buffer = {};
|
||||
|
||||
OutputFormat output_format = OutputFormat::Stereo;
|
||||
@@ -60,8 +59,7 @@ private:
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& current_frame;
|
||||
ar& state.intermediate_mixer_volume;
|
||||
ar& state.mixer1_enabled;
|
||||
ar& state.mixer2_enabled;
|
||||
ar& state.aux_bus_enable;
|
||||
ar& state.intermediate_mix_buffer;
|
||||
ar& state.output_format;
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ struct SourceConfiguration {
|
||||
BitField<25, 1, u32> gain_0_dirty;
|
||||
BitField<26, 1, u32> gain_1_dirty;
|
||||
BitField<27, 1, u32> gain_2_dirty;
|
||||
BitField<28, 1, u32> sync_dirty;
|
||||
BitField<28, 1, u32> sync_count_dirty;
|
||||
BitField<29, 1, u32> reset_flag;
|
||||
BitField<30, 1, u32> embedded_buffer_dirty;
|
||||
};
|
||||
@@ -251,7 +251,7 @@ struct SourceConfiguration {
|
||||
u32_dsp loop_related;
|
||||
u8 enable;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
u16_le sync; ///< Application-side sync (See also: SourceStatus::sync)
|
||||
u16_le sync_count; ///< Application-side sync count (See also: SourceStatus::sync_count)
|
||||
u32_dsp play_position; ///< Position. (Units: number of samples)
|
||||
INSERT_PADDING_DSPWORDS(2);
|
||||
|
||||
@@ -313,9 +313,9 @@ struct SourceStatus {
|
||||
struct Status {
|
||||
u8 is_enabled; ///< Is this channel enabled? (Doesn't have to be playing anything.)
|
||||
u8 current_buffer_id_dirty; ///< Non-zero when current_buffer_id changes
|
||||
u16_le sync; ///< Is set by the DSP to the value of SourceConfiguration::sync
|
||||
u32_dsp buffer_position; ///< Number of samples into the current buffer
|
||||
u16_le current_buffer_id; ///< Updated when a buffer finishes playing
|
||||
u16_le sync_count; ///< Is set by the DSP to the value of SourceConfiguration::sync_count
|
||||
u32_dsp buffer_position; ///< Number of samples into the current buffer
|
||||
u16_le current_buffer_id; ///< Updated when a buffer finishes playing
|
||||
INSERT_PADDING_DSPWORDS(1);
|
||||
};
|
||||
|
||||
@@ -329,27 +329,35 @@ struct DspConfiguration {
|
||||
union {
|
||||
u32_le dirty_raw;
|
||||
|
||||
BitField<8, 1, u32> mixer1_enabled_dirty;
|
||||
BitField<9, 1, u32> mixer2_enabled_dirty;
|
||||
BitField<6, 1, u32> aux_front_bypass_0_dirty;
|
||||
BitField<7, 1, u32> aux_front_bypass_1_dirty;
|
||||
BitField<8, 1, u32> aux_bus_enable_0_dirty;
|
||||
BitField<9, 1, u32> aux_bus_enable_1_dirty;
|
||||
BitField<10, 1, u32> delay_effect_0_dirty;
|
||||
BitField<11, 1, u32> delay_effect_1_dirty;
|
||||
BitField<12, 1, u32> reverb_effect_0_dirty;
|
||||
BitField<13, 1, u32> reverb_effect_1_dirty;
|
||||
|
||||
BitField<16, 1, u32> volume_0_dirty;
|
||||
BitField<15, 1, u32> output_buffer_count_dirty;
|
||||
BitField<16, 1, u32> master_volume_dirty;
|
||||
|
||||
BitField<24, 1, u32> volume_1_dirty;
|
||||
BitField<25, 1, u32> volume_2_dirty;
|
||||
BitField<24, 1, u32> aux_return_volume_0_dirty;
|
||||
BitField<25, 1, u32> aux_return_volume_1_dirty;
|
||||
BitField<26, 1, u32> output_format_dirty;
|
||||
BitField<27, 1, u32> limiter_enabled_dirty;
|
||||
BitField<27, 1, u32> clipping_mode_dirty;
|
||||
BitField<28, 1, u32> headphones_connected_dirty;
|
||||
BitField<29, 1, u32> surround_depth_dirty;
|
||||
BitField<30, 1, u32> surround_speaker_position_dirty;
|
||||
BitField<31, 1, u32> rear_ratio_dirty;
|
||||
};
|
||||
|
||||
/// The DSP has three intermediate audio mixers. This controls the volume level (0.0-1.0) for
|
||||
/// each at the final mixer.
|
||||
float_le volume[3];
|
||||
float_le master_volume;
|
||||
std::array<float_le, 2> aux_return_volume;
|
||||
|
||||
INSERT_PADDING_DSPWORDS(3);
|
||||
u16_le output_buffer_count;
|
||||
INSERT_PADDING_DSPWORDS(2);
|
||||
|
||||
enum class OutputFormat : u16_le {
|
||||
Mono = 0,
|
||||
@@ -359,12 +367,15 @@ struct DspConfiguration {
|
||||
|
||||
OutputFormat output_format;
|
||||
|
||||
u16_le limiter_enabled; ///< Not sure of the exact gain equation for the limiter.
|
||||
u16_le clipping_mode; ///< Not sure of the exact gain equation for the limiter.
|
||||
u16_le headphones_connected; ///< Application updates the DSP on headphone status.
|
||||
INSERT_PADDING_DSPWORDS(4); ///< TODO: Surround sound related
|
||||
INSERT_PADDING_DSPWORDS(2); ///< TODO: Intermediate mixer 1/2 related
|
||||
u16_le mixer1_enabled;
|
||||
u16_le mixer2_enabled;
|
||||
|
||||
u16_le surround_depth;
|
||||
u16_le surround_speaker_position;
|
||||
INSERT_PADDING_DSPWORDS(1); ///< TODO: Surround sound related
|
||||
u16_le rear_ratio;
|
||||
std::array<u16_le, 2> aux_front_bypass;
|
||||
std::array<u16_le, 2> aux_bus_enable;
|
||||
|
||||
/**
|
||||
* This is delay with feedback.
|
||||
@@ -406,11 +417,19 @@ struct DspConfiguration {
|
||||
|
||||
ReverbEffect reverb_effect[2];
|
||||
|
||||
INSERT_PADDING_DSPWORDS(4);
|
||||
u16_le sync_mode;
|
||||
INSERT_PADDING_DSPWORDS(1);
|
||||
union {
|
||||
u32_le dirty2_raw;
|
||||
|
||||
BitField<16, 1, u32> sync_mode_dirty;
|
||||
};
|
||||
};
|
||||
ASSERT_DSP_STRUCT(DspConfiguration, 196);
|
||||
ASSERT_DSP_STRUCT(DspConfiguration::DelayEffect, 20);
|
||||
ASSERT_DSP_STRUCT(DspConfiguration::ReverbEffect, 52);
|
||||
static_assert(offsetof(DspConfiguration, sync_mode) == 0xBC);
|
||||
static_assert(offsetof(DspConfiguration, dirty2_raw) == 0xC0);
|
||||
|
||||
struct AdpcmCoefficients {
|
||||
/// Coefficients are signed fixed point with 11 fractional bits.
|
||||
|
||||
@@ -72,10 +72,10 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config,
|
||||
LOG_TRACE(Audio_DSP, "source_id={} enable={}", source_id, state.enabled);
|
||||
}
|
||||
|
||||
if (config.sync_dirty) {
|
||||
config.sync_dirty.Assign(0);
|
||||
state.sync = config.sync;
|
||||
LOG_TRACE(Audio_DSP, "source_id={} sync={}", source_id, state.sync);
|
||||
if (config.sync_count_dirty) {
|
||||
config.sync_count_dirty.Assign(0);
|
||||
state.sync_count = config.sync_count;
|
||||
LOG_TRACE(Audio_DSP, "source_id={} sync={}", source_id, state.sync_count);
|
||||
}
|
||||
|
||||
if (config.rate_multiplier_dirty) {
|
||||
@@ -231,7 +231,8 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config,
|
||||
}
|
||||
}
|
||||
LOG_TRACE(Audio_DSP, "partially updating embedded buffer addr={:#010x} len={} id={}",
|
||||
state.current_buffer_physical_address, config.length, config.buffer_id);
|
||||
state.current_buffer_physical_address, static_cast<u32>(config.length),
|
||||
config.buffer_id);
|
||||
}
|
||||
|
||||
if (config.embedded_buffer_dirty) {
|
||||
@@ -248,7 +249,7 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config,
|
||||
LOG_ERROR(Audio_DSP,
|
||||
"Skipping embedded buffer sample! Game passed in improper value for length. "
|
||||
"addr {:X} length {:X}",
|
||||
config.physical_address, config.length);
|
||||
static_cast<u32>(config.physical_address), static_cast<u32>(config.length));
|
||||
} else {
|
||||
state.input_queue.emplace(Buffer{
|
||||
config.physical_address,
|
||||
@@ -266,8 +267,8 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config,
|
||||
});
|
||||
}
|
||||
LOG_TRACE(Audio_DSP, "enqueuing embedded addr={:#010x} len={} id={} start={}",
|
||||
config.physical_address, config.length, config.buffer_id,
|
||||
static_cast<u32>(config.play_position));
|
||||
static_cast<u32>(config.physical_address), static_cast<u32>(config.length),
|
||||
config.buffer_id, static_cast<u32>(config.play_position));
|
||||
}
|
||||
|
||||
if (config.loop_related_dirty && config.loop_related != 0) {
|
||||
@@ -285,7 +286,7 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config,
|
||||
LOG_ERROR(Audio_DSP,
|
||||
"Skipping buffer queue sample! Game passed in improper value for "
|
||||
"length. addr {:X} length {:X}",
|
||||
b.physical_address, b.length);
|
||||
static_cast<u32>(b.physical_address), static_cast<u32>(b.length));
|
||||
} else {
|
||||
state.input_queue.emplace(Buffer{
|
||||
b.physical_address,
|
||||
@@ -303,7 +304,8 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config,
|
||||
});
|
||||
}
|
||||
LOG_TRACE(Audio_DSP, "enqueuing queued {} addr={:#010x} len={} id={}", i,
|
||||
b.physical_address, b.length, b.buffer_id);
|
||||
static_cast<u32>(b.physical_address), static_cast<u32>(b.length),
|
||||
b.buffer_id);
|
||||
}
|
||||
}
|
||||
config.buffers_dirty = 0;
|
||||
@@ -432,7 +434,7 @@ SourceStatus::Status Source::GetCurrentStatus() {
|
||||
state.buffer_update = false;
|
||||
ret.current_buffer_id = state.current_buffer_id;
|
||||
ret.buffer_position = state.current_sample_number;
|
||||
ret.sync = state.sync;
|
||||
ret.sync_count = state.sync_count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ private:
|
||||
// State variables
|
||||
|
||||
bool enabled = false;
|
||||
u16 sync = 0;
|
||||
u16 sync_count = 0;
|
||||
|
||||
// Mixing
|
||||
|
||||
@@ -164,7 +164,7 @@ private:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& enabled;
|
||||
ar& sync;
|
||||
ar& sync_count;
|
||||
ar& gain;
|
||||
ar& input_queue;
|
||||
ar& mono_or_stereo;
|
||||
|
||||
@@ -5,10 +5,15 @@
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <SoundTouch.h>
|
||||
|
||||
#include "audio_core/audio_types.h"
|
||||
#include "audio_core/time_stretch.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace AudioCore {
|
||||
@@ -62,8 +67,44 @@ std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
|
||||
LOG_TRACE(Audio, "{:5}/{:5} ratio:{:0.6f} backlog:{:0.6f}", num_in, num_out, stretch_ratio,
|
||||
backlog_fullness);
|
||||
|
||||
sound_touch->putSamples(in, static_cast<u32>(num_in));
|
||||
return sound_touch->receiveSamples(out, static_cast<u32>(num_out));
|
||||
if constexpr (std::is_floating_point<soundtouch::SAMPLETYPE>()) {
|
||||
// The SoundTouch library on most systems expects float samples
|
||||
// use this vector to store input if soundtouch::SAMPLETYPE is a float
|
||||
std::vector<soundtouch::SAMPLETYPE> float_in(2 * num_in);
|
||||
std::vector<soundtouch::SAMPLETYPE> float_out(2 * num_out);
|
||||
|
||||
for (std::size_t i = 0; i < (2 * num_in); i++) {
|
||||
// Conventional integer PCM uses a range of -32768 to 32767,
|
||||
// but float samples use -1 to 1
|
||||
// As a result we need to scale sample values during conversion
|
||||
const float temp = static_cast<float>(in[i]) / std::numeric_limits<s16>::max();
|
||||
float_in[i] = static_cast<soundtouch::SAMPLETYPE>(temp);
|
||||
}
|
||||
|
||||
sound_touch->putSamples(float_in.data(), static_cast<u32>(num_in));
|
||||
|
||||
const std::size_t samples_received =
|
||||
sound_touch->receiveSamples(float_out.data(), static_cast<u32>(num_out));
|
||||
|
||||
// Converting output samples back to shorts so we can use them
|
||||
for (std::size_t i = 0; i < (2 * num_out); i++) {
|
||||
const s16 temp = static_cast<s16>(float_out[i] * std::numeric_limits<s16>::max());
|
||||
out[i] = temp;
|
||||
}
|
||||
|
||||
return samples_received;
|
||||
} else if (std::is_same<soundtouch::SAMPLETYPE, s16>()) {
|
||||
// Use reinterpret_cast to workaround compile error when SAMPLETYPE is float.
|
||||
sound_touch->putSamples(reinterpret_cast<const soundtouch::SAMPLETYPE*>(in),
|
||||
static_cast<u32>(num_in));
|
||||
return sound_touch->receiveSamples(reinterpret_cast<soundtouch::SAMPLETYPE*>(out),
|
||||
static_cast<u32>(num_out));
|
||||
} else {
|
||||
static_assert(std::is_floating_point<soundtouch::SAMPLETYPE>() ||
|
||||
std::is_same<soundtouch::SAMPLETYPE, s16>());
|
||||
UNREACHABLE_MSG("Invalid SAMPLETYPE {}", typeid(soundtouch::SAMPLETYPE).name());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void TimeStretcher::Clear() {
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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<EmuWindow_SDL2_GL>(system, fullscreen, is_secondary);
|
||||
case Settings::GraphicsAPI::Vulkan:
|
||||
return std::make_unique<EmuWindow_SDL2_VK>(system, fullscreen, is_secondary);
|
||||
case Settings::GraphicsAPI::Software:
|
||||
return std::make_unique<EmuWindow_SDL2_SW>(system, fullscreen, is_secondary);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <SDL.h>
|
||||
#include <SDL_syswm.h>
|
||||
#include <fmt/format.h>
|
||||
#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<void*>(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<void*>(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_GetLayer(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<void*>(wm.info.android.window);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
LOG_CRITICAL(Frontend, "Window manager subsystem {} not implemented", wm.subsystem);
|
||||
std::exit(EXIT_FAILURE);
|
||||
break;
|
||||
}
|
||||
|
||||
render_window_id = SDL_GetWindowID(render_window);
|
||||
|
||||
OnResize();
|
||||
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
|
||||
SDL_PumpEvents();
|
||||
}
|
||||
|
||||
EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() = default;
|
||||
|
||||
std::unique_ptr<Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const {
|
||||
return std::make_unique<DummyContext>();
|
||||
}
|
||||
@@ -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 <memory>
|
||||
#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<Frontend::GraphicsContext> CreateSharedContext() const override;
|
||||
};
|
||||
@@ -185,6 +185,8 @@ add_executable(citra-qt
|
||||
util/spinbox.h
|
||||
util/util.cpp
|
||||
util/util.h
|
||||
util/vk_device_info.cpp
|
||||
util/vk_device_info.h
|
||||
)
|
||||
|
||||
file(GLOB COMPAT_LIST
|
||||
|
||||
@@ -229,20 +229,10 @@ class RenderWidget : public QWidget {
|
||||
public:
|
||||
RenderWidget(GRenderWindow* parent) : QWidget(parent) {
|
||||
setMouseTracking(true);
|
||||
}
|
||||
|
||||
virtual ~RenderWidget() = default;
|
||||
|
||||
virtual void Present() {}
|
||||
|
||||
void paintEvent(QPaintEvent* event) override {
|
||||
Present();
|
||||
update();
|
||||
}
|
||||
|
||||
std::pair<unsigned, unsigned> GetSize() const {
|
||||
return std::make_pair(width(), height());
|
||||
}
|
||||
virtual ~RenderWidget() = default;
|
||||
};
|
||||
|
||||
#ifdef HAS_OPENGL
|
||||
@@ -262,7 +252,7 @@ public:
|
||||
context = std::move(context_);
|
||||
}
|
||||
|
||||
void Present() override {
|
||||
void Present() {
|
||||
if (!isVisible()) {
|
||||
return;
|
||||
}
|
||||
@@ -278,6 +268,11 @@ public:
|
||||
glFinish();
|
||||
}
|
||||
|
||||
void paintEvent(QPaintEvent* event) override {
|
||||
Present();
|
||||
update();
|
||||
}
|
||||
|
||||
QPaintEngine* paintEngine() const override {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -289,11 +284,31 @@ 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);
|
||||
}
|
||||
#ifdef __APPLE__
|
||||
windowHandle()->setSurfaceType(QWindow::MetalSurface);
|
||||
#else
|
||||
windowHandle()->setSurfaceType(QWindow::VulkanSurface);
|
||||
#endif
|
||||
}
|
||||
|
||||
QPaintEngine* paintEngine() const override {
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
struct SoftwareRenderWidget : public RenderWidget {
|
||||
explicit SoftwareRenderWidget(GRenderWindow* parent, Core::System& system_)
|
||||
: RenderWidget(parent), system(system_) {}
|
||||
|
||||
void Present() override {
|
||||
void Present() {
|
||||
if (!isVisible()) {
|
||||
return;
|
||||
}
|
||||
@@ -323,6 +338,11 @@ struct SoftwareRenderWidget : public RenderWidget {
|
||||
painter.end();
|
||||
}
|
||||
|
||||
void paintEvent(QPaintEvent* event) override {
|
||||
Present();
|
||||
update();
|
||||
}
|
||||
|
||||
QImage LoadFramebuffer(VideoCore::ScreenId screen_id) {
|
||||
const auto& renderer = static_cast<SwRenderer::RendererSoftware&>(system.Renderer());
|
||||
const auto& info = renderer.Screen(screen_id);
|
||||
@@ -601,6 +621,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 +709,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<DummyContext>();
|
||||
}
|
||||
|
||||
void GRenderWindow::InitializeSoftware() {
|
||||
child_widget = new SoftwareRenderWidget(this, system);
|
||||
main_context = std::make_unique<DummyContext>();
|
||||
|
||||
@@ -187,6 +187,7 @@ private:
|
||||
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
|
||||
|
||||
bool InitializeOpenGL();
|
||||
void InitializeVulkan();
|
||||
void InitializeSoftware();
|
||||
bool LoadOpenGL();
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -1269,7 +1278,7 @@ void Config::SaveUpdaterValues() {
|
||||
void Config::SaveWebServiceValues() {
|
||||
qt_config->beginGroup(QStringLiteral("WebService"));
|
||||
|
||||
WriteSetting(QStringLiteral("enable_telemetry"), NetSettings::values.enable_telemetry, true);
|
||||
WriteSetting(QStringLiteral("enable_telemetry"), NetSettings::values.enable_telemetry, false);
|
||||
WriteSetting(QStringLiteral("web_api_url"),
|
||||
QString::fromStdString(NetSettings::values.web_api_url),
|
||||
QStringLiteral("https://api.citra-emu.org"));
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QDesktopServices>
|
||||
#include <QMessageBox>
|
||||
#include <QUrl>
|
||||
#include "citra_qt/configuration/configuration_shared.h"
|
||||
#include "citra_qt/configuration/configure_debug.h"
|
||||
@@ -12,6 +13,7 @@
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/settings.h"
|
||||
#include "ui_configure_debug.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
|
||||
// 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
|
||||
@@ -34,8 +36,39 @@ ConfigureDebug::ConfigureDebug(bool is_powered_on_, QWidget* parent)
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
||||
});
|
||||
|
||||
connect(ui->toggle_renderer_debug, &QCheckBox::clicked, this, [this](bool checked) {
|
||||
if (checked && Settings::values.graphics_api.GetValue() == Settings::GraphicsAPI::Vulkan) {
|
||||
try {
|
||||
Vulkan::Instance debug_inst{true};
|
||||
} catch (vk::LayerNotPresentError&) {
|
||||
ui->toggle_renderer_debug->toggle();
|
||||
QMessageBox::warning(this, tr("Validation layer not available"),
|
||||
tr("Unable to enable debug renderer because the layer "
|
||||
"<strong>VK_LAYER_KHRONOS_validation</strong> is missing. "
|
||||
"Please install the Vulkan SDK or the appropriate package "
|
||||
"of your distribution"));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
connect(ui->toggle_dump_command_buffers, &QCheckBox::clicked, this, [this](bool checked) {
|
||||
if (checked && Settings::values.graphics_api.GetValue() == Settings::GraphicsAPI::Vulkan) {
|
||||
try {
|
||||
Vulkan::Instance debug_inst{false, true};
|
||||
} catch (vk::LayerNotPresentError&) {
|
||||
ui->toggle_dump_command_buffers->toggle();
|
||||
QMessageBox::warning(this, tr("Command buffer dumping not available"),
|
||||
tr("Unable to enable command buffer dumping because the layer "
|
||||
"<strong>VK_LAYER_LUNARG_api_dump</strong> is missing. "
|
||||
"Please install the Vulkan SDK or the appropriate package "
|
||||
"of your distribution"));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ui->toggle_cpu_jit->setEnabled(!is_powered_on);
|
||||
ui->toggle_renderer_debug->setEnabled(!is_powered_on);
|
||||
ui->toggle_dump_command_buffers->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 +95,7 @@ void ConfigureDebug::SetConfiguration() {
|
||||
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());
|
||||
ui->toggle_dump_command_buffers->setChecked(Settings::values.dump_command_buffers.GetValue());
|
||||
|
||||
if (!Settings::IsConfiguringGlobal()) {
|
||||
if (Settings::values.cpu_clock_percentage.UsingGlobal()) {
|
||||
@@ -92,6 +126,7 @@ void ConfigureDebug::ApplyConfiguration() {
|
||||
Common::Log::SetGlobalFilter(filter);
|
||||
Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked();
|
||||
Settings::values.renderer_debug = ui->toggle_renderer_debug->isChecked();
|
||||
Settings::values.dump_command_buffers = ui->toggle_dump_command_buffers->isChecked();
|
||||
|
||||
ConfigurationShared::ApplyPerGameSetting(
|
||||
&Settings::values.cpu_clock_percentage, ui->clock_speed_combo,
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>523</width>
|
||||
<height>447</height>
|
||||
<height>458</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -112,16 +112,6 @@
|
||||
<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">
|
||||
@@ -202,6 +192,16 @@
|
||||
</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>
|
||||
<property name="text">
|
||||
<string>Enable CPU JIT</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="toggle_renderer_debug">
|
||||
<property name="text">
|
||||
@@ -209,6 +209,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="toggle_dump_command_buffers">
|
||||
<property name="text">
|
||||
<string>Dump command buffers</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -23,14 +23,14 @@
|
||||
#include "ui_configure.h"
|
||||
|
||||
ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, Core::System& system_,
|
||||
bool enable_web_config)
|
||||
std::span<const QString> physical_devices, bool enable_web_config)
|
||||
: QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()}, registry{registry_},
|
||||
system{system_}, is_powered_on{system.IsPoweredOn()},
|
||||
general_tab{std::make_unique<ConfigureGeneral>(this)},
|
||||
system_tab{std::make_unique<ConfigureSystem>(system, this)},
|
||||
input_tab{std::make_unique<ConfigureInput>(this)},
|
||||
hotkeys_tab{std::make_unique<ConfigureHotkeys>(this)},
|
||||
graphics_tab{std::make_unique<ConfigureGraphics>(is_powered_on, this)},
|
||||
graphics_tab{std::make_unique<ConfigureGraphics>(physical_devices, is_powered_on, this)},
|
||||
enhancements_tab{std::make_unique<ConfigureEnhancements>(this)},
|
||||
audio_tab{std::make_unique<ConfigureAudio>(is_powered_on, this)},
|
||||
camera_tab{std::make_unique<ConfigureCamera>(this)},
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <QDialog>
|
||||
#include <QString>
|
||||
|
||||
class HotkeyRegistry;
|
||||
|
||||
@@ -35,6 +37,7 @@ class ConfigureDialog : public QDialog {
|
||||
|
||||
public:
|
||||
explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, Core::System& system,
|
||||
std::span<const QString> physical_devices,
|
||||
bool enable_web_config = true);
|
||||
~ConfigureDialog() override;
|
||||
|
||||
|
||||
@@ -31,7 +31,8 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
|
||||
|
||||
// Set a minimum width for the label to prevent the slider from changing size.
|
||||
// This scales across DPIs, and is acceptable for uncapitalized strings.
|
||||
ui->emulation_speed_display_label->setMinimumWidth(tr("unthrottled").size() * 6);
|
||||
const auto width = static_cast<int>(tr("unthrottled").size() * 6);
|
||||
ui->emulation_speed_display_label->setMinimumWidth(width);
|
||||
ui->emulation_speed_combo->setVisible(!Settings::IsConfiguringGlobal());
|
||||
ui->screenshot_combo->setVisible(!Settings::IsConfiguringGlobal());
|
||||
ui->updateBox->setVisible(UISettings::values.updater_found);
|
||||
|
||||
@@ -7,16 +7,34 @@
|
||||
#include "citra_qt/configuration/configure_graphics.h"
|
||||
#include "common/settings.h"
|
||||
#include "ui_configure_graphics.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
|
||||
ConfigureGraphics::ConfigureGraphics(bool is_powered_on, QWidget* parent)
|
||||
ConfigureGraphics::ConfigureGraphics(std::span<const QString> physical_devices, bool is_powered_on,
|
||||
QWidget* parent)
|
||||
: QWidget(parent), ui(std::make_unique<Ui::ConfigureGraphics>()) {
|
||||
ui->setupUi(this);
|
||||
|
||||
SetupPerGameUI();
|
||||
|
||||
for (const QString& name : physical_devices) {
|
||||
ui->physical_device_combo->addItem(name);
|
||||
}
|
||||
|
||||
ui->toggle_vsync_new->setEnabled(!is_powered_on);
|
||||
ui->graphics_api_combo->setEnabled(!is_powered_on);
|
||||
ui->physical_device_combo->setEnabled(!is_powered_on);
|
||||
ui->toggle_async_shaders->setEnabled(!is_powered_on);
|
||||
ui->toggle_async_present->setEnabled(!is_powered_on);
|
||||
// Set the index to -1 to ensure the below lambda is called with setCurrentIndex
|
||||
ui->graphics_api_combo->setCurrentIndex(-1);
|
||||
|
||||
if (physical_devices.empty()) {
|
||||
const u32 index = static_cast<u32>(Settings::GraphicsAPI::Vulkan);
|
||||
ui->graphics_api_combo->removeItem(index);
|
||||
ui->physical_device_combo->setVisible(false);
|
||||
ui->spirv_shader_gen->setVisible(false);
|
||||
}
|
||||
|
||||
connect(ui->graphics_api_combo, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
[this](int index) {
|
||||
const auto graphics_api =
|
||||
@@ -35,7 +53,9 @@ ConfigureGraphics::ConfigureGraphics(bool is_powered_on, QWidget* parent)
|
||||
ui->toggle_disk_shader_cache->setEnabled(checked && enabled);
|
||||
});
|
||||
|
||||
SetupPerGameUI();
|
||||
connect(ui->graphics_api_combo, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
&ConfigureGraphics::SetPhysicalDeviceComboVisibility);
|
||||
|
||||
SetConfiguration();
|
||||
}
|
||||
|
||||
@@ -47,15 +67,24 @@ void ConfigureGraphics::SetConfiguration() {
|
||||
!Settings::values.graphics_api.UsingGlobal());
|
||||
ConfigurationShared::SetPerGameSetting(ui->graphics_api_combo,
|
||||
&Settings::values.graphics_api);
|
||||
ConfigurationShared::SetHighlight(ui->physical_device_group,
|
||||
!Settings::values.physical_device.UsingGlobal());
|
||||
ConfigurationShared::SetPerGameSetting(ui->physical_device_combo,
|
||||
&Settings::values.physical_device);
|
||||
} else {
|
||||
ui->graphics_api_combo->setCurrentIndex(
|
||||
static_cast<int>(Settings::values.graphics_api.GetValue()));
|
||||
ui->physical_device_combo->setCurrentIndex(
|
||||
static_cast<int>(Settings::values.physical_device.GetValue()));
|
||||
}
|
||||
|
||||
ui->toggle_hw_shader->setChecked(Settings::values.use_hw_shader.GetValue());
|
||||
ui->toggle_accurate_mul->setChecked(Settings::values.shaders_accurate_mul.GetValue());
|
||||
ui->toggle_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue());
|
||||
ui->toggle_vsync_new->setChecked(Settings::values.use_vsync_new.GetValue());
|
||||
ui->spirv_shader_gen->setChecked(Settings::values.spirv_shader_gen.GetValue());
|
||||
ui->toggle_async_shaders->setChecked(Settings::values.async_shader_compilation.GetValue());
|
||||
ui->toggle_async_present->setChecked(Settings::values.async_presentation.GetValue());
|
||||
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit.GetValue());
|
||||
@@ -65,6 +94,14 @@ void ConfigureGraphics::SetConfiguration() {
|
||||
void ConfigureGraphics::ApplyConfiguration() {
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.graphics_api,
|
||||
ui->graphics_api_combo);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.physical_device,
|
||||
ui->physical_device_combo);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.async_shader_compilation,
|
||||
ui->toggle_async_shaders, async_shader_compilation);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.async_presentation,
|
||||
ui->toggle_async_present, async_presentation);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.spirv_shader_gen,
|
||||
ui->spirv_shader_gen, spirv_shader_gen);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_hw_shader, ui->toggle_hw_shader,
|
||||
use_hw_shader);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.shaders_accurate_mul,
|
||||
@@ -93,6 +130,11 @@ void ConfigureGraphics::SetupPerGameUI() {
|
||||
Settings::values.use_disk_shader_cache.UsingGlobal());
|
||||
ui->toggle_vsync_new->setEnabled(ui->toggle_vsync_new->isEnabled() &&
|
||||
Settings::values.use_vsync_new.UsingGlobal());
|
||||
ui->toggle_async_shaders->setEnabled(
|
||||
Settings::values.async_shader_compilation.UsingGlobal());
|
||||
ui->toggle_async_present->setEnabled(Settings::values.async_presentation.UsingGlobal());
|
||||
ui->graphics_api_combo->setEnabled(Settings::values.graphics_api.UsingGlobal());
|
||||
ui->physical_device_combo->setEnabled(Settings::values.physical_device.UsingGlobal());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -102,6 +144,10 @@ void ConfigureGraphics::SetupPerGameUI() {
|
||||
ui->graphics_api_combo, ui->graphics_api_group,
|
||||
static_cast<u32>(Settings::values.graphics_api.GetValue(true)));
|
||||
|
||||
ConfigurationShared::SetColoredComboBox(
|
||||
ui->physical_device_combo, ui->physical_device_group,
|
||||
static_cast<u32>(Settings::values.physical_device.GetValue(true)));
|
||||
|
||||
ConfigurationShared::SetColoredTristate(ui->toggle_hw_shader, Settings::values.use_hw_shader,
|
||||
use_hw_shader);
|
||||
ConfigurationShared::SetColoredTristate(
|
||||
@@ -111,4 +157,34 @@ void ConfigureGraphics::SetupPerGameUI() {
|
||||
use_disk_shader_cache);
|
||||
ConfigurationShared::SetColoredTristate(ui->toggle_vsync_new, Settings::values.use_vsync_new,
|
||||
use_vsync_new);
|
||||
ConfigurationShared::SetColoredTristate(ui->toggle_async_shaders,
|
||||
Settings::values.async_shader_compilation,
|
||||
async_shader_compilation);
|
||||
ConfigurationShared::SetColoredTristate(
|
||||
ui->toggle_async_present, Settings::values.async_presentation, async_presentation);
|
||||
ConfigurationShared::SetColoredTristate(ui->spirv_shader_gen, Settings::values.spirv_shader_gen,
|
||||
spirv_shader_gen);
|
||||
}
|
||||
|
||||
void ConfigureGraphics::SetPhysicalDeviceComboVisibility(int index) {
|
||||
bool is_visible{};
|
||||
|
||||
// When configuring per-game the physical device combo should be
|
||||
// shown either when the global api is used and that is Vulkan or
|
||||
// Vulkan is set as the per-game api.
|
||||
if (!Settings::IsConfiguringGlobal()) {
|
||||
const auto global_graphics_api = Settings::values.graphics_api.GetValue(true);
|
||||
const bool using_global = index == 0;
|
||||
if (!using_global) {
|
||||
index -= ConfigurationShared::USE_GLOBAL_OFFSET;
|
||||
}
|
||||
const auto graphics_api = static_cast<Settings::GraphicsAPI>(index);
|
||||
is_visible = (using_global && global_graphics_api == Settings::GraphicsAPI::Vulkan) ||
|
||||
graphics_api == Settings::GraphicsAPI::Vulkan;
|
||||
} else {
|
||||
const auto graphics_api = static_cast<Settings::GraphicsAPI>(index);
|
||||
is_visible = graphics_api == Settings::GraphicsAPI::Vulkan;
|
||||
}
|
||||
ui->physical_device_group->setVisible(is_visible);
|
||||
ui->spirv_shader_gen->setVisible(is_visible);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
|
||||
namespace Ui {
|
||||
@@ -19,7 +21,8 @@ class ConfigureGraphics : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConfigureGraphics(bool is_powered_on, QWidget* parent = nullptr);
|
||||
explicit ConfigureGraphics(std::span<const QString> physical_devices, bool is_powered_on,
|
||||
QWidget* parent = nullptr);
|
||||
~ConfigureGraphics() override;
|
||||
|
||||
void ApplyConfiguration();
|
||||
@@ -30,11 +33,15 @@ public:
|
||||
|
||||
private:
|
||||
void SetupPerGameUI();
|
||||
void SetPhysicalDeviceComboVisibility(int index);
|
||||
|
||||
ConfigurationShared::CheckState use_hw_shader;
|
||||
ConfigurationShared::CheckState shaders_accurate_mul;
|
||||
ConfigurationShared::CheckState use_disk_shader_cache;
|
||||
ConfigurationShared::CheckState use_vsync_new;
|
||||
ConfigurationShared::CheckState async_shader_compilation;
|
||||
ConfigurationShared::CheckState async_presentation;
|
||||
ConfigurationShared::CheckState spirv_shader_gen;
|
||||
std::unique_ptr<Ui::ConfigureGraphics> ui;
|
||||
QColor bg_color;
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>443</height>
|
||||
<height>509</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
@@ -63,11 +63,51 @@
|
||||
<string>OpenGL</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Vulkan</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="physical_device_group" native="true">
|
||||
<layout class="QHBoxLayout" name="physical_device_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="physical_device_label">
|
||||
<property name="text">
|
||||
<string>Physical Device</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="physical_device_combo"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="spirv_shader_gen">
|
||||
<property name="text">
|
||||
<string>SPIR-V Shader Generation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -95,7 +135,7 @@
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_hw_shader">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Use OpenGL to accelerate shader emulation.</p><p>Requires a relatively powerful GPU for better performance.</p></body></html></string>
|
||||
<string><html><head/><body><p>Use the selected graphics API to accelerate shader emulation.</p><p>Requires a relatively powerful GPU for better performance.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable Hardware Shader</string>
|
||||
@@ -143,6 +183,26 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_async_shaders">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Compile shaders using background threads to avoid shader compilation stutter. Expect temporary graphical glitches</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable Async Shader Compilation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_async_present">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Perform presentation on separate threads. Improves performance when using Vulkan in most games.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable Async Presentation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#include "ui_configure_per_game.h"
|
||||
|
||||
ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const QString& file_name,
|
||||
Core::System& system_)
|
||||
std::span<const QString> physical_devices, Core::System& system_)
|
||||
: QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()),
|
||||
filename{file_name.toStdString()}, title_id{title_id_}, system{system_} {
|
||||
const auto config_file_name = title_id == 0 ? std::string(FileUtil::GetFilename(filename))
|
||||
@@ -35,7 +35,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const QString
|
||||
audio_tab = std::make_unique<ConfigureAudio>(is_powered_on, this);
|
||||
general_tab = std::make_unique<ConfigureGeneral>(this);
|
||||
enhancements_tab = std::make_unique<ConfigureEnhancements>(this);
|
||||
graphics_tab = std::make_unique<ConfigureGraphics>(is_powered_on, this);
|
||||
graphics_tab = std::make_unique<ConfigureGraphics>(physical_devices, is_powered_on, this);
|
||||
system_tab = std::make_unique<ConfigureSystem>(system, this);
|
||||
debug_tab = std::make_unique<ConfigureDebug>(is_powered_on, this);
|
||||
cheat_tab = std::make_unique<ConfigureCheats>(system, title_id, this);
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <QDialog>
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include "citra_qt/configuration/config.h"
|
||||
|
||||
namespace Core {
|
||||
@@ -35,9 +37,8 @@ class ConfigurePerGame : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// Cannot use std::filesystem::path due to https://bugreports.qt.io/browse/QTBUG-73263
|
||||
explicit ConfigurePerGame(QWidget* parent, u64 title_id_, const QString& file_name,
|
||||
Core::System& system_);
|
||||
std::span<const QString> physical_devices, Core::System& system_);
|
||||
~ConfigurePerGame() override;
|
||||
|
||||
/// Loads all button configurations to settings file
|
||||
|
||||
@@ -285,12 +285,12 @@ void ConfigureSystem::SetConfiguration() {
|
||||
date_time.setSecsSinceEpoch(Settings::values.init_time.GetValue());
|
||||
ui->edit_init_time->setDateTime(date_time);
|
||||
|
||||
long long init_time_offset = Settings::values.init_time_offset.GetValue();
|
||||
long long days_offset = init_time_offset / 86400;
|
||||
s64 init_time_offset = Settings::values.init_time_offset.GetValue();
|
||||
int days_offset = static_cast<int>(init_time_offset / 86400);
|
||||
ui->edit_init_time_offset_days->setValue(days_offset);
|
||||
|
||||
unsigned long long time_offset = std::abs(init_time_offset) - std::abs(days_offset * 86400);
|
||||
QTime time = QTime::fromMSecsSinceStartOfDay(time_offset * 1000);
|
||||
u64 time_offset = std::abs(init_time_offset) - std::abs(days_offset * 86400);
|
||||
QTime time = QTime::fromMSecsSinceStartOfDay(static_cast<int>(time_offset * 1000));
|
||||
ui->edit_init_time_offset_time->setTime(time);
|
||||
|
||||
if (!enabled) {
|
||||
|
||||
@@ -180,9 +180,10 @@ void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size
|
||||
}
|
||||
const auto eta_mseconds = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
rolling_average * (total - value));
|
||||
const auto limited_mseconds = std::max<long>(eta_mseconds.count(), 1000);
|
||||
estimate = tr("Estimated Time %1")
|
||||
.arg(QTime(0, 0, 0, 0)
|
||||
.addMSecs(std::max<long>(eta_mseconds.count(), 1000))
|
||||
.addMSecs(static_cast<int>(limited_mseconds))
|
||||
.toString(QStringLiteral("mm:ss")));
|
||||
}
|
||||
|
||||
|
||||
+15
-4
@@ -62,6 +62,7 @@
|
||||
#include "citra_qt/uisettings.h"
|
||||
#include "citra_qt/updater/updater.h"
|
||||
#include "citra_qt/util/clickable_label.h"
|
||||
#include "citra_qt/util/vk_device_info.h"
|
||||
#include "common/arch.h"
|
||||
#include "common/common_paths.h"
|
||||
#include "common/detached_tasks.h"
|
||||
@@ -263,6 +264,14 @@ GMainWindow::GMainWindow(Core::System& system_)
|
||||
connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor);
|
||||
connect(ui->menubar, &QMenuBar::hovered, this, &GMainWindow::OnMouseActivity);
|
||||
|
||||
physical_devices = GetVulkanPhysicalDevices();
|
||||
if (physical_devices.empty()) {
|
||||
QMessageBox::warning(this, tr("No Suitable Vulkan Devices Detected"),
|
||||
tr("Vulkan initialization failed during boot.<br/>"
|
||||
"Your GPU may not support Vulkan 1.1, or you do not "
|
||||
"have the latest graphics driver."));
|
||||
}
|
||||
|
||||
#if ENABLE_QT_UPDATER
|
||||
if (UISettings::values.check_for_update_on_start) {
|
||||
CheckForUpdates();
|
||||
@@ -2010,7 +2019,7 @@ void GMainWindow::OnLoadState() {
|
||||
void GMainWindow::OnConfigure() {
|
||||
game_list->SetDirectoryWatcherEnabled(false);
|
||||
Settings::SetConfiguringGlobal(true);
|
||||
ConfigureDialog configureDialog(this, hotkey_registry, system,
|
||||
ConfigureDialog configureDialog(this, hotkey_registry, system, physical_devices,
|
||||
!multiplayer_state->IsHostingPublicRoom());
|
||||
connect(&configureDialog, &ConfigureDialog::LanguageChanged, this,
|
||||
&GMainWindow::OnLanguageChanged);
|
||||
@@ -2470,9 +2479,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<u32>(Settings::values.graphics_api.GetValue());
|
||||
if (update) {
|
||||
@@ -2764,7 +2775,7 @@ void GMainWindow::OnConfigurePerGame() {
|
||||
|
||||
void GMainWindow::OpenPerGameConfiguration(u64 title_id, const QString& file_name) {
|
||||
Settings::SetConfiguringGlobal(false);
|
||||
ConfigurePerGame dialog(this, title_id, file_name, system);
|
||||
ConfigurePerGame dialog(this, title_id, file_name, physical_devices, system);
|
||||
const auto result = dialog.exec();
|
||||
|
||||
if (result != QDialog::Accepted) {
|
||||
|
||||
@@ -6,8 +6,10 @@
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <QMainWindow>
|
||||
#include <QPushButton>
|
||||
#include <QString>
|
||||
#include <QTimer>
|
||||
#include <QTranslator>
|
||||
#include "citra_qt/compatibility_list.h"
|
||||
@@ -326,6 +328,8 @@ private:
|
||||
// Whether game was paused due to stopping video dumping
|
||||
bool game_paused_for_dumping = false;
|
||||
|
||||
std::vector<QString> physical_devices;
|
||||
|
||||
// Debugger panes
|
||||
ProfilerWidget* profilerWidget;
|
||||
MicroProfileDialog* microProfileDialog;
|
||||
|
||||
@@ -125,8 +125,8 @@ void MoviePlayDialog::UpdateUIDisplay() {
|
||||
} else {
|
||||
const u64 msecs = Service::HID::Module::pad_update_ticks * metadata.input_count * 1000 /
|
||||
BASE_CLOCK_RATE_ARM11;
|
||||
ui->lengthLineEdit->setText(
|
||||
QTime::fromMSecsSinceStartOfDay(msecs).toString(QStringLiteral("hh:mm:ss.zzz")));
|
||||
ui->lengthLineEdit->setText(QTime::fromMSecsSinceStartOfDay(static_cast<int>(msecs))
|
||||
.toString(QStringLiteral("hh:mm:ss.zzz")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,7 +284,7 @@ bool LobbyFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& s
|
||||
// filter by empty rooms
|
||||
if (filter_empty) {
|
||||
QModelIndex member_list = sourceModel()->index(sourceRow, Column::MEMBER, sourceParent);
|
||||
const int player_count =
|
||||
const qsizetype player_count =
|
||||
sourceModel()->data(member_list, LobbyItemMemberList::MemberListRole).toList().size();
|
||||
if (player_count == 0) {
|
||||
return false;
|
||||
@@ -294,7 +294,7 @@ bool LobbyFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& s
|
||||
// filter by filled rooms
|
||||
if (filter_full) {
|
||||
QModelIndex member_list = sourceModel()->index(sourceRow, Column::MEMBER, sourceParent);
|
||||
const int player_count =
|
||||
const qsizetype player_count =
|
||||
sourceModel()->data(member_list, LobbyItemMemberList::MemberListRole).toList().size();
|
||||
const int max_players =
|
||||
sourceModel()->data(member_list, LobbyItemMemberList::MaxPlayerRole).toInt();
|
||||
|
||||
@@ -198,8 +198,8 @@ public:
|
||||
|
||||
bool operator<(const QStandardItem& other) const override {
|
||||
// sort by rooms that have the most players
|
||||
int left_members = data(MemberListRole).toList().size();
|
||||
int right_members = other.data(MemberListRole).toList().size();
|
||||
qsizetype left_members = data(MemberListRole).toList().size();
|
||||
qsizetype right_members = other.data(MemberListRole).toList().size();
|
||||
return left_members < right_members;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "citra_qt/uisettings.h"
|
||||
#include "citra_qt/updater/updater.h"
|
||||
#include "citra_qt/updater/updater_p.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
#ifdef Q_OS_OSX
|
||||
@@ -110,9 +111,22 @@ QString UpdaterPrivate::ToSystemExe(QString base_path) {
|
||||
#endif
|
||||
}
|
||||
|
||||
QFileInfo UpdaterPrivate::GetMaintenanceTool() const {
|
||||
#if defined(Q_OS_UNIX) && !defined(Q_OS_OSX)
|
||||
const auto appimage_path = QProcessEnvironment::systemEnvironment()
|
||||
.value(QStringLiteral("APPIMAGE"), {})
|
||||
.toStdString();
|
||||
if (!appimage_path.empty()) {
|
||||
const auto appimage_dir = FileUtil::GetParentPath(appimage_path);
|
||||
LOG_DEBUG(Frontend, "Detected app image directory: {}", appimage_dir);
|
||||
return QFileInfo(QString::fromStdString(std::string(appimage_dir)), tool_path);
|
||||
}
|
||||
#endif
|
||||
return QFileInfo(QCoreApplication::applicationDirPath(), tool_path);
|
||||
}
|
||||
|
||||
bool UpdaterPrivate::HasUpdater() const {
|
||||
QFileInfo tool_info(QCoreApplication::applicationDirPath(), tool_path);
|
||||
return tool_info.exists();
|
||||
return GetMaintenanceTool().exists();
|
||||
}
|
||||
|
||||
bool UpdaterPrivate::StartUpdateCheck() {
|
||||
@@ -125,10 +139,9 @@ bool UpdaterPrivate::StartUpdateCheck() {
|
||||
last_error_code = EXIT_SUCCESS;
|
||||
last_error_log.clear();
|
||||
|
||||
QFileInfo tool_info(QCoreApplication::applicationDirPath(), tool_path);
|
||||
main_process = new QProcess(this);
|
||||
main_process->setProgram(tool_info.absoluteFilePath());
|
||||
main_process->setArguments({QStringLiteral("--checkupdates"), QStringLiteral("-v")});
|
||||
main_process->setProgram(GetMaintenanceTool().absoluteFilePath());
|
||||
main_process->setArguments({QStringLiteral("--checkupdates")});
|
||||
|
||||
connect(main_process, qOverload<int, QProcess::ExitStatus>(&QProcess::finished), this,
|
||||
&UpdaterPrivate::UpdaterReady, Qt::QueuedConnection);
|
||||
@@ -271,7 +284,7 @@ void UpdaterPrivate::LaunchWithArguments(const QStringList& args) {
|
||||
return;
|
||||
}
|
||||
|
||||
QFileInfo tool_info(QCoreApplication::applicationDirPath(), tool_path);
|
||||
QFileInfo tool_info = GetMaintenanceTool();
|
||||
|
||||
if (!QProcess::startDetached(tool_info.absoluteFilePath(), args, tool_info.absolutePath())) {
|
||||
LOG_WARNING(Frontend, "Unable to start program {}",
|
||||
|
||||
@@ -26,6 +26,7 @@ public:
|
||||
|
||||
static QString ToSystemExe(QString base_path);
|
||||
|
||||
QFileInfo GetMaintenanceTool() const;
|
||||
bool HasUpdater() const;
|
||||
|
||||
bool StartUpdateCheck();
|
||||
|
||||
@@ -194,7 +194,7 @@ QString CSpinBox::TextFromValue() {
|
||||
}
|
||||
|
||||
qint64 CSpinBox::ValueFromText() {
|
||||
unsigned strpos = prefix.length();
|
||||
qsizetype strpos = prefix.length();
|
||||
|
||||
QString num_string = text().mid(strpos, text().length() - strpos - suffix.length());
|
||||
return num_string.toLongLong(nullptr, base);
|
||||
@@ -216,7 +216,7 @@ QValidator::State CSpinBox::validate(QString& input, int& pos) const {
|
||||
if (!prefix.isEmpty() && input.left(prefix.length()) != prefix)
|
||||
return QValidator::Invalid;
|
||||
|
||||
int strpos = prefix.length();
|
||||
qsizetype strpos = prefix.length();
|
||||
|
||||
// Empty "numbers" allowed as intermediate values
|
||||
if (strpos >= input.length() - HasSign() - suffix.length())
|
||||
@@ -245,7 +245,7 @@ QValidator::State CSpinBox::validate(QString& input, int& pos) const {
|
||||
|
||||
// Match string
|
||||
QRegularExpression num_regexp(QRegularExpression::anchoredPattern(regexp));
|
||||
int num_pos = strpos;
|
||||
qsizetype num_pos = strpos;
|
||||
QString sub_input = input.mid(strpos, input.length() - strpos - suffix.length());
|
||||
|
||||
auto match = num_regexp.match(sub_input);
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "citra_qt/util/vk_device_info.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
|
||||
std::vector<QString> GetVulkanPhysicalDevices() {
|
||||
std::vector<QString> result;
|
||||
try {
|
||||
Vulkan::Instance instance{};
|
||||
const auto physical_devices = instance.GetPhysicalDevices();
|
||||
|
||||
for (const vk::PhysicalDevice physical_device : physical_devices) {
|
||||
const QString name = QString::fromUtf8(physical_device.getProperties().deviceName, -1);
|
||||
result.push_back(name);
|
||||
}
|
||||
} catch (const std::runtime_error& err) {
|
||||
LOG_ERROR(Frontend, "Error occured while querying for physical devices: {}", err.what());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <QString>
|
||||
|
||||
/// Returns a list of all available vulkan GPUs.
|
||||
std::vector<QString> GetVulkanPhysicalDevices();
|
||||
@@ -15,14 +15,22 @@ add_custom_command(OUTPUT scm_rev.cpp
|
||||
DEPENDS
|
||||
# WARNING! It was too much work to try and make a common location for this list,
|
||||
# so if you need to change it, please update CMakeModules/GenerateSCMRev.cmake as well
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.cpp"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.h"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.cpp"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.h"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_gen.cpp"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_gen.h"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_util.cpp"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_util.h"
|
||||
"${VIDEO_CORE}/renderer_vulkan/vk_shader_util.cpp"
|
||||
"${VIDEO_CORE}/renderer_vulkan/vk_shader_util.h"
|
||||
"${VIDEO_CORE}/shader/generator/glsl_shader_decompiler.cpp"
|
||||
"${VIDEO_CORE}/shader/generator/glsl_shader_decompiler.h"
|
||||
"${VIDEO_CORE}/shader/generator/glsl_shader_gen.cpp"
|
||||
"${VIDEO_CORE}/shader/generator/glsl_shader_gen.h"
|
||||
"${VIDEO_CORE}/shader/generator/shader_gen.cpp"
|
||||
"${VIDEO_CORE}/shader/generator/shader_gen.h"
|
||||
"${VIDEO_CORE}/shader/generator/shader_uniforms.cpp"
|
||||
"${VIDEO_CORE}/shader/generator/shader_uniforms.h"
|
||||
"${VIDEO_CORE}/shader/generator/spv_shader_gen.cpp"
|
||||
"${VIDEO_CORE}/shader/generator/spv_shader_gen.h"
|
||||
"${VIDEO_CORE}/shader/shader.cpp"
|
||||
"${VIDEO_CORE}/shader/shader.h"
|
||||
"${VIDEO_CORE}/pica.cpp"
|
||||
@@ -167,7 +175,6 @@ create_target_directory_groups(citra_common)
|
||||
|
||||
target_link_libraries(citra_common PUBLIC fmt::fmt library-headers microprofile Boost::boost Boost::serialization Boost::iostreams)
|
||||
target_link_libraries(citra_common PRIVATE libzstd_static)
|
||||
set_target_properties(citra_common PROPERTIES INTERPROCEDURAL_OPTIMIZATION ${ENABLE_LTO})
|
||||
|
||||
if ("x86_64" IN_LIST ARCHITECTURE)
|
||||
target_link_libraries(citra_common PRIVATE xbyak)
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
|
||||
namespace Common {
|
||||
|
||||
DynamicLibrary::DynamicLibrary() = default;
|
||||
|
||||
DynamicLibrary::DynamicLibrary(void* handle_) : handle{handle_} {}
|
||||
|
||||
DynamicLibrary::DynamicLibrary(std::string_view name, int major, int minor) {
|
||||
auto full_name = GetLibraryName(name, major, minor);
|
||||
void(Load(full_name));
|
||||
|
||||
@@ -11,11 +11,12 @@ namespace Common {
|
||||
class DynamicLibrary {
|
||||
public:
|
||||
explicit DynamicLibrary();
|
||||
explicit DynamicLibrary(void* handle);
|
||||
explicit DynamicLibrary(std::string_view name, int major = -1, int minor = -1);
|
||||
~DynamicLibrary();
|
||||
|
||||
/// Returns true if the library is loaded, otherwise false.
|
||||
[[nodiscard]] bool IsLoaded() {
|
||||
[[nodiscard]] bool IsLoaded() const noexcept {
|
||||
return handle != nullptr;
|
||||
}
|
||||
|
||||
|
||||
+30
-30
@@ -65,7 +65,7 @@ struct no_init_t {
|
||||
* Additionally, this requires E to be trivially destructible
|
||||
*/
|
||||
template <typename T, typename E, bool = std::is_trivially_destructible_v<T>>
|
||||
requires std::is_trivially_destructible_v<E>
|
||||
requires std::is_trivially_destructible_v<E>
|
||||
struct expected_storage_base {
|
||||
constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {}
|
||||
|
||||
@@ -112,7 +112,7 @@ struct expected_storage_base {
|
||||
* Additionally, this requires E to be trivially destructible
|
||||
*/
|
||||
template <typename T, typename E>
|
||||
requires std::is_trivially_destructible_v<E>
|
||||
requires std::is_trivially_destructible_v<E>
|
||||
struct expected_storage_base<T, E, true> {
|
||||
constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {}
|
||||
|
||||
@@ -252,7 +252,7 @@ struct expected_operations_base : expected_storage_base<T, E> {
|
||||
* Additionally, this requires E to be trivially copy constructible
|
||||
*/
|
||||
template <typename T, typename E, bool = std::is_trivially_copy_constructible_v<T>>
|
||||
requires std::is_trivially_copy_constructible_v<E>
|
||||
requires std::is_trivially_copy_constructible_v<E>
|
||||
struct expected_copy_base : expected_operations_base<T, E> {
|
||||
using expected_operations_base<T, E>::expected_operations_base;
|
||||
};
|
||||
@@ -262,7 +262,7 @@ struct expected_copy_base : expected_operations_base<T, E> {
|
||||
* Additionally, this requires E to be trivially copy constructible
|
||||
*/
|
||||
template <typename T, typename E>
|
||||
requires std::is_trivially_copy_constructible_v<E>
|
||||
requires std::is_trivially_copy_constructible_v<E>
|
||||
struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
|
||||
using expected_operations_base<T, E>::expected_operations_base;
|
||||
|
||||
@@ -290,7 +290,7 @@ struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
|
||||
* Additionally, this requires E to be trivially move constructible
|
||||
*/
|
||||
template <typename T, typename E, bool = std::is_trivially_move_constructible_v<T>>
|
||||
requires std::is_trivially_move_constructible_v<E>
|
||||
requires std::is_trivially_move_constructible_v<E>
|
||||
struct expected_move_base : expected_copy_base<T, E> {
|
||||
using expected_copy_base<T, E>::expected_copy_base;
|
||||
};
|
||||
@@ -300,7 +300,7 @@ struct expected_move_base : expected_copy_base<T, E> {
|
||||
* Additionally, this requires E to be trivially move constructible
|
||||
*/
|
||||
template <typename T, typename E>
|
||||
requires std::is_trivially_move_constructible_v<E>
|
||||
requires std::is_trivially_move_constructible_v<E>
|
||||
struct expected_move_base<T, E, false> : expected_copy_base<T, E> {
|
||||
using expected_copy_base<T, E>::expected_copy_base;
|
||||
|
||||
@@ -331,9 +331,9 @@ template <typename T, typename E,
|
||||
bool = std::conjunction_v<std::is_trivially_copy_assignable<T>,
|
||||
std::is_trivially_copy_constructible<T>,
|
||||
std::is_trivially_destructible<T>>>
|
||||
requires std::conjunction_v<std::is_trivially_copy_assignable<E>,
|
||||
std::is_trivially_copy_constructible<E>,
|
||||
std::is_trivially_destructible<E>>
|
||||
requires std::conjunction_v<std::is_trivially_copy_assignable<E>,
|
||||
std::is_trivially_copy_constructible<E>,
|
||||
std::is_trivially_destructible<E>>
|
||||
struct expected_copy_assign_base : expected_move_base<T, E> {
|
||||
using expected_move_base<T, E>::expected_move_base;
|
||||
};
|
||||
@@ -343,9 +343,9 @@ struct expected_copy_assign_base : expected_move_base<T, E> {
|
||||
* Additionally, this requires E to be trivially copy assignable
|
||||
*/
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_trivially_copy_assignable<E>,
|
||||
std::is_trivially_copy_constructible<E>,
|
||||
std::is_trivially_destructible<E>>
|
||||
requires std::conjunction_v<std::is_trivially_copy_assignable<E>,
|
||||
std::is_trivially_copy_constructible<E>,
|
||||
std::is_trivially_destructible<E>>
|
||||
struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> {
|
||||
using expected_move_base<T, E>::expected_move_base;
|
||||
|
||||
@@ -372,9 +372,9 @@ template <typename T, typename E,
|
||||
bool = std::conjunction_v<std::is_trivially_move_assignable<T>,
|
||||
std::is_trivially_move_constructible<T>,
|
||||
std::is_trivially_destructible<T>>>
|
||||
requires std::conjunction_v<std::is_trivially_move_assignable<E>,
|
||||
std::is_trivially_move_constructible<E>,
|
||||
std::is_trivially_destructible<E>>
|
||||
requires std::conjunction_v<std::is_trivially_move_assignable<E>,
|
||||
std::is_trivially_move_constructible<E>,
|
||||
std::is_trivially_destructible<E>>
|
||||
struct expected_move_assign_base : expected_copy_assign_base<T, E> {
|
||||
using expected_copy_assign_base<T, E>::expected_copy_assign_base;
|
||||
};
|
||||
@@ -384,9 +384,9 @@ struct expected_move_assign_base : expected_copy_assign_base<T, E> {
|
||||
* Additionally, this requires E to be trivially move assignable
|
||||
*/
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_trivially_move_assignable<E>,
|
||||
std::is_trivially_move_constructible<E>,
|
||||
std::is_trivially_destructible<E>>
|
||||
requires std::conjunction_v<std::is_trivially_move_assignable<E>,
|
||||
std::is_trivially_move_constructible<E>,
|
||||
std::is_trivially_destructible<E>>
|
||||
struct expected_move_assign_base<T, E, false> : expected_copy_assign_base<T, E> {
|
||||
using expected_copy_assign_base<T, E>::expected_copy_assign_base;
|
||||
|
||||
@@ -413,7 +413,7 @@ struct expected_move_assign_base<T, E, false> : expected_copy_assign_base<T, E>
|
||||
*/
|
||||
template <typename T, typename E, bool EnableCopy = std::is_copy_constructible_v<T>,
|
||||
bool EnableMove = std::is_move_constructible_v<T>>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
|
||||
struct expected_delete_ctor_base {
|
||||
expected_delete_ctor_base() = default;
|
||||
expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
|
||||
@@ -423,7 +423,7 @@ struct expected_delete_ctor_base {
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
|
||||
struct expected_delete_ctor_base<T, E, true, false> {
|
||||
expected_delete_ctor_base() = default;
|
||||
expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
|
||||
@@ -433,7 +433,7 @@ struct expected_delete_ctor_base<T, E, true, false> {
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
|
||||
struct expected_delete_ctor_base<T, E, false, true> {
|
||||
expected_delete_ctor_base() = default;
|
||||
expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
|
||||
@@ -443,7 +443,7 @@ struct expected_delete_ctor_base<T, E, false, true> {
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
|
||||
struct expected_delete_ctor_base<T, E, false, false> {
|
||||
expected_delete_ctor_base() = default;
|
||||
expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
|
||||
@@ -461,8 +461,8 @@ template <
|
||||
typename T, typename E,
|
||||
bool EnableCopy = std::conjunction_v<std::is_copy_constructible<T>, std::is_copy_assignable<T>>,
|
||||
bool EnableMove = std::conjunction_v<std::is_move_constructible<T>, std::is_move_assignable<T>>>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
|
||||
std::is_copy_assignable<E>, std::is_move_assignable<E>>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
|
||||
std::is_copy_assignable<E>, std::is_move_assignable<E>>
|
||||
struct expected_delete_assign_base {
|
||||
expected_delete_assign_base() = default;
|
||||
expected_delete_assign_base(const expected_delete_assign_base&) = default;
|
||||
@@ -472,8 +472,8 @@ struct expected_delete_assign_base {
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
|
||||
std::is_copy_assignable<E>, std::is_move_assignable<E>>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
|
||||
std::is_copy_assignable<E>, std::is_move_assignable<E>>
|
||||
struct expected_delete_assign_base<T, E, true, false> {
|
||||
expected_delete_assign_base() = default;
|
||||
expected_delete_assign_base(const expected_delete_assign_base&) = default;
|
||||
@@ -483,8 +483,8 @@ struct expected_delete_assign_base<T, E, true, false> {
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
|
||||
std::is_copy_assignable<E>, std::is_move_assignable<E>>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
|
||||
std::is_copy_assignable<E>, std::is_move_assignable<E>>
|
||||
struct expected_delete_assign_base<T, E, false, true> {
|
||||
expected_delete_assign_base() = default;
|
||||
expected_delete_assign_base(const expected_delete_assign_base&) = default;
|
||||
@@ -494,8 +494,8 @@ struct expected_delete_assign_base<T, E, false, true> {
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
|
||||
std::is_copy_assignable<E>, std::is_move_assignable<E>>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
|
||||
std::is_copy_assignable<E>, std::is_move_assignable<E>>
|
||||
struct expected_delete_assign_base<T, E, false, false> {
|
||||
expected_delete_assign_base() = default;
|
||||
expected_delete_assign_base(const expected_delete_assign_base&) = default;
|
||||
|
||||
+2
-2
@@ -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 <typename T>
|
||||
|
||||
@@ -205,7 +205,7 @@ public:
|
||||
using callback_type = Callback;
|
||||
|
||||
template <typename C>
|
||||
requires constructible_from<Callback, C>
|
||||
requires constructible_from<Callback, C>
|
||||
explicit stop_callback(const stop_token& st,
|
||||
C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
|
||||
: m_stop_state(st.m_stop_state) {
|
||||
@@ -214,7 +214,7 @@ public:
|
||||
}
|
||||
}
|
||||
template <typename C>
|
||||
requires constructible_from<Callback, C>
|
||||
requires constructible_from<Callback, C>
|
||||
explicit stop_callback(stop_token&& st,
|
||||
C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
|
||||
: m_stop_state(std::move(st.m_stop_state)) {
|
||||
|
||||
@@ -31,6 +31,8 @@ std::string_view GetGraphicsAPIName(GraphicsAPI api) {
|
||||
return "Software";
|
||||
case GraphicsAPI::OpenGL:
|
||||
return "OpenGL";
|
||||
case GraphicsAPI::Vulkan:
|
||||
return "Vulkan";
|
||||
default:
|
||||
return "Invalid";
|
||||
}
|
||||
@@ -72,6 +74,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 +164,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);
|
||||
|
||||
+15
-8
@@ -17,10 +17,10 @@
|
||||
|
||||
namespace Settings {
|
||||
|
||||
constexpr u32 GraphicsAPICount = 2;
|
||||
enum class GraphicsAPI {
|
||||
Software = 0,
|
||||
OpenGL = 1,
|
||||
Vulkan = 2,
|
||||
};
|
||||
|
||||
enum class InitClock : u32 {
|
||||
@@ -178,7 +178,8 @@ public:
|
||||
* @param default_val Intial value of the setting, and default value of the setting
|
||||
* @param name Label for the setting
|
||||
*/
|
||||
explicit Setting(const Type& default_val, const std::string& name) requires(!ranged)
|
||||
explicit Setting(const Type& default_val, const std::string& name)
|
||||
requires(!ranged)
|
||||
: value{default_val}, default_value{default_val}, label{name} {}
|
||||
virtual ~Setting() = default;
|
||||
|
||||
@@ -191,7 +192,8 @@ public:
|
||||
* @param name Label for the setting
|
||||
*/
|
||||
explicit Setting(const Type& default_val, const Type& min_val, const Type& max_val,
|
||||
const std::string& name) requires(ranged)
|
||||
const std::string& name)
|
||||
requires(ranged)
|
||||
: value{default_val},
|
||||
default_value{default_val}, maximum{max_val}, minimum{min_val}, label{name} {}
|
||||
|
||||
@@ -279,7 +281,8 @@ public:
|
||||
* @param default_val Intial value of the setting, and default value of the setting
|
||||
* @param name Label for the setting
|
||||
*/
|
||||
explicit SwitchableSetting(const Type& default_val, const std::string& name) requires(!ranged)
|
||||
explicit SwitchableSetting(const Type& default_val, const std::string& name)
|
||||
requires(!ranged)
|
||||
: Setting<Type>{default_val, name} {}
|
||||
virtual ~SwitchableSetting() = default;
|
||||
|
||||
@@ -292,7 +295,8 @@ public:
|
||||
* @param name Label for the setting
|
||||
*/
|
||||
explicit SwitchableSetting(const Type& default_val, const Type& min_val, const Type& max_val,
|
||||
const std::string& name) requires(ranged)
|
||||
const std::string& name)
|
||||
requires(ranged)
|
||||
: Setting<Type, true>{default_val, min_val, max_val, name} {}
|
||||
|
||||
/**
|
||||
@@ -430,12 +434,15 @@ struct Values {
|
||||
Setting<bool> allow_plugin_loader{true, "allow_plugin_loader"};
|
||||
|
||||
// Renderer
|
||||
SwitchableSetting<GraphicsAPI, true> graphics_api{
|
||||
GraphicsAPI::OpenGL, GraphicsAPI::Software, static_cast<GraphicsAPI>(GraphicsAPICount - 1),
|
||||
"graphics_api"};
|
||||
SwitchableSetting<GraphicsAPI, true> graphics_api{GraphicsAPI::OpenGL, GraphicsAPI::Software,
|
||||
GraphicsAPI::Vulkan, "graphics_api"};
|
||||
SwitchableSetting<u32> physical_device{0, "physical_device"};
|
||||
Setting<bool> use_gles{false, "use_gles"};
|
||||
Setting<bool> renderer_debug{false, "renderer_debug"};
|
||||
Setting<bool> dump_command_buffers{false, "dump_command_buffers"};
|
||||
SwitchableSetting<bool> spirv_shader_gen{true, "spirv_shader_gen"};
|
||||
SwitchableSetting<bool> async_shader_compilation{false, "async_shader_compilation"};
|
||||
SwitchableSetting<bool> async_presentation{true, "async_presentation"};
|
||||
SwitchableSetting<bool> use_hw_shader{true, "use_hw_shader"};
|
||||
SwitchableSetting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"};
|
||||
SwitchableSetting<bool> shaders_accurate_mul{true, "shaders_accurate_mul"};
|
||||
|
||||
@@ -396,9 +396,7 @@ public:
|
||||
// _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all
|
||||
// component names (x<->r) and permutations (xy<->yx)
|
||||
#define _DEFINE_SWIZZLER2(a, b, name) \
|
||||
[[nodiscard]] constexpr Vec2<T> name() const { \
|
||||
return Vec2<T>(a, b); \
|
||||
}
|
||||
[[nodiscard]] constexpr Vec2<T> name() const { return Vec2<T>(a, b); }
|
||||
#define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \
|
||||
_DEFINE_SWIZZLER2(a, b, a##b); \
|
||||
_DEFINE_SWIZZLER2(a, b, a2##b2); \
|
||||
@@ -598,9 +596,7 @@ public:
|
||||
// DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and
|
||||
// permutations (xy<->yx)
|
||||
#define _DEFINE_SWIZZLER2(a, b, name) \
|
||||
[[nodiscard]] constexpr Vec2<T> name() const { \
|
||||
return Vec2<T>(a, b); \
|
||||
}
|
||||
[[nodiscard]] constexpr Vec2<T> name() const { return Vec2<T>(a, b); }
|
||||
#define DEFINE_SWIZZLER2_COMP1(a, a2) \
|
||||
_DEFINE_SWIZZLER2(a, a, a##a); \
|
||||
_DEFINE_SWIZZLER2(a, a, a2##a2)
|
||||
@@ -625,9 +621,7 @@ public:
|
||||
#undef _DEFINE_SWIZZLER2
|
||||
|
||||
#define _DEFINE_SWIZZLER3(a, b, c, name) \
|
||||
[[nodiscard]] constexpr Vec3<T> name() const { \
|
||||
return Vec3<T>(a, b, c); \
|
||||
}
|
||||
[[nodiscard]] constexpr Vec3<T> name() const { return Vec3<T>(a, b, c); }
|
||||
#define DEFINE_SWIZZLER3_COMP1(a, a2) \
|
||||
_DEFINE_SWIZZLER3(a, a, a, a##a##a); \
|
||||
_DEFINE_SWIZZLER3(a, a, a, a2##a2##a2)
|
||||
@@ -690,8 +684,8 @@ template <typename T>
|
||||
|
||||
// linear interpolation via float: 0.0=begin, 1.0=end
|
||||
template <typename X>
|
||||
[[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{})
|
||||
Lerp(const X& begin, const X& end, const float t) {
|
||||
[[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end,
|
||||
const float t) {
|
||||
return begin * (1.f - t) + end * t;
|
||||
}
|
||||
|
||||
|
||||
@@ -480,7 +480,6 @@ create_target_directory_groups(citra_core)
|
||||
target_link_libraries(citra_core PUBLIC citra_common PRIVATE audio_core network video_core)
|
||||
target_link_libraries(citra_core PRIVATE Boost::boost Boost::serialization Boost::iostreams httplib)
|
||||
target_link_libraries(citra_core PUBLIC dds-ktx PRIVATE cryptopp fmt::fmt lodepng open_source_archives)
|
||||
set_target_properties(citra_core PROPERTIES INTERPROCEDURAL_OPTIMIZATION ${ENABLE_LTO})
|
||||
|
||||
if (ENABLE_WEB_SERVICE)
|
||||
target_link_libraries(citra_core PRIVATE web_service)
|
||||
|
||||
@@ -35,8 +35,7 @@ public:
|
||||
const auto r = GetCpuRegister(i);
|
||||
ar << r;
|
||||
}
|
||||
std::size_t fpu_reg_count = file_version == 0 ? 16 : 64;
|
||||
for (std::size_t i = 0; i < fpu_reg_count; i++) {
|
||||
for (std::size_t i = 0; i < 64; i++) {
|
||||
const auto r = GetFpuRegister(i);
|
||||
ar << r;
|
||||
}
|
||||
@@ -55,8 +54,7 @@ public:
|
||||
ar >> r;
|
||||
SetCpuRegister(i, r);
|
||||
}
|
||||
std::size_t fpu_reg_count = file_version == 0 ? 16 : 64;
|
||||
for (std::size_t i = 0; i < fpu_reg_count; i++) {
|
||||
for (std::size_t i = 0; i < 64; i++) {
|
||||
ar >> r;
|
||||
SetFpuRegister(i, r);
|
||||
}
|
||||
@@ -268,8 +266,7 @@ private:
|
||||
ar << pc;
|
||||
const auto cpsr = GetCPSR();
|
||||
ar << cpsr;
|
||||
int vfp_reg_count = file_version == 0 ? 32 : 64;
|
||||
for (int i = 0; i < vfp_reg_count; i++) {
|
||||
for (int i = 0; i < 64; i++) {
|
||||
const auto r = GetVFPReg(i);
|
||||
ar << r;
|
||||
}
|
||||
@@ -316,8 +313,7 @@ private:
|
||||
SetPC(r);
|
||||
ar >> r;
|
||||
SetCPSR(r);
|
||||
int vfp_reg_count = file_version == 0 ? 32 : 64;
|
||||
for (int i = 0; i < vfp_reg_count; i++) {
|
||||
for (int i = 0; i < 64; i++) {
|
||||
ar >> r;
|
||||
SetVFPReg(i, r);
|
||||
}
|
||||
|
||||
@@ -365,7 +365,9 @@ void ARM_Dynarmic::ServeBreak() {
|
||||
std::unique_ptr<Dynarmic::A32::Jit> ARM_Dynarmic::MakeJit() {
|
||||
Dynarmic::A32::UserConfig config;
|
||||
config.callbacks = cb.get();
|
||||
config.page_table = ¤t_page_table->GetPointerArray();
|
||||
if (current_page_table) {
|
||||
config.page_table = ¤t_page_table->GetPointerArray();
|
||||
}
|
||||
config.coprocessors[15] = std::make_shared<DynarmicCP15>(cp15_state);
|
||||
config.define_unpredictable_behaviour = true;
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(add)(unsigned int inst, int index) {
|
||||
|
||||
return inst_base;
|
||||
}
|
||||
static ARM_INST_PTR INTERPRETER_TRANSLATE(and)(unsigned int inst, int index) {
|
||||
static ARM_INST_PTR INTERPRETER_TRANSLATE (and)(unsigned int inst, int index) {
|
||||
arm_inst* inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(and_inst));
|
||||
and_inst* inst_cream = (and_inst*)inst_base->component;
|
||||
|
||||
|
||||
@@ -197,9 +197,9 @@ GatewayCheat::CheatLine::CheatLine(const std::string& line) {
|
||||
if (type_temp == "D" || type_temp == "d")
|
||||
sub_type_temp = line.substr(1, 1);
|
||||
type = static_cast<CheatType>(std::stoi(type_temp + sub_type_temp, 0, 16));
|
||||
first = std::stoul(line.substr(0, 8), 0, 16);
|
||||
first = static_cast<u32>(std::stoul(line.substr(0, 8), 0, 16));
|
||||
address = first & 0x0FFFFFFF;
|
||||
value = std::stoul(line.substr(9, 8), 0, 16);
|
||||
value = static_cast<u32>(std::stoul(line.substr(9, 8), 0, 16));
|
||||
cheat_line = line;
|
||||
} catch (const std::logic_error&) {
|
||||
type = CheatType::Null;
|
||||
|
||||
+1
-3
@@ -717,9 +717,7 @@ void System::serialize(Archive& ar, const unsigned int file_version) {
|
||||
ar&* memory.get();
|
||||
ar&* kernel.get();
|
||||
VideoCore::serialize(ar, file_version);
|
||||
if (file_version >= 1) {
|
||||
ar& movie;
|
||||
}
|
||||
ar& movie;
|
||||
|
||||
// This needs to be set from somewhere - might as well be here!
|
||||
if (Archive::is_loading::value) {
|
||||
|
||||
@@ -513,7 +513,7 @@ bool FFmpegAudioStream::Init(FFmpegMuxer& muxer) {
|
||||
}
|
||||
|
||||
if (codec_context->frame_size) {
|
||||
frame_size = static_cast<u64>(codec_context->frame_size);
|
||||
frame_size = codec_context->frame_size;
|
||||
} else { // variable frame size support
|
||||
frame_size = std::tuple_size<AudioCore::StereoFrame16>::value;
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ Loader::ResultStatus FileSys::Plugin3GXLoader::Map(
|
||||
const u32 block_size = mem_region_sizes[header.infos.flags.memory_region_size.Value()];
|
||||
const u32 exe_size = (sizeof(PluginHeader) + text_section.size() + rodata_section.size() +
|
||||
data_section.size() + header.executable.bss_size + 0x1000) &
|
||||
~0xFFF;
|
||||
~0xFFFu;
|
||||
|
||||
// Allocate the framebuffer block so that is in the highest FCRAM position possible
|
||||
auto offset_fb =
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
#include "core/3ds.h"
|
||||
#include "core/frontend/framebuffer_layout.h"
|
||||
|
||||
namespace Common {
|
||||
class DynamicLibrary;
|
||||
}
|
||||
|
||||
namespace Frontend {
|
||||
|
||||
/// Information for the Graphics Backends signifying what type of screen pointer is in
|
||||
@@ -82,6 +86,11 @@ public:
|
||||
/// Releases (dunno if this is the "right" word) the context from the caller thread
|
||||
virtual void DoneCurrent(){};
|
||||
|
||||
/// Gets the GPU driver library (used by Android only)
|
||||
virtual std::shared_ptr<Common::DynamicLibrary> GetDriverLibrary() {
|
||||
return {};
|
||||
}
|
||||
|
||||
class Scoped {
|
||||
public:
|
||||
explicit Scoped(GraphicsContext& context_) : context(context_) {
|
||||
|
||||
@@ -509,7 +509,8 @@ void SendReply(const char* reply) {
|
||||
u8* ptr = command_buffer;
|
||||
u32 left = command_length + 4;
|
||||
while (left > 0) {
|
||||
int sent_size = send(gdbserver_socket, reinterpret_cast<char*>(ptr), left, 0);
|
||||
s32 sent_size =
|
||||
static_cast<s32>(send(gdbserver_socket, reinterpret_cast<char*>(ptr), left, 0));
|
||||
if (sent_size < 0) {
|
||||
LOG_ERROR(Debug_GDBStub, "gdb: send failed");
|
||||
return Shutdown();
|
||||
|
||||
@@ -90,7 +90,7 @@ MiiResult MiiSelector::GetStandardMiiResult() {
|
||||
// the LLEd Mii picker of version system version 11.8.0 to a file and then matching the values
|
||||
// to the members of the MiiResult struct
|
||||
Mii::MiiData mii_data;
|
||||
mii_data.magic = 0x03;
|
||||
mii_data.version = 0x03;
|
||||
mii_data.mii_options.raw = 0x00;
|
||||
mii_data.mii_pos.raw = 0x10;
|
||||
mii_data.console_identity.raw = 0x30;
|
||||
@@ -114,7 +114,7 @@ MiiResult MiiSelector::GetStandardMiiResult() {
|
||||
mii_data.beard_details.raw = 0x0029;
|
||||
mii_data.glasses_details.raw = 0x0052;
|
||||
mii_data.mole_details.raw = 0x4850;
|
||||
mii_data.author_name = {'f', 'l', 'T', 'o', 'b', 'i', 0x0, 0x0, 0x0, 0x0};
|
||||
mii_data.author_name = {u'f', u'l', u'T', u'o', u'b', u'i'};
|
||||
|
||||
MiiResult result;
|
||||
result.return_code = 0x0;
|
||||
|
||||
@@ -79,29 +79,13 @@ private:
|
||||
void WakeUp(ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
|
||||
std::shared_ptr<WaitObject> object) override;
|
||||
|
||||
class DummyCallback : public WakeupCallback {
|
||||
public:
|
||||
void WakeUp(ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
|
||||
std::shared_ptr<WaitObject> object) override {}
|
||||
};
|
||||
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version) {
|
||||
ar& boost::serialization::base_object<Object>(*this);
|
||||
if (file_version == 1) {
|
||||
// This rigmarole is needed because in past versions, AddressArbiter inherited
|
||||
// WakeupCallback But it turns out this breaks shared_from_this, so we split it out.
|
||||
// Using a dummy class to deserialize a base_object allows compatibility to be
|
||||
// maintained.
|
||||
DummyCallback x;
|
||||
ar& boost::serialization::base_object<WakeupCallback>(x);
|
||||
}
|
||||
ar& name;
|
||||
ar& waiting_threads;
|
||||
if (file_version > 1) {
|
||||
ar& timeout_callback;
|
||||
}
|
||||
ar& timeout_callback;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+41
-36
@@ -11,10 +11,12 @@
|
||||
|
||||
namespace Mii {
|
||||
|
||||
using Nickname = std::array<char16_t, 10>;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
// Reference: https://github.com/devkitPro/libctru/blob/master/libctru/include/3ds/mii.h
|
||||
struct MiiData {
|
||||
u8 magic; ///< Always 3?
|
||||
u8 version; ///< Always 3?
|
||||
|
||||
/// Mii options
|
||||
union {
|
||||
@@ -52,23 +54,23 @@ struct MiiData {
|
||||
union {
|
||||
u16_be raw;
|
||||
|
||||
BitField<0, 1, u16> sex; ///< Sex of Mii (False=Male, True=Female)
|
||||
BitField<1, 4, u16> bday_month; ///< Month of Mii's birthday
|
||||
BitField<5, 5, u16> bday_day; ///< Day of Mii's birthday
|
||||
BitField<10, 4, u16> shirt_color; ///< Color of Mii's shirt
|
||||
BitField<14, 1, u16> favorite; ///< Whether the Mii is one of your 10 favorite Mii's
|
||||
BitField<0, 1, u16> gender; ///< Gender of Mii (0=Male, 1=Female)
|
||||
BitField<1, 4, u16> bday_month; ///< Month of Mii's birthday
|
||||
BitField<5, 5, u16> bday_day; ///< Day of Mii's birthday
|
||||
BitField<10, 4, u16> favorite_color; ///< Color of Mii's shirt
|
||||
BitField<14, 1, u16> favorite; ///< Whether the Mii is one of your 10 favorite Mii's
|
||||
} mii_details;
|
||||
|
||||
std::array<u16_le, 10> mii_name; ///< Name of Mii (Encoded using UTF16)
|
||||
u8 height; ///< How tall the Mii is
|
||||
u8 width; ///< How wide the Mii is
|
||||
Nickname mii_name; ///< Name of Mii (Encoded using UTF16)
|
||||
u8 height; ///< How tall the Mii is
|
||||
u8 width; ///< How wide the Mii is
|
||||
|
||||
/// Face style
|
||||
union {
|
||||
u8 raw;
|
||||
|
||||
BitField<0, 1, u8> disable_sharing; ///< Whether or not Sharing of the Mii is allowed
|
||||
BitField<1, 4, u8> shape; ///< Face shape
|
||||
BitField<1, 4, u8> type; ///< Face type
|
||||
BitField<5, 3, u8> skin_color; ///< Color of skin
|
||||
} face_style;
|
||||
|
||||
@@ -94,13 +96,13 @@ struct MiiData {
|
||||
union {
|
||||
u32_be raw;
|
||||
|
||||
BitField<0, 6, u32> style;
|
||||
BitField<0, 6, u32> type;
|
||||
BitField<6, 3, u32> color;
|
||||
BitField<9, 4, u32> scale;
|
||||
BitField<13, 3, u32> y_scale;
|
||||
BitField<16, 5, u32> rotation;
|
||||
BitField<21, 4, u32> x_spacing;
|
||||
BitField<25, 5, u32> y_position;
|
||||
BitField<13, 3, u32> aspect;
|
||||
BitField<16, 5, u32> rotate;
|
||||
BitField<21, 4, u32> x;
|
||||
BitField<25, 5, u32> y;
|
||||
} eye_details;
|
||||
|
||||
/// Eyebrow details
|
||||
@@ -110,72 +112,70 @@ struct MiiData {
|
||||
BitField<0, 5, u32> style;
|
||||
BitField<5, 3, u32> color;
|
||||
BitField<8, 4, u32> scale;
|
||||
BitField<12, 3, u32> y_scale;
|
||||
BitField<15, 1, u32> pad;
|
||||
BitField<16, 5, u32> rotation;
|
||||
BitField<21, 4, u32> x_spacing;
|
||||
BitField<25, 5, u32> y_position;
|
||||
BitField<12, 3, u32> aspect;
|
||||
BitField<16, 5, u32> rotate;
|
||||
BitField<21, 4, u32> x;
|
||||
BitField<25, 5, u32> y;
|
||||
} eyebrow_details;
|
||||
|
||||
/// Nose details
|
||||
union {
|
||||
u16_be raw;
|
||||
|
||||
BitField<0, 5, u16> style;
|
||||
BitField<0, 5, u16> type;
|
||||
BitField<5, 4, u16> scale;
|
||||
BitField<9, 5, u16> y_position;
|
||||
BitField<9, 5, u16> y;
|
||||
} nose_details;
|
||||
|
||||
/// Mouth details
|
||||
union {
|
||||
u16_be raw;
|
||||
|
||||
BitField<0, 6, u16> style;
|
||||
BitField<0, 6, u16> type;
|
||||
BitField<6, 3, u16> color;
|
||||
BitField<9, 4, u16> scale;
|
||||
BitField<13, 3, u16> y_scale;
|
||||
BitField<13, 3, u16> aspect;
|
||||
} mouth_details;
|
||||
|
||||
/// Mustache details
|
||||
union {
|
||||
u16_be raw;
|
||||
|
||||
BitField<0, 5, u16> mouth_yposition;
|
||||
BitField<5, 3, u16> mustach_style;
|
||||
BitField<8, 2, u16> pad;
|
||||
BitField<0, 5, u16> mouth_y;
|
||||
BitField<5, 3, u16> mustache_type;
|
||||
} mustache_details;
|
||||
|
||||
/// Beard details
|
||||
union {
|
||||
u16_be raw;
|
||||
|
||||
BitField<0, 3, u16> style;
|
||||
BitField<0, 3, u16> type;
|
||||
BitField<3, 3, u16> color;
|
||||
BitField<6, 4, u16> scale;
|
||||
BitField<10, 5, u16> y_pos;
|
||||
BitField<10, 5, u16> y;
|
||||
} beard_details;
|
||||
|
||||
/// Glasses details
|
||||
union {
|
||||
u16_be raw;
|
||||
|
||||
BitField<0, 4, u16> style;
|
||||
BitField<0, 4, u16> type;
|
||||
BitField<4, 3, u16> color;
|
||||
BitField<7, 4, u16> scale;
|
||||
BitField<11, 5, u16> y_pos;
|
||||
BitField<11, 5, u16> y;
|
||||
} glasses_details;
|
||||
|
||||
/// Mole details
|
||||
union {
|
||||
u16_be raw;
|
||||
|
||||
BitField<0, 1, u16> enable;
|
||||
BitField<1, 5, u16> scale;
|
||||
BitField<6, 5, u16> x_pos;
|
||||
BitField<11, 5, u16> y_pos;
|
||||
BitField<0, 1, u16> type;
|
||||
BitField<1, 4, u16> scale;
|
||||
BitField<5, 5, u16> x;
|
||||
BitField<10, 5, u16> y;
|
||||
} mole_details;
|
||||
|
||||
std::array<u16_le, 10> author_name; ///< Name of Mii's author (Encoded using UTF16)
|
||||
Nickname author_name; ///< Name of Mii's author (Encoded using UTF16)
|
||||
private:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
@@ -209,6 +209,11 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
void SetMiiData(MiiData data) {
|
||||
mii_data = data;
|
||||
FixChecksum();
|
||||
}
|
||||
|
||||
operator MiiData() const {
|
||||
return mii_data;
|
||||
}
|
||||
|
||||
@@ -589,7 +589,7 @@ std::string GetTitleMetadataPath(Service::FS::MediaType media_type, u64 tid, boo
|
||||
Common::SplitPath(entry.virtualName, nullptr, &filename_filename, &filename_extension);
|
||||
|
||||
if (filename_extension == ".tmd") {
|
||||
const u32 id = std::stoul(filename_filename, nullptr, 16);
|
||||
const u32 id = static_cast<u32>(std::stoul(filename_filename, nullptr, 16));
|
||||
base_id = std::min(base_id, id);
|
||||
update_id = std::max(update_id, id);
|
||||
}
|
||||
|
||||
@@ -188,9 +188,7 @@ private:
|
||||
void serialize(Archive& ar, const unsigned int file_version) {
|
||||
ar& next_title_id;
|
||||
ar& next_media_type;
|
||||
if (file_version > 0) {
|
||||
ar& flags;
|
||||
}
|
||||
ar& flags;
|
||||
ar& current_title_id;
|
||||
ar& current_media_type;
|
||||
}
|
||||
@@ -517,25 +515,23 @@ private:
|
||||
void serialize(Archive& ar, const unsigned int file_version) {
|
||||
ar& next_parameter;
|
||||
ar& app_jump_parameters;
|
||||
if (file_version > 0) {
|
||||
ar& delayed_parameter;
|
||||
ar& app_start_parameters;
|
||||
ar& deliver_arg;
|
||||
ar& capture_info;
|
||||
ar& capture_buffer_info;
|
||||
ar& active_slot;
|
||||
ar& last_library_launcher_slot;
|
||||
ar& last_prepared_library_applet;
|
||||
ar& last_system_launcher_slot;
|
||||
ar& last_jump_to_home_slot;
|
||||
ar& ordered_to_close_sys_applet;
|
||||
ar& ordered_to_close_application;
|
||||
ar& application_cancelled;
|
||||
ar& application_close_target;
|
||||
ar& new_3ds_mode_blocked;
|
||||
ar& lock;
|
||||
ar& capture_info;
|
||||
}
|
||||
ar& delayed_parameter;
|
||||
ar& app_start_parameters;
|
||||
ar& deliver_arg;
|
||||
ar& capture_info;
|
||||
ar& capture_buffer_info;
|
||||
ar& active_slot;
|
||||
ar& last_library_launcher_slot;
|
||||
ar& last_prepared_library_applet;
|
||||
ar& last_system_launcher_slot;
|
||||
ar& last_jump_to_home_slot;
|
||||
ar& ordered_to_close_sys_applet;
|
||||
ar& ordered_to_close_application;
|
||||
ar& application_cancelled;
|
||||
ar& application_close_target;
|
||||
ar& new_3ds_mode_blocked;
|
||||
ar& lock;
|
||||
ar& capture_info;
|
||||
ar& applet_slots;
|
||||
ar& library_applet_closing_command;
|
||||
|
||||
|
||||
@@ -48,9 +48,7 @@ void Module::serialize(Archive& ar, const unsigned int file_version) {
|
||||
ar& cpu_percent;
|
||||
ar& screen_capture_post_permission;
|
||||
ar& applet_manager;
|
||||
if (file_version > 0) {
|
||||
ar& wireless_reboot_info;
|
||||
}
|
||||
ar& wireless_reboot_info;
|
||||
}
|
||||
|
||||
SERIALIZE_IMPL(Module)
|
||||
|
||||
@@ -30,11 +30,7 @@ void Module::serialize(Archive& ar, const unsigned int file_version) {
|
||||
ar& cameras;
|
||||
ar& ports;
|
||||
ar& is_camera_reload_pending;
|
||||
if (file_version > 0) {
|
||||
ar& initialized;
|
||||
} else {
|
||||
initialized = true;
|
||||
}
|
||||
ar& initialized;
|
||||
if (Archive::is_loading::value && initialized) {
|
||||
for (int i = 0; i < NumCameras; i++) {
|
||||
LoadCameraImplementation(cameras[i], i);
|
||||
|
||||
@@ -681,11 +681,6 @@ private:
|
||||
private:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version) {
|
||||
// For compatibility: put a nullptr here
|
||||
if (file_version == 0) {
|
||||
std::unique_ptr<Camera::CameraInterface> x;
|
||||
ar& x;
|
||||
}
|
||||
ar& contexts;
|
||||
ar& current_context;
|
||||
ar& frame_rate;
|
||||
|
||||
@@ -124,8 +124,9 @@ void Module::Interface::Open(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_CECD,
|
||||
"called, ncch_program_id={:#010x}, path_type={:#04x}, path={}, "
|
||||
"open_mode: raw={:#x}, unknown={}, read={}, write={}, create={}, check={}",
|
||||
ncch_program_id, path_type, path.AsString(), open_mode.raw, open_mode.unknown,
|
||||
open_mode.read, open_mode.write, open_mode.create, open_mode.check);
|
||||
ncch_program_id, path_type, path.AsString(), open_mode.raw, open_mode.unknown.Value(),
|
||||
open_mode.read.Value(), open_mode.write.Value(), open_mode.create.Value(),
|
||||
open_mode.check.Value());
|
||||
}
|
||||
|
||||
void Module::Interface::Read(Kernel::HLERequestContext& ctx) {
|
||||
@@ -139,9 +140,9 @@ void Module::Interface::Read(Kernel::HLERequestContext& ctx) {
|
||||
"path={}, open_mode: raw={:#x}, unknown={}, read={}, write={}, create={}, check={}",
|
||||
session_data->ncch_program_id, session_data->data_path_type,
|
||||
session_data->path.AsString(), session_data->open_mode.raw,
|
||||
session_data->open_mode.unknown, session_data->open_mode.read,
|
||||
session_data->open_mode.write, session_data->open_mode.create,
|
||||
session_data->open_mode.check);
|
||||
session_data->open_mode.unknown.Value(), session_data->open_mode.read.Value(),
|
||||
session_data->open_mode.write.Value(), session_data->open_mode.create.Value(),
|
||||
session_data->open_mode.check.Value());
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
|
||||
switch (session_data->data_path_type) {
|
||||
@@ -344,9 +345,9 @@ void Module::Interface::Write(Kernel::HLERequestContext& ctx) {
|
||||
"path={}, open_mode: raw={:#x}, unknown={}, read={}, write={}, create={}, check={}",
|
||||
session_data->ncch_program_id, session_data->data_path_type,
|
||||
session_data->path.AsString(), session_data->open_mode.raw,
|
||||
session_data->open_mode.unknown, session_data->open_mode.read,
|
||||
session_data->open_mode.write, session_data->open_mode.create,
|
||||
session_data->open_mode.check);
|
||||
session_data->open_mode.unknown.Value(), session_data->open_mode.read.Value(),
|
||||
session_data->open_mode.write.Value(), session_data->open_mode.create.Value(),
|
||||
session_data->open_mode.check.Value());
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
||||
switch (session_data->data_path_type) {
|
||||
@@ -778,8 +779,8 @@ void Module::Interface::OpenAndWrite(Kernel::HLERequestContext& ctx) {
|
||||
"called, ncch_program_id={:#010x}, path_type={:#04x}, path={}, buffer_size={:#x} "
|
||||
"open_mode: raw={:#x}, unknown={}, read={}, write={}, create={}, check={}",
|
||||
ncch_program_id, path_type, path.AsString(), buffer_size, open_mode.raw,
|
||||
open_mode.unknown, open_mode.read, open_mode.write, open_mode.create,
|
||||
open_mode.check);
|
||||
open_mode.unknown.Value(), open_mode.read.Value(), open_mode.write.Value(),
|
||||
open_mode.create.Value(), open_mode.check.Value());
|
||||
}
|
||||
|
||||
void Module::Interface::OpenAndRead(Kernel::HLERequestContext& ctx) {
|
||||
@@ -831,8 +832,8 @@ void Module::Interface::OpenAndRead(Kernel::HLERequestContext& ctx) {
|
||||
"called, ncch_program_id={:#010x}, path_type={:#04x}, path={}, buffer_size={:#x} "
|
||||
"open_mode: raw={:#x}, unknown={}, read={}, write={}, create={}, check={}",
|
||||
ncch_program_id, path_type, path.AsString(), buffer_size, open_mode.raw,
|
||||
open_mode.unknown, open_mode.read, open_mode.write, open_mode.create,
|
||||
open_mode.check);
|
||||
open_mode.unknown.Value(), open_mode.read.Value(), open_mode.write.Value(),
|
||||
open_mode.create.Value(), open_mode.check.Value());
|
||||
}
|
||||
|
||||
std::string Module::EncodeBase64(std::span<const u8> in) const {
|
||||
|
||||
@@ -309,7 +309,7 @@ void DSP_DSP::ForceHeadphoneOut(Kernel::HLERequestContext& ctx) {
|
||||
// that's waiting for an interrupt event. Immediately after this interrupt event, userland
|
||||
// normally updates the state in the next region and increments the relevant frame counter by two.
|
||||
void DSP_DSP::SignalInterrupt(InterruptType type, DspPipe pipe) {
|
||||
LOG_DEBUG(Service_DSP, "called, type={}, pipe={}", type, pipe);
|
||||
LOG_TRACE(Service_DSP, "called, type={}, pipe={}", type, pipe);
|
||||
const auto& event = GetInterruptEvent(type, pipe);
|
||||
if (event)
|
||||
event->Signal();
|
||||
|
||||
@@ -336,31 +336,31 @@ void GSP_GPU::SetBufferSwap(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
void GSP_GPU::FlushDataCache(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
u32 address = rp.Pop<u32>();
|
||||
u32 size = rp.Pop<u32>();
|
||||
auto process = rp.PopObject<Kernel::Process>();
|
||||
[[maybe_unused]] u32 address = rp.Pop<u32>();
|
||||
[[maybe_unused]] u32 size = rp.Pop<u32>();
|
||||
[[maybe_unused]] auto process = rp.PopObject<Kernel::Process>();
|
||||
|
||||
// TODO(purpasmart96): Verify return header on HW
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
LOG_DEBUG(Service_GSP, "(STUBBED) called address=0x{:08X}, size=0x{:08X}, process={}", address,
|
||||
LOG_TRACE(Service_GSP, "(STUBBED) called address=0x{:08X}, size=0x{:08X}, process={}", address,
|
||||
size, process->process_id);
|
||||
}
|
||||
|
||||
void GSP_GPU::InvalidateDataCache(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
u32 address = rp.Pop<u32>();
|
||||
u32 size = rp.Pop<u32>();
|
||||
auto process = rp.PopObject<Kernel::Process>();
|
||||
[[maybe_unused]] u32 address = rp.Pop<u32>();
|
||||
[[maybe_unused]] u32 size = rp.Pop<u32>();
|
||||
[[maybe_unused]] auto process = rp.PopObject<Kernel::Process>();
|
||||
|
||||
// TODO(purpasmart96): Verify return header on HW
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
LOG_DEBUG(Service_GSP, "(STUBBED) called address=0x{:08X}, size=0x{:08X}, process={}", address,
|
||||
LOG_TRACE(Service_GSP, "(STUBBED) called address=0x{:08X}, size=0x{:08X}, process={}", address,
|
||||
size, process->process_id);
|
||||
}
|
||||
|
||||
@@ -840,14 +840,14 @@ void GSP_GPU::ReleaseRight(Kernel::HLERequestContext& ctx) {
|
||||
void GSP_GPU::StoreDataCache(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
|
||||
u32 address = rp.Pop<u32>();
|
||||
u32 size = rp.Pop<u32>();
|
||||
[[maybe_unused]] u32 address = rp.Pop<u32>();
|
||||
[[maybe_unused]] u32 size = rp.Pop<u32>();
|
||||
auto process = rp.PopObject<Kernel::Process>();
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
LOG_DEBUG(Service_GSP, "(STUBBED) called address=0x{:08X}, size=0x{:08X}, process={}", address,
|
||||
LOG_TRACE(Service_GSP, "(STUBBED) called address=0x{:08X}, size=0x{:08X}, process={}", address,
|
||||
size, process->process_id);
|
||||
}
|
||||
|
||||
|
||||
@@ -46,9 +46,7 @@ void Module::serialize(Archive& ar, const unsigned int file_version) {
|
||||
if (Archive::is_loading::value) {
|
||||
LoadInputDevices();
|
||||
}
|
||||
if (file_version >= 1) {
|
||||
ar& state.hex;
|
||||
}
|
||||
ar& state.hex;
|
||||
// Update events are set in the constructor
|
||||
// Devices are set from the implementation (and are stateless afaik)
|
||||
}
|
||||
|
||||
@@ -400,18 +400,16 @@ private:
|
||||
ar& clamp;
|
||||
// mic interface set in constructor
|
||||
ar& state;
|
||||
if (file_version > 0) {
|
||||
// Maintain the internal mic state
|
||||
ar& encoding;
|
||||
bool is_sampling = mic && mic->IsSampling();
|
||||
ar& is_sampling;
|
||||
if (Archive::is_loading::value) {
|
||||
if (is_sampling) {
|
||||
CreateMic();
|
||||
StartSampling();
|
||||
} else if (mic) {
|
||||
mic->StopSampling();
|
||||
}
|
||||
// Maintain the internal mic state
|
||||
ar& encoding;
|
||||
bool is_sampling = mic && mic->IsSampling();
|
||||
ar& is_sampling;
|
||||
if (Archive::is_loading::value) {
|
||||
if (is_sampling) {
|
||||
CreateMic();
|
||||
StartSampling();
|
||||
} else if (mic) {
|
||||
mic->StopSampling();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,8 +155,8 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(u32* cmd_buf, const Funct
|
||||
|
||||
std::string result =
|
||||
fmt::format("function '{}': port='{}' cmd_buf={{[0]={:#x} (0x{:04X}, {}, {})",
|
||||
function_name, service_name, header.raw, header.command_id,
|
||||
header.normal_params_size, header.translate_params_size);
|
||||
function_name, service_name, header.raw, header.command_id.Value(),
|
||||
header.normal_params_size.Value(), header.translate_params_size.Value());
|
||||
for (int i = 1; i <= num_params; ++i) {
|
||||
result += fmt::format(", [{}]={:#x}", i, cmd_buf[i]);
|
||||
}
|
||||
|
||||
@@ -982,11 +982,13 @@ void SOC_U::SendToOther(Kernel::HLERequestContext& ctx) {
|
||||
CTRSockAddr ctr_dest_addr;
|
||||
std::memcpy(&ctr_dest_addr, dest_addr_buffer.data(), sizeof(ctr_dest_addr));
|
||||
sockaddr dest_addr = CTRSockAddr::ToPlatform(ctr_dest_addr);
|
||||
ret = ::sendto(fd_info->second.socket_fd, reinterpret_cast<const char*>(input_buff.data()),
|
||||
len, flags, &dest_addr, sizeof(dest_addr));
|
||||
ret = static_cast<s32>(::sendto(fd_info->second.socket_fd,
|
||||
reinterpret_cast<const char*>(input_buff.data()), len,
|
||||
flags, &dest_addr, sizeof(dest_addr)));
|
||||
} else {
|
||||
ret = ::sendto(fd_info->second.socket_fd, reinterpret_cast<const char*>(input_buff.data()),
|
||||
len, flags, nullptr, 0);
|
||||
ret = static_cast<s32>(::sendto(fd_info->second.socket_fd,
|
||||
reinterpret_cast<const char*>(input_buff.data()), len,
|
||||
flags, nullptr, 0));
|
||||
}
|
||||
|
||||
const auto send_error = (ret == SOCKET_ERROR_VALUE) ? GET_ERRNO : 0;
|
||||
@@ -1040,11 +1042,13 @@ void SOC_U::SendTo(Kernel::HLERequestContext& ctx) {
|
||||
CTRSockAddr ctr_dest_addr;
|
||||
std::memcpy(&ctr_dest_addr, dest_addr_buff.data(), sizeof(ctr_dest_addr));
|
||||
sockaddr dest_addr = CTRSockAddr::ToPlatform(ctr_dest_addr);
|
||||
ret = ::sendto(fd_info->second.socket_fd, reinterpret_cast<const char*>(input_buff.data()),
|
||||
len, flags, &dest_addr, sizeof(dest_addr));
|
||||
ret = static_cast<s32>(::sendto(fd_info->second.socket_fd,
|
||||
reinterpret_cast<const char*>(input_buff.data()), len,
|
||||
flags, &dest_addr, sizeof(dest_addr)));
|
||||
} else {
|
||||
ret = ::sendto(fd_info->second.socket_fd, reinterpret_cast<const char*>(input_buff.data()),
|
||||
len, flags, nullptr, 0);
|
||||
ret = static_cast<s32>(::sendto(fd_info->second.socket_fd,
|
||||
reinterpret_cast<const char*>(input_buff.data()), len,
|
||||
flags, nullptr, 0));
|
||||
}
|
||||
|
||||
auto send_error = (ret == SOCKET_ERROR_VALUE) ? GET_ERRNO : 0;
|
||||
@@ -1818,7 +1822,11 @@ std::optional<SOC_U::InterfaceInfo> SOC_U::GetDefaultInterfaceInfo() {
|
||||
}
|
||||
|
||||
InterfaceInfo ret;
|
||||
s64 sock_fd = -1;
|
||||
#ifdef _WIN32
|
||||
SOCKET sock_fd = -1;
|
||||
#else
|
||||
int sock_fd = -1;
|
||||
#endif
|
||||
bool interface_found = false;
|
||||
struct sockaddr_in s_in = {.sin_family = AF_INET, .sin_port = htons(53), .sin_addr = {}};
|
||||
s_in.sin_addr.s_addr = inet_addr("8.8.8.8");
|
||||
|
||||
+9
-1
@@ -128,7 +128,7 @@ static void MemoryFill(const Regs::MemoryFillConfig& config) {
|
||||
|
||||
static void DisplayTransfer(const Regs::DisplayTransferConfig& config) {
|
||||
const PAddr src_addr = config.GetPhysicalInputAddress();
|
||||
const PAddr dst_addr = config.GetPhysicalOutputAddress();
|
||||
PAddr dst_addr = config.GetPhysicalOutputAddress();
|
||||
|
||||
// TODO: do hwtest with these cases
|
||||
if (!g_memory->IsValidPhysicalAddress(src_addr)) {
|
||||
@@ -164,6 +164,14 @@ static void DisplayTransfer(const Regs::DisplayTransferConfig& config) {
|
||||
if (VideoCore::g_renderer->Rasterizer()->AccelerateDisplayTransfer(config))
|
||||
return;
|
||||
|
||||
// Using flip_vertically alongside crop_input_lines produces skewed output on hardware.
|
||||
// We have to emulate this because some games rely on this behaviour to render correctly.
|
||||
if (config.flip_vertically && config.crop_input_lines &&
|
||||
config.input_width > config.output_width) {
|
||||
dst_addr += (config.input_width - config.output_width) * (config.output_height - 1) *
|
||||
GPU::Regs::BytesPerPixel(config.output_format);
|
||||
}
|
||||
|
||||
u8* src_pointer = g_memory->GetPhysicalPointer(src_addr);
|
||||
u8* dst_pointer = g_memory->GetPhysicalPointer(dst_addr);
|
||||
|
||||
|
||||
+1
-1
@@ -186,7 +186,7 @@ struct Regs {
|
||||
return fmt::format("from {:#x} to {:#x} with {} scaling and stride {}, width {}",
|
||||
GetPhysicalInputAddress(), GetPhysicalOutputAddress(),
|
||||
scaling == NoScale ? "no" : (scaling == ScaleX ? "X" : "XY"),
|
||||
input_width, output_width);
|
||||
input_width.Value(), output_width.Value());
|
||||
}
|
||||
|
||||
union {
|
||||
|
||||
+12
-19
@@ -153,37 +153,30 @@ void Movie::serialize(Archive& ar, const unsigned int file_version) {
|
||||
u64 _current_byte = static_cast<u64>(current_byte);
|
||||
ar& _current_byte;
|
||||
current_byte = static_cast<std::size_t>(_current_byte);
|
||||
|
||||
if (file_version > 0) {
|
||||
ar& current_input;
|
||||
}
|
||||
ar& current_input;
|
||||
|
||||
std::vector<u8> recorded_input_ = recorded_input;
|
||||
ar& recorded_input_;
|
||||
|
||||
ar& init_time;
|
||||
|
||||
if (file_version > 0) {
|
||||
if (Archive::is_loading::value) {
|
||||
u64 savestate_movie_id;
|
||||
ar& savestate_movie_id;
|
||||
if (id != savestate_movie_id) {
|
||||
if (savestate_movie_id == 0) {
|
||||
throw std::runtime_error("You must close your movie to load this state");
|
||||
} else {
|
||||
throw std::runtime_error("You must load the same movie to load this state");
|
||||
}
|
||||
if (Archive::is_loading::value) {
|
||||
u64 savestate_movie_id;
|
||||
ar& savestate_movie_id;
|
||||
if (id != savestate_movie_id) {
|
||||
if (savestate_movie_id == 0) {
|
||||
throw std::runtime_error("You must close your movie to load this state");
|
||||
} else {
|
||||
throw std::runtime_error("You must load the same movie to load this state");
|
||||
}
|
||||
} else {
|
||||
ar& id;
|
||||
}
|
||||
} else {
|
||||
ar& id;
|
||||
}
|
||||
|
||||
// Whether the state was made in MovieFinished state
|
||||
bool post_movie = play_mode == PlayMode::MovieFinished;
|
||||
if (file_version > 0) {
|
||||
ar& post_movie;
|
||||
}
|
||||
ar& post_movie;
|
||||
|
||||
if (Archive::is_loading::value && id != 0) {
|
||||
if (!read_only) {
|
||||
|
||||
@@ -43,7 +43,6 @@ endif()
|
||||
|
||||
create_target_directory_groups(input_common)
|
||||
target_link_libraries(input_common PUBLIC citra_core PRIVATE citra_common ${Boost_LIBRARIES})
|
||||
set_target_properties(input_common PROPERTIES INTERPROCEDURAL_OPTIMIZATION ${ENABLE_LTO})
|
||||
|
||||
if (CITRA_USE_PRECOMPILED_HEADERS)
|
||||
target_precompile_headers(input_common PRIVATE precompiled_headers.h)
|
||||
|
||||
@@ -139,6 +139,7 @@ constexpr std::array<SDL_GameControllerButton, Settings::NativeButton::NumButton
|
||||
SDL_CONTROLLER_BUTTON_INVALID,
|
||||
SDL_CONTROLLER_BUTTON_INVALID,
|
||||
SDL_CONTROLLER_BUTTON_GUIDE,
|
||||
SDL_CONTROLLER_BUTTON_INVALID,
|
||||
}};
|
||||
|
||||
struct SDLJoystickDeleter {
|
||||
|
||||
@@ -24,8 +24,7 @@ if (ENABLE_WEB_SERVICE)
|
||||
target_link_libraries(network PRIVATE web_service)
|
||||
endif()
|
||||
|
||||
target_link_libraries(network PRIVATE citra_common enet Boost::serialization cryptopp httplib)
|
||||
set_target_properties(network PROPERTIES INTERPROCEDURAL_OPTIMIZATION ${ENABLE_LTO})
|
||||
target_link_libraries(network PRIVATE citra_common enet Boost::serialization httplib)
|
||||
|
||||
if (CITRA_USE_PRECOMPILED_HEADERS)
|
||||
target_precompile_headers(network PRIVATE precompiled_headers.h)
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "audio_core/hle/adts.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <fmt/core.h>
|
||||
|
||||
#include "audio_core/hle/decoder.h"
|
||||
#include "audio_core/hle/hle.h"
|
||||
#include "audio_core/lle/lle.h"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user