Compare commits
123 Commits
tex-filter
...
vulkan-sch
Author | SHA1 | Date | |
---|---|---|---|
|
cfd7bfbb20 | ||
|
fbe471693d | ||
|
4670114077 | ||
|
b5d4473bbb | ||
|
97455ae5f8 | ||
|
69cfff9022 | ||
|
7dd5049ae1 | ||
|
1396d39cf7 | ||
|
55220d70df | ||
|
aebb7f2d06 | ||
|
c3956ee207 | ||
|
9b582f2ba5 | ||
|
1f16ecad1a | ||
|
2d5e588d89 | ||
|
d551e2adc3 | ||
|
561398bbcd | ||
|
23cff45251 | ||
|
0223fa756c | ||
|
b7fa091db0 | ||
|
46ae192c05 | ||
|
e059fc5f4f | ||
|
0fe4225b22 | ||
|
3a60a6687d | ||
|
74b8081114 | ||
|
69db7d9d0d | ||
|
caf596e5eb | ||
|
9950e2aab5 | ||
|
13fe59ae55 | ||
|
d700e2c4cc | ||
|
59aeeca8ca | ||
|
54414d5a8f | ||
|
6086bfabca | ||
|
069df7741d | ||
|
8ec86d07d7 | ||
|
c625a5a0b4 | ||
|
04a188c96d | ||
|
8152881f06 | ||
|
0f4dc90acc | ||
|
d77574100d | ||
|
c10cf4414f | ||
|
4d7a00f324 | ||
|
e56d069ed5 | ||
|
e33adc1b11 | ||
|
11de7700aa | ||
|
b0fc94f155 | ||
|
0d4e530805 | ||
|
9a1cf869f9 | ||
|
523120e03d | ||
|
7f26562dce | ||
|
f750da1508 | ||
|
711b699689 | ||
|
7f7408b81e | ||
|
2da4c9ca90 | ||
|
20ccb995b1 | ||
|
601aac2a26 | ||
|
924257b2cc | ||
|
9e34ff40ed | ||
|
ba3c84168a | ||
|
77d6be6bde | ||
|
a6e2bcd986 | ||
|
2c30889fdc | ||
|
67a76bec5c | ||
|
98b7c33f62 | ||
|
dbfa06c6b1 | ||
|
88d0b7de13 | ||
|
de149e3ee9 | ||
|
99877f9465 | ||
|
075090569f | ||
|
f6af97fc16 | ||
|
5fa4a32cf6 | ||
|
fd77483a5f | ||
|
1bf1217a18 | ||
|
40db7b90fa | ||
|
8e1a23d971 | ||
|
9e8c403793 | ||
|
0e047a7a6e | ||
|
66158841cb | ||
|
634e6427a8 | ||
|
c1f46ed710 | ||
|
4776e21dd9 | ||
|
9e7b3bfa16 | ||
|
62d561c004 | ||
|
34a0571dc3 | ||
|
e46970a84a | ||
|
ccb1872604 | ||
|
1cd0b04399 | ||
|
ab3a228e5e | ||
|
7ae0d0ef27 | ||
|
19c82a76a3 | ||
|
366cdc854f | ||
|
d2fd8030dd | ||
|
a932a9f662 | ||
|
ca81c5a5f3 | ||
|
5d62b033df | ||
|
36d584cf3c | ||
|
7eb590153b | ||
|
e99ef32c6b | ||
|
066bdcfc40 | ||
|
dd1c06a55b | ||
|
fa7edc4a9c | ||
|
994b27ab5b | ||
|
77a99506cb | ||
|
5f8a884c2c | ||
|
a7cfe99ca1 | ||
|
424ed2df04 | ||
|
e22e641736 | ||
|
c080ed35c2 | ||
|
6dacd66f40 | ||
|
25a6da50ef | ||
|
38a5cc634f | ||
|
f26d00fbb4 | ||
|
b03c3b0d7d | ||
|
1f450d6d1d | ||
|
e464507b7a | ||
|
7158952ae7 | ||
|
841dee8ed8 | ||
|
01e53fe9d2 | ||
|
9762e24696 | ||
|
937c7e67a4 | ||
|
1d4f8db60d | ||
|
bc0c9f6eb7 | ||
|
667d978480 | ||
|
389d1862bb |
9
.gitmodules
vendored
9
.gitmodules
vendored
@@ -58,3 +58,12 @@
|
||||
[submodule "sdl2"]
|
||||
path = externals/sdl2/SDL
|
||||
url = https://github.com/libsdl-org/SDL
|
||||
[submodule "vulkan-headers"]
|
||||
path = externals/vulkan-headers
|
||||
url = https://github.com/KhronosGroup/Vulkan-Headers
|
||||
[submodule "glslang"]
|
||||
path = externals/glslang
|
||||
url = https://github.com/KhronosGroup/glslang
|
||||
[submodule "glm"]
|
||||
path = externals/glm
|
||||
url = https://github.com/g-truc/glm
|
||||
|
@@ -1,15 +1,15 @@
|
||||
# CMake 3.12 required for 20 to be a valid value for CXX_STANDARD
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL 3.15)
|
||||
# Don't override the warning flags in MSVC:
|
||||
cmake_policy(SET CMP0092 NEW)
|
||||
# Enforce new LTO setting
|
||||
cmake_policy(SET CMP0069 NEW)
|
||||
endif()
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
|
||||
# Don't override the warning flags in MSVC:
|
||||
cmake_policy(SET CMP0092 NEW)
|
||||
# Enforce new LTO setting
|
||||
cmake_policy(SET CMP0069 NEW)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules")
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules")
|
||||
include(DownloadExternals)
|
||||
include(GNUInstallDirs)
|
||||
include(CMakeDependentOption)
|
||||
|
||||
project(citra LANGUAGES C CXX ASM)
|
||||
|
2
dist/qt_themes/colorful/style.qrc
vendored
2
dist/qt_themes/colorful/style.qrc
vendored
@@ -13,6 +13,6 @@
|
||||
<file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="colorful">
|
||||
<file>style.qss</file>
|
||||
<file alias="style.qss">../default/style.qss</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
4
dist/qt_themes/colorful/style.qss
vendored
4
dist/qt_themes/colorful/style.qss
vendored
@@ -1,4 +0,0 @@
|
||||
/*
|
||||
This file is intentionally left blank.
|
||||
We do not want to apply any stylesheet for colorful, only icons.
|
||||
*/
|
17
dist/qt_themes/default/default.qrc
vendored
17
dist/qt_themes/default/default.qrc
vendored
@@ -1,33 +1,22 @@
|
||||
<RCC>
|
||||
<qresource prefix="icons/default">
|
||||
<file alias="index.theme">icons/index.theme</file>
|
||||
|
||||
<file alias="16x16/checked.png">icons/16x16/checked.png</file>
|
||||
|
||||
<file alias="16x16/failed.png">icons/16x16/failed.png</file>
|
||||
|
||||
<file alias="16x16/connected.png">icons/16x16/connected.png</file>
|
||||
|
||||
<file alias="16x16/disconnected.png">icons/16x16/disconnected.png</file>
|
||||
|
||||
<file alias="16x16/connected_notification.png">icons/16x16/connected_notification.png</file>
|
||||
|
||||
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
|
||||
|
||||
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
|
||||
|
||||
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
|
||||
|
||||
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
|
||||
|
||||
<file alias="48x48/no_avatar.png">icons/48x48/no_avatar.png</file>
|
||||
|
||||
<file alias="48x48/plus.png">icons/48x48/plus.png</file>
|
||||
|
||||
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
|
||||
|
||||
<file alias="256x256/citra.png">icons/256x256/citra.png</file>
|
||||
|
||||
<file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="default">
|
||||
<file>style.qss</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
13
dist/qt_themes/default/style.qss
vendored
Normal file
13
dist/qt_themes/default/style.qss
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
QPushButton#GraphicsAPIStatusBarButton {
|
||||
color: #656565;
|
||||
border: 1px solid transparent;
|
||||
background-color: transparent;
|
||||
padding: 0px 3px 0px 3px;
|
||||
text-align: center;
|
||||
min-width: 60px;
|
||||
min-height: 20px;
|
||||
}
|
||||
|
||||
QPushButton#GraphicsAPIStatusBarButton:hover {
|
||||
border: 1px solid #76797C;
|
||||
}
|
21
dist/qt_themes/qdarkstyle/style.qss
vendored
21
dist/qt_themes/qdarkstyle/style.qss
vendored
@@ -522,13 +522,12 @@ QToolButton#qt_toolbar_ext_button {
|
||||
|
||||
QPushButton {
|
||||
color: #eff0f1;
|
||||
border-width: 1px;
|
||||
border-color: #54575B;
|
||||
border-style: solid;
|
||||
padding: 6px 4px;
|
||||
border: 1px solid #54575B;
|
||||
border-radius: 2px;
|
||||
padding: 5px 0px 5px 0px;
|
||||
outline: none;
|
||||
min-width: 100px;
|
||||
min-height: 13px;
|
||||
background-color: #232629;
|
||||
}
|
||||
|
||||
@@ -1237,3 +1236,17 @@ QPlainTextEdit:disabled {
|
||||
TouchScreenPreview {
|
||||
qproperty-dotHighlightColor: #3daee9;
|
||||
}
|
||||
|
||||
QPushButton#GraphicsAPIStatusBarButton {
|
||||
color: #656565;
|
||||
border: 1px solid transparent;
|
||||
background-color: transparent;
|
||||
padding: 0px 3px 0px 3px;
|
||||
text-align: center;
|
||||
min-width: 60px;
|
||||
min-height: 20px;
|
||||
}
|
||||
|
||||
QPushButton#GraphicsAPIStatusBarButton:hover {
|
||||
border: 1px solid #76797C;
|
||||
}
|
19
externals/CMakeLists.txt
vendored
19
externals/CMakeLists.txt
vendored
@@ -57,6 +57,16 @@ endif()
|
||||
# Glad
|
||||
add_subdirectory(glad)
|
||||
|
||||
# glslang
|
||||
set(SKIP_GLSLANG_INSTALL ON)
|
||||
set(ENABLE_GLSLANG_BINARIES OFF)
|
||||
set(ENABLE_SPVREMAPPER OFF)
|
||||
set(ENABLE_CTEST OFF)
|
||||
add_subdirectory(glslang)
|
||||
|
||||
# glm
|
||||
add_subdirectory(glm)
|
||||
|
||||
# inih
|
||||
add_subdirectory(inih)
|
||||
|
||||
@@ -151,3 +161,12 @@ if(ANDROID)
|
||||
add_subdirectory(libyuv)
|
||||
target_include_directories(yuv INTERFACE ./libyuv/include)
|
||||
endif()
|
||||
|
||||
# VMA
|
||||
add_library(vma INTERFACE)
|
||||
target_include_directories(vma INTERFACE ./vma)
|
||||
|
||||
# vulkan-headers
|
||||
add_library(vulkan-headers INTERFACE)
|
||||
target_include_directories(vulkan-headers INTERFACE ./vulkan-headers/include)
|
||||
|
||||
|
1862
externals/glad/include/glad/glad.h
vendored
1862
externals/glad/include/glad/glad.h
vendored
File diff suppressed because it is too large
Load Diff
1037
externals/glad/src/glad.c
vendored
1037
externals/glad/src/glad.c
vendored
File diff suppressed because it is too large
Load Diff
1
externals/glm
vendored
Submodule
1
externals/glm
vendored
Submodule
Submodule externals/glm added at cc98465e35
1
externals/glslang
vendored
Submodule
1
externals/glslang
vendored
Submodule
Submodule externals/glslang added at c0cf8ad876
6
externals/microprofile/microprofile.h
vendored
6
externals/microprofile/microprofile.h
vendored
@@ -847,7 +847,7 @@ inline MicroProfileLogEntry MicroProfileMakeLogIndex(uint64_t nBegin, MicroProfi
|
||||
MicroProfileLogEntry Entry = (nBegin<<62) | ((0x3fff&nToken)<<48) | (MP_LOG_TICK_MASK&nTick);
|
||||
int t = MicroProfileLogType(Entry);
|
||||
uint64_t nTimerIndex = MicroProfileLogTimerIndex(Entry);
|
||||
MP_ASSERT(t == nBegin);
|
||||
MP_ASSERT(static_cast<uint64_t>(t) == nBegin);
|
||||
MP_ASSERT(nTimerIndex == (nToken&0x3fff));
|
||||
return Entry;
|
||||
|
||||
@@ -1579,10 +1579,10 @@ void MicroProfileFlip()
|
||||
|
||||
pFramePut->nFrameStartCpu = MP_TICK();
|
||||
pFramePut->nFrameStartGpu = (uint32_t)MicroProfileGpuInsertTimeStamp();
|
||||
if(pFrameNext->nFrameStartGpu != (uint64_t)-1)
|
||||
if(static_cast<uint64_t>(pFrameNext->nFrameStartGpu) != (uint64_t)-1)
|
||||
pFrameNext->nFrameStartGpu = MicroProfileGpuGetTimeStamp((uint32_t)pFrameNext->nFrameStartGpu);
|
||||
|
||||
if(pFrameCurrent->nFrameStartGpu == (uint64_t)-1)
|
||||
if(static_cast<uint64_t>(pFrameCurrent->nFrameStartGpu) == (uint64_t)-1)
|
||||
pFrameCurrent->nFrameStartGpu = pFrameNext->nFrameStartGpu + 1;
|
||||
|
||||
uint64_t nFrameStartCpu = pFrameCurrent->nFrameStartCpu;
|
||||
|
12
externals/microprofile/microprofileui.h
vendored
12
externals/microprofile/microprofileui.h
vendored
@@ -354,7 +354,7 @@ void MicroProfileInitUI()
|
||||
if(!bInitialized)
|
||||
{
|
||||
bInitialized = true;
|
||||
memset(&g_MicroProfileUI, 0, sizeof(g_MicroProfileUI));
|
||||
g_MicroProfileUI = {};
|
||||
UI.nActiveMenu = UINT32_MAX;
|
||||
UI.fDetailedOffsetTarget = UI.fDetailedOffset = 0.f;
|
||||
UI.fDetailedRangeTarget = UI.fDetailedRange = 50.f;
|
||||
@@ -845,8 +845,8 @@ void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int nBaseY,
|
||||
MicroProfile& S = *MicroProfileGet();
|
||||
MP_DEBUG_DUMP_RANGE();
|
||||
int nY = nBaseY - UI.nOffsetY;
|
||||
int64_t nNumBoxes = 0;
|
||||
int64_t nNumLines = 0;
|
||||
[[maybe_unused]] int64_t nNumBoxes = 0;
|
||||
[[maybe_unused]] int64_t nNumLines = 0;
|
||||
|
||||
uint32_t nFrameNext = (S.nFrameCurrent+1) % MICROPROFILE_MAX_FRAME_HISTORY;
|
||||
MicroProfileFrameState* pFrameCurrent = &S.Frames[S.nFrameCurrent];
|
||||
@@ -1988,7 +1988,7 @@ const char* MicroProfileUIMenuGroups(int nIndex, bool* bSelected)
|
||||
else
|
||||
{
|
||||
nIndex = nIndex-1;
|
||||
if(nIndex < UI.GroupMenuCount)
|
||||
if(static_cast<uint32_t>(nIndex) < UI.GroupMenuCount)
|
||||
{
|
||||
MicroProfileGroupMenuItem& Item = UI.GroupMenu[nIndex];
|
||||
static char buffer[MICROPROFILE_NAME_MAX_LEN+32];
|
||||
@@ -2135,7 +2135,7 @@ const char* MicroProfileUIMenuCustom(int nIndex, bool* bSelected)
|
||||
case 1: return "--";
|
||||
default:
|
||||
nIndex -= 2;
|
||||
if(nIndex < UI.nCustomCount)
|
||||
if(static_cast<uint32_t>(nIndex) < UI.nCustomCount)
|
||||
{
|
||||
return UI.Custom[nIndex].pName;
|
||||
}
|
||||
@@ -2185,7 +2185,7 @@ void MicroProfileUIClickGroups(int nIndex)
|
||||
else
|
||||
{
|
||||
nIndex -= 1;
|
||||
if(nIndex < UI.GroupMenuCount)
|
||||
if(static_cast<uint32_t>(nIndex) < UI.GroupMenuCount)
|
||||
{
|
||||
MicroProfileGroupMenuItem& Item = UI.GroupMenu[nIndex];
|
||||
if(Item.nIsCategory)
|
||||
|
2
externals/sdl2/CMakeLists.txt
vendored
2
externals/sdl2/CMakeLists.txt
vendored
@@ -39,9 +39,9 @@ set(SDL_JOYSTICK ON CACHE BOOL "")
|
||||
set(SDL_HAPTIC OFF CACHE BOOL "")
|
||||
set(SDL_HIDAPI ON CACHE BOOL "")
|
||||
set(SDL_POWER OFF CACHE BOOL "")
|
||||
set(SDL_THREADS ON CACHE BOOL "")
|
||||
set(SDL_TIMERS ON CACHE BOOL "")
|
||||
set(SDL_FILE ON CACHE BOOL "")
|
||||
set(SDL_THREADS ON CACHE BOOL "")
|
||||
set(SDL_LOADSO ON CACHE BOOL "")
|
||||
set(SDL_CPUINFO OFF CACHE BOOL "")
|
||||
set(SDL_FILESYSTEM OFF CACHE BOOL "")
|
||||
|
19670
externals/vma/vk_mem_alloc.h
vendored
Normal file
19670
externals/vma/vk_mem_alloc.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
externals/vulkan-headers
vendored
Submodule
1
externals/vulkan-headers
vendored
Submodule
Submodule externals/vulkan-headers added at 98f440ce68
@@ -103,6 +103,7 @@ else()
|
||||
|
||||
if (MINGW)
|
||||
add_definitions(-DMINGW_HAS_SECURE_API)
|
||||
add_compile_options("-Wa,-mbig-obj")
|
||||
if (COMPILE_WITH_DWARF)
|
||||
add_compile_options("-gdwarf")
|
||||
endif()
|
||||
|
@@ -22,7 +22,6 @@
|
||||
#include "core/frontend/applets/default_applets.h"
|
||||
#include "core/frontend/camera/factory.h"
|
||||
#include "core/frontend/mic.h"
|
||||
#include "core/frontend/scope_acquire_context.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/nfc/nfc.h"
|
||||
#include "core/savestate.h"
|
||||
|
@@ -7,17 +7,7 @@
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
// This needs to be included before getopt.h because the latter #defines symbols used by it
|
||||
#include "common/microprofile.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
// windows.h needs to be included before shellapi.h
|
||||
#include <windows.h>
|
||||
|
||||
#include <shellapi.h>
|
||||
#endif
|
||||
|
||||
#include "citra/config.h"
|
||||
#include "citra/emu_window/emu_window_sdl2.h"
|
||||
#include "citra/lodepng_image_interface.h"
|
||||
@@ -25,21 +15,17 @@
|
||||
#include "common/detached_tasks.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/filter.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/dumping/backend.h"
|
||||
#include "core/file_sys/cia_container.h"
|
||||
#include "core/frontend/applets/default_applets.h"
|
||||
#include "core/frontend/framebuffer_layout.h"
|
||||
#include "core/frontend/scope_acquire_context.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/cfg/cfg.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/movie.h"
|
||||
#include "core/settings.h"
|
||||
#include "network/network.h"
|
||||
@@ -52,6 +38,11 @@
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
// windows.h needs to be included before shellapi.h
|
||||
#include <windows.h>
|
||||
|
||||
#include <shellapi.h>
|
||||
|
||||
extern "C" {
|
||||
// tells Nvidia drivers to use the dedicated GPU by default on laptops with switchable graphics
|
||||
__declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
|
||||
@@ -104,35 +95,35 @@ static void OnNetworkError(const Network::RoomMember::Error& error) {
|
||||
break;
|
||||
case Network::RoomMember::Error::CouldNotConnect:
|
||||
LOG_ERROR(Network, "Error: Could not connect");
|
||||
exit(1);
|
||||
std::exit(1);
|
||||
break;
|
||||
case Network::RoomMember::Error::NameCollision:
|
||||
LOG_ERROR(
|
||||
Network,
|
||||
"You tried to use the same nickname as another user that is connected to the Room");
|
||||
exit(1);
|
||||
std::exit(1);
|
||||
break;
|
||||
case Network::RoomMember::Error::MacCollision:
|
||||
LOG_ERROR(Network, "You tried to use the same MAC-Address as another user that is "
|
||||
"connected to the Room");
|
||||
exit(1);
|
||||
std::exit(1);
|
||||
break;
|
||||
case Network::RoomMember::Error::ConsoleIdCollision:
|
||||
LOG_ERROR(Network, "Your Console ID conflicted with someone else in the Room");
|
||||
exit(1);
|
||||
std::exit(1);
|
||||
break;
|
||||
case Network::RoomMember::Error::WrongPassword:
|
||||
LOG_ERROR(Network, "Room replied with: Wrong password");
|
||||
exit(1);
|
||||
std::exit(1);
|
||||
break;
|
||||
case Network::RoomMember::Error::WrongVersion:
|
||||
LOG_ERROR(Network,
|
||||
"You are using a different version than the room you are trying to connect to");
|
||||
exit(1);
|
||||
std::exit(1);
|
||||
break;
|
||||
case Network::RoomMember::Error::RoomIsFull:
|
||||
LOG_ERROR(Network, "The room is full");
|
||||
exit(1);
|
||||
std::exit(1);
|
||||
break;
|
||||
case Network::RoomMember::Error::HostKicked:
|
||||
LOG_ERROR(Network, "You have been kicked by the host");
|
||||
@@ -140,6 +131,8 @@ static void OnNetworkError(const Network::RoomMember::Error& error) {
|
||||
case Network::RoomMember::Error::HostBanned:
|
||||
LOG_ERROR(Network, "You have been banned by the host");
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Network, "Unknown network error {}", error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,7 +352,7 @@ int main(int argc, char** argv) {
|
||||
Core::System::GetInstance().RegisterImageInterface(std::make_shared<LodePNGImageInterface>());
|
||||
|
||||
std::unique_ptr<EmuWindow_SDL2> emu_window{std::make_unique<EmuWindow_SDL2>(fullscreen)};
|
||||
Frontend::ScopeAcquireContext scope(*emu_window);
|
||||
const auto scope = emu_window->Acquire();
|
||||
Core::System& system = Core::System::GetInstance();
|
||||
|
||||
const Core::System::ResultStatus load_result{system.Load(*emu_window, filepath)};
|
||||
|
@@ -109,7 +109,8 @@ void Config::ReadValues() {
|
||||
sdl2_config->GetInteger("Core", "cpu_clock_percentage", 100);
|
||||
|
||||
// Renderer
|
||||
Settings::values.use_gles = sdl2_config->GetBoolean("Renderer", "use_gles", false);
|
||||
Settings::values.graphics_api =
|
||||
static_cast<Settings::GraphicsAPI>(sdl2_config->GetInteger("Renderer", "graphics_api", 0));
|
||||
Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", true);
|
||||
Settings::values.use_hw_shader = sdl2_config->GetBoolean("Renderer", "use_hw_shader", true);
|
||||
#ifdef __APPLE__
|
||||
|
@@ -147,7 +147,8 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
|
||||
|
||||
SDL_SetMainReady();
|
||||
|
||||
if (Settings::values.use_gles) {
|
||||
const bool is_opengles = Settings::values.graphics_api == Settings::GraphicsAPI::OpenGLES;
|
||||
if (is_opengles) {
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
||||
@@ -201,7 +202,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
auto gl_load_func = Settings::values.use_gles ? gladLoadGLES2Loader : gladLoadGLLoader;
|
||||
auto gl_load_func = is_opengles ? gladLoadGLES2Loader : gladLoadGLLoader;
|
||||
|
||||
if (!gl_load_func(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) {
|
||||
LOG_CRITICAL(Frontend, "Failed to initialize GL functions: {}", SDL_GetError());
|
||||
|
@@ -260,9 +260,13 @@ endif()
|
||||
create_target_directory_groups(citra-qt)
|
||||
|
||||
target_link_libraries(citra-qt PRIVATE audio_core common core input_common network video_core)
|
||||
target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt5::Widgets Qt5::Multimedia)
|
||||
target_link_libraries(citra-qt PRIVATE Boost::boost glad vma vulkan-headers nihstro-headers Qt5::Widgets Qt5::Multimedia)
|
||||
target_link_libraries(citra-qt PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
||||
|
||||
if (NOT WIN32)
|
||||
target_include_directories(citra-qt PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
target_compile_definitions(citra-qt PRIVATE
|
||||
# Use QStringBuilder for string concatenation to reduce
|
||||
# the overall number of temporary strings created.
|
||||
|
@@ -54,12 +54,12 @@ QtKeyboardDialog::QtKeyboardDialog(QWidget* parent, QtKeyboard* keyboard_)
|
||||
case ButtonConfig::None:
|
||||
break;
|
||||
}
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, [=] { Submit(); });
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, [=] {
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, [this] { Submit(); });
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, [this] {
|
||||
button = QtKeyboard::cancel_id;
|
||||
accept();
|
||||
});
|
||||
connect(buttons, &QDialogButtonBox::helpRequested, this, [=] {
|
||||
connect(buttons, &QDialogButtonBox::helpRequested, this, [this] {
|
||||
button = QtKeyboard::forgot_id;
|
||||
accept();
|
||||
});
|
||||
|
@@ -6,10 +6,10 @@
|
||||
#include <QDragEnterEvent>
|
||||
#include <QHBoxLayout>
|
||||
#include <QKeyEvent>
|
||||
#include <QMessageBox>
|
||||
#include <QOffscreenSurface>
|
||||
#include <QOpenGLContext>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QOpenGLFunctions_4_3_Core>
|
||||
#include <QOpenGLExtraFunctions>
|
||||
#include <fmt/format.h>
|
||||
#include "citra_qt/bootmanager.h"
|
||||
#include "citra_qt/main.h"
|
||||
@@ -17,56 +17,60 @@
|
||||
#include "common/scm_rev.h"
|
||||
#include "core/3ds.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/scope_acquire_context.h"
|
||||
#include "core/perf_stats.h"
|
||||
#include "core/settings.h"
|
||||
#include "input_common/keyboard.h"
|
||||
#include "input_common/main.h"
|
||||
#include "input_common/motion_emu.h"
|
||||
#include "network/network.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
#if !defined(WIN32)
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
#endif
|
||||
|
||||
EmuThread::EmuThread(Frontend::GraphicsContext& core_context) : core_context(core_context) {}
|
||||
|
||||
EmuThread::~EmuThread() = default;
|
||||
|
||||
static GMainWindow* GetMainWindow() {
|
||||
for (QWidget* w : qApp->topLevelWidgets()) {
|
||||
const auto widgets = qApp->topLevelWidgets();
|
||||
for (QWidget* w : widgets) {
|
||||
if (GMainWindow* main = qobject_cast<GMainWindow*>(w)) {
|
||||
return main;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void EmuThread::run() {
|
||||
MicroProfileOnThreadCreate("EmuThread");
|
||||
Frontend::ScopeAcquireContext scope(core_context);
|
||||
const auto scope = core_context.Acquire();
|
||||
|
||||
emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
|
||||
|
||||
Core::System::GetInstance().Renderer().Rasterizer()->LoadDiskResources(
|
||||
Core::System& system = Core::System::GetInstance();
|
||||
system.Renderer().Rasterizer()->LoadDiskResources(
|
||||
stop_run, [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
|
||||
emit LoadProgress(stage, value, total);
|
||||
});
|
||||
|
||||
emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
|
||||
emit HideLoadingScreen();
|
||||
|
||||
core_context.MakeCurrent();
|
||||
|
||||
if (Core::System::GetInstance().frame_limiter.IsFrameAdvancing()) {
|
||||
if (system.frame_limiter.IsFrameAdvancing()) {
|
||||
// Usually the loading screen is hidden after the first frame is drawn. In this case
|
||||
// we hide it immediately as we need to wait for user input to start the emulation.
|
||||
emit HideLoadingScreen();
|
||||
Core::System::GetInstance().frame_limiter.WaitOnce();
|
||||
system.frame_limiter.WaitOnce();
|
||||
}
|
||||
|
||||
// Holds whether the cpu was running during the last iteration,
|
||||
// so that the DebugModeLeft signal can be emitted before the
|
||||
// next execution step.
|
||||
bool was_active = false;
|
||||
Core::System& system = Core::System::GetInstance();
|
||||
while (!stop_run) {
|
||||
if (running) {
|
||||
if (!was_active)
|
||||
@@ -111,87 +115,234 @@ void EmuThread::run() {
|
||||
#endif
|
||||
}
|
||||
|
||||
OpenGLWindow::OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context)
|
||||
: QWindow(parent), context(std::make_unique<QOpenGLContext>(shared_context->parent())),
|
||||
event_handler(event_handler) {
|
||||
class OpenGLSharedContext : public Frontend::GraphicsContext {
|
||||
public:
|
||||
/// Create the original context that should be shared from
|
||||
explicit OpenGLSharedContext(QSurface* surface) : surface(surface) {
|
||||
QSurfaceFormat format;
|
||||
|
||||
// disable vsync for any shared contexts
|
||||
auto format = shared_context->format();
|
||||
format.setSwapInterval(Settings::values.use_vsync_new ? 1 : 0);
|
||||
this->setFormat(format);
|
||||
format.setVersion(4, 4);
|
||||
format.setProfile(QSurfaceFormat::CoreProfile);
|
||||
|
||||
context->setShareContext(shared_context);
|
||||
context->setScreen(this->screen());
|
||||
context->setFormat(format);
|
||||
context->create();
|
||||
if (Settings::values.renderer_debug) {
|
||||
format.setOption(QSurfaceFormat::FormatOption::DebugContext);
|
||||
}
|
||||
|
||||
setSurfaceType(QWindow::OpenGLSurface);
|
||||
// TODO: expose a setting for buffer value (ie default/single/double/triple)
|
||||
format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
|
||||
format.setSwapInterval(0);
|
||||
|
||||
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
|
||||
// WA_DontShowOnScreen, WA_DeleteOnClose
|
||||
}
|
||||
|
||||
OpenGLWindow::~OpenGLWindow() {
|
||||
context->doneCurrent();
|
||||
}
|
||||
|
||||
void OpenGLWindow::Present() {
|
||||
if (!isExposed())
|
||||
return;
|
||||
|
||||
context->makeCurrent(this);
|
||||
if (VideoCore::g_renderer) {
|
||||
VideoCore::g_renderer->TryPresent(100);
|
||||
context = std::make_unique<QOpenGLContext>();
|
||||
context->setFormat(format);
|
||||
if (!context->create()) {
|
||||
LOG_ERROR(Frontend, "Unable to create main openGL context");
|
||||
}
|
||||
}
|
||||
context->swapBuffers(this);
|
||||
auto f = context->versionFunctions<QOpenGLFunctions_4_3_Core>();
|
||||
f->glFinish();
|
||||
QWindow::requestUpdate();
|
||||
}
|
||||
|
||||
bool OpenGLWindow::event(QEvent* event) {
|
||||
switch (event->type()) {
|
||||
case QEvent::UpdateRequest:
|
||||
/// Create the shared contexts for rendering and presentation
|
||||
explicit OpenGLSharedContext(QOpenGLContext* share_context, QSurface* main_surface = nullptr) {
|
||||
|
||||
// disable vsync for any shared contexts
|
||||
auto format = share_context->format();
|
||||
format.setSwapInterval(main_surface ? Settings::values.use_vsync_new : 0);
|
||||
|
||||
context = std::make_unique<QOpenGLContext>();
|
||||
context->setShareContext(share_context);
|
||||
context->setFormat(format);
|
||||
if (!context->create()) {
|
||||
LOG_ERROR(Frontend, "Unable to create shared openGL context");
|
||||
}
|
||||
|
||||
if (!main_surface) {
|
||||
offscreen_surface = std::make_unique<QOffscreenSurface>(nullptr);
|
||||
offscreen_surface->setFormat(format);
|
||||
offscreen_surface->create();
|
||||
surface = offscreen_surface.get();
|
||||
} else {
|
||||
surface = main_surface;
|
||||
}
|
||||
}
|
||||
|
||||
~OpenGLSharedContext() {
|
||||
context->doneCurrent();
|
||||
}
|
||||
|
||||
void SwapBuffers() override {
|
||||
context->swapBuffers(surface);
|
||||
}
|
||||
|
||||
void MakeCurrent() override {
|
||||
// We can't track the current state of the underlying context in this wrapper class because
|
||||
// Qt may make the underlying context not current for one reason or another. In particular,
|
||||
// the WebBrowser uses GL, so it seems to conflict if we aren't careful.
|
||||
// Instead of always just making the context current (which does not have any caching to
|
||||
// check if the underlying context is already current) we can check for the current context
|
||||
// in the thread local data by calling `currentContext()` and checking if its ours.
|
||||
if (QOpenGLContext::currentContext() != context.get()) {
|
||||
context->makeCurrent(surface);
|
||||
}
|
||||
}
|
||||
|
||||
void DoneCurrent() override {
|
||||
context->doneCurrent();
|
||||
}
|
||||
|
||||
QOpenGLContext* GetShareContext() const {
|
||||
return context.get();
|
||||
}
|
||||
|
||||
private:
|
||||
// Avoid using Qt parent system here since we might move the QObjects to new threads
|
||||
// As a note, this means we should avoid using slots/signals with the objects too
|
||||
std::unique_ptr<QOpenGLContext> context;
|
||||
std::unique_ptr<QOffscreenSurface> offscreen_surface{};
|
||||
QSurface* surface;
|
||||
};
|
||||
|
||||
class DummyContext : public Frontend::GraphicsContext {};
|
||||
|
||||
class RenderWidget : public QWidget {
|
||||
public:
|
||||
RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) {
|
||||
setAttribute(Qt::WA_NativeWindow);
|
||||
setAttribute(Qt::WA_PaintOnScreen);
|
||||
}
|
||||
|
||||
virtual ~RenderWidget() = default;
|
||||
|
||||
virtual void Present() {}
|
||||
|
||||
void paintEvent(QPaintEvent* event) override {
|
||||
Present();
|
||||
return true;
|
||||
case QEvent::MouseButtonPress:
|
||||
case QEvent::MouseButtonRelease:
|
||||
case QEvent::MouseButtonDblClick:
|
||||
case QEvent::MouseMove:
|
||||
case QEvent::KeyPress:
|
||||
case QEvent::KeyRelease:
|
||||
case QEvent::FocusIn:
|
||||
case QEvent::FocusOut:
|
||||
case QEvent::FocusAboutToChange:
|
||||
case QEvent::Enter:
|
||||
case QEvent::Leave:
|
||||
case QEvent::Wheel:
|
||||
case QEvent::TabletMove:
|
||||
case QEvent::TabletPress:
|
||||
case QEvent::TabletRelease:
|
||||
case QEvent::TabletEnterProximity:
|
||||
case QEvent::TabletLeaveProximity:
|
||||
case QEvent::TouchBegin:
|
||||
case QEvent::TouchUpdate:
|
||||
case QEvent::TouchEnd:
|
||||
case QEvent::InputMethodQuery:
|
||||
case QEvent::TouchCancel:
|
||||
return QCoreApplication::sendEvent(event_handler, event);
|
||||
case QEvent::Drop:
|
||||
GetMainWindow()->DropAction(static_cast<QDropEvent*>(event));
|
||||
return true;
|
||||
case QEvent::DragEnter:
|
||||
case QEvent::DragMove:
|
||||
GetMainWindow()->AcceptDropEvent(static_cast<QDropEvent*>(event));
|
||||
return true;
|
||||
default:
|
||||
return QWindow::event(event);
|
||||
update();
|
||||
}
|
||||
|
||||
void resizeEvent(QResizeEvent* ev) override {
|
||||
render_window->resize(ev->size());
|
||||
render_window->OnFramebufferSizeChanged();
|
||||
}
|
||||
|
||||
void keyPressEvent(QKeyEvent* event) override {
|
||||
InputCommon::GetKeyboard()->PressKey(event->key());
|
||||
}
|
||||
|
||||
void keyReleaseEvent(QKeyEvent* event) override {
|
||||
InputCommon::GetKeyboard()->ReleaseKey(event->key());
|
||||
}
|
||||
|
||||
void mousePressEvent(QMouseEvent* event) override {
|
||||
if (event->source() == Qt::MouseEventSynthesizedBySystem)
|
||||
return; // touch input is handled in TouchBeginEvent
|
||||
|
||||
const auto pos{event->pos()};
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
const auto [x, y] = render_window->ScaleTouch(pos);
|
||||
render_window->TouchPressed(x, y);
|
||||
} else if (event->button() == Qt::RightButton) {
|
||||
InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
|
||||
}
|
||||
}
|
||||
|
||||
void mouseMoveEvent(QMouseEvent* event) override {
|
||||
if (event->source() == Qt::MouseEventSynthesizedBySystem)
|
||||
return; // touch input is handled in TouchUpdateEvent
|
||||
|
||||
const auto pos{event->pos()};
|
||||
const auto [x, y] = render_window->ScaleTouch(pos);
|
||||
render_window->TouchMoved(x, y);
|
||||
InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
|
||||
}
|
||||
|
||||
void mouseReleaseEvent(QMouseEvent* event) override {
|
||||
if (event->source() == Qt::MouseEventSynthesizedBySystem)
|
||||
return; // touch input is handled in TouchEndEvent
|
||||
|
||||
if (event->button() == Qt::LeftButton)
|
||||
render_window->TouchReleased();
|
||||
else if (event->button() == Qt::RightButton)
|
||||
InputCommon::GetMotionEmu()->EndTilt();
|
||||
}
|
||||
|
||||
std::pair<unsigned, unsigned> GetSize() const {
|
||||
return std::make_pair(width(), height());
|
||||
}
|
||||
|
||||
QPaintEngine* paintEngine() const override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
GRenderWindow* render_window;
|
||||
};
|
||||
|
||||
class OpenGLRenderWidget : public RenderWidget {
|
||||
public:
|
||||
explicit OpenGLRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {
|
||||
windowHandle()->setSurfaceType(QWindow::OpenGLSurface);
|
||||
}
|
||||
|
||||
void SetContext(std::unique_ptr<OpenGLSharedContext>&& context_) {
|
||||
context = std::move(context_);
|
||||
}
|
||||
|
||||
void Present() override {
|
||||
if (!isVisible()) {
|
||||
return;
|
||||
}
|
||||
if (!Core::System::GetInstance().IsPoweredOn()) {
|
||||
return;
|
||||
}
|
||||
context->MakeCurrent();
|
||||
const auto f = context->GetShareContext()->extraFunctions();
|
||||
f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
VideoCore::g_renderer->TryPresent(100);
|
||||
context->SwapBuffers();
|
||||
f->glFinish();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<OpenGLSharedContext> context{};
|
||||
};
|
||||
|
||||
class VulkanRenderWidget : public RenderWidget {
|
||||
public:
|
||||
explicit VulkanRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {
|
||||
windowHandle()->setSurfaceType(QWindow::VulkanSurface);
|
||||
}
|
||||
};
|
||||
|
||||
static Frontend::WindowSystemType GetWindowSystemType() {
|
||||
// Determine WSI type based on Qt platform.
|
||||
QString platform_name = QGuiApplication::platformName();
|
||||
if (platform_name == QStringLiteral("windows"))
|
||||
return Frontend::WindowSystemType::Windows;
|
||||
else if (platform_name == QStringLiteral("xcb"))
|
||||
return Frontend::WindowSystemType::X11;
|
||||
else if (platform_name == QStringLiteral("wayland"))
|
||||
return Frontend::WindowSystemType::Wayland;
|
||||
|
||||
LOG_CRITICAL(Frontend, "Unknown Qt platform!");
|
||||
return Frontend::WindowSystemType::Windows;
|
||||
}
|
||||
|
||||
void OpenGLWindow::exposeEvent(QExposeEvent* event) {
|
||||
QWindow::requestUpdate();
|
||||
QWindow::exposeEvent(event);
|
||||
static Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window) {
|
||||
Frontend::EmuWindow::WindowSystemInfo wsi;
|
||||
wsi.type = GetWindowSystemType();
|
||||
|
||||
// Our Win32 Qt external doesn't have the private API.
|
||||
#if defined(WIN32) || defined(__APPLE__)
|
||||
wsi.render_surface = window ? reinterpret_cast<void*>(window->winId()) : nullptr;
|
||||
#else
|
||||
QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface();
|
||||
wsi.display_connection = pni->nativeResourceForWindow("display", window);
|
||||
if (wsi.type == Frontend::WindowSystemType::Wayland)
|
||||
wsi.render_surface = window ? pni->nativeResourceForWindow("surface", window) : nullptr;
|
||||
else
|
||||
wsi.render_surface = window ? reinterpret_cast<void*>(window->winId()) : nullptr;
|
||||
#endif
|
||||
wsi.render_surface_scale = window ? static_cast<float>(window->devicePixelRatio()) : 1.0f;
|
||||
|
||||
return wsi;
|
||||
}
|
||||
|
||||
GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread)
|
||||
@@ -218,11 +369,11 @@ GRenderWindow::~GRenderWindow() {
|
||||
}
|
||||
|
||||
void GRenderWindow::MakeCurrent() {
|
||||
core_context->MakeCurrent();
|
||||
main_context->MakeCurrent();
|
||||
}
|
||||
|
||||
void GRenderWindow::DoneCurrent() {
|
||||
core_context->DoneCurrent();
|
||||
main_context->DoneCurrent();
|
||||
}
|
||||
|
||||
void GRenderWindow::PollEvents() {
|
||||
@@ -387,33 +538,70 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) {
|
||||
OnFramebufferSizeChanged();
|
||||
}
|
||||
|
||||
void GRenderWindow::InitRenderTarget() {
|
||||
std::unique_ptr<Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
|
||||
const Settings::GraphicsAPI graphics_api = Settings::values.graphics_api;
|
||||
if (graphics_api == Settings::GraphicsAPI::OpenGL ||
|
||||
graphics_api == Settings::GraphicsAPI::OpenGLES) {
|
||||
auto c = static_cast<OpenGLSharedContext*>(main_context.get());
|
||||
// Bind the shared contexts to the main surface in case the backend wants to take over
|
||||
// presentation
|
||||
return std::make_unique<OpenGLSharedContext>(c->GetShareContext(),
|
||||
child_widget->windowHandle());
|
||||
}
|
||||
|
||||
return std::make_unique<DummyContext>();
|
||||
}
|
||||
|
||||
bool GRenderWindow::InitRenderTarget() {
|
||||
ReleaseRenderTarget();
|
||||
|
||||
{
|
||||
// Create a dummy render widget so that Qt
|
||||
// places the render window at the correct position.
|
||||
const RenderWidget dummy_widget{this};
|
||||
}
|
||||
|
||||
first_frame = false;
|
||||
|
||||
GMainWindow* parent = GetMainWindow();
|
||||
QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr;
|
||||
child_window = new OpenGLWindow(parent_win_handle, this, QOpenGLContext::globalShareContext());
|
||||
child_window->create();
|
||||
child_widget = createWindowContainer(child_window, this);
|
||||
const Settings::GraphicsAPI graphics_api = Settings::values.graphics_api;
|
||||
switch (graphics_api) {
|
||||
case Settings::GraphicsAPI::OpenGL:
|
||||
case Settings::GraphicsAPI::OpenGLES:
|
||||
if (!InitializeOpenGL()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case Settings::GraphicsAPI::Vulkan:
|
||||
if (!InitializeVulkan()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Update the Window System information with the new render target
|
||||
window_info = GetWindowSystemInfo(child_widget->windowHandle());
|
||||
|
||||
child_widget->resize(Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight);
|
||||
|
||||
layout()->addWidget(child_widget);
|
||||
// Reset minimum required size to avoid resizing issues on the main window after restarting.
|
||||
setMinimumSize(1, 1);
|
||||
|
||||
core_context = CreateSharedContext();
|
||||
resize(Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight);
|
||||
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
|
||||
OnFramebufferSizeChanged();
|
||||
BackupGeometry();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GRenderWindow::ReleaseRenderTarget() {
|
||||
if (child_widget) {
|
||||
layout()->removeWidget(child_widget);
|
||||
delete child_widget;
|
||||
child_widget->deleteLater();
|
||||
child_widget = nullptr;
|
||||
}
|
||||
main_context.reset();
|
||||
}
|
||||
|
||||
void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) {
|
||||
@@ -423,7 +611,7 @@ void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_p
|
||||
screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32);
|
||||
VideoCore::RequestScreenshot(
|
||||
screenshot_image.bits(),
|
||||
[=] {
|
||||
[this, &screenshot_path] {
|
||||
const std::string std_screenshot_path = screenshot_path.toStdString();
|
||||
if (screenshot_image.mirrored(false, true).save(screenshot_path)) {
|
||||
LOG_INFO(Frontend, "Screenshot saved to \"{}\"", std_screenshot_path);
|
||||
@@ -438,6 +626,29 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal
|
||||
setMinimumSize(minimal_size.first, minimal_size.second);
|
||||
}
|
||||
|
||||
bool GRenderWindow::InitializeOpenGL() {
|
||||
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
|
||||
// WA_DontShowOnScreen, WA_DeleteOnClose
|
||||
auto child = new OpenGLRenderWidget(this);
|
||||
child_widget = child;
|
||||
child_widget->windowHandle()->create();
|
||||
auto context = std::make_shared<OpenGLSharedContext>(child->windowHandle());
|
||||
main_context = context;
|
||||
child->SetContext(
|
||||
std::make_unique<OpenGLSharedContext>(context->GetShareContext(), child->windowHandle()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GRenderWindow::InitializeVulkan() {
|
||||
auto child = new VulkanRenderWidget(this);
|
||||
child_widget = child;
|
||||
child_widget->windowHandle()->create();
|
||||
main_context = std::make_unique<DummyContext>();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) {
|
||||
this->emu_thread = emu_thread;
|
||||
}
|
||||
@@ -449,31 +660,3 @@ void GRenderWindow::OnEmulationStopping() {
|
||||
void GRenderWindow::showEvent(QShowEvent* event) {
|
||||
QWidget::showEvent(event);
|
||||
}
|
||||
|
||||
std::unique_ptr<Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
|
||||
return std::make_unique<GLContext>(QOpenGLContext::globalShareContext());
|
||||
}
|
||||
|
||||
GLContext::GLContext(QOpenGLContext* shared_context)
|
||||
: context(std::make_unique<QOpenGLContext>(shared_context->parent())),
|
||||
surface(std::make_unique<QOffscreenSurface>(nullptr)) {
|
||||
|
||||
// disable vsync for any shared contexts
|
||||
auto format = shared_context->format();
|
||||
format.setSwapInterval(0);
|
||||
|
||||
context->setShareContext(shared_context);
|
||||
context->setFormat(format);
|
||||
context->create();
|
||||
surface->setParent(shared_context->parent());
|
||||
surface->setFormat(format);
|
||||
surface->create();
|
||||
}
|
||||
|
||||
void GLContext::MakeCurrent() {
|
||||
context->makeCurrent(surface.get());
|
||||
}
|
||||
|
||||
void GLContext::DoneCurrent() {
|
||||
context->doneCurrent();
|
||||
}
|
||||
|
@@ -27,19 +27,6 @@ namespace VideoCore {
|
||||
enum class LoadCallbackStage;
|
||||
}
|
||||
|
||||
class GLContext : public Frontend::GraphicsContext {
|
||||
public:
|
||||
explicit GLContext(QOpenGLContext* shared_context);
|
||||
|
||||
void MakeCurrent() override;
|
||||
|
||||
void DoneCurrent() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<QOpenGLContext> context;
|
||||
std::unique_ptr<QOffscreenSurface> surface;
|
||||
};
|
||||
|
||||
class EmuThread final : public QThread {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -126,24 +113,6 @@ signals:
|
||||
void HideLoadingScreen();
|
||||
};
|
||||
|
||||
class OpenGLWindow : public QWindow {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context);
|
||||
|
||||
~OpenGLWindow();
|
||||
|
||||
void Present();
|
||||
|
||||
protected:
|
||||
bool event(QEvent* event) override;
|
||||
void exposeEvent(QExposeEvent* event) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<QOpenGLContext> context;
|
||||
QWidget* event_handler;
|
||||
};
|
||||
|
||||
class GRenderWindow : public QWidget, public Frontend::EmuWindow {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -179,13 +148,15 @@ public:
|
||||
|
||||
void focusOutEvent(QFocusEvent* event) override;
|
||||
|
||||
void InitRenderTarget();
|
||||
bool InitRenderTarget();
|
||||
|
||||
/// Destroy the previous run's child_widget which should also destroy the child_window
|
||||
void ReleaseRenderTarget();
|
||||
|
||||
void CaptureScreenshot(u32 res_scale, const QString& screenshot_path);
|
||||
|
||||
std::pair<u32, u32> ScaleTouch(const QPointF pos) const;
|
||||
|
||||
public slots:
|
||||
|
||||
void OnEmulationStarting(EmuThread* emu_thread);
|
||||
@@ -205,29 +176,29 @@ signals:
|
||||
void MouseActivity();
|
||||
|
||||
private:
|
||||
std::pair<u32, u32> ScaleTouch(QPointF pos) const;
|
||||
void TouchBeginEvent(const QTouchEvent* event);
|
||||
void TouchUpdateEvent(const QTouchEvent* event);
|
||||
void TouchEndEvent();
|
||||
|
||||
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
|
||||
|
||||
std::unique_ptr<GraphicsContext> core_context;
|
||||
|
||||
QByteArray geometry;
|
||||
|
||||
/// Native window handle that backs this presentation widget
|
||||
QWindow* child_window = nullptr;
|
||||
|
||||
/// In order to embed the window into GRenderWindow, you need to use createWindowContainer to
|
||||
/// put the child_window into a widget then add it to the layout. This child_widget can be
|
||||
/// parented to GRenderWindow and use Qt's lifetime system
|
||||
QWidget* child_widget = nullptr;
|
||||
bool InitializeOpenGL();
|
||||
bool InitializeVulkan();
|
||||
|
||||
EmuThread* emu_thread;
|
||||
|
||||
// Main context that will be shared with all other contexts that are requested.
|
||||
// If this is used in a shared context setting, then this should not be used directly, but
|
||||
// should instead be shared from
|
||||
std::shared_ptr<Frontend::GraphicsContext> main_context;
|
||||
|
||||
/// Temporary storage of the screenshot taken
|
||||
QImage screenshot_image;
|
||||
|
||||
QByteArray geometry;
|
||||
|
||||
QWidget* child_widget = nullptr;
|
||||
|
||||
bool first_frame = false;
|
||||
|
||||
protected:
|
||||
|
@@ -37,7 +37,7 @@ CheatDialog::CheatDialog(QWidget* parent)
|
||||
connect(ui->textNotes, &QPlainTextEdit::textChanged, this, &CheatDialog::OnTextEdited);
|
||||
connect(ui->textCode, &QPlainTextEdit::textChanged, this, &CheatDialog::OnTextEdited);
|
||||
|
||||
connect(ui->buttonSave, &QPushButton::clicked,
|
||||
connect(ui->buttonSave, &QPushButton::clicked, this,
|
||||
[this] { SaveCheat(ui->tableCheats->currentRow()); });
|
||||
connect(ui->buttonDelete, &QPushButton::clicked, this, &CheatDialog::OnDeleteCheat);
|
||||
|
||||
@@ -91,7 +91,7 @@ bool CheatDialog::SaveCheat(int row) {
|
||||
}
|
||||
|
||||
// Check if the cheat lines are valid
|
||||
auto code_lines = ui->textCode->toPlainText().split(QLatin1Char{'\n'}, QString::SkipEmptyParts);
|
||||
auto code_lines = ui->textCode->toPlainText().split(QLatin1Char{'\n'}, Qt::SkipEmptyParts);
|
||||
for (int i = 0; i < code_lines.size(); ++i) {
|
||||
Cheats::GatewayCheat::CheatLine cheat_line(code_lines[i].toStdString());
|
||||
if (cheat_line.valid)
|
||||
@@ -190,8 +190,9 @@ void CheatDialog::OnDeleteCheat() {
|
||||
if (newly_created) {
|
||||
newly_created = false;
|
||||
} else {
|
||||
Core::System::GetInstance().CheatEngine().RemoveCheat(ui->tableCheats->currentRow());
|
||||
Core::System::GetInstance().CheatEngine().SaveCheatFile();
|
||||
auto& cheat_engine = Core::System::GetInstance().CheatEngine();
|
||||
cheat_engine.RemoveCheat(ui->tableCheats->currentRow());
|
||||
cheat_engine.SaveCheatFile();
|
||||
}
|
||||
|
||||
LoadCheats();
|
||||
|
@@ -331,6 +331,9 @@ void Config::ReadDebuggingValues() {
|
||||
qt_config->value(QStringLiteral("record_frame_times"), false).toBool();
|
||||
Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool();
|
||||
Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt();
|
||||
Settings::values.renderer_debug = ReadSetting(QStringLiteral("renderer_debug"), false).toBool();
|
||||
Settings::values.dump_command_buffers =
|
||||
ReadSetting(QStringLiteral("dump_command_buffers"), false).toBool();
|
||||
|
||||
qt_config->beginGroup(QStringLiteral("LLE"));
|
||||
for (const auto& service_module : Service::service_module_map) {
|
||||
@@ -482,6 +485,10 @@ void Config::ReadPathValues() {
|
||||
void Config::ReadRendererValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Renderer"));
|
||||
|
||||
Settings::values.graphics_api = static_cast<Settings::GraphicsAPI>(
|
||||
ReadSetting(QStringLiteral("graphics_api"), static_cast<u32>(Settings::GraphicsAPI::OpenGL))
|
||||
.toUInt());
|
||||
Settings::values.physical_device = ReadSetting(QStringLiteral("physical_device"), 0).toUInt();
|
||||
Settings::values.use_hw_renderer =
|
||||
ReadSetting(QStringLiteral("use_hw_renderer"), true).toBool();
|
||||
Settings::values.use_hw_shader = ReadSetting(QStringLiteral("use_hw_shader"), true).toBool();
|
||||
@@ -521,7 +528,7 @@ void Config::ReadRendererValues() {
|
||||
void Config::ReadShortcutValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Shortcuts"));
|
||||
|
||||
for (auto [name, group, shortcut] : default_hotkeys) {
|
||||
for (const auto& [name, group, shortcut] : default_hotkeys) {
|
||||
auto [keyseq, context] = shortcut;
|
||||
qt_config->beginGroup(group);
|
||||
qt_config->beginGroup(name);
|
||||
@@ -556,7 +563,7 @@ void Config::ReadSystemValues() {
|
||||
// https://developers.google.com/media/vp9/live-encoding
|
||||
const QString DEFAULT_VIDEO_ENCODER_OPTIONS =
|
||||
QStringLiteral("quality:realtime,speed:6,tile-columns:4,frame-parallel:1,threads:8,row-mt:1");
|
||||
const QString DEFAULT_AUDIO_ENCODER_OPTIONS = QString{};
|
||||
const QString DEFAULT_AUDIO_ENCODER_OPTIONS = QStringLiteral("");
|
||||
|
||||
void Config::ReadVideoDumpingValues() {
|
||||
qt_config->beginGroup(QStringLiteral("VideoDumping"));
|
||||
@@ -886,6 +893,9 @@ void Config::SaveDebuggingValues() {
|
||||
qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times);
|
||||
WriteSetting(QStringLiteral("use_gdbstub"), Settings::values.use_gdbstub, false);
|
||||
WriteSetting(QStringLiteral("gdbstub_port"), Settings::values.gdbstub_port, 24689);
|
||||
WriteSetting(QStringLiteral("renderer_debug"), Settings::values.renderer_debug, false);
|
||||
WriteSetting(QStringLiteral("dump_command_buffers"), Settings::values.dump_command_buffers,
|
||||
false);
|
||||
|
||||
qt_config->beginGroup(QStringLiteral("LLE"));
|
||||
for (const auto& service_module : Settings::values.lle_modules) {
|
||||
@@ -994,6 +1004,9 @@ void Config::SavePathValues() {
|
||||
void Config::SaveRendererValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Renderer"));
|
||||
|
||||
WriteSetting(QStringLiteral("graphics_api"), static_cast<u32>(Settings::values.graphics_api),
|
||||
static_cast<u32>(Settings::GraphicsAPI::OpenGL));
|
||||
WriteSetting(QStringLiteral("physical_device"), Settings::values.physical_device, 0);
|
||||
WriteSetting(QStringLiteral("use_hw_renderer"), Settings::values.use_hw_renderer, true);
|
||||
WriteSetting(QStringLiteral("use_hw_shader"), Settings::values.use_hw_shader, true);
|
||||
#ifdef __APPLE__
|
||||
@@ -1015,9 +1028,9 @@ void Config::SaveRendererValues() {
|
||||
200);
|
||||
|
||||
// Cast to double because Qt's written float values are not human-readable
|
||||
WriteSetting(QStringLiteral("bg_red"), (double)Settings::values.bg_red, 0.0);
|
||||
WriteSetting(QStringLiteral("bg_green"), (double)Settings::values.bg_green, 0.0);
|
||||
WriteSetting(QStringLiteral("bg_blue"), (double)Settings::values.bg_blue, 0.0);
|
||||
WriteSetting(QStringLiteral("bg_red"), static_cast<double>(Settings::values.bg_red), 0.0);
|
||||
WriteSetting(QStringLiteral("bg_green"), static_cast<double>(Settings::values.bg_green), 0.0);
|
||||
WriteSetting(QStringLiteral("bg_blue"), static_cast<double>(Settings::values.bg_blue), 0.0);
|
||||
|
||||
WriteSetting(QStringLiteral("texture_filter_name"),
|
||||
QString::fromStdString(Settings::values.texture_filter_name),
|
||||
|
@@ -9,8 +9,6 @@
|
||||
#include <QMessageBox>
|
||||
#include <QWidget>
|
||||
#include "citra_qt/configuration/configure_camera.h"
|
||||
#include "citra_qt/uisettings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/camera/factory.h"
|
||||
#include "core/frontend/camera/interface.h"
|
||||
#include "core/hle/service/cam/cam.h"
|
||||
@@ -91,7 +89,7 @@ void ConfigureCamera::ConnectEvents() {
|
||||
SetConfiguration();
|
||||
});
|
||||
connect(ui->toolButton, &QToolButton::clicked, this, &ConfigureCamera::OnToolButtonClicked);
|
||||
connect(ui->preview_button, &QPushButton::clicked, this, [=] { StartPreviewing(); });
|
||||
connect(ui->preview_button, &QPushButton::clicked, this, [this] { StartPreviewing(); });
|
||||
connect(ui->prompt_before_load, &QCheckBox::stateChanged, this, [this](int state) {
|
||||
ui->camera_file->setDisabled(state == Qt::Checked);
|
||||
ui->toolButton->setDisabled(state == Qt::Checked);
|
||||
@@ -99,12 +97,11 @@ void ConfigureCamera::ConnectEvents() {
|
||||
ui->camera_file->setText(QString{});
|
||||
}
|
||||
});
|
||||
connect(ui->camera_file, &QLineEdit::textChanged, this, [=] { StopPreviewing(); });
|
||||
connect(ui->system_camera,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
|
||||
[=] { StopPreviewing(); });
|
||||
connect(ui->camera_flip, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
this, [=] { StopPreviewing(); });
|
||||
connect(ui->camera_file, &QLineEdit::textChanged, this, [this] { StopPreviewing(); });
|
||||
connect(ui->system_camera, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
[this] { StopPreviewing(); });
|
||||
connect(ui->camera_flip, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
[this] { StopPreviewing(); });
|
||||
}
|
||||
|
||||
void ConfigureCamera::UpdateCameraMode() {
|
||||
|
@@ -8,8 +8,6 @@
|
||||
#include "citra_qt/debugger/console.h"
|
||||
#include "citra_qt/uisettings.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/filter.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/settings.h"
|
||||
@@ -24,7 +22,11 @@ ConfigureDebug::ConfigureDebug(QWidget* parent)
|
||||
QString path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LogDir));
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
||||
});
|
||||
ui->toggle_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn());
|
||||
|
||||
const bool is_powered_on = Core::System::GetInstance().IsPoweredOn();
|
||||
ui->toggle_cpu_jit->setEnabled(!is_powered_on);
|
||||
ui->toggle_renderer_debug->setEnabled(!is_powered_on);
|
||||
ui->toggle_dump_command_buffers->setEnabled(!is_powered_on);
|
||||
}
|
||||
|
||||
ConfigureDebug::~ConfigureDebug() = default;
|
||||
@@ -37,6 +39,8 @@ void ConfigureDebug::SetConfiguration() {
|
||||
ui->toggle_console->setChecked(UISettings::values.show_console);
|
||||
ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter));
|
||||
ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit);
|
||||
ui->toggle_renderer_debug->setChecked(Settings::values.renderer_debug);
|
||||
ui->toggle_dump_command_buffers->setChecked(Settings::values.dump_command_buffers);
|
||||
}
|
||||
|
||||
void ConfigureDebug::ApplyConfiguration() {
|
||||
@@ -49,6 +53,8 @@ void ConfigureDebug::ApplyConfiguration() {
|
||||
filter.ParseFilterString(Settings::values.log_filter);
|
||||
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();
|
||||
}
|
||||
|
||||
void ConfigureDebug::RetranslateUI() {
|
||||
|
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>443</width>
|
||||
<height>300</height>
|
||||
<width>454</width>
|
||||
<height>356</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -114,11 +114,31 @@
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<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>
|
||||
<widget class="QCheckBox" name="toggle_renderer_debug">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Enables debug reporting in the currently selected graphics API. Causes measurable performance loss, don't enable unless for debugging purposes</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable debug renderer</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_dump_command_buffers">
|
||||
<property name="text">
|
||||
<string>Dump command buffers</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
@@ -10,15 +10,27 @@
|
||||
#include "core/core.h"
|
||||
#include "core/settings.h"
|
||||
#include "ui_configure_graphics.h"
|
||||
#include "video_core/renderer_opengl/post_processing_opengl.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
|
||||
ConfigureGraphics::ConfigureGraphics(QWidget* parent)
|
||||
: QWidget(parent), ui(std::make_unique<Ui::ConfigureGraphics>()) {
|
||||
ui->setupUi(this);
|
||||
DiscoverPhysicalDevices();
|
||||
SetConfiguration();
|
||||
|
||||
ui->hw_renderer_group->setEnabled(ui->toggle_hw_renderer->isChecked());
|
||||
ui->toggle_vsync_new->setEnabled(!Core::System::GetInstance().IsPoweredOn());
|
||||
const bool not_running = !Core::System::GetInstance().IsPoweredOn();
|
||||
const bool hw_renderer_enabled = ui->toggle_hw_renderer->isChecked();
|
||||
ui->toggle_hw_renderer->setEnabled(not_running);
|
||||
ui->hw_renderer_group->setEnabled(hw_renderer_enabled && not_running);
|
||||
ui->toggle_vsync_new->setEnabled(not_running);
|
||||
ui->graphics_api_combo->setEnabled(not_running);
|
||||
ui->toggle_shader_jit->setEnabled(not_running);
|
||||
ui->toggle_disk_shader_cache->setEnabled(hw_renderer_enabled && not_running);
|
||||
ui->physical_device_combo->setEnabled(not_running);
|
||||
SetPhysicalDeviceComboVisibility(ui->graphics_api_combo->currentIndex());
|
||||
|
||||
connect(ui->graphics_api_combo, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
&ConfigureGraphics::SetPhysicalDeviceComboVisibility);
|
||||
|
||||
connect(ui->toggle_hw_renderer, &QCheckBox::toggled, this, [this] {
|
||||
auto checked = ui->toggle_hw_renderer->isChecked();
|
||||
@@ -31,7 +43,7 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
|
||||
ui->toggle_hw_shader->isChecked());
|
||||
|
||||
connect(ui->toggle_hw_shader, &QCheckBox::toggled, this, [this] {
|
||||
auto checked = ui->toggle_hw_shader->isChecked();
|
||||
const bool checked = ui->toggle_hw_shader->isChecked();
|
||||
ui->hw_shader_group->setEnabled(checked);
|
||||
ui->toggle_disk_shader_cache->setEnabled(checked);
|
||||
});
|
||||
@@ -69,6 +81,8 @@ void ConfigureGraphics::SetConfiguration() {
|
||||
ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit);
|
||||
ui->toggle_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache);
|
||||
ui->toggle_vsync_new->setChecked(Settings::values.use_vsync_new);
|
||||
ui->graphics_api_combo->setCurrentIndex(static_cast<int>(Settings::values.graphics_api));
|
||||
ui->physical_device_combo->setCurrentIndex(static_cast<int>(Settings::values.physical_device));
|
||||
}
|
||||
|
||||
void ConfigureGraphics::ApplyConfiguration() {
|
||||
@@ -79,8 +93,29 @@ void ConfigureGraphics::ApplyConfiguration() {
|
||||
Settings::values.use_shader_jit = ui->toggle_shader_jit->isChecked();
|
||||
Settings::values.use_disk_shader_cache = ui->toggle_disk_shader_cache->isChecked();
|
||||
Settings::values.use_vsync_new = ui->toggle_vsync_new->isChecked();
|
||||
Settings::values.graphics_api =
|
||||
static_cast<Settings::GraphicsAPI>(ui->graphics_api_combo->currentIndex());
|
||||
Settings::values.physical_device = static_cast<u16>(ui->physical_device_combo->currentIndex());
|
||||
}
|
||||
|
||||
void ConfigureGraphics::RetranslateUI() {
|
||||
ui->retranslateUi(this);
|
||||
}
|
||||
|
||||
void ConfigureGraphics::DiscoverPhysicalDevices() {
|
||||
Vulkan::Instance instance{};
|
||||
const auto physical_devices = instance.GetPhysicalDevices();
|
||||
|
||||
ui->physical_device_combo->clear();
|
||||
for (const vk::PhysicalDevice& physical_device : physical_devices) {
|
||||
const QString name = QString::fromLocal8Bit(physical_device.getProperties().deviceName);
|
||||
ui->physical_device_combo->addItem(name);
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureGraphics::SetPhysicalDeviceComboVisibility(int index) {
|
||||
const auto graphics_api = static_cast<Settings::GraphicsAPI>(index);
|
||||
const bool is_visible = graphics_api == Settings::GraphicsAPI::Vulkan;
|
||||
ui->physical_device_label->setVisible(is_visible);
|
||||
ui->physical_device_combo->setVisible(is_visible);
|
||||
}
|
||||
|
@@ -24,6 +24,11 @@ public:
|
||||
|
||||
void UpdateBackgroundColorButton(const QColor& color);
|
||||
|
||||
private:
|
||||
void DiscoverPhysicalDevices();
|
||||
void SetPhysicalDeviceComboVisibility(int index);
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::ConfigureGraphics> ui;
|
||||
QColor bg_color;
|
||||
};
|
||||
|
@@ -20,6 +20,59 @@
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="apiBox">
|
||||
<property name="title">
|
||||
<string>API Settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="graphics_api_label">
|
||||
<property name="text">
|
||||
<string>Graphics API</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="graphics_api_combo">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>OpenGL</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>OpenGLES</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Vulkan</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<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>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="rendererBox">
|
||||
<property name="title">
|
||||
|
@@ -192,10 +192,10 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
if (!button_map[button_id])
|
||||
continue;
|
||||
button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(button_map[button_id], &QPushButton::clicked, [=]() {
|
||||
connect(button_map[button_id], &QPushButton::clicked, [this, button_id]() {
|
||||
HandleClick(
|
||||
button_map[button_id],
|
||||
[=](const Common::ParamPackage& params) {
|
||||
[this, button_id](const Common::ParamPackage& params) {
|
||||
buttons_param[button_id] = params;
|
||||
// If the user closes the dialog, the changes are reverted in
|
||||
// `GMainWindow::OnConfigure()`
|
||||
@@ -204,16 +204,16 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
},
|
||||
InputCommon::Polling::DeviceType::Button);
|
||||
});
|
||||
connect(button_map[button_id], &QPushButton::customContextMenuRequested,
|
||||
[=](const QPoint& menu_location) {
|
||||
connect(button_map[button_id], &QPushButton::customContextMenuRequested, this,
|
||||
[this, button_id](const QPoint& menu_location) {
|
||||
QMenu context_menu;
|
||||
context_menu.addAction(tr("Clear"), [&] {
|
||||
context_menu.addAction(tr("Clear"), this, [&] {
|
||||
buttons_param[button_id].Clear();
|
||||
button_map[button_id]->setText(tr("[not set]"));
|
||||
ApplyConfiguration();
|
||||
Settings::SaveProfile(ui->profile->currentIndex());
|
||||
});
|
||||
context_menu.addAction(tr("Restore Default"), [&] {
|
||||
context_menu.addAction(tr("Restore Default"), this, [&] {
|
||||
buttons_param[button_id] = Common::ParamPackage{
|
||||
InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
|
||||
button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
|
||||
@@ -230,27 +230,29 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
continue;
|
||||
analog_map_buttons[analog_id][sub_button_id]->setContextMenuPolicy(
|
||||
Qt::CustomContextMenu);
|
||||
connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::clicked, [=]() {
|
||||
HandleClick(
|
||||
analog_map_buttons[analog_id][sub_button_id],
|
||||
[=](const Common::ParamPackage& params) {
|
||||
SetAnalogButton(params, analogs_param[analog_id],
|
||||
analog_sub_buttons[sub_button_id]);
|
||||
ApplyConfiguration();
|
||||
Settings::SaveProfile(ui->profile->currentIndex());
|
||||
},
|
||||
InputCommon::Polling::DeviceType::Button);
|
||||
});
|
||||
connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::clicked, this,
|
||||
[this, analog_id, sub_button_id]() {
|
||||
HandleClick(
|
||||
analog_map_buttons[analog_id][sub_button_id],
|
||||
[this, analog_id, sub_button_id](const Common::ParamPackage& params) {
|
||||
SetAnalogButton(params, analogs_param[analog_id],
|
||||
analog_sub_buttons[sub_button_id]);
|
||||
ApplyConfiguration();
|
||||
Settings::SaveProfile(ui->profile->currentIndex());
|
||||
},
|
||||
InputCommon::Polling::DeviceType::Button);
|
||||
});
|
||||
connect(analog_map_buttons[analog_id][sub_button_id],
|
||||
&QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) {
|
||||
&QPushButton::customContextMenuRequested, this,
|
||||
[this, analog_id, sub_button_id](const QPoint& menu_location) {
|
||||
QMenu context_menu;
|
||||
context_menu.addAction(tr("Clear"), [&] {
|
||||
context_menu.addAction(tr("Clear"), this, [&] {
|
||||
analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
|
||||
analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
|
||||
ApplyConfiguration();
|
||||
Settings::SaveProfile(ui->profile->currentIndex());
|
||||
});
|
||||
context_menu.addAction(tr("Restore Default"), [&] {
|
||||
context_menu.addAction(tr("Restore Default"), this, [&] {
|
||||
Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
|
||||
Config::default_analogs[analog_id][sub_button_id])};
|
||||
SetAnalogButton(params, analogs_param[analog_id],
|
||||
@@ -264,7 +266,7 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
menu_location));
|
||||
});
|
||||
}
|
||||
connect(analog_map_stick[analog_id], &QPushButton::clicked, [=]() {
|
||||
connect(analog_map_stick[analog_id], &QPushButton::clicked, this, [this, analog_id]() {
|
||||
if (QMessageBox::information(
|
||||
this, tr("Information"),
|
||||
tr("After pressing OK, first move your joystick horizontally, "
|
||||
@@ -272,7 +274,7 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) {
|
||||
HandleClick(
|
||||
analog_map_stick[analog_id],
|
||||
[=](const Common::ParamPackage& params) {
|
||||
[this, analog_id](const Common::ParamPackage& params) {
|
||||
analogs_param[analog_id] = params;
|
||||
ApplyConfiguration();
|
||||
Settings::SaveProfile(ui->profile->currentIndex());
|
||||
@@ -280,29 +282,31 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
InputCommon::Polling::DeviceType::Analog);
|
||||
}
|
||||
});
|
||||
connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, [=] {
|
||||
const int slider_value = analog_map_deadzone_and_modifier_slider[analog_id]->value();
|
||||
const auto engine = analogs_param[analog_id].Get("engine", "");
|
||||
if (engine == "sdl" || engine == "gcpad") {
|
||||
analog_map_deadzone_and_modifier_slider_label[analog_id]->setText(
|
||||
tr("Deadzone: %1%").arg(slider_value));
|
||||
analogs_param[analog_id].Set("deadzone", slider_value / 100.0f);
|
||||
} else {
|
||||
analog_map_deadzone_and_modifier_slider_label[analog_id]->setText(
|
||||
tr("Modifier Scale: %1%").arg(slider_value));
|
||||
analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f);
|
||||
}
|
||||
ApplyConfiguration();
|
||||
Settings::SaveProfile(ui->profile->currentIndex());
|
||||
});
|
||||
connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, this,
|
||||
[this, analog_id] {
|
||||
const int slider_value =
|
||||
analog_map_deadzone_and_modifier_slider[analog_id]->value();
|
||||
const auto engine = analogs_param[analog_id].Get("engine", "");
|
||||
if (engine == "sdl" || engine == "gcpad") {
|
||||
analog_map_deadzone_and_modifier_slider_label[analog_id]->setText(
|
||||
tr("Deadzone: %1%").arg(slider_value));
|
||||
analogs_param[analog_id].Set("deadzone", slider_value / 100.0f);
|
||||
} else {
|
||||
analog_map_deadzone_and_modifier_slider_label[analog_id]->setText(
|
||||
tr("Modifier Scale: %1%").arg(slider_value));
|
||||
analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f);
|
||||
}
|
||||
ApplyConfiguration();
|
||||
Settings::SaveProfile(ui->profile->currentIndex());
|
||||
});
|
||||
}
|
||||
|
||||
// The Circle Mod button is common for both the sticks, so update the modifier settings
|
||||
// for both the sticks.
|
||||
connect(ui->buttonCircleMod, &QPushButton::clicked, [=]() {
|
||||
connect(ui->buttonCircleMod, &QPushButton::clicked, this, [this]() {
|
||||
HandleClick(
|
||||
ui->buttonCircleMod,
|
||||
[=](const Common::ParamPackage& params) {
|
||||
[this](const Common::ParamPackage& params) {
|
||||
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs;
|
||||
analog_id++) {
|
||||
SetAnalogButton(params, analogs_param[analog_id], "modifier");
|
||||
@@ -312,10 +316,10 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
},
|
||||
InputCommon::Polling::DeviceType::Button);
|
||||
});
|
||||
connect(ui->buttonCircleMod, &QPushButton::customContextMenuRequested,
|
||||
connect(ui->buttonCircleMod, &QPushButton::customContextMenuRequested, this,
|
||||
[&](const QPoint& menu_location) {
|
||||
QMenu context_menu;
|
||||
context_menu.addAction(tr("Clear"), [&] {
|
||||
context_menu.addAction(tr("Clear"), this, [&] {
|
||||
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs;
|
||||
analog_id++) {
|
||||
analogs_param[analog_id].Erase("modifier");
|
||||
@@ -325,7 +329,7 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
Settings::SaveProfile(ui->profile->currentIndex());
|
||||
});
|
||||
|
||||
context_menu.addAction(tr("Restore Default"), [&] {
|
||||
context_menu.addAction(tr("Restore Default"), this, [&] {
|
||||
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs;
|
||||
analog_id++) {
|
||||
Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
|
||||
@@ -341,7 +345,7 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
context_menu.exec(ui->buttonCircleMod->mapToGlobal(menu_location));
|
||||
});
|
||||
|
||||
connect(ui->buttonMotionTouch, &QPushButton::clicked, [this] {
|
||||
connect(ui->buttonMotionTouch, &QPushButton::clicked, this, [this] {
|
||||
QDialog* motion_touch_dialog = new ConfigureMotionTouch(this);
|
||||
return motion_touch_dialog->exec();
|
||||
});
|
||||
@@ -356,18 +360,17 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
connect(ui->buttonDelete, &QPushButton::clicked, this, &ConfigureInput::DeleteProfile);
|
||||
connect(ui->buttonRename, &QPushButton::clicked, this, &ConfigureInput::RenameProfile);
|
||||
|
||||
connect(ui->profile, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
[this](int i) {
|
||||
ApplyConfiguration();
|
||||
Settings::SaveProfile(Settings::values.current_input_profile_index);
|
||||
Settings::LoadProfile(i);
|
||||
LoadConfiguration();
|
||||
});
|
||||
connect(ui->profile, qOverload<int>(&QComboBox::currentIndexChanged), this, [this](int i) {
|
||||
ApplyConfiguration();
|
||||
Settings::SaveProfile(Settings::values.current_input_profile_index);
|
||||
Settings::LoadProfile(i);
|
||||
LoadConfiguration();
|
||||
});
|
||||
|
||||
timeout_timer->setSingleShot(true);
|
||||
connect(timeout_timer.get(), &QTimer::timeout, [this]() { SetPollingResult({}, true); });
|
||||
connect(timeout_timer.get(), &QTimer::timeout, this, [this]() { SetPollingResult({}, true); });
|
||||
|
||||
connect(poll_timer.get(), &QTimer::timeout, [this]() {
|
||||
connect(poll_timer.get(), &QTimer::timeout, this, [this]() {
|
||||
Common::ParamPackage params;
|
||||
for (auto& poller : device_pollers) {
|
||||
params = poller->GetNextInput();
|
||||
@@ -554,7 +557,7 @@ void ConfigureInput::AutoMap() {
|
||||
QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) {
|
||||
return;
|
||||
}
|
||||
input_setter = [=](const Common::ParamPackage& params) {
|
||||
input_setter = [this](const Common::ParamPackage& params) {
|
||||
MapFromButton(params);
|
||||
ApplyConfiguration();
|
||||
Settings::SaveProfile(ui->profile->currentIndex());
|
||||
|
@@ -46,6 +46,9 @@ CalibrationConfigurationDialog::CalibrationConfigurationDialog(QWidget* parent,
|
||||
case CalibrationConfigurationJob::Status::Completed:
|
||||
text = tr("Configuration completed!");
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Frontend, "Unknown calibration status {}", status);
|
||||
break;
|
||||
}
|
||||
QMetaObject::invokeMethod(this, "UpdateLabelText", Q_ARG(QString, text));
|
||||
if (status == CalibrationConfigurationJob::Status::Completed) {
|
||||
@@ -99,9 +102,9 @@ ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent)
|
||||
"style=\"text-decoration: underline; color:#039be5;\">Learn More</span></a>"));
|
||||
|
||||
timeout_timer->setSingleShot(true);
|
||||
connect(timeout_timer.get(), &QTimer::timeout, [this]() { SetPollingResult({}, true); });
|
||||
connect(timeout_timer.get(), &QTimer::timeout, this, [this]() { SetPollingResult({}, true); });
|
||||
|
||||
connect(poll_timer.get(), &QTimer::timeout, [this]() {
|
||||
connect(poll_timer.get(), &QTimer::timeout, this, [this]() {
|
||||
Common::ParamPackage params;
|
||||
for (auto& poller : device_pollers) {
|
||||
params = poller->GetNextInput();
|
||||
@@ -202,7 +205,7 @@ void ConfigureMotionTouch::ConnectEvents() {
|
||||
connect(ui->touch_provider,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
|
||||
[this]([[maybe_unused]] int index) { UpdateUiDisplay(); });
|
||||
connect(ui->motion_controller_button, &QPushButton::clicked, [=]() {
|
||||
connect(ui->motion_controller_button, &QPushButton::clicked, this, [this]() {
|
||||
if (QMessageBox::information(this, tr("Information"),
|
||||
tr("After pressing OK, press a button on the controller whose "
|
||||
"motion you want to track."),
|
||||
@@ -210,7 +213,7 @@ void ConfigureMotionTouch::ConnectEvents() {
|
||||
ui->motion_controller_button->setText(tr("[press button]"));
|
||||
ui->motion_controller_button->setFocus();
|
||||
|
||||
input_setter = [=](const Common::ParamPackage& params) {
|
||||
input_setter = [this](const Common::ParamPackage& params) {
|
||||
guid = params.Get("guid", "0");
|
||||
port = params.Get("port", 0);
|
||||
};
|
||||
|
@@ -508,7 +508,7 @@ void TouchScreenPreview::mouseMoveEvent(QMouseEvent* event) {
|
||||
}
|
||||
const auto pos = MapToDeviceCoords(event->x(), event->y());
|
||||
if (pos) {
|
||||
coord_label->setText(QStringLiteral("X: %1, Y: %2").arg(pos->x()).arg(pos->y()));
|
||||
coord_label->setText(QStringLiteral("X: %1, Y: %2").arg(pos->x(), pos->y()));
|
||||
} else {
|
||||
coord_label->clear();
|
||||
}
|
||||
@@ -568,7 +568,7 @@ bool TouchScreenPreview::eventFilter(QObject* obj, QEvent* event) {
|
||||
emit DotMoved(drag_state.dot->property(PropId).toInt(), *device_coord);
|
||||
if (coord_label) {
|
||||
coord_label->setText(
|
||||
QStringLiteral("X: %1, Y: %2").arg(device_coord->x()).arg(device_coord->y()));
|
||||
QStringLiteral("X: %1, Y: %2").arg(device_coord->x(), device_coord->y()));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@@ -9,7 +9,6 @@
|
||||
#include <QVBoxLayout>
|
||||
#include "citra_qt/debugger/graphics/graphics_breakpoints.h"
|
||||
#include "citra_qt/debugger/graphics/graphics_breakpoints_p.h"
|
||||
#include "common/assert.h"
|
||||
|
||||
BreakPointModel::BreakPointModel(std::shared_ptr<Pica::DebugContext> debug_context, QObject* parent)
|
||||
: QAbstractListModel(parent), context_weak(debug_context),
|
||||
@@ -60,12 +59,15 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const {
|
||||
}
|
||||
|
||||
Qt::ItemFlags BreakPointModel::flags(const QModelIndex& index) const {
|
||||
if (!index.isValid())
|
||||
return 0;
|
||||
if (!index.isValid()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Qt::ItemFlags flags = Qt::ItemIsEnabled;
|
||||
if (index.column() == 0)
|
||||
if (index.column() == 0) {
|
||||
flags |= Qt::ItemIsUserCheckable;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
|
@@ -14,7 +14,6 @@
|
||||
#include <QTreeView>
|
||||
#include <QVBoxLayout>
|
||||
#include "citra_qt/debugger/graphics/graphics_cmdlists.h"
|
||||
#include "citra_qt/util/spinbox.h"
|
||||
#include "citra_qt/util/util.h"
|
||||
#include "common/vector_math.h"
|
||||
#include "core/core.h"
|
||||
@@ -130,7 +129,7 @@ void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) {
|
||||
COMMAND_IN_RANGE(command_id, texturing.texture1) ||
|
||||
COMMAND_IN_RANGE(command_id, texturing.texture2)) {
|
||||
|
||||
unsigned texture_index;
|
||||
[[maybe_unused]] u32 texture_index;
|
||||
if (COMMAND_IN_RANGE(command_id, texturing.texture0)) {
|
||||
texture_index = 0;
|
||||
} else if (COMMAND_IN_RANGE(command_id, texturing.texture1)) {
|
||||
|
@@ -16,7 +16,6 @@
|
||||
#include "citra_qt/util/spinbox.h"
|
||||
#include "common/color.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hw/gpu.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/pica_state.h"
|
||||
#include "video_core/regs_framebuffer.h"
|
||||
@@ -34,12 +33,15 @@ void SurfacePicture::mousePressEvent(QMouseEvent* event) {
|
||||
if (!(event->buttons() & Qt::LeftButton))
|
||||
return;
|
||||
|
||||
if (pixmap() == nullptr)
|
||||
const QPixmap pixmap = this->pixmap(Qt::ReturnByValue);
|
||||
if (pixmap.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (surface_widget)
|
||||
surface_widget->Pick(event->x() * pixmap()->width() / width(),
|
||||
event->y() * pixmap()->height() / height());
|
||||
if (surface_widget) {
|
||||
surface_widget->Pick(event->x() * pixmap.width() / width(),
|
||||
event->y() * pixmap.height() / height());
|
||||
}
|
||||
}
|
||||
|
||||
void SurfacePicture::mouseMoveEvent(QMouseEvent* event) {
|
||||
@@ -314,57 +316,46 @@ void GraphicsSurfaceWidget::Pick(int x, int y) {
|
||||
case Format::RGBA8: {
|
||||
auto value = Color::DecodeRGBA8(pixel) / 255.0f;
|
||||
return QStringLiteral("Red: %1, Green: %2, Blue: %3, Alpha: %4")
|
||||
.arg(QString::number(value.r(), 'f', 2))
|
||||
.arg(QString::number(value.g(), 'f', 2))
|
||||
.arg(QString::number(value.b(), 'f', 2))
|
||||
.arg(QString::number(value.a(), 'f', 2));
|
||||
.arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2),
|
||||
QString::number(value.b(), 'f', 2), QString::number(value.a(), 'f', 2));
|
||||
}
|
||||
case Format::RGB8: {
|
||||
auto value = Color::DecodeRGB8(pixel) / 255.0f;
|
||||
return QStringLiteral("Red: %1, Green: %2, Blue: %3")
|
||||
.arg(QString::number(value.r(), 'f', 2))
|
||||
.arg(QString::number(value.g(), 'f', 2))
|
||||
.arg(QString::number(value.b(), 'f', 2));
|
||||
.arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2),
|
||||
QString::number(value.b(), 'f', 2));
|
||||
}
|
||||
case Format::RGB5A1: {
|
||||
auto value = Color::DecodeRGB5A1(pixel) / 255.0f;
|
||||
return QStringLiteral("Red: %1, Green: %2, Blue: %3, Alpha: %4")
|
||||
.arg(QString::number(value.r(), 'f', 2))
|
||||
.arg(QString::number(value.g(), 'f', 2))
|
||||
.arg(QString::number(value.b(), 'f', 2))
|
||||
.arg(QString::number(value.a(), 'f', 2));
|
||||
.arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2),
|
||||
QString::number(value.b(), 'f', 2), QString::number(value.a(), 'f', 2));
|
||||
}
|
||||
case Format::RGB565: {
|
||||
auto value = Color::DecodeRGB565(pixel) / 255.0f;
|
||||
return QStringLiteral("Red: %1, Green: %2, Blue: %3")
|
||||
.arg(QString::number(value.r(), 'f', 2))
|
||||
.arg(QString::number(value.g(), 'f', 2))
|
||||
.arg(QString::number(value.b(), 'f', 2));
|
||||
.arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2),
|
||||
QString::number(value.b(), 'f', 2));
|
||||
}
|
||||
case Format::RGBA4: {
|
||||
auto value = Color::DecodeRGBA4(pixel) / 255.0f;
|
||||
return QStringLiteral("Red: %1, Green: %2, Blue: %3, Alpha: %4")
|
||||
.arg(QString::number(value.r(), 'f', 2))
|
||||
.arg(QString::number(value.g(), 'f', 2))
|
||||
.arg(QString::number(value.b(), 'f', 2))
|
||||
.arg(QString::number(value.a(), 'f', 2));
|
||||
.arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2),
|
||||
QString::number(value.b(), 'f', 2), QString::number(value.a(), 'f', 2));
|
||||
}
|
||||
case Format::IA8:
|
||||
return QStringLiteral("Index: %1, Alpha: %2").arg(pixel[0]).arg(pixel[1]);
|
||||
return QStringLiteral("Index: %1, Alpha: %2").arg(pixel[0], pixel[1]);
|
||||
case Format::RG8: {
|
||||
auto value = Color::DecodeRG8(pixel) / 255.0f;
|
||||
return QStringLiteral("Red: %1, Green: %2")
|
||||
.arg(QString::number(value.r(), 'f', 2))
|
||||
.arg(QString::number(value.g(), 'f', 2));
|
||||
.arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2));
|
||||
}
|
||||
case Format::I8:
|
||||
return QStringLiteral("Index: %1").arg(*pixel);
|
||||
case Format::A8:
|
||||
return QStringLiteral("Alpha: %1").arg(QString::number(*pixel / 255.0f, 'f', 2));
|
||||
case Format::IA4:
|
||||
return QStringLiteral("Index: %1, Alpha: %2")
|
||||
.arg(*pixel & 0xF)
|
||||
.arg((*pixel & 0xF0) >> 4);
|
||||
return QStringLiteral("Index: %1, Alpha: %2").arg(*pixel & 0xF, (*pixel & 0xF0) >> 4);
|
||||
case Format::I4: {
|
||||
u8 i = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF;
|
||||
return QStringLiteral("Index: %1").arg(i);
|
||||
@@ -390,8 +381,7 @@ void GraphicsSurfaceWidget::Pick(int x, int y) {
|
||||
case Format::X24S8: {
|
||||
auto values = Color::DecodeD24S8(pixel);
|
||||
return QStringLiteral("Depth: %1, Stencil: %2")
|
||||
.arg(QString::number(values[0] / (float)0xFFFFFF, 'f', 4))
|
||||
.arg(values[1]);
|
||||
.arg(QString::number(values[0] / (float)0xFFFFFF, 'f', 4), values[1]);
|
||||
}
|
||||
case Format::Unknown:
|
||||
return QStringLiteral("Unknown format");
|
||||
@@ -401,8 +391,8 @@ void GraphicsSurfaceWidget::Pick(int x, int y) {
|
||||
};
|
||||
|
||||
QString nibbles;
|
||||
for (unsigned i = 0; i < nibbles_per_pixel; i++) {
|
||||
unsigned nibble_index = i;
|
||||
for (u32 i = 0; i < nibbles_per_pixel; i++) {
|
||||
u32 nibble_index = i;
|
||||
if (nibble_mode) {
|
||||
nibble_index += (offset % 2) ? 0 : 1;
|
||||
}
|
||||
@@ -412,7 +402,7 @@ void GraphicsSurfaceWidget::Pick(int x, int y) {
|
||||
}
|
||||
|
||||
surface_info_label->setText(
|
||||
QStringLiteral("Raw: 0x%3\n(%4)").arg(nibbles).arg(GetText(surface_format, pixel)));
|
||||
QStringLiteral("Raw: 0x%3\n(%4)").arg(nibbles, GetText(surface_format, pixel)));
|
||||
surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
}
|
||||
|
||||
@@ -676,8 +666,8 @@ void GraphicsSurfaceWidget::SaveSurface() {
|
||||
}
|
||||
|
||||
if (selected_filter == png_filter) {
|
||||
const QPixmap* const pixmap = surface_picture_label->pixmap();
|
||||
ASSERT_MSG(pixmap != nullptr, "No pixmap set");
|
||||
const QPixmap pixmap = surface_picture_label->pixmap(Qt::ReturnByValue);
|
||||
ASSERT_MSG(!pixmap.isNull(), "No pixmap set");
|
||||
|
||||
QFile file{filename};
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
@@ -685,7 +675,7 @@ void GraphicsSurfaceWidget::SaveSurface() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pixmap->save(&file, "PNG")) {
|
||||
if (!pixmap.save(&file, "PNG")) {
|
||||
QMessageBox::warning(this, tr("Error"),
|
||||
tr("Failed to save surface data to file '%1'").arg(filename));
|
||||
}
|
||||
|
@@ -111,13 +111,13 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
|
||||
}
|
||||
};
|
||||
|
||||
const Instruction instr = par->info.code[index.row()];
|
||||
const Instruction& instr = par->info.code[index.row()];
|
||||
const OpCode opcode = instr.opcode;
|
||||
const OpCode::Info opcode_info = opcode.GetInfo();
|
||||
const u32 operand_desc_id = opcode_info.type == OpCode::Type::MultiplyAdd
|
||||
? instr.mad.operand_desc_id.Value()
|
||||
: instr.common.operand_desc_id.Value();
|
||||
const SwizzlePattern swizzle = par->info.swizzle_info[operand_desc_id].pattern;
|
||||
const SwizzlePattern& swizzle = par->info.swizzle_info[operand_desc_id].pattern;
|
||||
|
||||
// longest known instruction name: "setemit "
|
||||
int kOpcodeColumnWidth = 8;
|
||||
@@ -407,8 +407,8 @@ GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(
|
||||
static_cast<void (QSignalMapper::*)()>(&QSignalMapper::map));
|
||||
input_data_mapper->setMapping(input_data[i], i);
|
||||
}
|
||||
connect(input_data_mapper, static_cast<void (QSignalMapper::*)(int)>(&QSignalMapper::mapped),
|
||||
this, &GraphicsVertexShaderWidget::OnInputAttributeChanged);
|
||||
connect(input_data_mapper, &QSignalMapper::mappedInt, this,
|
||||
&GraphicsVertexShaderWidget::OnInputAttributeChanged);
|
||||
|
||||
auto main_widget = new QWidget;
|
||||
auto main_layout = new QVBoxLayout;
|
||||
@@ -514,8 +514,10 @@ void GraphicsVertexShaderWidget::Reload(bool replace_vertex_data, void* vertex_d
|
||||
info.code.push_back({instr});
|
||||
int num_attributes = shader_config.max_input_attribute_index + 1;
|
||||
|
||||
for (auto pattern : shader_setup.swizzle_data)
|
||||
info.swizzle_info.push_back({pattern});
|
||||
for (auto pattern : shader_setup.swizzle_data) {
|
||||
const nihstro::SwizzleInfo swizzle_info = {.pattern = nihstro::SwizzlePattern{pattern}};
|
||||
info.swizzle_info.push_back(swizzle_info);
|
||||
}
|
||||
|
||||
u32 entry_point = Pica::g_state.regs.vs.main_offset;
|
||||
info.labels.insert({entry_point, "main"});
|
||||
|
@@ -57,6 +57,7 @@ QString IPCRecorderWidget::GetStatusStr(const IPCDebugger::RequestRecord& record
|
||||
return tr("HLE Unimplemented");
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return QLatin1String{};
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -160,7 +160,8 @@ void MicroProfileWidget::mouseReleaseEvent(QMouseEvent* event) {
|
||||
}
|
||||
|
||||
void MicroProfileWidget::wheelEvent(QWheelEvent* event) {
|
||||
MicroProfileMousePosition(event->x() / x_scale, event->y() / y_scale, event->delta() / 120);
|
||||
MicroProfileMousePosition(event->position().x() / x_scale, event->position().y() / y_scale,
|
||||
event->angleDelta().y() / 120);
|
||||
event->accept();
|
||||
}
|
||||
|
||||
|
@@ -114,7 +114,7 @@ void OptionSetDialog::InitializeUI(const std::string& initial_value) {
|
||||
ui->formatLabel->text().append(QStringLiteral("\n"));
|
||||
}
|
||||
ui->formatLabel->setText(
|
||||
ui->formatLabel->text().append(tr("Range: %1 - %2").arg(option.min).arg(option.max)));
|
||||
ui->formatLabel->text().append(tr("Range: %1 - %2").arg(option.min, option.max)));
|
||||
}
|
||||
|
||||
// Decide and initialize layout
|
||||
|
@@ -169,8 +169,7 @@ GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} {
|
||||
* @return true if the haystack contains all words of userinput
|
||||
*/
|
||||
static bool ContainsAllWords(const QString& haystack, const QString& userinput) {
|
||||
const QStringList userinput_split =
|
||||
userinput.split(QLatin1Char{' '}, QString::SplitBehavior::SkipEmptyParts);
|
||||
const QStringList userinput_split = userinput.split(QLatin1Char{' '}, Qt::SkipEmptyParts);
|
||||
|
||||
return std::all_of(userinput_split.begin(), userinput_split.end(),
|
||||
[&haystack](const QString& s) { return haystack.contains(s); });
|
||||
@@ -511,42 +510,42 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, u64 progra
|
||||
|
||||
navigate_to_gamedb_entry->setVisible(it != compatibility_list.end());
|
||||
|
||||
connect(open_save_location, &QAction::triggered, [this, program_id] {
|
||||
connect(open_save_location, &QAction::triggered, this, [this, program_id] {
|
||||
emit OpenFolderRequested(program_id, GameListOpenTarget::SAVE_DATA);
|
||||
});
|
||||
connect(open_extdata_location, &QAction::triggered, [this, extdata_id] {
|
||||
connect(open_extdata_location, &QAction::triggered, this, [this, extdata_id] {
|
||||
emit OpenFolderRequested(extdata_id, GameListOpenTarget::EXT_DATA);
|
||||
});
|
||||
connect(open_application_location, &QAction::triggered, [this, program_id] {
|
||||
connect(open_application_location, &QAction::triggered, this, [this, program_id] {
|
||||
emit OpenFolderRequested(program_id, GameListOpenTarget::APPLICATION);
|
||||
});
|
||||
connect(open_update_location, &QAction::triggered, [this, program_id] {
|
||||
connect(open_update_location, &QAction::triggered, this, [this, program_id] {
|
||||
emit OpenFolderRequested(program_id, GameListOpenTarget::UPDATE_DATA);
|
||||
});
|
||||
connect(open_texture_dump_location, &QAction::triggered, [this, program_id] {
|
||||
connect(open_texture_dump_location, &QAction::triggered, this, [this, program_id] {
|
||||
if (FileUtil::CreateFullPath(fmt::format("{}textures/{:016X}/",
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::DumpDir),
|
||||
program_id))) {
|
||||
emit OpenFolderRequested(program_id, GameListOpenTarget::TEXTURE_DUMP);
|
||||
}
|
||||
});
|
||||
connect(open_texture_load_location, &QAction::triggered, [this, program_id] {
|
||||
connect(open_texture_load_location, &QAction::triggered, this, [this, program_id] {
|
||||
if (FileUtil::CreateFullPath(fmt::format("{}textures/{:016X}/",
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
|
||||
program_id))) {
|
||||
emit OpenFolderRequested(program_id, GameListOpenTarget::TEXTURE_LOAD);
|
||||
}
|
||||
});
|
||||
connect(open_mods_location, &QAction::triggered, [this, program_id] {
|
||||
connect(open_mods_location, &QAction::triggered, this, [this, program_id] {
|
||||
if (FileUtil::CreateFullPath(fmt::format("{}mods/{:016X}/",
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
|
||||
program_id))) {
|
||||
emit OpenFolderRequested(program_id, GameListOpenTarget::MODS);
|
||||
}
|
||||
});
|
||||
connect(dump_romfs, &QAction::triggered,
|
||||
connect(dump_romfs, &QAction::triggered, this,
|
||||
[this, path, program_id] { emit DumpRomFSRequested(path, program_id); });
|
||||
connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() {
|
||||
connect(navigate_to_gamedb_entry, &QAction::triggered, this, [this, program_id]() {
|
||||
emit NavigateToGamedbEntryRequested(program_id, compatibility_list);
|
||||
});
|
||||
};
|
||||
@@ -561,11 +560,11 @@ void GameList::AddCustomDirPopup(QMenu& context_menu, QModelIndex selected) {
|
||||
deep_scan->setCheckable(true);
|
||||
deep_scan->setChecked(game_dir.deep_scan);
|
||||
|
||||
connect(deep_scan, &QAction::triggered, [this, &game_dir] {
|
||||
connect(deep_scan, &QAction::triggered, this, [this, &game_dir] {
|
||||
game_dir.deep_scan = !game_dir.deep_scan;
|
||||
PopulateAsync(UISettings::values.game_dirs);
|
||||
});
|
||||
connect(delete_dir, &QAction::triggered, [this, &game_dir, selected] {
|
||||
connect(delete_dir, &QAction::triggered, this, [this, &game_dir, selected] {
|
||||
UISettings::values.game_dirs.removeOne(game_dir);
|
||||
item_model->invisibleRootItem()->removeRow(selected.row());
|
||||
});
|
||||
@@ -583,7 +582,7 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) {
|
||||
move_up->setEnabled(row > 0);
|
||||
move_down->setEnabled(row < item_model->rowCount() - 2);
|
||||
|
||||
connect(move_up, &QAction::triggered, [this, selected, row, game_dir_index] {
|
||||
connect(move_up, &QAction::triggered, this, [this, selected, row, game_dir_index] {
|
||||
const int other_index = selected.sibling(row - 1, 0).data(GameListDir::GameDirRole).toInt();
|
||||
// swap the items in the settings
|
||||
std::swap(UISettings::values.game_dirs[game_dir_index],
|
||||
@@ -598,7 +597,7 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) {
|
||||
tree_view->setExpanded(selected, UISettings::values.game_dirs[game_dir_index].expanded);
|
||||
});
|
||||
|
||||
connect(move_down, &QAction::triggered, [this, selected, row, game_dir_index] {
|
||||
connect(move_down, &QAction::triggered, this, [this, selected, row, game_dir_index] {
|
||||
const int other_index = selected.sibling(row + 1, 0).data(GameListDir::GameDirRole).toInt();
|
||||
// swap the items in the settings
|
||||
std::swap(UISettings::values.game_dirs[game_dir_index],
|
||||
@@ -613,7 +612,7 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) {
|
||||
tree_view->setExpanded(selected, UISettings::values.game_dirs[game_dir_index].expanded);
|
||||
});
|
||||
|
||||
connect(open_directory_location, &QAction::triggered, [this, game_dir_index] {
|
||||
connect(open_directory_location, &QAction::triggered, this, [this, game_dir_index] {
|
||||
emit OpenDirectory(UISettings::values.game_dirs[game_dir_index].path);
|
||||
});
|
||||
}
|
||||
@@ -640,7 +639,7 @@ void GameList::LoadCompatibilityList() {
|
||||
const QJsonDocument json = QJsonDocument::fromJson(content);
|
||||
const QJsonArray arr = json.array();
|
||||
|
||||
for (const QJsonValue value : arr) {
|
||||
for (const QJsonValue& value : arr) {
|
||||
const QJsonObject game = value.toObject();
|
||||
const QString compatibility_key = QStringLiteral("compatibility");
|
||||
|
||||
@@ -652,7 +651,7 @@ void GameList::LoadCompatibilityList() {
|
||||
const QString directory = game[QStringLiteral("directory")].toString();
|
||||
const QJsonArray ids = game[QStringLiteral("releases")].toArray();
|
||||
|
||||
for (const QJsonValue id_ref : ids) {
|
||||
for (const QJsonValue& id_ref : ids) {
|
||||
const QJsonObject id_object = id_ref.toObject();
|
||||
const QString id = id_object[QStringLiteral("id")].toString();
|
||||
|
||||
|
@@ -11,18 +11,11 @@
|
||||
#include <QFutureWatcher>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
#include <QOpenGLFunctions_4_3_Core>
|
||||
#include <QSysInfo>
|
||||
#include <QtConcurrent/QtConcurrentRun>
|
||||
#include <QtGui>
|
||||
#include <QtWidgets>
|
||||
#include <fmt/format.h>
|
||||
#ifdef __APPLE__
|
||||
#include <unistd.h> // for chdir
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#include "citra_qt/aboutdialog.h"
|
||||
#include "citra_qt/applets/mii_selector.h"
|
||||
#include "citra_qt/applets/swkbd.h"
|
||||
@@ -62,12 +55,11 @@
|
||||
#include "common/detached_tasks.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/filter.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/logging/text_formatter.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/string_util.h"
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#include "common/x64/cpu_detect.h"
|
||||
#endif
|
||||
@@ -76,20 +68,23 @@
|
||||
#include "core/file_sys/archive_extsavedata.h"
|
||||
#include "core/file_sys/archive_source_sd_savedata.h"
|
||||
#include "core/frontend/applets/default_applets.h"
|
||||
#include "core/frontend/scope_acquire_context.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hle/service/fs/archive.h"
|
||||
#include "core/hle/service/nfc/nfc.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/movie.h"
|
||||
#include "core/savestate.h"
|
||||
#include "core/settings.h"
|
||||
#include "game_list_p.h"
|
||||
#include "network/network_settings.h"
|
||||
#include "ui_main.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <unistd.h> // for chdir
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_DISCORD_PRESENCE
|
||||
#include "citra_qt/discord_impl.h"
|
||||
#endif
|
||||
@@ -152,8 +147,8 @@ static void InitializeLogging() {
|
||||
}
|
||||
|
||||
GMainWindow::GMainWindow()
|
||||
: config(std::make_unique<Config>()), emu_thread(nullptr),
|
||||
ui(std::make_unique<Ui::MainWindow>()) {
|
||||
: ui{std::make_unique<Ui::MainWindow>()}, config{std::make_unique<Config>()}, emu_thread{
|
||||
nullptr} {
|
||||
InitializeLogging();
|
||||
Debugger::ToggleConsole();
|
||||
Settings::LogSettings();
|
||||
@@ -263,7 +258,7 @@ void GMainWindow::InitializeWidgets() {
|
||||
loading_screen = new LoadingScreen(this);
|
||||
loading_screen->hide();
|
||||
ui->horizontalLayout->addWidget(loading_screen);
|
||||
connect(loading_screen, &LoadingScreen::Hidden, [&] {
|
||||
connect(loading_screen, &LoadingScreen::Hidden, this, [&] {
|
||||
loading_screen->Clear();
|
||||
if (emulation_running) {
|
||||
render_window->show();
|
||||
@@ -282,7 +277,6 @@ void GMainWindow::InitializeWidgets() {
|
||||
// Create status bar
|
||||
message_label = new QLabel();
|
||||
// Configured separately for left alignment
|
||||
message_label->setVisible(false);
|
||||
message_label->setFrameStyle(QFrame::NoFrame);
|
||||
message_label->setContentsMargins(4, 0, 4, 0);
|
||||
message_label->setAlignment(Qt::AlignLeft);
|
||||
@@ -307,10 +301,28 @@ void GMainWindow::InitializeWidgets() {
|
||||
label->setVisible(false);
|
||||
label->setFrameStyle(QFrame::NoFrame);
|
||||
label->setContentsMargins(4, 0, 4, 0);
|
||||
statusBar()->addPermanentWidget(label, 0);
|
||||
statusBar()->addPermanentWidget(label);
|
||||
}
|
||||
statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0);
|
||||
statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0);
|
||||
|
||||
// Setup Graphics API button
|
||||
graphics_api_button = new QPushButton();
|
||||
graphics_api_button->setObjectName(QStringLiteral("GraphicsAPIStatusBarButton"));
|
||||
graphics_api_button->setFocusPolicy(Qt::NoFocus);
|
||||
UpdateAPIIndicator(false);
|
||||
|
||||
connect(graphics_api_button, &QPushButton::clicked, this, [this] {
|
||||
if (emulation_running) {
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateAPIIndicator(true);
|
||||
});
|
||||
|
||||
statusBar()->insertPermanentWidget(0, graphics_api_button);
|
||||
|
||||
statusBar()->addPermanentWidget(multiplayer_state->GetStatusText());
|
||||
statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon());
|
||||
|
||||
statusBar()->setVisible(true);
|
||||
|
||||
// Removes an ugly inner border from the status bar widgets under Linux
|
||||
@@ -432,13 +444,13 @@ void GMainWindow::InitializeSaveStateMenuActions() {
|
||||
ui->menu_Save_State->addAction(actions_save_state[i]);
|
||||
}
|
||||
|
||||
connect(ui->action_Load_from_Newest_Slot, &QAction::triggered, [this] {
|
||||
connect(ui->action_Load_from_Newest_Slot, &QAction::triggered, this, [this] {
|
||||
UpdateSaveStates();
|
||||
if (newest_slot != 0) {
|
||||
actions_load_state[newest_slot - 1]->trigger();
|
||||
}
|
||||
});
|
||||
connect(ui->action_Save_to_Oldest_Slot, &QAction::triggered, [this] {
|
||||
connect(ui->action_Save_to_Oldest_Slot, &QAction::triggered, this, [this] {
|
||||
UpdateSaveStates();
|
||||
actions_save_state[oldest_slot - 1]->trigger();
|
||||
});
|
||||
@@ -674,7 +686,7 @@ void GMainWindow::ConnectWidgetEvents() {
|
||||
connect(game_list_placeholder, &GameListPlaceholder::AddDirectory, this,
|
||||
&GMainWindow::OnGameListAddDirectory);
|
||||
connect(game_list, &GameList::ShowList, this, &GMainWindow::OnGameListShowList);
|
||||
connect(game_list, &GameList::PopulatingCompleted,
|
||||
connect(game_list, &GameList::PopulatingCompleted, this,
|
||||
[this] { multiplayer_state->UpdateGameList(game_list->GetModel()); });
|
||||
|
||||
connect(this, &GMainWindow::EmulationStarting, render_window,
|
||||
@@ -765,7 +777,7 @@ void GMainWindow::ConnectMenuEvents() {
|
||||
connect(ui->action_Close_Movie, &QAction::triggered, this, &GMainWindow::OnCloseMovie);
|
||||
connect(ui->action_Save_Movie, &QAction::triggered, this, &GMainWindow::OnSaveMovie);
|
||||
connect(ui->action_Movie_Read_Only_Mode, &QAction::toggled, this,
|
||||
[this](bool checked) { Core::Movie::GetInstance().SetReadOnly(checked); });
|
||||
[](bool checked) { Core::Movie::GetInstance().SetReadOnly(checked); });
|
||||
connect(ui->action_Enable_Frame_Advancing, &QAction::triggered, this, [this] {
|
||||
if (emulation_running) {
|
||||
Core::System::GetInstance().frame_limiter.SetFrameAdvancing(
|
||||
@@ -914,20 +926,10 @@ bool GMainWindow::LoadROM(const QString& filename) {
|
||||
|
||||
render_window->InitRenderTarget();
|
||||
|
||||
Frontend::ScopeAcquireContext scope(*render_window);
|
||||
const auto scope = render_window->Acquire();
|
||||
|
||||
const QString below_gl43_title = tr("OpenGL 4.3 Unsupported");
|
||||
const QString below_gl43_message = tr("Your GPU may not support OpenGL 4.3, or you do not "
|
||||
"have the latest graphics driver.");
|
||||
|
||||
if (!QOpenGLContext::globalShareContext()->versionFunctions<QOpenGLFunctions_4_3_Core>()) {
|
||||
QMessageBox::critical(this, below_gl43_title, below_gl43_message);
|
||||
return false;
|
||||
}
|
||||
|
||||
Core::System& system{Core::System::GetInstance()};
|
||||
|
||||
const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())};
|
||||
Core::System& system = Core::System::GetInstance();
|
||||
const Core::System::ResultStatus result = system.Load(*render_window, filename.toStdString());
|
||||
|
||||
if (result != Core::System::ResultStatus::Success) {
|
||||
switch (result) {
|
||||
@@ -976,7 +978,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
|
||||
case Core::System::ResultStatus::ErrorVideoCore:
|
||||
QMessageBox::critical(
|
||||
this, tr("Video Core Error"),
|
||||
tr("An error has occurred. Please <a "
|
||||
tr("An error has occurred during intialization of the video backend. Please <a "
|
||||
"href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>see "
|
||||
"the "
|
||||
"log</a> for more details. "
|
||||
@@ -991,10 +993,6 @@ bool GMainWindow::LoadROM(const QString& filename) {
|
||||
"proper drivers for your graphics card from the manufacturer's website."));
|
||||
break;
|
||||
|
||||
case Core::System::ResultStatus::ErrorVideoCore_ErrorBelowGL43:
|
||||
QMessageBox::critical(this, below_gl43_title, below_gl43_message);
|
||||
break;
|
||||
|
||||
default:
|
||||
QMessageBox::critical(
|
||||
this, tr("Error while loading ROM!"),
|
||||
@@ -1206,7 +1204,6 @@ void GMainWindow::ShutdownGame() {
|
||||
|
||||
// Disable status bar updates
|
||||
status_bar_update_timer.stop();
|
||||
message_label->setVisible(false);
|
||||
message_label_used_for_movie = false;
|
||||
emu_speed_label->setVisible(false);
|
||||
game_fps_label->setVisible(false);
|
||||
@@ -1410,7 +1407,7 @@ void GMainWindow::OnGameListDumpRomFS(QString game_path, u64 program_id) {
|
||||
program_id | 0x0004000e00000000);
|
||||
using FutureWatcher = QFutureWatcher<std::pair<Loader::ResultStatus, Loader::ResultStatus>>;
|
||||
auto* future_watcher = new FutureWatcher(this);
|
||||
connect(future_watcher, &FutureWatcher::finished,
|
||||
connect(future_watcher, &FutureWatcher::finished, this,
|
||||
[this, dialog, base_path, update_path, future_watcher] {
|
||||
dialog->hide();
|
||||
const auto& [base, update] = future_watcher->result();
|
||||
@@ -1512,7 +1509,7 @@ void GMainWindow::InstallCIA(QStringList filepaths) {
|
||||
const auto cia_progress = [&](std::size_t written, std::size_t total) {
|
||||
emit UpdateProgress(written, total);
|
||||
};
|
||||
for (const auto current_path : filepaths) {
|
||||
for (const auto& current_path : filepaths) {
|
||||
status = Service::AM::InstallCIA(current_path.toStdString(), cia_progress);
|
||||
emit CIAInstallReport(status, current_path);
|
||||
}
|
||||
@@ -1550,6 +1547,10 @@ void GMainWindow::OnCIAInstallReport(Service::AM::InstallStatus status, QString
|
||||
"before being used with Citra. A real 3DS is required.")
|
||||
.arg(filename));
|
||||
break;
|
||||
case Service::AM::InstallStatus::ErrorFileNotFound:
|
||||
QMessageBox::critical(this, tr("Unable to find File"),
|
||||
tr("Could not find %1").arg(filename));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1727,6 +1728,8 @@ void GMainWindow::ToggleScreenLayout() {
|
||||
case Settings::LayoutOption::SideScreen:
|
||||
new_layout = Settings::LayoutOption::Default;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Frontend, "Unknown layout option {}", Settings::values.layout_option);
|
||||
}
|
||||
|
||||
Settings::values.layout_option = new_layout;
|
||||
@@ -1796,6 +1799,7 @@ void GMainWindow::OnConfigure() {
|
||||
} else {
|
||||
setMouseTracking(false);
|
||||
}
|
||||
UpdateAPIIndicator(false);
|
||||
} else {
|
||||
Settings::values.input_profiles = old_input_profiles;
|
||||
Settings::values.touch_from_button_maps = old_touch_from_button_maps;
|
||||
@@ -1973,7 +1977,7 @@ void GMainWindow::OnCaptureScreenshot() {
|
||||
const QString filename = game_title.remove(QRegularExpression(QStringLiteral("[\\/:?\"<>|]")));
|
||||
const QString timestamp =
|
||||
QDateTime::currentDateTime().toString(QStringLiteral("dd.MM.yy_hh.mm.ss.z"));
|
||||
path.append(QStringLiteral("/%1_%2.png").arg(filename).arg(timestamp));
|
||||
path.append(QStringLiteral("/%1_%2.png").arg(filename, timestamp));
|
||||
render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor, path);
|
||||
OnStartGame();
|
||||
}
|
||||
@@ -2048,7 +2052,7 @@ void GMainWindow::UpdateStatusBar() {
|
||||
message_label_used_for_movie = true;
|
||||
ui->action_Save_Movie->setEnabled(true);
|
||||
} else if (play_mode == Core::Movie::PlayMode::Playing) {
|
||||
message_label->setText(tr("Playing %1 / %2").arg(current).arg(total));
|
||||
message_label->setText(tr("Playing %1 / %2").arg(current, total));
|
||||
message_label->setVisible(true);
|
||||
message_label_used_for_movie = true;
|
||||
ui->action_Save_Movie->setEnabled(false);
|
||||
@@ -2107,6 +2111,26 @@ void GMainWindow::ShowMouseCursor() {
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::UpdateAPIIndicator(bool override) {
|
||||
static std::array graphics_apis = {QStringLiteral("OPENGL"), QStringLiteral("OPENGLES"),
|
||||
QStringLiteral("VULKAN")};
|
||||
|
||||
static std::array graphics_api_colors = {QStringLiteral("#00ccdd"), QStringLiteral("#ba2a8d"),
|
||||
QStringLiteral("#91242a")};
|
||||
|
||||
u32 api_index = static_cast<u32>(Settings::values.graphics_api);
|
||||
if (override) {
|
||||
api_index = (api_index + 1) % graphics_apis.size();
|
||||
Settings::values.graphics_api = static_cast<Settings::GraphicsAPI>(api_index);
|
||||
}
|
||||
|
||||
const QString style_sheet = QStringLiteral("QPushButton { font-weight: bold; color: %0; }")
|
||||
.arg(graphics_api_colors[api_index]);
|
||||
|
||||
graphics_api_button->setText(graphics_apis[api_index]);
|
||||
graphics_api_button->setStyleSheet(style_sheet);
|
||||
}
|
||||
|
||||
void GMainWindow::OnMouseActivity() {
|
||||
ShowMouseCursor();
|
||||
}
|
||||
@@ -2292,8 +2316,16 @@ void GMainWindow::UpdateUITheme() {
|
||||
QStringList theme_paths(default_theme_paths);
|
||||
|
||||
if (is_default_theme || current_theme.isEmpty()) {
|
||||
qApp->setStyleSheet({});
|
||||
setStyleSheet({});
|
||||
const QString theme_uri(QStringLiteral(":default/style.qss"));
|
||||
QFile f(theme_uri);
|
||||
if (f.open(QFile::ReadOnly | QFile::Text)) {
|
||||
QTextStream ts(&f);
|
||||
qApp->setStyleSheet(ts.readAll());
|
||||
setStyleSheet(ts.readAll());
|
||||
} else {
|
||||
qApp->setStyleSheet({});
|
||||
setStyleSheet({});
|
||||
}
|
||||
theme_paths.append(default_icons);
|
||||
QIcon::setThemeName(default_icons);
|
||||
} else {
|
||||
@@ -2440,14 +2472,6 @@ int main(int argc, char* argv[]) {
|
||||
QCoreApplication::setOrganizationName(QStringLiteral("Citra team"));
|
||||
QCoreApplication::setApplicationName(QStringLiteral("Citra"));
|
||||
|
||||
QSurfaceFormat format;
|
||||
format.setVersion(4, 3);
|
||||
format.setProfile(QSurfaceFormat::CoreProfile);
|
||||
format.setSwapInterval(0);
|
||||
// TODO: expose a setting for buffer value (ie default/single/double/triple)
|
||||
format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
|
||||
QSurfaceFormat::setDefaultFormat(format);
|
||||
|
||||
#ifdef __APPLE__
|
||||
std::string bin_path = FileUtil::GetBundleDirectory() + DIR_SEP + "..";
|
||||
chdir(bin_path.c_str());
|
||||
|
@@ -7,6 +7,7 @@
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <QMainWindow>
|
||||
#include <QPushButton>
|
||||
#include <QTimer>
|
||||
#include <QTranslator>
|
||||
#include "citra_qt/compatibility_list.h"
|
||||
@@ -234,6 +235,7 @@ private:
|
||||
void InstallCIA(QStringList filepaths);
|
||||
void HideMouseCursor();
|
||||
void ShowMouseCursor();
|
||||
void UpdateAPIIndicator(bool override);
|
||||
|
||||
std::unique_ptr<Ui::MainWindow> ui;
|
||||
|
||||
@@ -248,6 +250,7 @@ private:
|
||||
QLabel* emu_speed_label = nullptr;
|
||||
QLabel* game_fps_label = nullptr;
|
||||
QLabel* emu_frametime_label = nullptr;
|
||||
QPushButton* graphics_api_button = nullptr;
|
||||
QTimer status_bar_update_timer;
|
||||
bool message_label_used_for_movie = false;
|
||||
|
||||
|
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <QDialog>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
@@ -7,7 +7,6 @@
|
||||
#include <QIcon>
|
||||
#include <QMessageBox>
|
||||
#include <QStandardItemModel>
|
||||
#include "citra_qt/game_list.h"
|
||||
#include "citra_qt/multiplayer/client_room.h"
|
||||
#include "citra_qt/multiplayer/direct_connect.h"
|
||||
#include "citra_qt/multiplayer/host_room.h"
|
||||
@@ -16,7 +15,6 @@
|
||||
#include "citra_qt/multiplayer/state.h"
|
||||
#include "citra_qt/uisettings.h"
|
||||
#include "citra_qt/util/clickable_label.h"
|
||||
#include "common/announce_multiplayer_room.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
MultiplayerState::MultiplayerState(QWidget* parent, QStandardItemModel* game_list_model,
|
||||
@@ -231,8 +229,9 @@ bool MultiplayerState::OnCloseRoom() {
|
||||
if (room->GetState() != Network::Room::State::Open) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Save ban list
|
||||
UISettings::values.ban_list = std::move(room->GetBanList());
|
||||
UISettings::values.ban_list = room->GetBanList();
|
||||
|
||||
room->Destroy();
|
||||
announce_multiplayer_session->Stop();
|
||||
|
@@ -77,6 +77,7 @@ add_library(common STATIC
|
||||
logging/backend.h
|
||||
logging/filter.cpp
|
||||
logging/filter.h
|
||||
logging/formatter.h
|
||||
logging/log.h
|
||||
logging/text_formatter.cpp
|
||||
logging/text_formatter.h
|
||||
|
@@ -59,6 +59,60 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
|
||||
|
||||
#endif // _MSC_VER ndef
|
||||
|
||||
#define DECLARE_ENUM_FLAG_OPERATORS(type) \
|
||||
[[nodiscard]] constexpr type operator|(type a, type b) noexcept { \
|
||||
using T = std::underlying_type_t<type>; \
|
||||
return static_cast<type>(static_cast<T>(a) | static_cast<T>(b)); \
|
||||
} \
|
||||
[[nodiscard]] constexpr type operator&(type a, type b) noexcept { \
|
||||
using T = std::underlying_type_t<type>; \
|
||||
return static_cast<type>(static_cast<T>(a) & static_cast<T>(b)); \
|
||||
} \
|
||||
[[nodiscard]] constexpr type operator^(type a, type b) noexcept { \
|
||||
using T = std::underlying_type_t<type>; \
|
||||
return static_cast<type>(static_cast<T>(a) ^ static_cast<T>(b)); \
|
||||
} \
|
||||
[[nodiscard]] constexpr type operator<<(type a, type b) noexcept { \
|
||||
using T = std::underlying_type_t<type>; \
|
||||
return static_cast<type>(static_cast<T>(a) << static_cast<T>(b)); \
|
||||
} \
|
||||
[[nodiscard]] constexpr type operator>>(type a, type b) noexcept { \
|
||||
using T = std::underlying_type_t<type>; \
|
||||
return static_cast<type>(static_cast<T>(a) >> static_cast<T>(b)); \
|
||||
} \
|
||||
constexpr type& operator|=(type& a, type b) noexcept { \
|
||||
a = a | b; \
|
||||
return a; \
|
||||
} \
|
||||
constexpr type& operator&=(type& a, type b) noexcept { \
|
||||
a = a & b; \
|
||||
return a; \
|
||||
} \
|
||||
constexpr type& operator^=(type& a, type b) noexcept { \
|
||||
a = a ^ b; \
|
||||
return a; \
|
||||
} \
|
||||
constexpr type& operator<<=(type& a, type b) noexcept { \
|
||||
a = a << b; \
|
||||
return a; \
|
||||
} \
|
||||
constexpr type& operator>>=(type& a, type b) noexcept { \
|
||||
a = a >> b; \
|
||||
return a; \
|
||||
} \
|
||||
[[nodiscard]] constexpr type operator~(type key) noexcept { \
|
||||
using T = std::underlying_type_t<type>; \
|
||||
return static_cast<type>(~static_cast<T>(key)); \
|
||||
} \
|
||||
[[nodiscard]] constexpr bool True(type key) noexcept { \
|
||||
using T = std::underlying_type_t<type>; \
|
||||
return static_cast<T>(key) != 0; \
|
||||
} \
|
||||
[[nodiscard]] constexpr bool False(type key) noexcept { \
|
||||
using T = std::underlying_type_t<type>; \
|
||||
return static_cast<T>(key) == 0; \
|
||||
}
|
||||
|
||||
// Generic function to get last error message.
|
||||
// Call directly after the command or use the error num.
|
||||
// This function might change the error code.
|
||||
|
@@ -31,8 +31,10 @@
|
||||
#endif
|
||||
|
||||
// 64 bit offsets for MSVC and MinGW. MinGW also needs this for using _wstat64
|
||||
#ifndef __MINGW64__
|
||||
#define stat _stat64
|
||||
#define fstat _fstat64
|
||||
#endif
|
||||
|
||||
#else
|
||||
#ifdef __APPLE__
|
||||
|
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include "common/cityhash.h"
|
||||
@@ -41,6 +42,13 @@ inline u64 HashCombine(std::size_t& seed, const u64 hash) {
|
||||
return seed ^= hash + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct IdentityHash {
|
||||
T operator()(const T& value) const {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
/// A helper template that ensures the padding in a struct is initialized by memsetting to 0.
|
||||
template <typename T>
|
||||
struct HashableStruct {
|
||||
|
@@ -235,6 +235,7 @@ void DebuggerBackend::Write(const Entry& entry) {
|
||||
CLS(Render) \
|
||||
SUB(Render, Software) \
|
||||
SUB(Render, OpenGL) \
|
||||
SUB(Render, Vulkan) \
|
||||
CLS(Audio) \
|
||||
SUB(Audio, DSP) \
|
||||
SUB(Audio, Sink) \
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#include <array>
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/formatter.h"
|
||||
|
||||
namespace Log {
|
||||
|
||||
// trims up to and including the last of ../, ..\, src/, src\ in a string
|
||||
@@ -102,6 +103,7 @@ enum class Class : ClassType {
|
||||
Render, ///< Emulator video output and hardware acceleration
|
||||
Render_Software, ///< Software renderer backend
|
||||
Render_OpenGL, ///< OpenGL backend
|
||||
Render_Vulkan, ///< Vulkan backend
|
||||
Audio, ///< Audio emulation
|
||||
Audio_DSP, ///< The HLE and LLE implementations of the DSP
|
||||
Audio_Sink, ///< Emulator audio output backend
|
||||
|
@@ -4,13 +4,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <compare>
|
||||
#include <cstdlib>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Common {
|
||||
|
||||
constexpr float PI = 3.14159265f;
|
||||
|
||||
template <class T>
|
||||
struct Rectangle {
|
||||
T left{};
|
||||
@@ -23,19 +22,31 @@ struct Rectangle {
|
||||
constexpr Rectangle(T left, T top, T right, T bottom)
|
||||
: left(left), top(top), right(right), bottom(bottom) {}
|
||||
|
||||
[[nodiscard]] T GetWidth() const {
|
||||
constexpr auto operator<=>(const Rectangle&) const = default;
|
||||
|
||||
constexpr void operator*=(const T value) {
|
||||
left *= value;
|
||||
top *= value;
|
||||
right *= value;
|
||||
bottom *= value;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr Rectangle operator*(const T value) const {
|
||||
return Rectangle{left * value, top * value, right * value, bottom * value};
|
||||
}
|
||||
[[nodiscard]] constexpr T GetWidth() const {
|
||||
return std::abs(static_cast<std::make_signed_t<T>>(right - left));
|
||||
}
|
||||
[[nodiscard]] T GetHeight() const {
|
||||
[[nodiscard]] constexpr T GetHeight() const {
|
||||
return std::abs(static_cast<std::make_signed_t<T>>(bottom - top));
|
||||
}
|
||||
[[nodiscard]] Rectangle<T> TranslateX(const T x) const {
|
||||
[[nodiscard]] constexpr Rectangle<T> TranslateX(const T x) const {
|
||||
return Rectangle{left + x, top, right + x, bottom};
|
||||
}
|
||||
[[nodiscard]] Rectangle<T> TranslateY(const T y) const {
|
||||
[[nodiscard]] constexpr Rectangle<T> TranslateY(const T y) const {
|
||||
return Rectangle{left, top + y, right, bottom + y};
|
||||
}
|
||||
[[nodiscard]] Rectangle<T> Scale(const float s) const {
|
||||
[[nodiscard]] constexpr Rectangle<T> Scale(const float s) const {
|
||||
return Rectangle{left, top, static_cast<T>(left + GetWidth() * s),
|
||||
static_cast<T>(top + GetHeight() * s)};
|
||||
}
|
||||
|
@@ -3,8 +3,8 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include <boost/serialization/shared_ptr.hpp>
|
||||
@@ -65,8 +65,11 @@ private:
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(BufferMem);
|
||||
|
||||
/// A managed reference to host-side memory. Fast enough to be used everywhere instead of u8*
|
||||
/// Supports serialization.
|
||||
/**
|
||||
* A managed reference to host-side memory.
|
||||
* Fast enough to be used everywhere instead of u8*
|
||||
* Supports serialization.
|
||||
*/
|
||||
class MemoryRef {
|
||||
public:
|
||||
MemoryRef() = default;
|
||||
@@ -75,35 +78,52 @@ public:
|
||||
: backing_mem(std::move(backing_mem_)), offset(0) {
|
||||
Init();
|
||||
}
|
||||
|
||||
MemoryRef(std::shared_ptr<BackingMem> backing_mem_, u64 offset_)
|
||||
: backing_mem(std::move(backing_mem_)), offset(offset_) {
|
||||
ASSERT(offset <= backing_mem->GetSize());
|
||||
Init();
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return cptr != nullptr;
|
||||
}
|
||||
|
||||
operator u8*() {
|
||||
return cptr;
|
||||
}
|
||||
u8* GetPtr() {
|
||||
return cptr;
|
||||
}
|
||||
|
||||
operator const u8*() const {
|
||||
return cptr;
|
||||
}
|
||||
|
||||
u8* GetPtr() {
|
||||
return cptr;
|
||||
}
|
||||
|
||||
const u8* GetPtr() const {
|
||||
return cptr;
|
||||
}
|
||||
|
||||
auto GetWriteBytes(std::size_t size) {
|
||||
return std::span{reinterpret_cast<std::byte*>(cptr), size > csize ? csize : size};
|
||||
}
|
||||
|
||||
auto GetReadBytes(std::size_t size) const {
|
||||
return std::span{reinterpret_cast<const std::byte*>(cptr), size > csize ? csize : size};
|
||||
}
|
||||
|
||||
std::size_t GetSize() const {
|
||||
return csize;
|
||||
}
|
||||
|
||||
MemoryRef& operator+=(u32 offset_by) {
|
||||
ASSERT(offset_by < csize);
|
||||
offset += offset_by;
|
||||
Init();
|
||||
return *this;
|
||||
}
|
||||
|
||||
MemoryRef operator+(u32 offset_by) const {
|
||||
ASSERT(offset_by < csize);
|
||||
return MemoryRef(backing_mem, offset + offset_by);
|
||||
|
@@ -23,12 +23,3 @@ typedef void* HANDLE;
|
||||
#include <microprofile.h>
|
||||
|
||||
#define MP_RGB(r, g, b) ((r) << 16 | (g) << 8 | (b) << 0)
|
||||
|
||||
// On OS X, some Mach header included by MicroProfile defines these as macros, conflicting with
|
||||
// identifiers we use.
|
||||
#ifdef PAGE_SIZE
|
||||
#undef PAGE_SIZE
|
||||
#endif
|
||||
#ifdef PAGE_MASK
|
||||
#undef PAGE_MASK
|
||||
#endif
|
||||
|
@@ -108,8 +108,6 @@ add_library(core STATIC
|
||||
frontend/input.h
|
||||
frontend/mic.cpp
|
||||
frontend/mic.h
|
||||
frontend/scope_acquire_context.cpp
|
||||
frontend/scope_acquire_context.h
|
||||
gdbstub/gdbstub.cpp
|
||||
gdbstub/gdbstub.h
|
||||
hle/applets/applet.cpp
|
||||
|
@@ -208,6 +208,8 @@ u32 ARM_Dynarmic::GetVFPSystemReg(VFPSystemRegister reg) const {
|
||||
default:
|
||||
UNREACHABLE_MSG("Unknown VFP system register: {}", reg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::SetVFPSystemReg(VFPSystemRegister reg, u32 value) {
|
||||
@@ -240,6 +242,8 @@ u32 ARM_Dynarmic::GetCP15Register(CP15Register reg) const {
|
||||
default:
|
||||
UNREACHABLE_MSG("Unknown CP15 register: {}", reg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::SetCP15Register(CP15Register reg, u32 value) {
|
||||
|
@@ -849,17 +849,13 @@ static int InterpreterTranslateBlock(ARMul_State* cpu, std::size_t& bb_start, u3
|
||||
// Save start addr of basicblock in CreamCache
|
||||
ARM_INST_PTR inst_base = nullptr;
|
||||
TransExtData ret = TransExtData::NON_BRANCH;
|
||||
int size = 0; // instruction size of basic block
|
||||
bb_start = trans_cache_buf_top;
|
||||
|
||||
u32 phys_addr = addr;
|
||||
u32 pc_start = cpu->Reg[15];
|
||||
|
||||
while (ret == TransExtData::NON_BRANCH) {
|
||||
unsigned int inst_size = InterpreterTranslateInstruction(cpu, phys_addr, inst_base);
|
||||
|
||||
size++;
|
||||
|
||||
u32 inst_size = InterpreterTranslateInstruction(cpu, phys_addr, inst_base);
|
||||
phys_addr += inst_size;
|
||||
|
||||
if ((phys_addr & 0xfff) == 0) {
|
||||
@@ -972,7 +968,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
|
||||
|
||||
// GCC and Clang have a C++ extension to support a lookup table of labels. Otherwise, fallback to a
|
||||
// clunky switch statement.
|
||||
#if defined __GNUC__ || defined __clang__
|
||||
#if defined __GNUC__ || (defined __clang__ && !defined _MSC_VER)
|
||||
#define GOTO_NEXT_INST \
|
||||
GDB_BP_CHECK; \
|
||||
if (num_instrs >= cpu->NumInstrsToExecute) \
|
||||
|
@@ -1218,7 +1218,7 @@ u32 vfp_double_cpdo(ARMul_State* state, u32 inst, u32 fpscr) {
|
||||
|
||||
for (vecitr = 0; vecitr <= veclen; vecitr += 1 << FPSCR_LENGTH_BIT) {
|
||||
u32 except;
|
||||
char type;
|
||||
[[maybe_unused]] char type;
|
||||
|
||||
type = (fop->flags & OP_SD) ? 's' : 'd';
|
||||
if (op == FOP_EXT)
|
||||
|
@@ -1242,7 +1242,7 @@ u32 vfp_single_cpdo(ARMul_State* state, u32 inst, u32 fpscr) {
|
||||
for (vecitr = 0; vecitr <= veclen; vecitr += 1 << FPSCR_LENGTH_BIT) {
|
||||
s32 m = vfp_get_float(state, sm);
|
||||
u32 except;
|
||||
char type;
|
||||
[[maybe_unused]] char type;
|
||||
|
||||
type = (fop->flags & OP_DD) ? 'd' : 's';
|
||||
if (op == FOP_EXT)
|
||||
|
@@ -47,7 +47,7 @@ void CheatEngine::AddCheat(const std::shared_ptr<CheatBase>& cheat) {
|
||||
|
||||
void CheatEngine::RemoveCheat(int index) {
|
||||
std::unique_lock<std::shared_mutex> lock(cheats_list_mutex);
|
||||
if (index < 0 || index >= cheats_list.size()) {
|
||||
if (index < 0 || index >= static_cast<int>(cheats_list.size())) {
|
||||
LOG_ERROR(Core_Cheats, "Invalid index {}", index);
|
||||
return;
|
||||
}
|
||||
@@ -56,7 +56,7 @@ void CheatEngine::RemoveCheat(int index) {
|
||||
|
||||
void CheatEngine::UpdateCheat(int index, const std::shared_ptr<CheatBase>& new_cheat) {
|
||||
std::unique_lock<std::shared_mutex> lock(cheats_list_mutex);
|
||||
if (index < 0 || index >= cheats_list.size()) {
|
||||
if (index < 0 || index >= static_cast<int>(cheats_list.size())) {
|
||||
LOG_ERROR(Core_Cheats, "Invalid index {}", index);
|
||||
return;
|
||||
}
|
||||
|
@@ -422,8 +422,6 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, u32 system_mo
|
||||
switch (result) {
|
||||
case VideoCore::ResultStatus::ErrorGenericDrivers:
|
||||
return ResultStatus::ErrorVideoCore_ErrorGenericDrivers;
|
||||
case VideoCore::ResultStatus::ErrorBelowGL43:
|
||||
return ResultStatus::ErrorVideoCore_ErrorBelowGL43;
|
||||
default:
|
||||
return ResultStatus::ErrorVideoCore;
|
||||
}
|
||||
|
@@ -87,8 +87,6 @@ public:
|
||||
ErrorVideoCore, ///< Error in the video core
|
||||
ErrorVideoCore_ErrorGenericDrivers, ///< Error in the video core due to the user having
|
||||
/// generic drivers installed
|
||||
ErrorVideoCore_ErrorBelowGL43, ///< Error in the video core due to the user not having
|
||||
/// OpenGL 4.3 or higher
|
||||
ErrorSavestate, ///< Error saving or loading
|
||||
ShutdownRequested, ///< Emulated program requested a system shutdown
|
||||
ErrorUnknown ///< Any other error
|
||||
|
@@ -293,10 +293,6 @@ private:
|
||||
std::vector<std::shared_ptr<Timer>> timers;
|
||||
Timer* current_timer = nullptr;
|
||||
|
||||
// Stores a scaling for the internal clockspeed. Changing this number results in
|
||||
// under/overclocking the guest cpu
|
||||
double cpu_clock_scale = 1.0;
|
||||
|
||||
// When true, the event queue can't be modified. Used while deserializing to workaround
|
||||
// destructor side effects.
|
||||
bool event_queue_locked = false;
|
||||
|
@@ -12,6 +12,15 @@
|
||||
|
||||
namespace Frontend {
|
||||
|
||||
/// Information for the Graphics Backends signifying what type of screen pointer is in
|
||||
/// WindowInformation
|
||||
enum class WindowSystemType : u8 {
|
||||
Headless,
|
||||
Windows,
|
||||
X11,
|
||||
Wayland,
|
||||
};
|
||||
|
||||
struct Frame;
|
||||
/**
|
||||
* For smooth Vsync rendering, we want to always present the latest frame that the core generates,
|
||||
@@ -60,11 +69,33 @@ class GraphicsContext {
|
||||
public:
|
||||
virtual ~GraphicsContext();
|
||||
|
||||
/// Inform the driver to swap the front/back buffers and present the current image
|
||||
virtual void SwapBuffers(){};
|
||||
|
||||
/// Makes the graphics context current for the caller thread
|
||||
virtual void MakeCurrent() = 0;
|
||||
virtual void MakeCurrent(){};
|
||||
|
||||
/// Releases (dunno if this is the "right" word) the context from the caller thread
|
||||
virtual void DoneCurrent() = 0;
|
||||
virtual void DoneCurrent(){};
|
||||
|
||||
class Scoped {
|
||||
public:
|
||||
explicit Scoped(GraphicsContext& context_) : context(context_) {
|
||||
context.MakeCurrent();
|
||||
}
|
||||
~Scoped() {
|
||||
context.DoneCurrent();
|
||||
}
|
||||
|
||||
private:
|
||||
GraphicsContext& context;
|
||||
};
|
||||
|
||||
/// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value
|
||||
/// ends
|
||||
[[nodiscard]] Scoped Acquire() {
|
||||
return Scoped{*this};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -95,6 +126,23 @@ public:
|
||||
std::pair<unsigned, unsigned> min_client_area_size;
|
||||
};
|
||||
|
||||
/// Data describing host window system information
|
||||
struct WindowSystemInfo {
|
||||
// Window system type. Determines which GL context or Vulkan WSI is used.
|
||||
WindowSystemType type = WindowSystemType::Headless;
|
||||
|
||||
// Connection to a display server. This is used on X11 and Wayland platforms.
|
||||
void* display_connection = nullptr;
|
||||
|
||||
// Render surface. This is a pointer to the native window handle, which depends
|
||||
// on the platform. e.g. HWND for Windows, Window for X11. If the surface is
|
||||
// set to nullptr, the video backend will run in headless mode.
|
||||
void* render_surface = nullptr;
|
||||
|
||||
// Scale of the render surface. For hidpi systems, this will be >1.
|
||||
float render_surface_scale = 1.0f;
|
||||
};
|
||||
|
||||
/// Polls window events
|
||||
virtual void PollEvents() = 0;
|
||||
|
||||
@@ -158,6 +206,13 @@ public:
|
||||
config = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns system information about the drawing area.
|
||||
*/
|
||||
const WindowSystemInfo& GetWindowInfo() const {
|
||||
return window_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the framebuffer layout (width, height, and screen regions)
|
||||
* @note This method is thread-safe
|
||||
@@ -204,6 +259,8 @@ protected:
|
||||
framebuffer_layout = layout;
|
||||
}
|
||||
|
||||
WindowSystemInfo window_info;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Handler called when the minimal client area was requested to be changed via SetConfig.
|
||||
|
@@ -1,17 +0,0 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/frontend/scope_acquire_context.h"
|
||||
|
||||
namespace Frontend {
|
||||
|
||||
ScopeAcquireContext::ScopeAcquireContext(Frontend::GraphicsContext& context) : context{context} {
|
||||
context.MakeCurrent();
|
||||
}
|
||||
ScopeAcquireContext::~ScopeAcquireContext() {
|
||||
context.DoneCurrent();
|
||||
}
|
||||
|
||||
} // namespace Frontend
|
@@ -1,23 +0,0 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Frontend {
|
||||
|
||||
class GraphicsContext;
|
||||
|
||||
/// Helper class to acquire/release window context within a given scope
|
||||
class ScopeAcquireContext : NonCopyable {
|
||||
public:
|
||||
explicit ScopeAcquireContext(Frontend::GraphicsContext& context);
|
||||
~ScopeAcquireContext();
|
||||
|
||||
private:
|
||||
Frontend::GraphicsContext& context;
|
||||
};
|
||||
|
||||
} // namespace Frontend
|
@@ -849,14 +849,14 @@ static void ReadMemory() {
|
||||
SendReply("E01");
|
||||
}
|
||||
|
||||
if (!Memory::IsValidVirtualAddress(*Core::System::GetInstance().Kernel().GetCurrentProcess(),
|
||||
addr)) {
|
||||
auto& memory = Core::System::GetInstance().Memory();
|
||||
if (!memory.IsValidVirtualAddress(*Core::System::GetInstance().Kernel().GetCurrentProcess(),
|
||||
addr)) {
|
||||
return SendReply("E00");
|
||||
}
|
||||
|
||||
std::vector<u8> data(len);
|
||||
Core::System::GetInstance().Memory().ReadBlock(
|
||||
*Core::System::GetInstance().Kernel().GetCurrentProcess(), addr, data.data(), len);
|
||||
memory.ReadBlock(addr, data.data(), len);
|
||||
|
||||
MemToGdbHex(reply, data.data(), len);
|
||||
reply[len * 2] = '\0';
|
||||
@@ -873,16 +873,16 @@ static void WriteMemory() {
|
||||
auto len_pos = std::find(start_offset, command_buffer + command_length, ':');
|
||||
u32 len = HexToInt(start_offset, static_cast<u32>(len_pos - start_offset));
|
||||
|
||||
if (!Memory::IsValidVirtualAddress(*Core::System::GetInstance().Kernel().GetCurrentProcess(),
|
||||
addr)) {
|
||||
auto& memory = Core::System::GetInstance().Memory();
|
||||
if (!memory.IsValidVirtualAddress(*Core::System::GetInstance().Kernel().GetCurrentProcess(),
|
||||
addr)) {
|
||||
return SendReply("E00");
|
||||
}
|
||||
|
||||
std::vector<u8> data(len);
|
||||
|
||||
GdbHexToMem(data.data(), len_pos + 1, len);
|
||||
Core::System::GetInstance().Memory().WriteBlock(
|
||||
*Core::System::GetInstance().Kernel().GetCurrentProcess(), addr, data.data(), len);
|
||||
memory.WriteBlock(addr, data.data(), len);
|
||||
Core::GetRunningCore().ClearInstructionCache();
|
||||
SendReply("OK");
|
||||
}
|
||||
|
@@ -80,7 +80,7 @@ private:
|
||||
std::shared_ptr<Callback> timeout_callback;
|
||||
|
||||
void WakeUp(ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
|
||||
std::shared_ptr<WaitObject> object);
|
||||
std::shared_ptr<WaitObject> object) override;
|
||||
|
||||
class DummyCallback : public WakeupCallback {
|
||||
public:
|
||||
|
@@ -138,10 +138,10 @@ ResultCode TranslateCommandBuffer(Kernel::KernelSystem& kernel, Memory::MemorySy
|
||||
u32 size = static_cast<u32>(descInfo.size);
|
||||
IPC::MappedBufferPermissions permissions = descInfo.perms;
|
||||
|
||||
VAddr page_start = Common::AlignDown(source_address, Memory::PAGE_SIZE);
|
||||
VAddr page_start = Common::AlignDown(source_address, Memory::CITRA_PAGE_SIZE);
|
||||
u32 page_offset = source_address - page_start;
|
||||
u32 num_pages =
|
||||
Common::AlignUp(page_offset + size, Memory::PAGE_SIZE) >> Memory::PAGE_BITS;
|
||||
u32 num_pages = Common::AlignUp(page_offset + size, Memory::CITRA_PAGE_SIZE) >>
|
||||
Memory::CITRA_PAGE_BITS;
|
||||
|
||||
// Skip when the size is zero and num_pages == 0
|
||||
if (size == 0) {
|
||||
@@ -171,8 +171,8 @@ ResultCode TranslateCommandBuffer(Kernel::KernelSystem& kernel, Memory::MemorySy
|
||||
found->target_address, size);
|
||||
}
|
||||
|
||||
VAddr prev_reserve = page_start - Memory::PAGE_SIZE;
|
||||
VAddr next_reserve = page_start + num_pages * Memory::PAGE_SIZE;
|
||||
VAddr prev_reserve = page_start - Memory::CITRA_PAGE_SIZE;
|
||||
VAddr next_reserve = page_start + num_pages * Memory::CITRA_PAGE_SIZE;
|
||||
|
||||
auto& prev_vma = src_process->vm_manager.FindVMA(prev_reserve)->second;
|
||||
auto& next_vma = src_process->vm_manager.FindVMA(next_reserve)->second;
|
||||
@@ -180,8 +180,9 @@ ResultCode TranslateCommandBuffer(Kernel::KernelSystem& kernel, Memory::MemorySy
|
||||
next_vma.meminfo_state == MemoryState::Reserved);
|
||||
|
||||
// Unmap the buffer and guard pages from the source process
|
||||
ResultCode result = src_process->vm_manager.UnmapRange(
|
||||
page_start - Memory::PAGE_SIZE, (num_pages + 2) * Memory::PAGE_SIZE);
|
||||
ResultCode result =
|
||||
src_process->vm_manager.UnmapRange(page_start - Memory::CITRA_PAGE_SIZE,
|
||||
(num_pages + 2) * Memory::CITRA_PAGE_SIZE);
|
||||
ASSERT(result == RESULT_SUCCESS);
|
||||
|
||||
mapped_buffer_context.erase(found);
|
||||
@@ -196,13 +197,13 @@ ResultCode TranslateCommandBuffer(Kernel::KernelSystem& kernel, Memory::MemorySy
|
||||
|
||||
// Reserve a page of memory before the mapped buffer
|
||||
std::shared_ptr<BackingMem> reserve_buffer =
|
||||
std::make_shared<BufferMem>(Memory::PAGE_SIZE);
|
||||
std::make_shared<BufferMem>(Memory::CITRA_PAGE_SIZE);
|
||||
dst_process->vm_manager.MapBackingMemoryToBase(
|
||||
Memory::IPC_MAPPING_VADDR, Memory::IPC_MAPPING_SIZE, reserve_buffer,
|
||||
Memory::PAGE_SIZE, Kernel::MemoryState::Reserved);
|
||||
Memory::CITRA_PAGE_SIZE, Kernel::MemoryState::Reserved);
|
||||
|
||||
std::shared_ptr<BackingMem> buffer =
|
||||
std::make_shared<BufferMem>(num_pages * Memory::PAGE_SIZE);
|
||||
std::make_shared<BufferMem>(num_pages * Memory::CITRA_PAGE_SIZE);
|
||||
memory.ReadBlock(*src_process, source_address, buffer->GetPtr() + page_offset, size);
|
||||
|
||||
// Map the page(s) into the target process' address space.
|
||||
|
@@ -127,7 +127,7 @@ void Process::ParseKernelCaps(const u32* kernel_caps, std::size_t len) {
|
||||
// Mapped memory page
|
||||
AddressMapping mapping;
|
||||
mapping.address = descriptor << 12;
|
||||
mapping.size = Memory::PAGE_SIZE;
|
||||
mapping.size = Memory::CITRA_PAGE_SIZE;
|
||||
mapping.read_only = false;
|
||||
mapping.unk_flag = false;
|
||||
|
||||
@@ -265,7 +265,7 @@ ResultCode Process::HeapFree(VAddr target, u32 size) {
|
||||
|
||||
// Free heaps block by block
|
||||
CASCADE_RESULT(auto backing_blocks, vm_manager.GetBackingBlocksForRange(target, size));
|
||||
for (const auto [backing_memory, block_size] : backing_blocks) {
|
||||
for (const auto& [backing_memory, block_size] : backing_blocks) {
|
||||
memory_region->Free(kernel.memory.GetFCRAMOffset(backing_memory.GetPtr()), block_size);
|
||||
}
|
||||
|
||||
@@ -396,7 +396,7 @@ ResultCode Process::Map(VAddr target, VAddr source, u32 size, VMAPermission perm
|
||||
|
||||
CASCADE_RESULT(auto backing_blocks, vm_manager.GetBackingBlocksForRange(source, size));
|
||||
VAddr interval_target = target;
|
||||
for (const auto [backing_memory, block_size] : backing_blocks) {
|
||||
for (const auto& [backing_memory, block_size] : backing_blocks) {
|
||||
auto target_vma =
|
||||
vm_manager.MapBackingMemory(interval_target, backing_memory, block_size, target_state);
|
||||
ASSERT(target_vma.Succeeded());
|
||||
|
@@ -10,7 +10,6 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
@@ -39,7 +38,6 @@
|
||||
#include "core/hle/kernel/wait_object.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -219,10 +217,10 @@ ResultCode SVC::ControlMemory(u32* out_addr, u32 addr0, u32 addr1, u32 size, u32
|
||||
"size=0x{:X}, permissions=0x{:08X}",
|
||||
operation, addr0, addr1, size, permissions);
|
||||
|
||||
if ((addr0 & Memory::PAGE_MASK) != 0 || (addr1 & Memory::PAGE_MASK) != 0) {
|
||||
if ((addr0 & Memory::CITRA_PAGE_MASK) != 0 || (addr1 & Memory::CITRA_PAGE_MASK) != 0) {
|
||||
return ERR_MISALIGNED_ADDRESS;
|
||||
}
|
||||
if ((size & Memory::PAGE_MASK) != 0) {
|
||||
if ((size & Memory::CITRA_PAGE_MASK) != 0) {
|
||||
return ERR_MISALIGNED_SIZE;
|
||||
}
|
||||
|
||||
@@ -374,7 +372,7 @@ ResultCode SVC::UnmapMemoryBlock(Handle handle, u32 addr) {
|
||||
|
||||
/// Connect to an OS service given the port name, returns the handle to the port to out
|
||||
ResultCode SVC::ConnectToPort(Handle* out_handle, VAddr port_name_address) {
|
||||
if (!Memory::IsValidVirtualAddress(*kernel.GetCurrentProcess(), port_name_address))
|
||||
if (!memory.IsValidVirtualAddress(*kernel.GetCurrentProcess(), port_name_address))
|
||||
return ERR_NOT_FOUND;
|
||||
|
||||
static constexpr std::size_t PortNameMaxLength = 11;
|
||||
@@ -541,7 +539,7 @@ ResultCode SVC::WaitSynchronizationN(s32* out, VAddr handles_address, s32 handle
|
||||
bool wait_all, s64 nano_seconds) {
|
||||
Thread* thread = kernel.GetCurrentThreadManager().GetCurrentThread();
|
||||
|
||||
if (!Memory::IsValidVirtualAddress(*kernel.GetCurrentProcess(), handles_address))
|
||||
if (!memory.IsValidVirtualAddress(*kernel.GetCurrentProcess(), handles_address))
|
||||
return ERR_INVALID_POINTER;
|
||||
|
||||
// NOTE: on real hardware, there is no nullptr check for 'out' (tested with firmware 4.4). If
|
||||
@@ -687,7 +685,7 @@ static ResultCode ReceiveIPCRequest(Kernel::KernelSystem& kernel, Memory::Memory
|
||||
/// In a single operation, sends a IPC reply and waits for a new request.
|
||||
ResultCode SVC::ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_count,
|
||||
Handle reply_target) {
|
||||
if (!Memory::IsValidVirtualAddress(*kernel.GetCurrentProcess(), handles_address))
|
||||
if (!memory.IsValidVirtualAddress(*kernel.GetCurrentProcess(), handles_address))
|
||||
return ERR_INVALID_POINTER;
|
||||
|
||||
// Check if 'handle_count' is invalid
|
||||
@@ -1288,7 +1286,7 @@ s64 SVC::GetSystemTick() {
|
||||
/// Creates a memory block at the specified address with the specified permissions and size
|
||||
ResultCode SVC::CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32 my_permission,
|
||||
u32 other_permission) {
|
||||
if (size % Memory::PAGE_SIZE != 0)
|
||||
if (size % Memory::CITRA_PAGE_SIZE != 0)
|
||||
return ERR_MISALIGNED_SIZE;
|
||||
|
||||
std::shared_ptr<SharedMemory> shared_memory = nullptr;
|
||||
@@ -1393,7 +1391,7 @@ ResultCode SVC::AcceptSession(Handle* out_server_session, Handle server_port_han
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static void CopyStringPart(char* out, const char* in, int offset, int max_length) {
|
||||
static void CopyStringPart(char* out, const char* in, size_t offset, size_t max_length) {
|
||||
size_t str_size = strlen(in);
|
||||
if (offset < str_size) {
|
||||
strncpy(out, in + offset, max_length - 1);
|
||||
@@ -1509,7 +1507,7 @@ ResultCode SVC::GetProcessInfo(s64* out, Handle process_handle, u32 type) {
|
||||
// TODO(yuriks): Type 0 returns a slightly higher number than type 2, but I'm not sure
|
||||
// what's the difference between them.
|
||||
*out = process->memory_used;
|
||||
if (*out % Memory::PAGE_SIZE != 0) {
|
||||
if (*out % Memory::CITRA_PAGE_SIZE != 0) {
|
||||
LOG_ERROR(Kernel_SVC, "called, memory size not page-aligned");
|
||||
return ERR_MISALIGNED_SIZE;
|
||||
}
|
||||
|
@@ -105,9 +105,9 @@ void Thread::Stop() {
|
||||
ReleaseThreadMutexes(this);
|
||||
|
||||
// Mark the TLS slot in the thread's page as free.
|
||||
u32 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE;
|
||||
u32 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::CITRA_PAGE_SIZE;
|
||||
u32 tls_slot =
|
||||
((tls_address - Memory::TLS_AREA_VADDR) % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE;
|
||||
((tls_address - Memory::TLS_AREA_VADDR) % Memory::CITRA_PAGE_SIZE) / Memory::TLS_ENTRY_SIZE;
|
||||
ASSERT(owner_process.lock());
|
||||
owner_process.lock()->tls_slots[tls_page].reset(tls_slot);
|
||||
}
|
||||
@@ -337,8 +337,7 @@ ResultVal<std::shared_ptr<Thread>> KernelSystem::CreateThread(
|
||||
}
|
||||
|
||||
// TODO(yuriks): Other checks, returning 0xD9001BEA
|
||||
|
||||
if (!Memory::IsValidVirtualAddress(*owner_process, entry_point)) {
|
||||
if (!memory.IsValidVirtualAddress(*owner_process, entry_point)) {
|
||||
LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:08x}", name, entry_point);
|
||||
// TODO: Verify error
|
||||
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
|
||||
@@ -374,13 +373,13 @@ ResultVal<std::shared_ptr<Thread>> KernelSystem::CreateThread(
|
||||
auto memory_region = GetMemoryRegion(MemoryRegion::BASE);
|
||||
|
||||
// Allocate some memory from the end of the linear heap for this region.
|
||||
auto offset = memory_region->LinearAllocate(Memory::PAGE_SIZE);
|
||||
auto offset = memory_region->LinearAllocate(Memory::CITRA_PAGE_SIZE);
|
||||
if (!offset) {
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Not enough space in region to allocate a new TLS page for thread");
|
||||
return ERR_OUT_OF_MEMORY;
|
||||
}
|
||||
owner_process->memory_used += Memory::PAGE_SIZE;
|
||||
owner_process->memory_used += Memory::CITRA_PAGE_SIZE;
|
||||
|
||||
tls_slots.emplace_back(0); // The page is completely available at the start
|
||||
available_page = tls_slots.size() - 1;
|
||||
@@ -390,14 +389,14 @@ ResultVal<std::shared_ptr<Thread>> KernelSystem::CreateThread(
|
||||
|
||||
// Map the page to the current process' address space.
|
||||
vm_manager.MapBackingMemory(
|
||||
Memory::TLS_AREA_VADDR + static_cast<VAddr>(available_page) * Memory::PAGE_SIZE,
|
||||
memory.GetFCRAMRef(*offset), Memory::PAGE_SIZE, MemoryState::Locked);
|
||||
Memory::TLS_AREA_VADDR + static_cast<VAddr>(available_page) * Memory::CITRA_PAGE_SIZE,
|
||||
memory.GetFCRAMRef(*offset), Memory::CITRA_PAGE_SIZE, MemoryState::Locked);
|
||||
}
|
||||
|
||||
// Mark the slot as used
|
||||
tls_slots[available_page].set(available_slot);
|
||||
thread->tls_address = Memory::TLS_AREA_VADDR +
|
||||
static_cast<VAddr>(available_page) * Memory::PAGE_SIZE +
|
||||
static_cast<VAddr>(available_page) * Memory::CITRA_PAGE_SIZE +
|
||||
static_cast<VAddr>(available_slot) * Memory::TLS_ENTRY_SIZE;
|
||||
|
||||
memory.ZeroBlock(*owner_process, thread->tls_address, Memory::TLS_ENTRY_SIZE);
|
||||
|
@@ -260,8 +260,8 @@ VMManager::VMAIter VMManager::StripIterConstness(const VMAHandle& iter) {
|
||||
}
|
||||
|
||||
ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u32 size) {
|
||||
ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: {:#10X}", size);
|
||||
ASSERT_MSG((base & Memory::PAGE_MASK) == 0, "non-page aligned base: {:#010X}", base);
|
||||
ASSERT_MSG((size & Memory::CITRA_PAGE_MASK) == 0, "non-page aligned size: {:#10X}", size);
|
||||
ASSERT_MSG((base & Memory::CITRA_PAGE_MASK) == 0, "non-page aligned base: {:#010X}", base);
|
||||
|
||||
VMAIter vma_handle = StripIterConstness(FindVMA(base));
|
||||
if (vma_handle == vma_map.end()) {
|
||||
@@ -296,8 +296,8 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u32 size) {
|
||||
}
|
||||
|
||||
ResultVal<VMManager::VMAIter> VMManager::CarveVMARange(VAddr target, u32 size) {
|
||||
ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: {:#10X}", size);
|
||||
ASSERT_MSG((target & Memory::PAGE_MASK) == 0, "non-page aligned base: {:#010X}", target);
|
||||
ASSERT_MSG((size & Memory::CITRA_PAGE_MASK) == 0, "non-page aligned size: {:#10X}", size);
|
||||
ASSERT_MSG((target & Memory::CITRA_PAGE_MASK) == 0, "non-page aligned base: {:#010X}", target);
|
||||
|
||||
const VAddr target_end = target + size;
|
||||
ASSERT(target_end >= target);
|
||||
|
@@ -17,12 +17,9 @@
|
||||
#include "core/file_sys/errors.h"
|
||||
#include "core/file_sys/ncch_container.h"
|
||||
#include "core/file_sys/title_metadata.h"
|
||||
#include "core/hle/ipc.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/client_session.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
#include "core/hle/kernel/session.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
@@ -610,7 +607,6 @@ void Module::Interface::FindDLCContentInfos(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
std::string tmd_path = GetTitleMetadataPath(media_type, title_id);
|
||||
|
||||
u32 content_read = 0;
|
||||
FileSys::TitleMetadata tmd;
|
||||
if (tmd.Load(tmd_path) == Loader::ResultStatus::Success) {
|
||||
std::size_t write_offset = 0;
|
||||
@@ -642,7 +638,6 @@ void Module::Interface::FindDLCContentInfos(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
content_info_out.Write(&content_info, write_offset, sizeof(ContentInfo));
|
||||
write_offset += sizeof(ContentInfo);
|
||||
content_read++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -2,7 +2,6 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/common_paths.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/applets/applet.h"
|
||||
#include "core/hle/service/apt/applet_manager.h"
|
||||
@@ -27,47 +26,66 @@ struct AppletTitleData {
|
||||
|
||||
static constexpr std::size_t NumApplets = 29;
|
||||
static constexpr std::array<AppletTitleData, NumApplets> applet_titleids = {{
|
||||
{AppletId::HomeMenu, AppletId::None, 0x4003000008202, 0x4003000008F02, 0x4003000009802,
|
||||
0x4003000008202, 0x400300000A102, 0x400300000A902, 0x400300000B102},
|
||||
{AppletId::AlternateMenu, AppletId::None, 0x4003000008102, 0x4003000008102, 0x4003000008102,
|
||||
0x4003000008102, 0x4003000008102, 0x4003000008102, 0x4003000008102},
|
||||
{AppletId::Camera, AppletId::None, 0x4003000008402, 0x4003000009002, 0x4003000009902,
|
||||
0x4003000008402, 0x400300000A202, 0x400300000AA02, 0x400300000B202},
|
||||
{AppletId::FriendList, AppletId::None, 0x4003000008D02, 0x4003000009602, 0x4003000009F02,
|
||||
0x4003000008D02, 0x400300000A702, 0x400300000AF02, 0x400300000B702},
|
||||
{AppletId::GameNotes, AppletId::None, 0x4003000008702, 0x4003000009302, 0x4003000009C02,
|
||||
0x4003000008702, 0x400300000A502, 0x400300000AD02, 0x400300000B502},
|
||||
{AppletId::InternetBrowser, AppletId::None, 0x4003000008802, 0x4003000009402, 0x4003000009D02,
|
||||
0x4003000008802, 0x400300000A602, 0x400300000AE02, 0x400300000B602},
|
||||
{AppletId::InstructionManual, AppletId::None, 0x4003000008602, 0x4003000009202, 0x4003000009B02,
|
||||
0x4003000008602, 0x400300000A402, 0x400300000AC02, 0x400300000B402},
|
||||
{AppletId::Notifications, AppletId::None, 0x4003000008E02, 0x4003000009702, 0x400300000A002,
|
||||
0x4003000008E02, 0x400300000A802, 0x400300000B002, 0x400300000B802},
|
||||
{AppletId::Miiverse, AppletId::None, 0x400300000BC02, 0x400300000BD02, 0x400300000BE02,
|
||||
0x400300000BC02, 0x4003000009E02, 0x4003000009502, 0x400300000B902},
|
||||
{{AppletId::HomeMenu, AppletId::None},
|
||||
{0x4003000008202, 0x4003000008F02, 0x4003000009802, 0x4003000008202, 0x400300000A102,
|
||||
0x400300000A902, 0x400300000B102}},
|
||||
{{AppletId::AlternateMenu, AppletId::None},
|
||||
{0x4003000008102, 0x4003000008102, 0x4003000008102, 0x4003000008102, 0x4003000008102,
|
||||
0x4003000008102, 0x4003000008102}},
|
||||
{{AppletId::Camera, AppletId::None},
|
||||
{0x4003000008402, 0x4003000009002, 0x4003000009902, 0x4003000008402, 0x400300000A202,
|
||||
0x400300000AA02, 0x400300000B202}},
|
||||
{{AppletId::FriendList, AppletId::None},
|
||||
{0x4003000008D02, 0x4003000009602, 0x4003000009F02, 0x4003000008D02, 0x400300000A702,
|
||||
0x400300000AF02, 0x400300000B702}},
|
||||
{{AppletId::GameNotes, AppletId::None},
|
||||
{0x4003000008702, 0x4003000009302, 0x4003000009C02, 0x4003000008702, 0x400300000A502,
|
||||
0x400300000AD02, 0x400300000B502}},
|
||||
{{AppletId::InternetBrowser, AppletId::None},
|
||||
{0x4003000008802, 0x4003000009402, 0x4003000009D02, 0x4003000008802, 0x400300000A602,
|
||||
0x400300000AE02, 0x400300000B602}},
|
||||
{{AppletId::InstructionManual, AppletId::None},
|
||||
{0x4003000008602, 0x4003000009202, 0x4003000009B02, 0x4003000008602, 0x400300000A402,
|
||||
0x400300000AC02, 0x400300000B402}},
|
||||
{{AppletId::Notifications, AppletId::None},
|
||||
{0x4003000008E02, 0x4003000009702, 0x400300000A002, 0x4003000008E02, 0x400300000A802,
|
||||
0x400300000B002, 0x400300000B802}},
|
||||
{{AppletId::Miiverse, AppletId::None},
|
||||
{0x400300000BC02, 0x400300000BD02, 0x400300000BE02, 0x400300000BC02, 0x4003000009E02,
|
||||
0x4003000009502, 0x400300000B902}},
|
||||
// These values obtained from an older NS dump firmware 4.5
|
||||
{AppletId::MiiversePost, AppletId::None, 0x400300000BA02, 0x400300000BA02, 0x400300000BA02,
|
||||
0x400300000BA02, 0x400300000BA02, 0x400300000BA02, 0x400300000BA02},
|
||||
{{AppletId::MiiversePost, AppletId::None},
|
||||
{0x400300000BA02, 0x400300000BA02, 0x400300000BA02, 0x400300000BA02, 0x400300000BA02,
|
||||
0x400300000BA02, 0x400300000BA02}},
|
||||
// {AppletId::MiiversePost, AppletId::None, 0x4003000008302, 0x4003000008B02, 0x400300000BA02,
|
||||
// 0x4003000008302, 0x0, 0x0, 0x0},
|
||||
{AppletId::AmiiboSettings, AppletId::None, 0x4003000009502, 0x4003000009E02, 0x400300000B902,
|
||||
0x4003000009502, 0x0, 0x4003000008C02, 0x400300000BF02},
|
||||
{AppletId::SoftwareKeyboard1, AppletId::SoftwareKeyboard2, 0x400300000C002, 0x400300000C802,
|
||||
0x400300000D002, 0x400300000C002, 0x400300000D802, 0x400300000DE02, 0x400300000E402},
|
||||
{AppletId::Ed1, AppletId::Ed2, 0x400300000C102, 0x400300000C902, 0x400300000D102,
|
||||
0x400300000C102, 0x400300000D902, 0x400300000DF02, 0x400300000E502},
|
||||
{AppletId::PnoteApp, AppletId::PnoteApp2, 0x400300000C302, 0x400300000CB02, 0x400300000D302,
|
||||
0x400300000C302, 0x400300000DB02, 0x400300000E102, 0x400300000E702},
|
||||
{AppletId::SnoteApp, AppletId::SnoteApp2, 0x400300000C402, 0x400300000CC02, 0x400300000D402,
|
||||
0x400300000C402, 0x400300000DC02, 0x400300000E202, 0x400300000E802},
|
||||
{AppletId::Error, AppletId::Error2, 0x400300000C502, 0x400300000C502, 0x400300000C502,
|
||||
0x400300000C502, 0x400300000CF02, 0x400300000CF02, 0x400300000CF02},
|
||||
{AppletId::Mint, AppletId::Mint2, 0x400300000C602, 0x400300000CE02, 0x400300000D602,
|
||||
0x400300000C602, 0x400300000DD02, 0x400300000E302, 0x400300000E902},
|
||||
{AppletId::Extrapad, AppletId::Extrapad2, 0x400300000CD02, 0x400300000CD02, 0x400300000CD02,
|
||||
0x400300000CD02, 0x400300000D502, 0x400300000D502, 0x400300000D502},
|
||||
{AppletId::Memolib, AppletId::Memolib2, 0x400300000F602, 0x400300000F602, 0x400300000F602,
|
||||
0x400300000F602, 0x400300000F602, 0x400300000F602, 0x400300000F602},
|
||||
{{AppletId::AmiiboSettings, AppletId::None},
|
||||
{0x4003000009502, 0x4003000009E02, 0x400300000B902, 0x4003000009502, 0x0, 0x4003000008C02,
|
||||
0x400300000BF02}},
|
||||
{{AppletId::SoftwareKeyboard1, AppletId::SoftwareKeyboard2},
|
||||
{0x400300000C002, 0x400300000C802, 0x400300000D002, 0x400300000C002, 0x400300000D802,
|
||||
0x400300000DE02, 0x400300000E402}},
|
||||
{{AppletId::Ed1, AppletId::Ed2},
|
||||
{0x400300000C102, 0x400300000C902, 0x400300000D102, 0x400300000C102, 0x400300000D902,
|
||||
0x400300000DF02, 0x400300000E502}},
|
||||
{{AppletId::PnoteApp, AppletId::PnoteApp2},
|
||||
{0x400300000C302, 0x400300000CB02, 0x400300000D302, 0x400300000C302, 0x400300000DB02,
|
||||
0x400300000E102, 0x400300000E702}},
|
||||
{{AppletId::SnoteApp, AppletId::SnoteApp2},
|
||||
{0x400300000C402, 0x400300000CC02, 0x400300000D402, 0x400300000C402, 0x400300000DC02,
|
||||
0x400300000E202, 0x400300000E802}},
|
||||
{{AppletId::Error, AppletId::Error2},
|
||||
{0x400300000C502, 0x400300000C502, 0x400300000C502, 0x400300000C502, 0x400300000CF02,
|
||||
0x400300000CF02, 0x400300000CF02}},
|
||||
{{AppletId::Mint, AppletId::Mint2},
|
||||
{0x400300000C602, 0x400300000CE02, 0x400300000D602, 0x400300000C602, 0x400300000DD02,
|
||||
0x400300000E302, 0x400300000E902}},
|
||||
{{AppletId::Extrapad, AppletId::Extrapad2},
|
||||
{0x400300000CD02, 0x400300000CD02, 0x400300000CD02, 0x400300000CD02, 0x400300000D502,
|
||||
0x400300000D502, 0x400300000D502}},
|
||||
{{AppletId::Memolib, AppletId::Memolib2},
|
||||
{0x400300000F602, 0x400300000F602, 0x400300000F602, 0x400300000F602, 0x400300000F602,
|
||||
0x400300000F602, 0x400300000F602}},
|
||||
// TODO(Subv): Fill in the rest of the titleids
|
||||
}};
|
||||
|
||||
|
@@ -10,7 +10,6 @@
|
||||
#include <cryptopp/osrng.h>
|
||||
#include <cryptopp/sha.h>
|
||||
#include "common/archives.h"
|
||||
#include "common/common_paths.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
@@ -107,7 +106,6 @@ static_assert(sizeof(ConsoleCountryInfo) == 4, "ConsoleCountryInfo must be exact
|
||||
|
||||
constexpr EULAVersion MAX_EULA_VERSION{0x7F, 0x7F};
|
||||
constexpr ConsoleModelInfo CONSOLE_MODEL_OLD{NINTENDO_3DS_XL, {0, 0, 0}};
|
||||
constexpr ConsoleModelInfo CONSOLE_MODEL_NEW{NEW_NINTENDO_3DS_XL, {0, 0, 0}};
|
||||
constexpr u8 CONSOLE_LANGUAGE = LANGUAGE_EN;
|
||||
constexpr UsernameBlock CONSOLE_USERNAME_BLOCK{u"CITRA", 0, 0};
|
||||
constexpr BirthdayBlock PROFILE_BIRTHDAY{3, 25}; // March 25th, 2014
|
||||
@@ -269,7 +267,7 @@ void Module::Interface::GetSystemModel(Kernel::HLERequestContext& ctx) {
|
||||
void Module::Interface::GetModelNintendo2DS(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x06, 0, 0);
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||
u32 data;
|
||||
u32 data{};
|
||||
|
||||
// TODO(Subv): Find out the correct error codes
|
||||
rb.Push(cfg->GetConfigInfoBlock(ConsoleModelBlockID, 4, 0x8, reinterpret_cast<u8*>(&data)));
|
||||
@@ -370,7 +368,7 @@ ResultVal<void*> Module::GetConfigInfoBlockPointer(u32 block_id, u32 size, u32 f
|
||||
}
|
||||
|
||||
ResultCode Module::GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, void* output) {
|
||||
void* pointer;
|
||||
void* pointer = nullptr;
|
||||
CASCADE_RESULT(pointer, GetConfigInfoBlockPointer(block_id, size, flag));
|
||||
memcpy(output, pointer, size);
|
||||
|
||||
@@ -378,7 +376,7 @@ ResultCode Module::GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, void* ou
|
||||
}
|
||||
|
||||
ResultCode Module::SetConfigInfoBlock(u32 block_id, u32 size, u32 flag, const void* input) {
|
||||
void* pointer;
|
||||
void* pointer = nullptr;
|
||||
CASCADE_RESULT(pointer, GetConfigInfoBlockPointer(block_id, size, flag));
|
||||
memcpy(pointer, input, size);
|
||||
return RESULT_SUCCESS;
|
||||
@@ -701,7 +699,7 @@ void Module::SetSystemLanguage(SystemLanguage language) {
|
||||
}
|
||||
|
||||
SystemLanguage Module::GetSystemLanguage() {
|
||||
u8 block;
|
||||
u8 block{};
|
||||
GetConfigInfoBlock(LanguageBlockID, sizeof(block), 8, &block);
|
||||
return static_cast<SystemLanguage>(block);
|
||||
}
|
||||
@@ -712,7 +710,7 @@ void Module::SetSoundOutputMode(SoundOutputMode mode) {
|
||||
}
|
||||
|
||||
SoundOutputMode Module::GetSoundOutputMode() {
|
||||
u8 block;
|
||||
u8 block{};
|
||||
GetConfigInfoBlock(SoundOutputModeBlockID, sizeof(block), 8, &block);
|
||||
return static_cast<SoundOutputMode>(block);
|
||||
}
|
||||
@@ -723,7 +721,7 @@ void Module::SetCountryCode(u8 country_code) {
|
||||
}
|
||||
|
||||
u8 Module::GetCountryCode() {
|
||||
ConsoleCountryInfo block;
|
||||
ConsoleCountryInfo block{};
|
||||
GetConfigInfoBlock(CountryInfoBlockID, sizeof(block), 8, &block);
|
||||
return block.country_code;
|
||||
}
|
||||
@@ -734,7 +732,7 @@ void Module::SetCountryInfo(u8 country_code, u8 state_code) {
|
||||
}
|
||||
|
||||
u8 Module::GetStateCode() {
|
||||
ConsoleCountryInfo block;
|
||||
ConsoleCountryInfo block{};
|
||||
GetConfigInfoBlock(CountryInfoBlockID, sizeof(block), 8, &block);
|
||||
return block.state_code;
|
||||
}
|
||||
|
@@ -7,6 +7,7 @@
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
|
@@ -192,7 +192,7 @@ static_assert(sizeof(CaptureState) == 0x8, "CaptureState structure size is wrong
|
||||
|
||||
void CSND_SND::Initialize(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x01, 5, 0);
|
||||
const u32 size = Common::AlignUp(rp.Pop<u32>(), Memory::PAGE_SIZE);
|
||||
const u32 size = Common::AlignUp(rp.Pop<u32>(), Memory::CITRA_PAGE_SIZE);
|
||||
master_state_offset = rp.Pop<u32>();
|
||||
channel_state_offset = rp.Pop<u32>();
|
||||
capture_state_offset = rp.Pop<u32>();
|
||||
|
@@ -95,6 +95,9 @@ void DSP_DSP::WriteProcessPipe(Kernel::HLERequestContext& ctx) {
|
||||
buffer[6] = 0;
|
||||
buffer[7] = 0;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Service_DSP, "Unknown pipe {}", pipe);
|
||||
break;
|
||||
}
|
||||
|
||||
system.DSP().PipeWrite(pipe, buffer);
|
||||
|
@@ -884,6 +884,8 @@ ResultVal<u16> FS_USER::GetSpecialContentIndexFromTMD(MediaType media_type, u64
|
||||
default:
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
FS_USER::FS_USER(Core::System& system)
|
||||
|
@@ -8,9 +8,7 @@
|
||||
#include "common/microprofile.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/shared_memory.h"
|
||||
#include "core/hle/kernel/shared_page.h"
|
||||
#include "core/hle/result.h"
|
||||
@@ -83,7 +81,9 @@ u32 GSP_GPU::GetUnusedThreadId() const {
|
||||
if (!used_thread_ids[id])
|
||||
return id;
|
||||
}
|
||||
ASSERT_MSG(false, "All GSP threads are in use");
|
||||
|
||||
UNREACHABLE_MSG("All GSP threads are in use");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Gets a pointer to a thread command buffer in GSP shared memory
|
||||
|
@@ -1502,7 +1502,7 @@ u32 CROHelper::Fix(u32 fix_level) {
|
||||
}
|
||||
}
|
||||
|
||||
fix_end = Common::AlignUp(fix_end, Memory::PAGE_SIZE);
|
||||
fix_end = Common::AlignUp(fix_end, Memory::CITRA_PAGE_SIZE);
|
||||
|
||||
u32 fixed_size = fix_end - module_address;
|
||||
SetField(FixedSize, fixed_size);
|
||||
@@ -1525,8 +1525,8 @@ std::tuple<VAddr, u32> CROHelper::GetExecutablePages() const {
|
||||
SegmentEntry entry;
|
||||
GetEntry(system.Memory(), i, entry);
|
||||
if (entry.type == SegmentType::Code && entry.size != 0) {
|
||||
VAddr begin = Common::AlignDown(entry.offset, Memory::PAGE_SIZE);
|
||||
VAddr end = Common::AlignUp(entry.offset + entry.size, Memory::PAGE_SIZE);
|
||||
VAddr begin = Common::AlignDown(entry.offset, Memory::CITRA_PAGE_SIZE);
|
||||
VAddr end = Common::AlignUp(entry.offset + entry.size, Memory::CITRA_PAGE_SIZE);
|
||||
return std::make_tuple(begin, end - begin);
|
||||
}
|
||||
}
|
||||
|
@@ -87,19 +87,19 @@ void RO::Initialize(Kernel::HLERequestContext& ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (crs_buffer_ptr & Memory::PAGE_MASK) {
|
||||
if (crs_buffer_ptr & Memory::CITRA_PAGE_MASK) {
|
||||
LOG_ERROR(Service_LDR, "CRS original address is not aligned");
|
||||
rb.Push(ERROR_MISALIGNED_ADDRESS);
|
||||
return;
|
||||
}
|
||||
|
||||
if (crs_address & Memory::PAGE_MASK) {
|
||||
if (crs_address & Memory::CITRA_PAGE_MASK) {
|
||||
LOG_ERROR(Service_LDR, "CRS mapping address is not aligned");
|
||||
rb.Push(ERROR_MISALIGNED_ADDRESS);
|
||||
return;
|
||||
}
|
||||
|
||||
if (crs_size & Memory::PAGE_MASK) {
|
||||
if (crs_size & Memory::CITRA_PAGE_MASK) {
|
||||
LOG_ERROR(Service_LDR, "CRS size is not aligned");
|
||||
rb.Push(ERROR_MISALIGNED_SIZE);
|
||||
return;
|
||||
@@ -207,21 +207,21 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cro_buffer_ptr & Memory::PAGE_MASK) {
|
||||
if (cro_buffer_ptr & Memory::CITRA_PAGE_MASK) {
|
||||
LOG_ERROR(Service_LDR, "CRO original address is not aligned");
|
||||
rb.Push(ERROR_MISALIGNED_ADDRESS);
|
||||
rb.Push<u32>(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cro_address & Memory::PAGE_MASK) {
|
||||
if (cro_address & Memory::CITRA_PAGE_MASK) {
|
||||
LOG_ERROR(Service_LDR, "CRO mapping address is not aligned");
|
||||
rb.Push(ERROR_MISALIGNED_ADDRESS);
|
||||
rb.Push<u32>(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cro_size & Memory::PAGE_MASK) {
|
||||
if (cro_size & Memory::CITRA_PAGE_MASK) {
|
||||
LOG_ERROR(Service_LDR, "CRO size is not aligned");
|
||||
rb.Push(ERROR_MISALIGNED_SIZE);
|
||||
rb.Push<u32>(0);
|
||||
@@ -354,7 +354,7 @@ void RO::UnloadCRO(Kernel::HLERequestContext& ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cro_address & Memory::PAGE_MASK) {
|
||||
if (cro_address & Memory::CITRA_PAGE_MASK) {
|
||||
LOG_ERROR(Service_LDR, "CRO address is not aligned");
|
||||
rb.Push(ERROR_MISALIGNED_ADDRESS);
|
||||
return;
|
||||
@@ -421,7 +421,7 @@ void RO::LinkCRO(Kernel::HLERequestContext& ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cro_address & Memory::PAGE_MASK) {
|
||||
if (cro_address & Memory::CITRA_PAGE_MASK) {
|
||||
LOG_ERROR(Service_LDR, "CRO address is not aligned");
|
||||
rb.Push(ERROR_MISALIGNED_ADDRESS);
|
||||
return;
|
||||
@@ -461,7 +461,7 @@ void RO::UnlinkCRO(Kernel::HLERequestContext& ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cro_address & Memory::PAGE_MASK) {
|
||||
if (cro_address & Memory::CITRA_PAGE_MASK) {
|
||||
LOG_ERROR(Service_LDR, "CRO address is not aligned");
|
||||
rb.Push(ERROR_MISALIGNED_ADDRESS);
|
||||
return;
|
||||
|
@@ -92,7 +92,8 @@ u16 NWM_UDS::GetNextAvailableNodeId() {
|
||||
}
|
||||
|
||||
// Any connection attempts to an already full network should have been refused.
|
||||
ASSERT_MSG(false, "No available connection slots in the network");
|
||||
UNREACHABLE_MSG("No available connection slots in the network");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void NWM_UDS::BroadcastNodeMap() {
|
||||
|
@@ -15,9 +15,6 @@
|
||||
|
||||
namespace Service::NWM {
|
||||
|
||||
// 802.11 broadcast MAC address
|
||||
constexpr MacAddress BroadcastMac = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||
|
||||
constexpr u64 DefaultNetworkUptime = 900000000; // 15 minutes in microseconds.
|
||||
|
||||
// Note: These values were taken from a packet capture of an o3DS XL
|
||||
|
@@ -178,15 +178,6 @@ static const std::unordered_map<int, int> sockopt_map = {{
|
||||
{0x1009, SO_ERROR},
|
||||
}};
|
||||
|
||||
/// Converts a socket option from 3ds-specific to platform-specific
|
||||
static int TranslateSockOpt(int console_opt_name) {
|
||||
auto found = sockopt_map.find(console_opt_name);
|
||||
if (found != sockopt_map.end()) {
|
||||
return found->second;
|
||||
}
|
||||
return console_opt_name;
|
||||
}
|
||||
|
||||
/// Structure to represent the 3ds' pollfd structure, which is different than most implementations
|
||||
struct CTRPollFD {
|
||||
u32 fd; ///< Socket handle
|
||||
|
@@ -68,9 +68,11 @@ struct Regs {
|
||||
case PixelFormat::RGB5A1:
|
||||
case PixelFormat::RGBA4:
|
||||
return 2;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
|
||||
INSERT_PADDING_WORDS(0x4);
|
||||
|
@@ -94,13 +94,13 @@ ResultStatus AppLoader_NCCH::LoadExec(std::shared_ptr<Kernel::Process>& process)
|
||||
codeset->CodeSegment().offset = 0;
|
||||
codeset->CodeSegment().addr = overlay_ncch->exheader_header.codeset_info.text.address;
|
||||
codeset->CodeSegment().size =
|
||||
overlay_ncch->exheader_header.codeset_info.text.num_max_pages * Memory::PAGE_SIZE;
|
||||
overlay_ncch->exheader_header.codeset_info.text.num_max_pages * Memory::CITRA_PAGE_SIZE;
|
||||
|
||||
codeset->RODataSegment().offset =
|
||||
codeset->CodeSegment().offset + codeset->CodeSegment().size;
|
||||
codeset->RODataSegment().addr = overlay_ncch->exheader_header.codeset_info.ro.address;
|
||||
codeset->RODataSegment().size =
|
||||
overlay_ncch->exheader_header.codeset_info.ro.num_max_pages * Memory::PAGE_SIZE;
|
||||
overlay_ncch->exheader_header.codeset_info.ro.num_max_pages * Memory::CITRA_PAGE_SIZE;
|
||||
|
||||
// TODO(yuriks): Not sure if the bss size is added to the page-aligned .data size or just
|
||||
// to the regular size. Playing it safe for now.
|
||||
@@ -111,7 +111,8 @@ ResultStatus AppLoader_NCCH::LoadExec(std::shared_ptr<Kernel::Process>& process)
|
||||
codeset->RODataSegment().offset + codeset->RODataSegment().size;
|
||||
codeset->DataSegment().addr = overlay_ncch->exheader_header.codeset_info.data.address;
|
||||
codeset->DataSegment().size =
|
||||
overlay_ncch->exheader_header.codeset_info.data.num_max_pages * Memory::PAGE_SIZE +
|
||||
overlay_ncch->exheader_header.codeset_info.data.num_max_pages *
|
||||
Memory::CITRA_PAGE_SIZE +
|
||||
bss_page_size;
|
||||
|
||||
// Apply patches now that the entire codeset (including .bss) has been allocated
|
||||
|
@@ -2,7 +2,6 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <boost/serialization/array.hpp>
|
||||
#include <boost/serialization/binary_object.hpp>
|
||||
@@ -15,9 +14,7 @@
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/core.h"
|
||||
#include "core/global.h"
|
||||
#include "core/hle/kernel/memory.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/settings.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
@@ -54,20 +51,20 @@ public:
|
||||
private:
|
||||
bool* At(VAddr addr) {
|
||||
if (addr >= VRAM_VADDR && addr < VRAM_VADDR_END) {
|
||||
return &vram[(addr - VRAM_VADDR) / PAGE_SIZE];
|
||||
return &vram[(addr - VRAM_VADDR) / CITRA_PAGE_SIZE];
|
||||
}
|
||||
if (addr >= LINEAR_HEAP_VADDR && addr < LINEAR_HEAP_VADDR_END) {
|
||||
return &linear_heap[(addr - LINEAR_HEAP_VADDR) / PAGE_SIZE];
|
||||
return &linear_heap[(addr - LINEAR_HEAP_VADDR) / CITRA_PAGE_SIZE];
|
||||
}
|
||||
if (addr >= NEW_LINEAR_HEAP_VADDR && addr < NEW_LINEAR_HEAP_VADDR_END) {
|
||||
return &new_linear_heap[(addr - NEW_LINEAR_HEAP_VADDR) / PAGE_SIZE];
|
||||
return &new_linear_heap[(addr - NEW_LINEAR_HEAP_VADDR) / CITRA_PAGE_SIZE];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::array<bool, VRAM_SIZE / PAGE_SIZE> vram{};
|
||||
std::array<bool, LINEAR_HEAP_SIZE / PAGE_SIZE> linear_heap{};
|
||||
std::array<bool, NEW_LINEAR_HEAP_SIZE / PAGE_SIZE> new_linear_heap{};
|
||||
std::array<bool, VRAM_SIZE / CITRA_PAGE_SIZE> vram{};
|
||||
std::array<bool, LINEAR_HEAP_SIZE / CITRA_PAGE_SIZE> linear_heap{};
|
||||
std::array<bool, NEW_LINEAR_HEAP_SIZE / CITRA_PAGE_SIZE> new_linear_heap{};
|
||||
|
||||
static_assert(sizeof(bool) == 1);
|
||||
friend class boost::serialization::access;
|
||||
@@ -145,6 +142,144 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void WalkBlock(const Kernel::Process& process, const VAddr src_addr, const std::size_t size,
|
||||
auto on_unmapped, auto on_memory, auto on_special, auto on_rasterizer,
|
||||
auto increment) {
|
||||
auto& page_table = *process.vm_manager.page_table;
|
||||
|
||||
std::size_t remaining_size = size;
|
||||
std::size_t page_index = src_addr >> CITRA_PAGE_BITS;
|
||||
std::size_t page_offset = src_addr & CITRA_PAGE_MASK;
|
||||
|
||||
while (remaining_size > 0) {
|
||||
const std::size_t copy_amount = std::min(CITRA_PAGE_SIZE - page_offset, remaining_size);
|
||||
const VAddr current_vaddr =
|
||||
static_cast<VAddr>((page_index << CITRA_PAGE_BITS) + page_offset);
|
||||
|
||||
switch (page_table.attributes[page_index]) {
|
||||
case PageType::Unmapped: {
|
||||
on_unmapped(copy_amount, current_vaddr);
|
||||
break;
|
||||
}
|
||||
case PageType::Memory: {
|
||||
DEBUG_ASSERT(page_table.pointers[page_index]);
|
||||
u8* const src_ptr = page_table.pointers[page_index] + page_offset;
|
||||
on_memory(copy_amount, src_ptr);
|
||||
break;
|
||||
}
|
||||
case PageType::Special: {
|
||||
MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr);
|
||||
DEBUG_ASSERT(handler);
|
||||
on_special(handler, copy_amount, current_vaddr);
|
||||
break;
|
||||
}
|
||||
case PageType::RasterizerCachedMemory: {
|
||||
u8* const rasterizer_ptr = GetPointerForRasterizerCache(current_vaddr);
|
||||
on_rasterizer(current_vaddr, copy_amount, rasterizer_ptr);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
page_index++;
|
||||
page_offset = 0;
|
||||
increment(copy_amount);
|
||||
remaining_size -= copy_amount;
|
||||
}
|
||||
}
|
||||
|
||||
template <bool UNSAFE>
|
||||
void ReadBlockImpl(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer,
|
||||
const std::size_t size) {
|
||||
WalkBlock(
|
||||
process, src_addr, size,
|
||||
[src_addr, size, &dest_buffer](const std::size_t copy_amount,
|
||||
const VAddr current_vaddr) {
|
||||
LOG_ERROR(HW_Memory,
|
||||
"Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
|
||||
current_vaddr, src_addr, size);
|
||||
std::memset(dest_buffer, 0, copy_amount);
|
||||
},
|
||||
[&dest_buffer](const std::size_t copy_amount, const u8* const src_ptr) {
|
||||
std::memcpy(dest_buffer, src_ptr, copy_amount);
|
||||
},
|
||||
[&dest_buffer](MMIORegionPointer& handler, const std::size_t copy_amount,
|
||||
const VAddr current_vaddr) {
|
||||
handler->ReadBlock(current_vaddr, dest_buffer, copy_amount);
|
||||
},
|
||||
[&dest_buffer](const VAddr current_vaddr, const std::size_t copy_amount,
|
||||
const u8* const rasterizer_ptr) {
|
||||
if constexpr (!UNSAFE) {
|
||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||
FlushMode::Flush);
|
||||
}
|
||||
std::memcpy(dest_buffer, rasterizer_ptr, copy_amount);
|
||||
},
|
||||
[&dest_buffer](const std::size_t copy_amount) {
|
||||
dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
|
||||
});
|
||||
}
|
||||
|
||||
template <bool UNSAFE>
|
||||
void WriteBlockImpl(const Kernel::Process& process, const VAddr dest_addr,
|
||||
const void* src_buffer, const std::size_t size) {
|
||||
WalkBlock(
|
||||
process, dest_addr, size,
|
||||
[dest_addr, size](const std::size_t copy_amount, const VAddr current_vaddr) {
|
||||
LOG_ERROR(HW_Memory,
|
||||
"Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
|
||||
current_vaddr, dest_addr, size);
|
||||
},
|
||||
[&src_buffer](const std::size_t copy_amount, u8* const dest_ptr) {
|
||||
std::memcpy(dest_ptr, src_buffer, copy_amount);
|
||||
},
|
||||
[&src_buffer](MMIORegionPointer& handler, const std::size_t copy_amount,
|
||||
const VAddr current_vaddr) {
|
||||
handler->WriteBlock(current_vaddr, src_buffer, copy_amount);
|
||||
},
|
||||
[&src_buffer](const VAddr current_vaddr, const std::size_t copy_amount,
|
||||
u8* const host_ptr) {
|
||||
if constexpr (!UNSAFE) {
|
||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||
FlushMode::Invalidate);
|
||||
}
|
||||
std::memcpy(host_ptr, src_buffer, copy_amount);
|
||||
},
|
||||
[&src_buffer](const std::size_t copy_amount) {
|
||||
src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
|
||||
});
|
||||
}
|
||||
|
||||
MemoryRef GetPointerForRasterizerCache(VAddr addr) const {
|
||||
if (addr >= LINEAR_HEAP_VADDR && addr < LINEAR_HEAP_VADDR_END) {
|
||||
return {fcram_mem, addr - LINEAR_HEAP_VADDR};
|
||||
}
|
||||
if (addr >= NEW_LINEAR_HEAP_VADDR && addr < NEW_LINEAR_HEAP_VADDR_END) {
|
||||
return {fcram_mem, addr - NEW_LINEAR_HEAP_VADDR};
|
||||
}
|
||||
if (addr >= VRAM_VADDR && addr < VRAM_VADDR_END) {
|
||||
return {vram_mem, addr - VRAM_VADDR};
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return MemoryRef{};
|
||||
}
|
||||
|
||||
/**
|
||||
* This function should only be called for virtual addreses with attribute `PageType::Special`.
|
||||
*/
|
||||
MMIORegionPointer GetMMIOHandler(const PageTable& page_table, VAddr vaddr) {
|
||||
for (const auto& region : page_table.special_regions) {
|
||||
if (vaddr >= region.base && vaddr < (region.base + region.size)) {
|
||||
return region.handler;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_MSG(false, "Mapped IO page without a handler @ {:08X}", vaddr);
|
||||
return nullptr; // Should never happen
|
||||
}
|
||||
|
||||
private:
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
@@ -220,10 +355,10 @@ std::shared_ptr<PageTable> MemorySystem::GetCurrentPageTable() const {
|
||||
|
||||
void MemorySystem::MapPages(PageTable& page_table, u32 base, u32 size, MemoryRef memory,
|
||||
PageType type) {
|
||||
LOG_DEBUG(HW_Memory, "Mapping {} onto {:08X}-{:08X}", (void*)memory.GetPtr(), base * PAGE_SIZE,
|
||||
(base + size) * PAGE_SIZE);
|
||||
LOG_DEBUG(HW_Memory, "Mapping {} onto {:08X}-{:08X}", (void*)memory.GetPtr(),
|
||||
base * CITRA_PAGE_SIZE, (base + size) * CITRA_PAGE_SIZE);
|
||||
|
||||
RasterizerFlushVirtualRegion(base << PAGE_BITS, size * PAGE_SIZE,
|
||||
RasterizerFlushVirtualRegion(base << CITRA_PAGE_BITS, size * CITRA_PAGE_SIZE,
|
||||
FlushMode::FlushAndInvalidate);
|
||||
|
||||
u32 end = base + size;
|
||||
@@ -234,49 +369,42 @@ void MemorySystem::MapPages(PageTable& page_table, u32 base, u32 size, MemoryRef
|
||||
page_table.pointers[base] = memory;
|
||||
|
||||
// If the memory to map is already rasterizer-cached, mark the page
|
||||
if (type == PageType::Memory && impl->cache_marker.IsCached(base * PAGE_SIZE)) {
|
||||
if (type == PageType::Memory && impl->cache_marker.IsCached(base * CITRA_PAGE_SIZE)) {
|
||||
page_table.attributes[base] = PageType::RasterizerCachedMemory;
|
||||
page_table.pointers[base] = nullptr;
|
||||
}
|
||||
|
||||
base += 1;
|
||||
if (memory != nullptr && memory.GetSize() > PAGE_SIZE)
|
||||
memory += PAGE_SIZE;
|
||||
if (memory != nullptr && memory.GetSize() > CITRA_PAGE_SIZE)
|
||||
memory += CITRA_PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
void MemorySystem::MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, MemoryRef target) {
|
||||
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:08X}", size);
|
||||
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:08X}", base);
|
||||
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory);
|
||||
ASSERT_MSG((size & CITRA_PAGE_MASK) == 0, "non-page aligned size: {:08X}", size);
|
||||
ASSERT_MSG((base & CITRA_PAGE_MASK) == 0, "non-page aligned base: {:08X}", base);
|
||||
MapPages(page_table, base / CITRA_PAGE_SIZE, size / CITRA_PAGE_SIZE, target, PageType::Memory);
|
||||
}
|
||||
|
||||
void MemorySystem::MapIoRegion(PageTable& page_table, VAddr base, u32 size,
|
||||
MMIORegionPointer mmio_handler) {
|
||||
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:08X}", size);
|
||||
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:08X}", base);
|
||||
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special);
|
||||
ASSERT_MSG((size & CITRA_PAGE_MASK) == 0, "non-page aligned size: {:08X}", size);
|
||||
ASSERT_MSG((base & CITRA_PAGE_MASK) == 0, "non-page aligned base: {:08X}", base);
|
||||
MapPages(page_table, base / CITRA_PAGE_SIZE, size / CITRA_PAGE_SIZE, nullptr,
|
||||
PageType::Special);
|
||||
|
||||
page_table.special_regions.emplace_back(SpecialRegion{base, size, mmio_handler});
|
||||
}
|
||||
|
||||
void MemorySystem::UnmapRegion(PageTable& page_table, VAddr base, u32 size) {
|
||||
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:08X}", size);
|
||||
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:08X}", base);
|
||||
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped);
|
||||
ASSERT_MSG((size & CITRA_PAGE_MASK) == 0, "non-page aligned size: {:08X}", size);
|
||||
ASSERT_MSG((base & CITRA_PAGE_MASK) == 0, "non-page aligned base: {:08X}", base);
|
||||
MapPages(page_table, base / CITRA_PAGE_SIZE, size / CITRA_PAGE_SIZE, nullptr,
|
||||
PageType::Unmapped);
|
||||
}
|
||||
|
||||
MemoryRef MemorySystem::GetPointerForRasterizerCache(VAddr addr) const {
|
||||
if (addr >= LINEAR_HEAP_VADDR && addr < LINEAR_HEAP_VADDR_END) {
|
||||
return {impl->fcram_mem, addr - LINEAR_HEAP_VADDR};
|
||||
}
|
||||
if (addr >= NEW_LINEAR_HEAP_VADDR && addr < NEW_LINEAR_HEAP_VADDR_END) {
|
||||
return {impl->fcram_mem, addr - NEW_LINEAR_HEAP_VADDR};
|
||||
}
|
||||
if (addr >= VRAM_VADDR && addr < VRAM_VADDR_END) {
|
||||
return {impl->vram_mem, addr - VRAM_VADDR};
|
||||
}
|
||||
UNREACHABLE();
|
||||
return impl->GetPointerForRasterizerCache(addr);
|
||||
}
|
||||
|
||||
void MemorySystem::RegisterPageTable(std::shared_ptr<PageTable> page_table) {
|
||||
@@ -290,33 +418,20 @@ void MemorySystem::UnregisterPageTable(std::shared_ptr<PageTable> page_table) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function should only be called for virtual addreses with attribute `PageType::Special`.
|
||||
*/
|
||||
static MMIORegionPointer GetMMIOHandler(const PageTable& page_table, VAddr vaddr) {
|
||||
for (const auto& region : page_table.special_regions) {
|
||||
if (vaddr >= region.base && vaddr < (region.base + region.size)) {
|
||||
return region.handler;
|
||||
}
|
||||
}
|
||||
ASSERT_MSG(false, "Mapped IO page without a handler @ {:08X}", vaddr);
|
||||
return nullptr; // Should never happen
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T ReadMMIO(MMIORegionPointer mmio_handler, VAddr addr);
|
||||
|
||||
template <typename T>
|
||||
T MemorySystem::Read(const VAddr vaddr) {
|
||||
const u8* page_pointer = impl->current_page_table->pointers[vaddr >> PAGE_BITS];
|
||||
const u8* page_pointer = impl->current_page_table->pointers[vaddr >> CITRA_PAGE_BITS];
|
||||
if (page_pointer) {
|
||||
// NOTE: Avoid adding any extra logic to this fast-path block
|
||||
T value;
|
||||
std::memcpy(&value, &page_pointer[vaddr & PAGE_MASK], sizeof(T));
|
||||
std::memcpy(&value, &page_pointer[vaddr & CITRA_PAGE_MASK], sizeof(T));
|
||||
return value;
|
||||
}
|
||||
|
||||
PageType type = impl->current_page_table->attributes[vaddr >> PAGE_BITS];
|
||||
PageType type = impl->current_page_table->attributes[vaddr >> CITRA_PAGE_BITS];
|
||||
switch (type) {
|
||||
case PageType::Unmapped:
|
||||
LOG_ERROR(HW_Memory, "unmapped Read{} @ 0x{:08X} at PC 0x{:08X}", sizeof(T) * 8, vaddr,
|
||||
@@ -333,10 +448,12 @@ T MemorySystem::Read(const VAddr vaddr) {
|
||||
return value;
|
||||
}
|
||||
case PageType::Special:
|
||||
return ReadMMIO<T>(GetMMIOHandler(*impl->current_page_table, vaddr), vaddr);
|
||||
return ReadMMIO<T>(impl->GetMMIOHandler(*impl->current_page_table, vaddr), vaddr);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return T{};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -344,14 +461,14 @@ void WriteMMIO(MMIORegionPointer mmio_handler, VAddr addr, const T data);
|
||||
|
||||
template <typename T>
|
||||
void MemorySystem::Write(const VAddr vaddr, const T data) {
|
||||
u8* page_pointer = impl->current_page_table->pointers[vaddr >> PAGE_BITS];
|
||||
u8* page_pointer = impl->current_page_table->pointers[vaddr >> CITRA_PAGE_BITS];
|
||||
if (page_pointer) {
|
||||
// NOTE: Avoid adding any extra logic to this fast-path block
|
||||
std::memcpy(&page_pointer[vaddr & PAGE_MASK], &data, sizeof(T));
|
||||
std::memcpy(&page_pointer[vaddr & CITRA_PAGE_MASK], &data, sizeof(T));
|
||||
return;
|
||||
}
|
||||
|
||||
PageType type = impl->current_page_table->attributes[vaddr >> PAGE_BITS];
|
||||
PageType type = impl->current_page_table->attributes[vaddr >> CITRA_PAGE_BITS];
|
||||
switch (type) {
|
||||
case PageType::Unmapped:
|
||||
LOG_ERROR(HW_Memory, "unmapped Write{} 0x{:08X} @ 0x{:08X} at PC 0x{:08X}",
|
||||
@@ -366,27 +483,27 @@ void MemorySystem::Write(const VAddr vaddr, const T data) {
|
||||
break;
|
||||
}
|
||||
case PageType::Special:
|
||||
WriteMMIO<T>(GetMMIOHandler(*impl->current_page_table, vaddr), vaddr, data);
|
||||
WriteMMIO<T>(impl->GetMMIOHandler(*impl->current_page_table, vaddr), vaddr, data);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) {
|
||||
bool MemorySystem::IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) {
|
||||
auto& page_table = *process.vm_manager.page_table;
|
||||
|
||||
auto page_pointer = page_table.pointers[vaddr >> PAGE_BITS];
|
||||
auto page_pointer = page_table.pointers[vaddr >> CITRA_PAGE_BITS];
|
||||
if (page_pointer)
|
||||
return true;
|
||||
|
||||
if (page_table.attributes[vaddr >> PAGE_BITS] == PageType::RasterizerCachedMemory)
|
||||
if (page_table.attributes[vaddr >> CITRA_PAGE_BITS] == PageType::RasterizerCachedMemory)
|
||||
return true;
|
||||
|
||||
if (page_table.attributes[vaddr >> PAGE_BITS] != PageType::Special)
|
||||
if (page_table.attributes[vaddr >> CITRA_PAGE_BITS] != PageType::Special)
|
||||
return false;
|
||||
|
||||
MMIORegionPointer mmio_region = GetMMIOHandler(page_table, vaddr);
|
||||
MMIORegionPointer mmio_region = impl->GetMMIOHandler(page_table, vaddr);
|
||||
if (mmio_region) {
|
||||
return mmio_region->IsValidAddress(vaddr);
|
||||
}
|
||||
@@ -395,16 +512,16 @@ bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) {
|
||||
}
|
||||
|
||||
bool MemorySystem::IsValidPhysicalAddress(const PAddr paddr) const {
|
||||
return GetPhysicalPointer(paddr) != nullptr;
|
||||
return GetPhysicalRef(paddr);
|
||||
}
|
||||
|
||||
u8* MemorySystem::GetPointer(const VAddr vaddr) {
|
||||
u8* page_pointer = impl->current_page_table->pointers[vaddr >> PAGE_BITS];
|
||||
u8* page_pointer = impl->current_page_table->pointers[vaddr >> CITRA_PAGE_BITS];
|
||||
if (page_pointer) {
|
||||
return page_pointer + (vaddr & PAGE_MASK);
|
||||
return page_pointer + (vaddr & CITRA_PAGE_MASK);
|
||||
}
|
||||
|
||||
if (impl->current_page_table->attributes[vaddr >> PAGE_BITS] ==
|
||||
if (impl->current_page_table->attributes[vaddr >> CITRA_PAGE_BITS] ==
|
||||
PageType::RasterizerCachedMemory) {
|
||||
return GetPointerForRasterizerCache(vaddr);
|
||||
}
|
||||
@@ -415,12 +532,12 @@ u8* MemorySystem::GetPointer(const VAddr vaddr) {
|
||||
}
|
||||
|
||||
const u8* MemorySystem::GetPointer(const VAddr vaddr) const {
|
||||
const u8* page_pointer = impl->current_page_table->pointers[vaddr >> PAGE_BITS];
|
||||
const u8* page_pointer = impl->current_page_table->pointers[vaddr >> CITRA_PAGE_BITS];
|
||||
if (page_pointer) {
|
||||
return page_pointer + (vaddr & PAGE_MASK);
|
||||
return page_pointer + (vaddr & CITRA_PAGE_MASK);
|
||||
}
|
||||
|
||||
if (impl->current_page_table->attributes[vaddr >> PAGE_BITS] ==
|
||||
if (impl->current_page_table->attributes[vaddr >> CITRA_PAGE_BITS] ==
|
||||
PageType::RasterizerCachedMemory) {
|
||||
return GetPointerForRasterizerCache(vaddr);
|
||||
}
|
||||
@@ -434,11 +551,14 @@ std::string MemorySystem::ReadCString(VAddr vaddr, std::size_t max_length) {
|
||||
string.reserve(max_length);
|
||||
for (std::size_t i = 0; i < max_length; ++i) {
|
||||
char c = Read8(vaddr);
|
||||
if (c == '\0')
|
||||
if (c == '\0') {
|
||||
break;
|
||||
}
|
||||
|
||||
string.push_back(c);
|
||||
++vaddr;
|
||||
}
|
||||
|
||||
string.shrink_to_fit();
|
||||
return string;
|
||||
}
|
||||
@@ -447,40 +567,30 @@ u8* MemorySystem::GetPhysicalPointer(PAddr address) {
|
||||
return GetPhysicalRef(address);
|
||||
}
|
||||
|
||||
const u8* MemorySystem::GetPhysicalPointer(PAddr address) const {
|
||||
return GetPhysicalRef(address);
|
||||
}
|
||||
|
||||
MemoryRef MemorySystem::GetPhysicalRef(PAddr address) const {
|
||||
struct MemoryArea {
|
||||
PAddr paddr_base;
|
||||
u32 size;
|
||||
constexpr std::array memory_areas = {
|
||||
std::make_pair(VRAM_PADDR, VRAM_SIZE),
|
||||
std::make_pair(DSP_RAM_PADDR, DSP_RAM_SIZE),
|
||||
std::make_pair(FCRAM_PADDR, FCRAM_N3DS_SIZE),
|
||||
std::make_pair(N3DS_EXTRA_RAM_PADDR, N3DS_EXTRA_RAM_SIZE),
|
||||
};
|
||||
|
||||
static constexpr MemoryArea memory_areas[] = {
|
||||
{VRAM_PADDR, VRAM_SIZE},
|
||||
{DSP_RAM_PADDR, DSP_RAM_SIZE},
|
||||
{FCRAM_PADDR, FCRAM_N3DS_SIZE},
|
||||
{N3DS_EXTRA_RAM_PADDR, N3DS_EXTRA_RAM_SIZE},
|
||||
};
|
||||
const auto area = std::find_if(memory_areas.begin(), memory_areas.end(), [&](const auto& area) {
|
||||
// Note: the region end check is inclusive because the user can pass in an address that
|
||||
// represents an open right bound
|
||||
return address >= area.first && address <= area.first + area.second;
|
||||
});
|
||||
|
||||
const auto area =
|
||||
std::find_if(std::begin(memory_areas), std::end(memory_areas), [&](const auto& area) {
|
||||
// Note: the region end check is inclusive because the user can pass in an address that
|
||||
// represents an open right bound
|
||||
return address >= area.paddr_base && address <= area.paddr_base + area.size;
|
||||
});
|
||||
|
||||
if (area == std::end(memory_areas)) {
|
||||
LOG_ERROR(HW_Memory, "unknown GetPhysicalPointer @ 0x{:08X} at PC 0x{:08X}", address,
|
||||
if (area == memory_areas.end()) {
|
||||
LOG_ERROR(HW_Memory, "Unknown GetPhysicalPointer @ {:#08X} at PC {:#08X}", address,
|
||||
Core::GetRunningCore().GetPC());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
u32 offset_into_region = address - area->paddr_base;
|
||||
u32 offset_into_region = address - area->first;
|
||||
|
||||
std::shared_ptr<BackingMem> target_mem = nullptr;
|
||||
switch (area->paddr_base) {
|
||||
switch (area->first) {
|
||||
case VRAM_PADDR:
|
||||
target_mem = impl->vram_mem;
|
||||
break;
|
||||
@@ -529,14 +639,14 @@ void MemorySystem::RasterizerMarkRegionCached(PAddr start, u32 size, bool cached
|
||||
return;
|
||||
}
|
||||
|
||||
u32 num_pages = ((start + size - 1) >> PAGE_BITS) - (start >> PAGE_BITS) + 1;
|
||||
u32 num_pages = ((start + size - 1) >> CITRA_PAGE_BITS) - (start >> CITRA_PAGE_BITS) + 1;
|
||||
PAddr paddr = start;
|
||||
|
||||
for (unsigned i = 0; i < num_pages; ++i, paddr += PAGE_SIZE) {
|
||||
for (unsigned i = 0; i < num_pages; ++i, paddr += CITRA_PAGE_SIZE) {
|
||||
for (VAddr vaddr : PhysicalToVirtualAddressForRasterizer(paddr)) {
|
||||
impl->cache_marker.Mark(vaddr, cached);
|
||||
for (auto page_table : impl->page_table_list) {
|
||||
PageType& page_type = page_table->attributes[vaddr >> PAGE_BITS];
|
||||
PageType& page_type = page_table->attributes[vaddr >> CITRA_PAGE_BITS];
|
||||
|
||||
if (cached) {
|
||||
// Switch page type to cached if now cached
|
||||
@@ -547,7 +657,7 @@ void MemorySystem::RasterizerMarkRegionCached(PAddr start, u32 size, bool cached
|
||||
break;
|
||||
case PageType::Memory:
|
||||
page_type = PageType::RasterizerCachedMemory;
|
||||
page_table->pointers[vaddr >> PAGE_BITS] = nullptr;
|
||||
page_table->pointers[vaddr >> CITRA_PAGE_BITS] = nullptr;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
@@ -561,8 +671,8 @@ void MemorySystem::RasterizerMarkRegionCached(PAddr start, u32 size, bool cached
|
||||
break;
|
||||
case PageType::RasterizerCachedMemory: {
|
||||
page_type = PageType::Memory;
|
||||
page_table->pointers[vaddr >> PAGE_BITS] =
|
||||
GetPointerForRasterizerCache(vaddr & ~PAGE_MASK);
|
||||
page_table->pointers[vaddr >> CITRA_PAGE_BITS] =
|
||||
GetPointerForRasterizerCache(vaddr & ~CITRA_PAGE_MASK);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -667,53 +777,12 @@ u64 MemorySystem::Read64(const VAddr addr) {
|
||||
|
||||
void MemorySystem::ReadBlock(const Kernel::Process& process, const VAddr src_addr,
|
||||
void* dest_buffer, const std::size_t size) {
|
||||
auto& page_table = *process.vm_manager.page_table;
|
||||
return impl->ReadBlockImpl<false>(process, src_addr, dest_buffer, size);
|
||||
}
|
||||
|
||||
std::size_t remaining_size = size;
|
||||
std::size_t page_index = src_addr >> PAGE_BITS;
|
||||
std::size_t page_offset = src_addr & PAGE_MASK;
|
||||
|
||||
while (remaining_size > 0) {
|
||||
const std::size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size);
|
||||
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
|
||||
|
||||
switch (page_table.attributes[page_index]) {
|
||||
case PageType::Unmapped: {
|
||||
LOG_ERROR(HW_Memory,
|
||||
"unmapped ReadBlock @ 0x{:08X} (start address = 0x{:08X}, size = {}) at PC "
|
||||
"0x{:08X}",
|
||||
current_vaddr, src_addr, size, Core::GetRunningCore().GetPC());
|
||||
std::memset(dest_buffer, 0, copy_amount);
|
||||
break;
|
||||
}
|
||||
case PageType::Memory: {
|
||||
DEBUG_ASSERT(page_table.pointers[page_index]);
|
||||
|
||||
const u8* src_ptr = page_table.pointers[page_index] + page_offset;
|
||||
std::memcpy(dest_buffer, src_ptr, copy_amount);
|
||||
break;
|
||||
}
|
||||
case PageType::Special: {
|
||||
MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr);
|
||||
DEBUG_ASSERT(handler);
|
||||
handler->ReadBlock(current_vaddr, dest_buffer, copy_amount);
|
||||
break;
|
||||
}
|
||||
case PageType::RasterizerCachedMemory: {
|
||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||
FlushMode::Flush);
|
||||
std::memcpy(dest_buffer, GetPointerForRasterizerCache(current_vaddr), copy_amount);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
page_index++;
|
||||
page_offset = 0;
|
||||
dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
|
||||
remaining_size -= copy_amount;
|
||||
}
|
||||
void MemorySystem::ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) {
|
||||
const auto& process = *Core::System::GetInstance().Kernel().GetCurrentProcess();
|
||||
return impl->ReadBlockImpl<false>(process, src_addr, dest_buffer, size);
|
||||
}
|
||||
|
||||
void MemorySystem::Write8(const VAddr addr, const u8 data) {
|
||||
@@ -734,101 +803,39 @@ void MemorySystem::Write64(const VAddr addr, const u64 data) {
|
||||
|
||||
void MemorySystem::WriteBlock(const Kernel::Process& process, const VAddr dest_addr,
|
||||
const void* src_buffer, const std::size_t size) {
|
||||
auto& page_table = *process.vm_manager.page_table;
|
||||
std::size_t remaining_size = size;
|
||||
std::size_t page_index = dest_addr >> PAGE_BITS;
|
||||
std::size_t page_offset = dest_addr & PAGE_MASK;
|
||||
return impl->WriteBlockImpl<false>(process, dest_addr, src_buffer, size);
|
||||
}
|
||||
|
||||
while (remaining_size > 0) {
|
||||
const std::size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size);
|
||||
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
|
||||
|
||||
switch (page_table.attributes[page_index]) {
|
||||
case PageType::Unmapped: {
|
||||
LOG_ERROR(HW_Memory,
|
||||
"unmapped WriteBlock @ 0x{:08X} (start address = 0x{:08X}, size = {}) at PC "
|
||||
"0x{:08X}",
|
||||
current_vaddr, dest_addr, size, Core::GetRunningCore().GetPC());
|
||||
break;
|
||||
}
|
||||
case PageType::Memory: {
|
||||
DEBUG_ASSERT(page_table.pointers[page_index]);
|
||||
|
||||
u8* dest_ptr = page_table.pointers[page_index] + page_offset;
|
||||
std::memcpy(dest_ptr, src_buffer, copy_amount);
|
||||
break;
|
||||
}
|
||||
case PageType::Special: {
|
||||
MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr);
|
||||
DEBUG_ASSERT(handler);
|
||||
handler->WriteBlock(current_vaddr, src_buffer, copy_amount);
|
||||
break;
|
||||
}
|
||||
case PageType::RasterizerCachedMemory: {
|
||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||
FlushMode::Invalidate);
|
||||
std::memcpy(GetPointerForRasterizerCache(current_vaddr), src_buffer, copy_amount);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
page_index++;
|
||||
page_offset = 0;
|
||||
src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
|
||||
remaining_size -= copy_amount;
|
||||
}
|
||||
void MemorySystem::WriteBlock(const VAddr dest_addr, const void* src_buffer,
|
||||
const std::size_t size) {
|
||||
auto& process = *Core::System::GetInstance().Kernel().GetCurrentProcess();
|
||||
return impl->WriteBlockImpl<false>(process, dest_addr, src_buffer, size);
|
||||
}
|
||||
|
||||
void MemorySystem::ZeroBlock(const Kernel::Process& process, const VAddr dest_addr,
|
||||
const std::size_t size) {
|
||||
auto& page_table = *process.vm_manager.page_table;
|
||||
std::size_t remaining_size = size;
|
||||
std::size_t page_index = dest_addr >> PAGE_BITS;
|
||||
std::size_t page_offset = dest_addr & PAGE_MASK;
|
||||
static const std::array<u8, CITRA_PAGE_SIZE> zeros{0};
|
||||
|
||||
static const std::array<u8, PAGE_SIZE> zeros = {};
|
||||
|
||||
while (remaining_size > 0) {
|
||||
const std::size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size);
|
||||
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
|
||||
|
||||
switch (page_table.attributes[page_index]) {
|
||||
case PageType::Unmapped: {
|
||||
impl->WalkBlock(
|
||||
process, dest_addr, size,
|
||||
[dest_addr, size](const std::size_t copy_amount, const VAddr current_vaddr) {
|
||||
LOG_ERROR(HW_Memory,
|
||||
"unmapped ZeroBlock @ 0x{:08X} (start address = 0x{:08X}, size = {}) at PC "
|
||||
"0x{:08X}",
|
||||
current_vaddr, dest_addr, size, Core::GetRunningCore().GetPC());
|
||||
break;
|
||||
}
|
||||
case PageType::Memory: {
|
||||
DEBUG_ASSERT(page_table.pointers[page_index]);
|
||||
|
||||
u8* dest_ptr = page_table.pointers[page_index] + page_offset;
|
||||
"Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
|
||||
current_vaddr, dest_addr, size);
|
||||
},
|
||||
[](const std::size_t copy_amount, u8* const dest_ptr) {
|
||||
std::memset(dest_ptr, 0, copy_amount);
|
||||
break;
|
||||
}
|
||||
case PageType::Special: {
|
||||
MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr);
|
||||
DEBUG_ASSERT(handler);
|
||||
},
|
||||
[&zeros = zeros](MMIORegionPointer& handler, const std::size_t copy_amount,
|
||||
const VAddr current_vaddr) {
|
||||
handler->WriteBlock(current_vaddr, zeros.data(), copy_amount);
|
||||
break;
|
||||
}
|
||||
case PageType::RasterizerCachedMemory: {
|
||||
},
|
||||
[](const VAddr current_vaddr, const std::size_t copy_amount, u8* const rasterizer_ptr) {
|
||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||
FlushMode::Invalidate);
|
||||
std::memset(GetPointerForRasterizerCache(current_vaddr), 0, copy_amount);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
page_index++;
|
||||
page_offset = 0;
|
||||
remaining_size -= copy_amount;
|
||||
}
|
||||
std::memset(rasterizer_ptr, 0, copy_amount);
|
||||
},
|
||||
[](const std::size_t copy_amount) {});
|
||||
}
|
||||
|
||||
void MemorySystem::CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,
|
||||
@@ -839,55 +846,35 @@ void MemorySystem::CopyBlock(const Kernel::Process& process, VAddr dest_addr, VA
|
||||
void MemorySystem::CopyBlock(const Kernel::Process& dest_process,
|
||||
const Kernel::Process& src_process, VAddr dest_addr, VAddr src_addr,
|
||||
std::size_t size) {
|
||||
auto& page_table = *src_process.vm_manager.page_table;
|
||||
std::size_t remaining_size = size;
|
||||
std::size_t page_index = src_addr >> PAGE_BITS;
|
||||
std::size_t page_offset = src_addr & PAGE_MASK;
|
||||
std::array<u8, CITRA_PAGE_SIZE> copy_buffer{};
|
||||
|
||||
while (remaining_size > 0) {
|
||||
const std::size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size);
|
||||
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
|
||||
|
||||
switch (page_table.attributes[page_index]) {
|
||||
case PageType::Unmapped: {
|
||||
impl->WalkBlock(
|
||||
src_process, src_addr, size,
|
||||
[this, &dest_process, &dest_addr, &src_addr, size](const std::size_t copy_amount,
|
||||
const VAddr current_vaddr) {
|
||||
LOG_ERROR(HW_Memory,
|
||||
"unmapped CopyBlock @ 0x{:08X} (start address = 0x{:08X}, size = {}) at PC "
|
||||
"0x{:08X}",
|
||||
current_vaddr, src_addr, size, Core::GetRunningCore().GetPC());
|
||||
"unmapped CopyBlock @ 0x{:08X} (start address = 0x{:08X}, size = {})",
|
||||
current_vaddr, src_addr, size);
|
||||
ZeroBlock(dest_process, dest_addr, copy_amount);
|
||||
break;
|
||||
}
|
||||
case PageType::Memory: {
|
||||
DEBUG_ASSERT(page_table.pointers[page_index]);
|
||||
const u8* src_ptr = page_table.pointers[page_index] + page_offset;
|
||||
WriteBlock(dest_process, dest_addr, src_ptr, copy_amount);
|
||||
break;
|
||||
}
|
||||
case PageType::Special: {
|
||||
MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr);
|
||||
DEBUG_ASSERT(handler);
|
||||
std::vector<u8> buffer(copy_amount);
|
||||
handler->ReadBlock(current_vaddr, buffer.data(), buffer.size());
|
||||
WriteBlock(dest_process, dest_addr, buffer.data(), buffer.size());
|
||||
break;
|
||||
}
|
||||
case PageType::RasterizerCachedMemory: {
|
||||
},
|
||||
[this, &dest_process, &dest_addr](const std::size_t copy_amount, const u8* const src_ptr) {
|
||||
impl->WriteBlockImpl<false>(dest_process, dest_addr, src_ptr, copy_amount);
|
||||
},
|
||||
[this, &dest_process, &dest_addr, ©_buffer](
|
||||
MMIORegionPointer& handler, const std::size_t copy_amount, const VAddr current_vaddr) {
|
||||
handler->ReadBlock(current_vaddr, copy_buffer.data(), copy_amount);
|
||||
impl->WriteBlockImpl<false>(dest_process, dest_addr, copy_buffer.data(), copy_amount);
|
||||
},
|
||||
[this, &dest_process, &dest_addr](const VAddr current_vaddr, const std::size_t copy_amount,
|
||||
u8* const rasterizer_ptr) {
|
||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||
FlushMode::Flush);
|
||||
WriteBlock(dest_process, dest_addr, GetPointerForRasterizerCache(current_vaddr),
|
||||
copy_amount);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
page_index++;
|
||||
page_offset = 0;
|
||||
dest_addr += static_cast<VAddr>(copy_amount);
|
||||
src_addr += static_cast<VAddr>(copy_amount);
|
||||
remaining_size -= copy_amount;
|
||||
}
|
||||
impl->WriteBlockImpl<false>(dest_process, dest_addr, rasterizer_ptr, copy_amount);
|
||||
},
|
||||
[&dest_addr, &src_addr](const std::size_t copy_amount) {
|
||||
dest_addr += static_cast<VAddr>(copy_amount);
|
||||
src_addr += static_cast<VAddr>(copy_amount);
|
||||
});
|
||||
}
|
||||
|
||||
template <>
|
||||
|
@@ -3,15 +3,11 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/serialization/array.hpp>
|
||||
#include <boost/serialization/vector.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "common/memory_ref.h"
|
||||
#include "core/mmio.h"
|
||||
|
||||
@@ -27,17 +23,14 @@ class DspInterface;
|
||||
|
||||
namespace Memory {
|
||||
|
||||
// Are defined in a system header
|
||||
#undef PAGE_SIZE
|
||||
#undef PAGE_MASK
|
||||
/**
|
||||
* Page size used by the ARM architecture. This is the smallest granularity with which memory can
|
||||
* be mapped.
|
||||
*/
|
||||
const u32 PAGE_SIZE = 0x1000;
|
||||
const u32 PAGE_MASK = PAGE_SIZE - 1;
|
||||
const int PAGE_BITS = 12;
|
||||
const std::size_t PAGE_TABLE_NUM_ENTRIES = 1 << (32 - PAGE_BITS);
|
||||
constexpr u32 CITRA_PAGE_SIZE = 0x1000;
|
||||
constexpr u32 CITRA_PAGE_MASK = CITRA_PAGE_SIZE - 1;
|
||||
constexpr int CITRA_PAGE_BITS = 12;
|
||||
constexpr std::size_t PAGE_TABLE_NUM_ENTRIES = 1 << (32 - CITRA_PAGE_BITS);
|
||||
|
||||
enum class PageType {
|
||||
/// Page is unmapped and should cause an access error.
|
||||
@@ -106,11 +99,10 @@ struct PageTable {
|
||||
|
||||
private:
|
||||
std::array<u8*, PAGE_TABLE_NUM_ENTRIES> raw;
|
||||
|
||||
std::array<MemoryRef, PAGE_TABLE_NUM_ENTRIES> refs;
|
||||
|
||||
friend struct PageTable;
|
||||
};
|
||||
|
||||
Pointers pointers;
|
||||
|
||||
/**
|
||||
@@ -317,39 +309,259 @@ public:
|
||||
void SetCurrentPageTable(std::shared_ptr<PageTable> page_table);
|
||||
std::shared_ptr<PageTable> GetCurrentPageTable() const;
|
||||
|
||||
/**
|
||||
* Gets a pointer to the given address.
|
||||
*
|
||||
* @param vaddr Virtual address to retrieve a pointer to.
|
||||
*
|
||||
* @returns The pointer to the given address, if the address is valid.
|
||||
* If the address is not valid, nullptr will be returned.
|
||||
*/
|
||||
u8* GetPointer(VAddr vaddr);
|
||||
|
||||
/**
|
||||
* Gets a pointer to the given address.
|
||||
*
|
||||
* @param vaddr Virtual address to retrieve a pointer to.
|
||||
*
|
||||
* @returns The pointer to the given address, if the address is valid.
|
||||
* If the address is not valid, nullptr will be returned.
|
||||
*/
|
||||
const u8* GetPointer(VAddr vaddr) const;
|
||||
|
||||
/**
|
||||
* Reads an 8-bit unsigned value from the current process' address space
|
||||
* at the given virtual address.
|
||||
*
|
||||
* @param addr The virtual address to read the 8-bit value from.
|
||||
*
|
||||
* @returns the read 8-bit unsigned value.
|
||||
*/
|
||||
u8 Read8(VAddr addr);
|
||||
|
||||
/**
|
||||
* Reads a 16-bit unsigned value from the current process' address space
|
||||
* at the given virtual address.
|
||||
*
|
||||
* @param addr The virtual address to read the 16-bit value from.
|
||||
*
|
||||
* @returns the read 16-bit unsigned value.
|
||||
*/
|
||||
u16 Read16(VAddr addr);
|
||||
|
||||
/**
|
||||
* Reads a 32-bit unsigned value from the current process' address space
|
||||
* at the given virtual address.
|
||||
*
|
||||
* @param addr The virtual address to read the 32-bit value from.
|
||||
*
|
||||
* @returns the read 32-bit unsigned value.
|
||||
*/
|
||||
u32 Read32(VAddr addr);
|
||||
|
||||
/**
|
||||
* Reads a 64-bit unsigned value from the current process' address space
|
||||
* at the given virtual address.
|
||||
*
|
||||
* @param addr The virtual address to read the 64-bit value from.
|
||||
*
|
||||
* @returns the read 64-bit value.
|
||||
*/
|
||||
u64 Read64(VAddr addr);
|
||||
|
||||
/**
|
||||
* Writes an 8-bit unsigned integer to the given virtual address in
|
||||
* the current process' address space.
|
||||
*
|
||||
* @param addr The virtual address to write the 8-bit unsigned integer to.
|
||||
* @param data The 8-bit unsigned integer to write to the given virtual address.
|
||||
*
|
||||
* @post The memory at the given virtual address contains the specified data value.
|
||||
*/
|
||||
void Write8(VAddr addr, u8 data);
|
||||
|
||||
/**
|
||||
* Writes a 16-bit unsigned integer to the given virtual address in
|
||||
* the current process' address space.
|
||||
*
|
||||
* @param addr The virtual address to write the 16-bit unsigned integer to.
|
||||
* @param data The 16-bit unsigned integer to write to the given virtual address.
|
||||
*
|
||||
* @post The memory range [addr, sizeof(data)) contains the given data value.
|
||||
*/
|
||||
void Write16(VAddr addr, u16 data);
|
||||
|
||||
/**
|
||||
* Writes a 32-bit unsigned integer to the given virtual address in
|
||||
* the current process' address space.
|
||||
*
|
||||
* @param addr The virtual address to write the 32-bit unsigned integer to.
|
||||
* @param data The 32-bit unsigned integer to write to the given virtual address.
|
||||
*
|
||||
* @post The memory range [addr, sizeof(data)) contains the given data value.
|
||||
*/
|
||||
void Write32(VAddr addr, u32 data);
|
||||
|
||||
/**
|
||||
* Writes a 64-bit unsigned integer to the given virtual address in
|
||||
* the current process' address space.
|
||||
*
|
||||
* @param addr The virtual address to write the 64-bit unsigned integer to.
|
||||
* @param data The 64-bit unsigned integer to write to the given virtual address.
|
||||
*
|
||||
* @post The memory range [addr, sizeof(data)) contains the given data value.
|
||||
*/
|
||||
void Write64(VAddr addr, u64 data);
|
||||
|
||||
/**
|
||||
* Reads a null-terminated string from the given virtual address.
|
||||
* This function will continually read characters until either:
|
||||
*
|
||||
* - A null character ('\0') is reached.
|
||||
* - max_length characters have been read.
|
||||
*
|
||||
* @note The final null-terminating character (if found) is not included
|
||||
* in the returned string.
|
||||
*
|
||||
* @param vaddr The address to begin reading the string from.
|
||||
* @param max_length The maximum length of the string to read in characters.
|
||||
*
|
||||
* @returns The read string.
|
||||
*/
|
||||
std::string ReadCString(VAddr vaddr, std::size_t max_length);
|
||||
|
||||
/**
|
||||
* Reads a contiguous block of bytes from a specified process' address space.
|
||||
*
|
||||
* @param process The process to read the data from.
|
||||
* @param src_addr The virtual address to begin reading from.
|
||||
* @param dest_buffer The buffer to place the read bytes into.
|
||||
* @param size The amount of data to read, in bytes.
|
||||
*
|
||||
* @note If a size of 0 is specified, then this function reads nothing and
|
||||
* no attempts to access memory are made at all.
|
||||
*
|
||||
* @pre dest_buffer must be at least size bytes in length, otherwise a
|
||||
* buffer overrun will occur.
|
||||
*
|
||||
* @post The range [dest_buffer, size) contains the read bytes from the
|
||||
* process' address space.
|
||||
*/
|
||||
void ReadBlock(const Kernel::Process& process, VAddr src_addr, void* dest_buffer,
|
||||
std::size_t size);
|
||||
|
||||
/**
|
||||
* Reads a contiguous block of bytes from the current process' address space.
|
||||
*
|
||||
* @param src_addr The virtual address to begin reading from.
|
||||
* @param dest_buffer The buffer to place the read bytes into.
|
||||
* @param size The amount of data to read, in bytes.
|
||||
*
|
||||
* @note If a size of 0 is specified, then this function reads nothing and
|
||||
* no attempts to access memory are made at all.
|
||||
*
|
||||
* @pre dest_buffer must be at least size bytes in length, otherwise a
|
||||
* buffer overrun will occur.
|
||||
*
|
||||
* @post The range [dest_buffer, size) contains the read bytes from the
|
||||
* current process' address space.
|
||||
*/
|
||||
void ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size);
|
||||
|
||||
/**
|
||||
* Writes a range of bytes into a given process' address space at the specified
|
||||
* virtual address.
|
||||
*
|
||||
* @param process The process to write data into the address space of.
|
||||
* @param dest_addr The destination virtual address to begin writing the data at.
|
||||
* @param src_buffer The data to write into the process' address space.
|
||||
* @param size The size of the data to write, in bytes.
|
||||
*
|
||||
* @post The address range [dest_addr, size) in the process' address space
|
||||
* contains the data that was within src_buffer.
|
||||
*
|
||||
* @post If an attempt is made to write into an unmapped region of memory, the writes
|
||||
* will be ignored and an error will be logged.
|
||||
*
|
||||
* @post If a write is performed into a region of memory that is considered cached
|
||||
* rasterizer memory, will cause the currently active rasterizer to be notified
|
||||
* and will mark that region as invalidated to caches that the active
|
||||
* graphics backend may be maintaining over the course of execution.
|
||||
*/
|
||||
void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer,
|
||||
std::size_t size);
|
||||
|
||||
/**
|
||||
* Writes a range of bytes into a given process' address space at the specified
|
||||
* virtual address.
|
||||
*
|
||||
* @param dest_addr The destination virtual address to begin writing the data at.
|
||||
* @param src_buffer The data to write into the process' address space.
|
||||
* @param size The size of the data to write, in bytes.
|
||||
*
|
||||
* @post The address range [dest_addr, size) in the process' address space
|
||||
* contains the data that was within src_buffer.
|
||||
*
|
||||
* @post If an attempt is made to write into an unmapped region of memory, the writes
|
||||
* will be ignored and an error will be logged.
|
||||
*
|
||||
* @post If a write is performed into a region of memory that is considered cached
|
||||
* rasterizer memory, will cause the currently active rasterizer to be notified
|
||||
* and will mark that region as invalidated to caches that the active
|
||||
* graphics backend may be maintaining over the course of execution.
|
||||
*/
|
||||
void WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size);
|
||||
|
||||
/**
|
||||
* Zeros a range of bytes within the current process' address space at the specified
|
||||
* virtual address.
|
||||
*
|
||||
* @param process The process that will have data zeroed within its address space.
|
||||
* @param dest_addr The destination virtual address to zero the data from.
|
||||
* @param size The size of the range to zero out, in bytes.
|
||||
*
|
||||
* @post The range [dest_addr, size) within the process' address space contains the
|
||||
* value 0.
|
||||
*/
|
||||
void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, const std::size_t size);
|
||||
|
||||
/**
|
||||
* Copies data within a process' address space to another location within the
|
||||
* same address space.
|
||||
*
|
||||
* @param process The process that will have data copied within its address space.
|
||||
* @param dest_addr The destination virtual address to begin copying the data into.
|
||||
* @param src_addr The source virtual address to begin copying the data from.
|
||||
* @param size The size of the data to copy, in bytes.
|
||||
*
|
||||
* @post The range [dest_addr, size) within the process' address space contains the
|
||||
* same data within the range [src_addr, size).
|
||||
*/
|
||||
void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,
|
||||
std::size_t size);
|
||||
void CopyBlock(const Kernel::Process& dest_process, const Kernel::Process& src_process,
|
||||
VAddr dest_addr, VAddr src_addr, std::size_t size);
|
||||
|
||||
std::string ReadCString(VAddr vaddr, std::size_t max_length);
|
||||
/**
|
||||
* Marks each page within the specified address range as cached or uncached.
|
||||
*
|
||||
* @param vaddr The virtual address indicating the start of the address range.
|
||||
* @param size The size of the address range in bytes.
|
||||
* @param cached Whether or not any pages within the address range should be
|
||||
* marked as cached or uncached.
|
||||
*/
|
||||
void RasterizerMarkRegionCached(PAddr start, u32 size, bool cached);
|
||||
|
||||
/// Gets a pointer to the memory region beginning at the specified physical address.
|
||||
u8* GetPhysicalPointer(PAddr address);
|
||||
|
||||
/// Gets a pointer to the memory region beginning at the specified physical address.
|
||||
const u8* GetPhysicalPointer(PAddr address) const;
|
||||
|
||||
/// Returns a reference to the memory region beginning at the specified physical address
|
||||
MemoryRef GetPhysicalRef(PAddr address) const;
|
||||
|
||||
u8* GetPointer(VAddr vaddr);
|
||||
const u8* GetPointer(VAddr vaddr) const;
|
||||
/// Determines if the given VAddr is valid for the specified process.
|
||||
bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr);
|
||||
|
||||
/// Returns true if the address refers to a valid memory region
|
||||
bool IsValidPhysicalAddress(PAddr paddr) const;
|
||||
|
||||
/// Gets offset in FCRAM from a pointer inside FCRAM range
|
||||
@@ -364,11 +576,6 @@ public:
|
||||
/// Gets a serializable ref to FCRAM with the given offset
|
||||
MemoryRef GetFCRAMRef(std::size_t offset) const;
|
||||
|
||||
/**
|
||||
* Mark each page touching the region as cached.
|
||||
*/
|
||||
void RasterizerMarkRegionCached(PAddr start, u32 size, bool cached);
|
||||
|
||||
/// Registers page table for rasterizer cache marking
|
||||
void RegisterPageTable(std::shared_ptr<PageTable> page_table);
|
||||
|
||||
@@ -395,7 +602,6 @@ private:
|
||||
void MapPages(PageTable& page_table, u32 base, u32 size, MemoryRef memory, PageType type);
|
||||
|
||||
class Impl;
|
||||
|
||||
std::unique_ptr<Impl> impl;
|
||||
|
||||
friend class boost::serialization::access;
|
||||
@@ -407,9 +613,6 @@ public:
|
||||
class BackingMemImpl;
|
||||
};
|
||||
|
||||
/// Determines if the given VAddr is valid for the specified process.
|
||||
bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr);
|
||||
|
||||
} // namespace Memory
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(Memory::MemorySystem::BackingMemImpl<Memory::Region::FCRAM>)
|
||||
|
@@ -7,7 +7,6 @@
|
||||
#include "audio_core/dsp_interface.h"
|
||||
#include "core/core.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hle/kernel/shared_page.h"
|
||||
#include "core/hle/service/cam/cam.h"
|
||||
#include "core/hle/service/hid/hid.h"
|
||||
#include "core/hle/service/ir/ir_rst.h"
|
||||
@@ -19,10 +18,20 @@
|
||||
|
||||
namespace Settings {
|
||||
|
||||
std::string_view GetAPIName(GraphicsAPI api) {
|
||||
switch (api) {
|
||||
case GraphicsAPI::OpenGL:
|
||||
return "OpenGL";
|
||||
case GraphicsAPI::OpenGLES:
|
||||
return "OpenGLES";
|
||||
case GraphicsAPI::Vulkan:
|
||||
return "Vulkan";
|
||||
}
|
||||
}
|
||||
|
||||
Values values = {};
|
||||
|
||||
void Apply() {
|
||||
|
||||
GDBStub::SetServerPort(values.gdbstub_port);
|
||||
GDBStub::ToggleServer(values.use_gdbstub);
|
||||
|
||||
@@ -73,59 +82,58 @@ void Apply() {
|
||||
}
|
||||
|
||||
void LogSettings() {
|
||||
const auto log_setting = [](std::string_view name, const auto& value) {
|
||||
const auto LogSetting = [](std::string_view name, const auto& value) {
|
||||
LOG_INFO(Config, "{}: {}", name, value);
|
||||
};
|
||||
|
||||
LOG_INFO(Config, "Citra Configuration:");
|
||||
log_setting("Core_UseCpuJit", values.use_cpu_jit);
|
||||
log_setting("Core_CPUClockPercentage", values.cpu_clock_percentage);
|
||||
log_setting("Renderer_UseGLES", values.use_gles);
|
||||
log_setting("Renderer_UseHwRenderer", values.use_hw_renderer);
|
||||
log_setting("Renderer_UseHwShader", values.use_hw_shader);
|
||||
log_setting("Renderer_SeparableShader", values.separable_shader);
|
||||
log_setting("Renderer_ShadersAccurateMul", values.shaders_accurate_mul);
|
||||
log_setting("Renderer_UseShaderJit", values.use_shader_jit);
|
||||
log_setting("Renderer_UseResolutionFactor", values.resolution_factor);
|
||||
log_setting("Renderer_FrameLimit", values.frame_limit);
|
||||
log_setting("Renderer_UseFrameLimitAlternate", values.use_frame_limit_alternate);
|
||||
log_setting("Renderer_FrameLimitAlternate", values.frame_limit_alternate);
|
||||
log_setting("Renderer_VSyncNew", values.use_vsync_new);
|
||||
log_setting("Renderer_PostProcessingShader", values.pp_shader_name);
|
||||
log_setting("Renderer_FilterMode", values.filter_mode);
|
||||
log_setting("Renderer_TextureFilterName", values.texture_filter_name);
|
||||
log_setting("Stereoscopy_Render3d", values.render_3d);
|
||||
log_setting("Stereoscopy_Factor3d", values.factor_3d);
|
||||
log_setting("Layout_LayoutOption", values.layout_option);
|
||||
log_setting("Layout_SwapScreen", values.swap_screen);
|
||||
log_setting("Layout_UprightScreen", values.upright_screen);
|
||||
log_setting("Utility_DumpTextures", values.dump_textures);
|
||||
log_setting("Utility_CustomTextures", values.custom_textures);
|
||||
log_setting("Utility_UseDiskShaderCache", values.use_disk_shader_cache);
|
||||
log_setting("Audio_EnableDspLle", values.enable_dsp_lle);
|
||||
log_setting("Audio_EnableDspLleMultithread", values.enable_dsp_lle_multithread);
|
||||
log_setting("Audio_OutputEngine", values.sink_id);
|
||||
log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching);
|
||||
log_setting("Audio_OutputDevice", values.audio_device_id);
|
||||
log_setting("Audio_InputDeviceType", values.mic_input_type);
|
||||
log_setting("Audio_InputDevice", values.mic_input_device);
|
||||
using namespace Service::CAM;
|
||||
log_setting("Camera_OuterRightName", values.camera_name[OuterRightCamera]);
|
||||
log_setting("Camera_OuterRightConfig", values.camera_config[OuterRightCamera]);
|
||||
log_setting("Camera_OuterRightFlip", values.camera_flip[OuterRightCamera]);
|
||||
log_setting("Camera_InnerName", values.camera_name[InnerCamera]);
|
||||
log_setting("Camera_InnerConfig", values.camera_config[InnerCamera]);
|
||||
log_setting("Camera_InnerFlip", values.camera_flip[InnerCamera]);
|
||||
log_setting("Camera_OuterLeftName", values.camera_name[OuterLeftCamera]);
|
||||
log_setting("Camera_OuterLeftConfig", values.camera_config[OuterLeftCamera]);
|
||||
log_setting("Camera_OuterLeftFlip", values.camera_flip[OuterLeftCamera]);
|
||||
log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd);
|
||||
log_setting("DataStorage_SdmcDir", FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir));
|
||||
log_setting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir));
|
||||
log_setting("System_IsNew3ds", values.is_new_3ds);
|
||||
log_setting("System_RegionValue", values.region_value);
|
||||
log_setting("Debugging_UseGdbstub", values.use_gdbstub);
|
||||
log_setting("Debugging_GdbstubPort", values.gdbstub_port);
|
||||
LogSetting("Core_UseCpuJit", values.use_cpu_jit);
|
||||
LogSetting("Core_CPUClockPercentage", values.cpu_clock_percentage);
|
||||
LogSetting("Renderer_GraphicsAPI", GetAPIName(values.graphics_api));
|
||||
LogSetting("Renderer_UseHwRenderer", values.use_hw_renderer);
|
||||
LogSetting("Renderer_UseHwShader", values.use_hw_shader);
|
||||
LogSetting("Renderer_SeparableShader", values.separable_shader);
|
||||
LogSetting("Renderer_ShadersAccurateMul", values.shaders_accurate_mul);
|
||||
LogSetting("Renderer_UseShaderJit", values.use_shader_jit);
|
||||
LogSetting("Renderer_UseResolutionFactor", values.resolution_factor);
|
||||
LogSetting("Renderer_FrameLimit", values.frame_limit);
|
||||
LogSetting("Renderer_UseFrameLimitAlternate", values.use_frame_limit_alternate);
|
||||
LogSetting("Renderer_FrameLimitAlternate", values.frame_limit_alternate);
|
||||
LogSetting("Renderer_VSyncNew", values.use_vsync_new);
|
||||
LogSetting("Renderer_PostProcessingShader", values.pp_shader_name);
|
||||
LogSetting("Renderer_FilterMode", values.filter_mode);
|
||||
LogSetting("Renderer_TextureFilterName", values.texture_filter_name);
|
||||
LogSetting("Stereoscopy_Render3d", values.render_3d);
|
||||
LogSetting("Stereoscopy_Factor3d", values.factor_3d);
|
||||
LogSetting("Layout_LayoutOption", values.layout_option);
|
||||
LogSetting("Layout_SwapScreen", values.swap_screen);
|
||||
LogSetting("Layout_UprightScreen", values.upright_screen);
|
||||
LogSetting("Utility_DumpTextures", values.dump_textures);
|
||||
LogSetting("Utility_CustomTextures", values.custom_textures);
|
||||
LogSetting("Utility_UseDiskShaderCache", values.use_disk_shader_cache);
|
||||
LogSetting("Audio_EnableDspLle", values.enable_dsp_lle);
|
||||
LogSetting("Audio_EnableDspLleMultithread", values.enable_dsp_lle_multithread);
|
||||
LogSetting("Audio_OutputEngine", values.sink_id);
|
||||
LogSetting("Audio_EnableAudioStretching", values.enable_audio_stretching);
|
||||
LogSetting("Audio_OutputDevice", values.audio_device_id);
|
||||
LogSetting("Audio_InputDeviceType", values.mic_input_type);
|
||||
LogSetting("Audio_InputDevice", values.mic_input_device);
|
||||
LogSetting("Camera_OuterRightName", values.camera_name[Service::CAM::OuterRightCamera]);
|
||||
LogSetting("Camera_OuterRightConfig", values.camera_config[Service::CAM::OuterRightCamera]);
|
||||
LogSetting("Camera_OuterRightFlip", values.camera_flip[Service::CAM::OuterRightCamera]);
|
||||
LogSetting("Camera_InnerName", values.camera_name[Service::CAM::InnerCamera]);
|
||||
LogSetting("Camera_InnerConfig", values.camera_config[Service::CAM::InnerCamera]);
|
||||
LogSetting("Camera_InnerFlip", values.camera_flip[Service::CAM::InnerCamera]);
|
||||
LogSetting("Camera_OuterLeftName", values.camera_name[Service::CAM::OuterLeftCamera]);
|
||||
LogSetting("Camera_OuterLeftConfig", values.camera_config[Service::CAM::OuterLeftCamera]);
|
||||
LogSetting("Camera_OuterLeftFlip", values.camera_flip[Service::CAM::OuterLeftCamera]);
|
||||
LogSetting("DataStorage_UseVirtualSd", values.use_virtual_sd);
|
||||
LogSetting("DataStorage_SdmcDir", FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir));
|
||||
LogSetting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir));
|
||||
LogSetting("System_IsNew3ds", values.is_new_3ds);
|
||||
LogSetting("System_RegionValue", values.region_value);
|
||||
LogSetting("Debugging_UseGdbstub", values.use_gdbstub);
|
||||
LogSetting("Debugging_GdbstubPort", values.gdbstub_port);
|
||||
}
|
||||
|
||||
void LoadProfile(int index) {
|
||||
|
@@ -14,6 +14,8 @@
|
||||
|
||||
namespace Settings {
|
||||
|
||||
enum class GraphicsAPI { OpenGL = 0, OpenGLES = 1, Vulkan = 2 };
|
||||
|
||||
enum class InitClock {
|
||||
SystemTime = 0,
|
||||
FixedTime = 1,
|
||||
@@ -162,13 +164,17 @@ struct Values {
|
||||
u64 init_time;
|
||||
|
||||
// Renderer
|
||||
bool use_gles;
|
||||
GraphicsAPI graphics_api;
|
||||
u16 physical_device;
|
||||
bool renderer_debug;
|
||||
bool dump_command_buffers;
|
||||
bool use_hw_renderer;
|
||||
bool use_hw_shader;
|
||||
bool separable_shader;
|
||||
bool use_disk_shader_cache;
|
||||
bool shaders_accurate_mul;
|
||||
bool use_shader_jit;
|
||||
bool use_vsync_new;
|
||||
u16 resolution_factor;
|
||||
bool use_frame_limit_alternate;
|
||||
u16 frame_limit;
|
||||
@@ -202,12 +208,11 @@ struct Values {
|
||||
bool filter_mode;
|
||||
std::string pp_shader_name;
|
||||
|
||||
// Custom textures
|
||||
bool dump_textures;
|
||||
bool custom_textures;
|
||||
bool preload_textures;
|
||||
|
||||
bool use_vsync_new;
|
||||
|
||||
// Audio
|
||||
bool enable_dsp_lle;
|
||||
bool enable_dsp_lle_multithread;
|
||||
|
@@ -3,12 +3,9 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cryptopp/osrng.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "core/core.h"
|
||||
#include "core/settings.h"
|
||||
#include "core/telemetry_session.h"
|
||||
|
@@ -5,6 +5,7 @@
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <numbers>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include "common/math_util.h"
|
||||
@@ -46,7 +47,7 @@ public:
|
||||
} else {
|
||||
tilt_direction = mouse_move.Cast<float>();
|
||||
tilt_angle = std::clamp(tilt_direction.Normalize() * sensitivity, 0.0f,
|
||||
Common::PI * this->tilt_clamp / 180.0f);
|
||||
std::numbers::pi_v<float> * this->tilt_clamp / 180.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,7 +110,7 @@ private:
|
||||
|
||||
// Find the angular rate vector in world space
|
||||
auto angular_rate = ((q - old_q) * inv_q).xyz * 2;
|
||||
angular_rate *= 1000 / update_millisecond / Common::PI * 180;
|
||||
angular_rate *= 1000 / update_millisecond / std::numbers::pi_v<float> * 180;
|
||||
|
||||
// Transform the two vectors from world space to 3DS space
|
||||
gravity = QuaternionRotate(inv_q, gravity);
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <mutex>
|
||||
#include <numbers>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
@@ -15,9 +16,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <SDL.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/math_util.h"
|
||||
#include "common/param_package.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "core/frontend/input.h"
|
||||
@@ -597,9 +596,9 @@ void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
|
||||
event.csensor.data[2] / SDL_STANDARD_GRAVITY);
|
||||
break;
|
||||
case SDL_SENSOR_GYRO:
|
||||
joystick->SetGyro(-event.csensor.data[0] * (180.0f / Common::PI),
|
||||
event.csensor.data[1] * (180.0f / Common::PI),
|
||||
-event.csensor.data[2] * (180.0f / Common::PI));
|
||||
joystick->SetGyro(-event.csensor.data[0] * (180.0f / std::numbers::pi),
|
||||
event.csensor.data[1] * (180.0f / std::numbers::pi),
|
||||
-event.csensor.data[2] * (180.0f / std::numbers::pi));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1053,7 +1052,7 @@ public:
|
||||
}
|
||||
|
||||
Common::ParamPackage GetNextInput() override {
|
||||
SDL_Event event;
|
||||
SDL_Event event{};
|
||||
while (state.event_queue.Pop(event)) {
|
||||
if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) {
|
||||
continue;
|
||||
|
@@ -137,7 +137,7 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel
|
||||
}
|
||||
|
||||
SECTION("translates StaticBuffer descriptors") {
|
||||
auto mem = std::make_shared<BufferMem>(Memory::PAGE_SIZE);
|
||||
auto mem = std::make_shared<BufferMem>(Memory::CITRA_PAGE_SIZE);
|
||||
MemoryRef buffer{mem};
|
||||
std::fill(buffer.GetPtr(), buffer.GetPtr() + buffer.GetSize(), 0xAB);
|
||||
|
||||
@@ -161,7 +161,7 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel
|
||||
}
|
||||
|
||||
SECTION("translates MappedBuffer descriptors") {
|
||||
auto mem = std::make_shared<BufferMem>(Memory::PAGE_SIZE);
|
||||
auto mem = std::make_shared<BufferMem>(Memory::CITRA_PAGE_SIZE);
|
||||
MemoryRef buffer{mem};
|
||||
std::fill(buffer.GetPtr(), buffer.GetPtr() + buffer.GetSize(), 0xCD);
|
||||
|
||||
@@ -187,11 +187,11 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel
|
||||
}
|
||||
|
||||
SECTION("translates mixed params") {
|
||||
auto mem_static = std::make_shared<BufferMem>(Memory::PAGE_SIZE);
|
||||
auto mem_static = std::make_shared<BufferMem>(Memory::CITRA_PAGE_SIZE);
|
||||
MemoryRef buffer_static{mem_static};
|
||||
std::fill(buffer_static.GetPtr(), buffer_static.GetPtr() + buffer_static.GetSize(), 0xCE);
|
||||
|
||||
auto mem_mapped = std::make_shared<BufferMem>(Memory::PAGE_SIZE);
|
||||
auto mem_mapped = std::make_shared<BufferMem>(Memory::CITRA_PAGE_SIZE);
|
||||
MemoryRef buffer_mapped{mem_mapped};
|
||||
std::fill(buffer_mapped.GetPtr(), buffer_mapped.GetPtr() + buffer_mapped.GetSize(), 0xDF);
|
||||
|
||||
@@ -321,12 +321,12 @@ TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") {
|
||||
}
|
||||
|
||||
SECTION("translates StaticBuffer descriptors") {
|
||||
std::vector<u8> input_buffer(Memory::PAGE_SIZE);
|
||||
std::vector<u8> input_buffer(Memory::CITRA_PAGE_SIZE);
|
||||
std::fill(input_buffer.begin(), input_buffer.end(), 0xAB);
|
||||
|
||||
context.AddStaticBuffer(0, input_buffer);
|
||||
|
||||
auto output_mem = std::make_shared<BufferMem>(Memory::PAGE_SIZE);
|
||||
auto output_mem = std::make_shared<BufferMem>(Memory::CITRA_PAGE_SIZE);
|
||||
MemoryRef output_buffer{output_mem};
|
||||
|
||||
VAddr target_address = 0x10000000;
|
||||
@@ -355,10 +355,10 @@ TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") {
|
||||
}
|
||||
|
||||
SECTION("translates StaticBuffer descriptors") {
|
||||
std::vector<u8> input_buffer(Memory::PAGE_SIZE);
|
||||
std::vector<u8> input_buffer(Memory::CITRA_PAGE_SIZE);
|
||||
std::fill(input_buffer.begin(), input_buffer.end(), 0xAB);
|
||||
|
||||
auto output_mem = std::make_shared<BufferMem>(Memory::PAGE_SIZE);
|
||||
auto output_mem = std::make_shared<BufferMem>(Memory::CITRA_PAGE_SIZE);
|
||||
MemoryRef output_buffer{output_mem};
|
||||
|
||||
VAddr target_address = 0x10000000;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user