summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--externals/microprofile/microprofile.h20
-rw-r--r--externals/microprofile/microprofileui.h108
-rw-r--r--externals/opus/CMakeLists.txt6
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/common/bit_field.h2
-rw-r--r--src/core/crypto/partition_data_manager.cpp7
-rw-r--r--src/core/gdbstub/gdbstub.cpp7
-rw-r--r--src/core/hle/kernel/memory/memory_block.h23
-rw-r--r--src/core/hle/kernel/memory/memory_block_manager.cpp36
-rw-r--r--src/core/hle/kernel/memory/memory_block_manager.h3
-rw-r--r--src/core/hle/kernel/memory/page_table.cpp44
-rw-r--r--src/core/hle/kernel/memory/page_table.h2
-rw-r--r--src/core/hle/kernel/shared_memory.cpp8
-rw-r--r--src/core/hle/kernel/shared_memory.h2
-rw-r--r--src/core/hle/kernel/svc.cpp143
-rw-r--r--src/core/hle/kernel/thread.cpp3
-rw-r--r--src/core/hle/service/acc/acc_su.cpp2
-rw-r--r--src/core/hle/service/acc/acc_u1.cpp1
-rw-r--r--src/core/hle/service/am/am.cpp10
-rw-r--r--src/core/hle/service/audio/audctl.cpp2
-rw-r--r--src/core/hle/service/bcat/backend/boxcat.cpp7
-rw-r--r--src/core/hle/service/bcat/module.cpp1
-rw-r--r--src/core/hle/service/es/es.cpp1
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp4
-rw-r--r--src/core/hle/service/friend/friend.cpp1
-rw-r--r--src/core/hle/service/hid/hid.cpp50
-rw-r--r--src/core/hle/service/hid/hid.h1
-rw-r--r--src/core/hle/service/ldr/ldr.cpp1
-rw-r--r--src/core/hle/service/ncm/ncm.cpp1
-rw-r--r--src/core/hle/service/npns/npns.cpp2
-rw-r--r--src/core/hle/service/ns/ns.cpp22
-rw-r--r--src/core/hle/service/ns/pl_u.cpp1
-rw-r--r--src/core/hle/service/pctl/module.cpp2
-rw-r--r--src/core/hle/service/prepo/prepo.cpp12
-rw-r--r--src/core/hle/service/set/set_cal.cpp2
-rw-r--r--src/core/hle/service/set/set_sys.cpp12
-rw-r--r--src/core/hle/service/sockets/bsd.cpp1
-rw-r--r--src/core/hle/service/time/time.cpp50
-rw-r--r--src/core/reporter.h1
-rw-r--r--src/core/settings.cpp10
-rw-r--r--src/core/settings.h12
-rw-r--r--src/core/telemetry_session.cpp16
-rw-r--r--src/tests/core/core_timing.cpp18
-rw-r--r--src/video_core/CMakeLists.txt24
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h119
-rw-r--r--src/video_core/buffer_cache/map_interval.h18
-rw-r--r--src/video_core/dma_pusher.cpp31
-rw-r--r--src/video_core/dma_pusher.h1
-rw-r--r--src/video_core/engines/fermi_2d.cpp8
-rw-r--r--src/video_core/engines/fermi_2d.h3
-rw-r--r--src/video_core/engines/kepler_compute.cpp7
-rw-r--r--src/video_core/engines/kepler_compute.h3
-rw-r--r--src/video_core/engines/kepler_memory.cpp7
-rw-r--r--src/video_core/engines/kepler_memory.h3
-rw-r--r--src/video_core/engines/maxwell_3d.cpp82
-rw-r--r--src/video_core/engines/maxwell_3d.h7
-rw-r--r--src/video_core/engines/maxwell_dma.cpp18
-rw-r--r--src/video_core/engines/maxwell_dma.h3
-rw-r--r--src/video_core/engines/shader_bytecode.h3
-rw-r--r--src/video_core/fence_manager.h170
-rw-r--r--src/video_core/gpu.cpp84
-rw-r--r--src/video_core/gpu.h39
-rw-r--r--src/video_core/gpu_asynch.cpp4
-rw-r--r--src/video_core/gpu_asynch.h2
-rw-r--r--src/video_core/gpu_thread.cpp39
-rw-r--r--src/video_core/gpu_thread.h11
-rw-r--r--src/video_core/memory_manager.cpp18
-rw-r--r--src/video_core/query_cache.h46
-rw-r--r--src/video_core/rasterizer_interface.h18
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp3
-rw-r--r--src/video_core/renderer_opengl/gl_fence_manager.cpp72
-rw-r--r--src/video_core/renderer_opengl/gl_fence_manager.h53
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp74
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h10
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp17
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h3
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.h2
-rw-r--r--src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp220
-rw-r--r--src/video_core/renderer_vulkan/nsight_aftermath_tracker.h87
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_device.cpp35
-rw-r--r--src/video_core/renderer_vulkan/vk_device.h14
-rw-r--r--src/video_core/renderer_vulkan/vk_fence_manager.cpp101
-rw-r--r--src/video_core/renderer_vulkan/vk_fence_manager.h74
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp22
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h3
-rw-r--r--src/video_core/renderer_vulkan/vk_query_cache.cpp15
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp107
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h14
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp10
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp2
-rw-r--r--src/video_core/renderer_vulkan/wrapper.cpp57
-rw-r--r--src/video_core/renderer_vulkan/wrapper.h54
-rw-r--r--src/video_core/shader/control_flow.cpp4
-rw-r--r--src/video_core/shader/decode/arithmetic_half.cpp51
-rw-r--r--src/video_core/shader/decode/arithmetic_integer.cpp4
-rw-r--r--src/video_core/texture_cache/surface_base.h18
-rw-r--r--src/video_core/texture_cache/texture_cache.h114
-rw-r--r--src/video_core/textures/decoders.cpp3
-rw-r--r--src/video_core/textures/decoders.h5
-rw-r--r--src/yuzu/applets/profile_select.cpp2
-rw-r--r--src/yuzu/configuration/config.cpp18
-rw-r--r--src/yuzu/configuration/config.h2
-rw-r--r--src/yuzu/configuration/configure_filesystem.cpp2
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp7
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.ui40
-rw-r--r--src/yuzu/configuration/configure_hotkeys.cpp80
-rw-r--r--src/yuzu/configuration/configure_hotkeys.h6
-rw-r--r--src/yuzu/configuration/configure_hotkeys.ui39
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp60
-rw-r--r--src/yuzu/configuration/configure_input_player.h6
-rw-r--r--src/yuzu/configuration/configure_input_player.ui16
-rw-r--r--src/yuzu/game_list_p.h14
-rw-r--r--src/yuzu_cmd/config.cpp6
-rw-r--r--src/yuzu_cmd/default_ini.h6
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp7
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp3
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h2
-rw-r--r--src/yuzu_tester/config.cpp6
120 files changed, 2599 insertions, 402 deletions
diff --git a/externals/microprofile/microprofile.h b/externals/microprofile/microprofile.h
index 9d830f7bf..0c0d0a4d3 100644
--- a/externals/microprofile/microprofile.h
+++ b/externals/microprofile/microprofile.h
@@ -910,14 +910,14 @@ typedef void* (*MicroProfileThreadFunc)(void*);
910 910
911#ifndef _WIN32 911#ifndef _WIN32
912typedef pthread_t MicroProfileThread; 912typedef pthread_t MicroProfileThread;
913void MicroProfileThreadStart(MicroProfileThread* pThread, MicroProfileThreadFunc Func) 913inline void MicroProfileThreadStart(MicroProfileThread* pThread, MicroProfileThreadFunc Func)
914{ 914{
915 pthread_attr_t Attr; 915 pthread_attr_t Attr;
916 int r = pthread_attr_init(&Attr); 916 int r = pthread_attr_init(&Attr);
917 MP_ASSERT(r == 0); 917 MP_ASSERT(r == 0);
918 pthread_create(pThread, &Attr, Func, 0); 918 pthread_create(pThread, &Attr, Func, 0);
919} 919}
920void MicroProfileThreadJoin(MicroProfileThread* pThread) 920inline void MicroProfileThreadJoin(MicroProfileThread* pThread)
921{ 921{
922 int r = pthread_join(*pThread, 0); 922 int r = pthread_join(*pThread, 0);
923 MP_ASSERT(r == 0); 923 MP_ASSERT(r == 0);
@@ -930,11 +930,11 @@ DWORD _stdcall ThreadTrampoline(void* pFunc)
930 return (uint32_t)F(0); 930 return (uint32_t)F(0);
931} 931}
932 932
933void MicroProfileThreadStart(MicroProfileThread* pThread, MicroProfileThreadFunc Func) 933inline void MicroProfileThreadStart(MicroProfileThread* pThread, MicroProfileThreadFunc Func)
934{ 934{
935 *pThread = CreateThread(0, 0, ThreadTrampoline, Func, 0, 0); 935 *pThread = CreateThread(0, 0, ThreadTrampoline, Func, 0, 0);
936} 936}
937void MicroProfileThreadJoin(MicroProfileThread* pThread) 937inline void MicroProfileThreadJoin(MicroProfileThread* pThread)
938{ 938{
939 WaitForSingleObject(*pThread, INFINITE); 939 WaitForSingleObject(*pThread, INFINITE);
940 CloseHandle(*pThread); 940 CloseHandle(*pThread);
@@ -1131,7 +1131,7 @@ inline void MicroProfileSetThreadLog(MicroProfileThreadLog* pLog)
1131 pthread_setspecific(g_MicroProfileThreadLogKey, pLog); 1131 pthread_setspecific(g_MicroProfileThreadLogKey, pLog);
1132} 1132}
1133#else 1133#else
1134MicroProfileThreadLog* MicroProfileGetThreadLog() 1134inline MicroProfileThreadLog* MicroProfileGetThreadLog()
1135{ 1135{
1136 return g_MicroProfileThreadLog; 1136 return g_MicroProfileThreadLog;
1137} 1137}
@@ -1247,7 +1247,7 @@ MicroProfileToken MicroProfileFindToken(const char* pGroup, const char* pName)
1247 return MICROPROFILE_INVALID_TOKEN; 1247 return MICROPROFILE_INVALID_TOKEN;
1248} 1248}
1249 1249
1250uint16_t MicroProfileGetGroup(const char* pGroup, MicroProfileTokenType Type) 1250inline uint16_t MicroProfileGetGroup(const char* pGroup, MicroProfileTokenType Type)
1251{ 1251{
1252 for(uint32_t i = 0; i < S.nGroupCount; ++i) 1252 for(uint32_t i = 0; i < S.nGroupCount; ++i)
1253 { 1253 {
@@ -1276,7 +1276,7 @@ uint16_t MicroProfileGetGroup(const char* pGroup, MicroProfileTokenType Type)
1276 return nGroupIndex; 1276 return nGroupIndex;
1277} 1277}
1278 1278
1279void MicroProfileRegisterGroup(const char* pGroup, const char* pCategory, uint32_t nColor) 1279inline void MicroProfileRegisterGroup(const char* pGroup, const char* pCategory, uint32_t nColor)
1280{ 1280{
1281 int nCategoryIndex = -1; 1281 int nCategoryIndex = -1;
1282 for(uint32_t i = 0; i < S.nCategoryCount; ++i) 1282 for(uint32_t i = 0; i < S.nCategoryCount; ++i)
@@ -1442,7 +1442,7 @@ void MicroProfileGpuLeave(MicroProfileToken nToken_, uint64_t nTickStart)
1442 } 1442 }
1443} 1443}
1444 1444
1445void MicroProfileContextSwitchPut(MicroProfileContextSwitch* pContextSwitch) 1445inline void MicroProfileContextSwitchPut(MicroProfileContextSwitch* pContextSwitch)
1446{ 1446{
1447 if(S.nRunning || pContextSwitch->nTicks <= S.nPauseTicks) 1447 if(S.nRunning || pContextSwitch->nTicks <= S.nPauseTicks)
1448 { 1448 {
@@ -1894,7 +1894,7 @@ void MicroProfileSetEnableAllGroups(bool bEnableAllGroups)
1894 S.nAllGroupsWanted = bEnableAllGroups ? 1 : 0; 1894 S.nAllGroupsWanted = bEnableAllGroups ? 1 : 0;
1895} 1895}
1896 1896
1897void MicroProfileEnableCategory(const char* pCategory, bool bEnabled) 1897inline void MicroProfileEnableCategory(const char* pCategory, bool bEnabled)
1898{ 1898{
1899 int nCategoryIndex = -1; 1899 int nCategoryIndex = -1;
1900 for(uint32_t i = 0; i < S.nCategoryCount; ++i) 1900 for(uint32_t i = 0; i < S.nCategoryCount; ++i)
@@ -2004,7 +2004,7 @@ void MicroProfileForceDisableGroup(const char* pGroup, MicroProfileTokenType Typ
2004} 2004}
2005 2005
2006 2006
2007void MicroProfileCalcAllTimers(float* pTimers, float* pAverage, float* pMax, float* pCallAverage, float* pExclusive, float* pAverageExclusive, float* pMaxExclusive, float* pTotal, uint32_t nSize) 2007inline void MicroProfileCalcAllTimers(float* pTimers, float* pAverage, float* pMax, float* pCallAverage, float* pExclusive, float* pAverageExclusive, float* pMaxExclusive, float* pTotal, uint32_t nSize)
2008{ 2008{
2009 for(uint32_t i = 0; i < S.nTotalTimers && i < nSize; ++i) 2009 for(uint32_t i = 0; i < S.nTotalTimers && i < nSize; ++i)
2010 { 2010 {
diff --git a/externals/microprofile/microprofileui.h b/externals/microprofile/microprofileui.h
index ddaebe55b..fe2410cf4 100644
--- a/externals/microprofile/microprofileui.h
+++ b/externals/microprofile/microprofileui.h
@@ -417,19 +417,19 @@ void MicroProfileToggleDisplayMode()
417} 417}
418 418
419 419
420void MicroProfileStringArrayClear(MicroProfileStringArray* pArray) 420inline void MicroProfileStringArrayClear(MicroProfileStringArray* pArray)
421{ 421{
422 pArray->nNumStrings = 0; 422 pArray->nNumStrings = 0;
423 pArray->pBufferPos = &pArray->Buffer[0]; 423 pArray->pBufferPos = &pArray->Buffer[0];
424} 424}
425 425
426void MicroProfileStringArrayAddLiteral(MicroProfileStringArray* pArray, const char* pLiteral) 426inline void MicroProfileStringArrayAddLiteral(MicroProfileStringArray* pArray, const char* pLiteral)
427{ 427{
428 MP_ASSERT(pArray->nNumStrings < MICROPROFILE_TOOLTIP_MAX_STRINGS); 428 MP_ASSERT(pArray->nNumStrings < MICROPROFILE_TOOLTIP_MAX_STRINGS);
429 pArray->ppStrings[pArray->nNumStrings++] = pLiteral; 429 pArray->ppStrings[pArray->nNumStrings++] = pLiteral;
430} 430}
431 431
432void MicroProfileStringArrayFormat(MicroProfileStringArray* pArray, const char* fmt, ...) 432inline void MicroProfileStringArrayFormat(MicroProfileStringArray* pArray, const char* fmt, ...)
433{ 433{
434 MP_ASSERT(pArray->nNumStrings < MICROPROFILE_TOOLTIP_MAX_STRINGS); 434 MP_ASSERT(pArray->nNumStrings < MICROPROFILE_TOOLTIP_MAX_STRINGS);
435 pArray->ppStrings[pArray->nNumStrings++] = pArray->pBufferPos; 435 pArray->ppStrings[pArray->nNumStrings++] = pArray->pBufferPos;
@@ -439,7 +439,7 @@ void MicroProfileStringArrayFormat(MicroProfileStringArray* pArray, const char*
439 va_end(args); 439 va_end(args);
440 MP_ASSERT(pArray->pBufferPos < pArray->Buffer + MICROPROFILE_TOOLTIP_STRING_BUFFER_SIZE); 440 MP_ASSERT(pArray->pBufferPos < pArray->Buffer + MICROPROFILE_TOOLTIP_STRING_BUFFER_SIZE);
441} 441}
442void MicroProfileStringArrayCopy(MicroProfileStringArray* pDest, MicroProfileStringArray* pSrc) 442inline void MicroProfileStringArrayCopy(MicroProfileStringArray* pDest, MicroProfileStringArray* pSrc)
443{ 443{
444 memcpy(&pDest->ppStrings[0], &pSrc->ppStrings[0], sizeof(pDest->ppStrings)); 444 memcpy(&pDest->ppStrings[0], &pSrc->ppStrings[0], sizeof(pDest->ppStrings));
445 memcpy(&pDest->Buffer[0], &pSrc->Buffer[0], sizeof(pDest->Buffer)); 445 memcpy(&pDest->Buffer[0], &pSrc->Buffer[0], sizeof(pDest->Buffer));
@@ -456,7 +456,7 @@ void MicroProfileStringArrayCopy(MicroProfileStringArray* pDest, MicroProfileStr
456 pDest->nNumStrings = pSrc->nNumStrings; 456 pDest->nNumStrings = pSrc->nNumStrings;
457} 457}
458 458
459void MicroProfileFloatWindowSize(const char** ppStrings, uint32_t nNumStrings, uint32_t* pColors, uint32_t& nWidth, uint32_t& nHeight, uint32_t* pStringLengths = 0) 459inline void MicroProfileFloatWindowSize(const char** ppStrings, uint32_t nNumStrings, uint32_t* pColors, uint32_t& nWidth, uint32_t& nHeight, uint32_t* pStringLengths = 0)
460{ 460{
461 uint32_t* nStringLengths = pStringLengths ? pStringLengths : (uint32_t*)alloca(nNumStrings * sizeof(uint32_t)); 461 uint32_t* nStringLengths = pStringLengths ? pStringLengths : (uint32_t*)alloca(nNumStrings * sizeof(uint32_t));
462 uint32_t nTextCount = nNumStrings/2; 462 uint32_t nTextCount = nNumStrings/2;
@@ -474,7 +474,7 @@ void MicroProfileFloatWindowSize(const char** ppStrings, uint32_t nNumStrings, u
474 nHeight = (MICROPROFILE_TEXT_HEIGHT+1) * nTextCount + 2 * MICROPROFILE_BORDER_SIZE; 474 nHeight = (MICROPROFILE_TEXT_HEIGHT+1) * nTextCount + 2 * MICROPROFILE_BORDER_SIZE;
475} 475}
476 476
477void MicroProfileDrawFloatWindow(uint32_t nX, uint32_t nY, const char** ppStrings, uint32_t nNumStrings, uint32_t nColor, uint32_t* pColors = 0) 477inline void MicroProfileDrawFloatWindow(uint32_t nX, uint32_t nY, const char** ppStrings, uint32_t nNumStrings, uint32_t nColor, uint32_t* pColors = 0)
478{ 478{
479 uint32_t nWidth = 0, nHeight = 0; 479 uint32_t nWidth = 0, nHeight = 0;
480 uint32_t* nStringLengths = (uint32_t*)alloca(nNumStrings * sizeof(uint32_t)); 480 uint32_t* nStringLengths = (uint32_t*)alloca(nNumStrings * sizeof(uint32_t));
@@ -503,7 +503,7 @@ void MicroProfileDrawFloatWindow(uint32_t nX, uint32_t nY, const char** ppString
503 nY += (MICROPROFILE_TEXT_HEIGHT+1); 503 nY += (MICROPROFILE_TEXT_HEIGHT+1);
504 } 504 }
505} 505}
506void MicroProfileDrawTextBox(uint32_t nX, uint32_t nY, const char** ppStrings, uint32_t nNumStrings, uint32_t nColor, uint32_t* pColors = 0) 506inline void MicroProfileDrawTextBox(uint32_t nX, uint32_t nY, const char** ppStrings, uint32_t nNumStrings, uint32_t nColor, uint32_t* pColors = 0)
507{ 507{
508 uint32_t nWidth = 0, nHeight = 0; 508 uint32_t nWidth = 0, nHeight = 0;
509 uint32_t* nStringLengths = (uint32_t*)alloca(nNumStrings * sizeof(uint32_t)); 509 uint32_t* nStringLengths = (uint32_t*)alloca(nNumStrings * sizeof(uint32_t));
@@ -529,7 +529,7 @@ void MicroProfileDrawTextBox(uint32_t nX, uint32_t nY, const char** ppStrings, u
529 529
530 530
531 531
532void MicroProfileToolTipMeta(MicroProfileStringArray* pToolTip) 532inline void MicroProfileToolTipMeta(MicroProfileStringArray* pToolTip)
533{ 533{
534 MicroProfile& S = *MicroProfileGet(); 534 MicroProfile& S = *MicroProfileGet();
535 if(UI.nRangeBeginIndex != UI.nRangeEndIndex && UI.pRangeLog) 535 if(UI.nRangeBeginIndex != UI.nRangeEndIndex && UI.pRangeLog)
@@ -608,7 +608,7 @@ void MicroProfileToolTipMeta(MicroProfileStringArray* pToolTip)
608 } 608 }
609} 609}
610 610
611void MicroProfileDrawFloatTooltip(uint32_t nX, uint32_t nY, uint32_t nToken, uint64_t nTime) 611inline void MicroProfileDrawFloatTooltip(uint32_t nX, uint32_t nY, uint32_t nToken, uint64_t nTime)
612{ 612{
613 MicroProfile& S = *MicroProfileGet(); 613 MicroProfile& S = *MicroProfileGet();
614 614
@@ -718,7 +718,7 @@ void MicroProfileDrawFloatTooltip(uint32_t nX, uint32_t nY, uint32_t nToken, uin
718} 718}
719 719
720 720
721void MicroProfileZoomTo(int64_t nTickStart, int64_t nTickEnd) 721inline void MicroProfileZoomTo(int64_t nTickStart, int64_t nTickEnd)
722{ 722{
723 MicroProfile& S = *MicroProfileGet(); 723 MicroProfile& S = *MicroProfileGet();
724 724
@@ -728,7 +728,7 @@ void MicroProfileZoomTo(int64_t nTickStart, int64_t nTickEnd)
728 UI.fDetailedRangeTarget = MicroProfileLogTickDifference(nTickStart, nTickEnd) * fToMs; 728 UI.fDetailedRangeTarget = MicroProfileLogTickDifference(nTickStart, nTickEnd) * fToMs;
729} 729}
730 730
731void MicroProfileCenter(int64_t nTickCenter) 731inline void MicroProfileCenter(int64_t nTickCenter)
732{ 732{
733 MicroProfile& S = *MicroProfileGet(); 733 MicroProfile& S = *MicroProfileGet();
734 int64_t nStart = S.Frames[S.nFrameCurrent].nFrameStartCpu; 734 int64_t nStart = S.Frames[S.nFrameCurrent].nFrameStartCpu;
@@ -739,7 +739,7 @@ void MicroProfileCenter(int64_t nTickCenter)
739#ifdef MICROPROFILE_DEBUG 739#ifdef MICROPROFILE_DEBUG
740uint64_t* g_pMicroProfileDumpStart = 0; 740uint64_t* g_pMicroProfileDumpStart = 0;
741uint64_t* g_pMicroProfileDumpEnd = 0; 741uint64_t* g_pMicroProfileDumpEnd = 0;
742void MicroProfileDebugDumpRange() 742inline void MicroProfileDebugDumpRange()
743{ 743{
744 MicroProfile& S = *MicroProfileGet(); 744 MicroProfile& S = *MicroProfileGet();
745 if(g_pMicroProfileDumpStart != g_pMicroProfileDumpEnd) 745 if(g_pMicroProfileDumpStart != g_pMicroProfileDumpEnd)
@@ -777,7 +777,7 @@ void MicroProfileDebugDumpRange()
777 777
778#define MICROPROFILE_HOVER_DIST 0.5f 778#define MICROPROFILE_HOVER_DIST 0.5f
779 779
780void MicroProfileDrawDetailedContextSwitchBars(uint32_t nY, uint32_t nThreadId, uint32_t nContextSwitchStart, uint32_t nContextSwitchEnd, int64_t nBaseTicks, uint32_t nBaseY) 780inline void MicroProfileDrawDetailedContextSwitchBars(uint32_t nY, uint32_t nThreadId, uint32_t nContextSwitchStart, uint32_t nContextSwitchEnd, int64_t nBaseTicks, uint32_t nBaseY)
781{ 781{
782 MicroProfile& S = *MicroProfileGet(); 782 MicroProfile& S = *MicroProfileGet();
783 int64_t nTickIn = -1; 783 int64_t nTickIn = -1;
@@ -841,7 +841,7 @@ void MicroProfileDrawDetailedContextSwitchBars(uint32_t nY, uint32_t nThreadId,
841 } 841 }
842} 842}
843 843
844void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int nBaseY, int nSelectedFrame) 844inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int nBaseY, int nSelectedFrame)
845{ 845{
846 MicroProfile& S = *MicroProfileGet(); 846 MicroProfile& S = *MicroProfileGet();
847 MP_DEBUG_DUMP_RANGE(); 847 MP_DEBUG_DUMP_RANGE();
@@ -1325,7 +1325,7 @@ void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int nBaseY,
1325} 1325}
1326 1326
1327 1327
1328void MicroProfileDrawDetailedFrameHistory(uint32_t nWidth, uint32_t nHeight, uint32_t nBaseY, uint32_t nSelectedFrame) 1328inline void MicroProfileDrawDetailedFrameHistory(uint32_t nWidth, uint32_t nHeight, uint32_t nBaseY, uint32_t nSelectedFrame)
1329{ 1329{
1330 MicroProfile& S = *MicroProfileGet(); 1330 MicroProfile& S = *MicroProfileGet();
1331 1331
@@ -1379,7 +1379,7 @@ void MicroProfileDrawDetailedFrameHistory(uint32_t nWidth, uint32_t nHeight, uin
1379 } 1379 }
1380 MicroProfileDrawBox(fSelectionStart, nBaseY, fSelectionEnd, nBaseY+MICROPROFILE_FRAME_HISTORY_HEIGHT, MICROPROFILE_FRAME_HISTORY_COLOR_HIGHTLIGHT, MicroProfileBoxTypeFlat); 1380 MicroProfileDrawBox(fSelectionStart, nBaseY, fSelectionEnd, nBaseY+MICROPROFILE_FRAME_HISTORY_HEIGHT, MICROPROFILE_FRAME_HISTORY_COLOR_HIGHTLIGHT, MicroProfileBoxTypeFlat);
1381} 1381}
1382void MicroProfileDrawDetailedView(uint32_t nWidth, uint32_t nHeight) 1382inline void MicroProfileDrawDetailedView(uint32_t nWidth, uint32_t nHeight)
1383{ 1383{
1384 MicroProfile& S = *MicroProfileGet(); 1384 MicroProfile& S = *MicroProfileGet();
1385 1385
@@ -1416,11 +1416,11 @@ void MicroProfileDrawDetailedView(uint32_t nWidth, uint32_t nHeight)
1416 MicroProfileDrawDetailedFrameHistory(nWidth, nHeight, nBaseY, nSelectedFrame); 1416 MicroProfileDrawDetailedFrameHistory(nWidth, nHeight, nBaseY, nSelectedFrame);
1417} 1417}
1418 1418
1419void MicroProfileDrawTextRight(uint32_t nX, uint32_t nY, uint32_t nColor, const char* pStr, uint32_t nStrLen) 1419inline void MicroProfileDrawTextRight(uint32_t nX, uint32_t nY, uint32_t nColor, const char* pStr, uint32_t nStrLen)
1420{ 1420{
1421 MicroProfileDrawText(nX - nStrLen * (MICROPROFILE_TEXT_WIDTH+1), nY, nColor, pStr, nStrLen); 1421 MicroProfileDrawText(nX - nStrLen * (MICROPROFILE_TEXT_WIDTH+1), nY, nColor, pStr, nStrLen);
1422} 1422}
1423void MicroProfileDrawHeader(int32_t nX, uint32_t nWidth, const char* pName) 1423inline void MicroProfileDrawHeader(int32_t nX, uint32_t nWidth, const char* pName)
1424{ 1424{
1425 if(pName) 1425 if(pName)
1426 { 1426 {
@@ -1432,7 +1432,7 @@ void MicroProfileDrawHeader(int32_t nX, uint32_t nWidth, const char* pName)
1432 1432
1433typedef void (*MicroProfileLoopGroupCallback)(uint32_t nTimer, uint32_t nIdx, uint64_t nGroupMask, uint32_t nX, uint32_t nY, void* pData); 1433typedef void (*MicroProfileLoopGroupCallback)(uint32_t nTimer, uint32_t nIdx, uint64_t nGroupMask, uint32_t nX, uint32_t nY, void* pData);
1434 1434
1435void MicroProfileLoopActiveGroupsDraw(int32_t nX, int32_t nY, const char* pName, MicroProfileLoopGroupCallback CB, void* pData) 1435inline void MicroProfileLoopActiveGroupsDraw(int32_t nX, int32_t nY, const char* pName, MicroProfileLoopGroupCallback CB, void* pData)
1436{ 1436{
1437 MicroProfile& S = *MicroProfileGet(); 1437 MicroProfile& S = *MicroProfileGet();
1438 nY += MICROPROFILE_TEXT_HEIGHT + 2; 1438 nY += MICROPROFILE_TEXT_HEIGHT + 2;
@@ -1465,7 +1465,7 @@ void MicroProfileLoopActiveGroupsDraw(int32_t nX, int32_t nY, const char* pName,
1465} 1465}
1466 1466
1467 1467
1468void MicroProfileCalcTimers(float* pTimers, float* pAverage, float* pMax, float* pCallAverage, float* pExclusive, float* pAverageExclusive, float* pMaxExclusive, uint64_t nGroup, uint32_t nSize) 1468inline void MicroProfileCalcTimers(float* pTimers, float* pAverage, float* pMax, float* pCallAverage, float* pExclusive, float* pAverageExclusive, float* pMaxExclusive, uint64_t nGroup, uint32_t nSize)
1469{ 1469{
1470 MicroProfile& S = *MicroProfileGet(); 1470 MicroProfile& S = *MicroProfileGet();
1471 1471
@@ -1527,7 +1527,7 @@ void MicroProfileCalcTimers(float* pTimers, float* pAverage, float* pMax, float*
1527 1527
1528#define SBUF_MAX 32 1528#define SBUF_MAX 32
1529 1529
1530void MicroProfileDrawBarArrayCallback(uint32_t nTimer, uint32_t nIdx, uint64_t nGroupMask, uint32_t nX, uint32_t nY, void* pExtra) 1530inline void MicroProfileDrawBarArrayCallback(uint32_t nTimer, uint32_t nIdx, uint64_t nGroupMask, uint32_t nX, uint32_t nY, void* pExtra)
1531{ 1531{
1532 const uint32_t nHeight = MICROPROFILE_TEXT_HEIGHT; 1532 const uint32_t nHeight = MICROPROFILE_TEXT_HEIGHT;
1533 const uint32_t nTextWidth = 6 * (1+MICROPROFILE_TEXT_WIDTH); 1533 const uint32_t nTextWidth = 6 * (1+MICROPROFILE_TEXT_WIDTH);
@@ -1547,7 +1547,7 @@ void MicroProfileDrawBarArrayCallback(uint32_t nTimer, uint32_t nIdx, uint64_t n
1547} 1547}
1548 1548
1549 1549
1550uint32_t MicroProfileDrawBarArray(int32_t nX, int32_t nY, float* pTimers, const char* pName, uint32_t nTotalHeight, float* pTimers2 = NULL) 1550inline uint32_t MicroProfileDrawBarArray(int32_t nX, int32_t nY, float* pTimers, const char* pName, uint32_t nTotalHeight, float* pTimers2 = NULL)
1551{ 1551{
1552 const uint32_t nTextWidth = 6 * (1+MICROPROFILE_TEXT_WIDTH); 1552 const uint32_t nTextWidth = 6 * (1+MICROPROFILE_TEXT_WIDTH);
1553 const uint32_t nWidth = MICROPROFILE_BAR_WIDTH; 1553 const uint32_t nWidth = MICROPROFILE_BAR_WIDTH;
@@ -1559,7 +1559,7 @@ uint32_t MicroProfileDrawBarArray(int32_t nX, int32_t nY, float* pTimers, const
1559 return nWidth + 5 + nTextWidth; 1559 return nWidth + 5 + nTextWidth;
1560 1560
1561} 1561}
1562void MicroProfileDrawBarCallCountCallback(uint32_t nTimer, uint32_t nIdx, uint64_t nGroupMask, uint32_t nX, uint32_t nY, void* pExtra) 1562inline void MicroProfileDrawBarCallCountCallback(uint32_t nTimer, uint32_t nIdx, uint64_t nGroupMask, uint32_t nX, uint32_t nY, void* pExtra)
1563{ 1563{
1564 MicroProfile& S = *MicroProfileGet(); 1564 MicroProfile& S = *MicroProfileGet();
1565 char sBuffer[SBUF_MAX]; 1565 char sBuffer[SBUF_MAX];
@@ -1567,7 +1567,7 @@ void MicroProfileDrawBarCallCountCallback(uint32_t nTimer, uint32_t nIdx, uint64
1567 MicroProfileDrawText(nX, nY, (uint32_t)-1, sBuffer, nLen); 1567 MicroProfileDrawText(nX, nY, (uint32_t)-1, sBuffer, nLen);
1568} 1568}
1569 1569
1570uint32_t MicroProfileDrawBarCallCount(int32_t nX, int32_t nY, const char* pName) 1570inline uint32_t MicroProfileDrawBarCallCount(int32_t nX, int32_t nY, const char* pName)
1571{ 1571{
1572 MicroProfileLoopActiveGroupsDraw(nX, nY, pName, MicroProfileDrawBarCallCountCallback, 0); 1572 MicroProfileLoopActiveGroupsDraw(nX, nY, pName, MicroProfileDrawBarCallCountCallback, 0);
1573 const uint32_t nTextWidth = 6 * MICROPROFILE_TEXT_WIDTH; 1573 const uint32_t nTextWidth = 6 * MICROPROFILE_TEXT_WIDTH;
@@ -1581,7 +1581,7 @@ struct MicroProfileMetaAverageArgs
1581 float fRcpFrames; 1581 float fRcpFrames;
1582}; 1582};
1583 1583
1584void MicroProfileDrawBarMetaAverageCallback(uint32_t nTimer, uint32_t nIdx, uint64_t nGroupMask, uint32_t nX, uint32_t nY, void* pExtra) 1584inline void MicroProfileDrawBarMetaAverageCallback(uint32_t nTimer, uint32_t nIdx, uint64_t nGroupMask, uint32_t nX, uint32_t nY, void* pExtra)
1585{ 1585{
1586 MicroProfileMetaAverageArgs* pArgs = (MicroProfileMetaAverageArgs*)pExtra; 1586 MicroProfileMetaAverageArgs* pArgs = (MicroProfileMetaAverageArgs*)pExtra;
1587 uint64_t* pCounters = pArgs->pCounters; 1587 uint64_t* pCounters = pArgs->pCounters;
@@ -1591,7 +1591,7 @@ void MicroProfileDrawBarMetaAverageCallback(uint32_t nTimer, uint32_t nIdx, uint
1591 MicroProfileDrawText(nX - nLen * (MICROPROFILE_TEXT_WIDTH+1), nY, (uint32_t)-1, sBuffer, nLen); 1591 MicroProfileDrawText(nX - nLen * (MICROPROFILE_TEXT_WIDTH+1), nY, (uint32_t)-1, sBuffer, nLen);
1592} 1592}
1593 1593
1594uint32_t MicroProfileDrawBarMetaAverage(int32_t nX, int32_t nY, uint64_t* pCounters, const char* pName, uint32_t nTotalHeight) 1594inline uint32_t MicroProfileDrawBarMetaAverage(int32_t nX, int32_t nY, uint64_t* pCounters, const char* pName, uint32_t nTotalHeight)
1595{ 1595{
1596 if(!pName) 1596 if(!pName)
1597 return 0; 1597 return 0;
@@ -1605,7 +1605,7 @@ uint32_t MicroProfileDrawBarMetaAverage(int32_t nX, int32_t nY, uint64_t* pCount
1605} 1605}
1606 1606
1607 1607
1608void MicroProfileDrawBarMetaCountCallback(uint32_t nTimer, uint32_t nIdx, uint64_t nGroupMask, uint32_t nX, uint32_t nY, void* pExtra) 1608inline void MicroProfileDrawBarMetaCountCallback(uint32_t nTimer, uint32_t nIdx, uint64_t nGroupMask, uint32_t nX, uint32_t nY, void* pExtra)
1609{ 1609{
1610 uint64_t* pCounters = (uint64_t*)pExtra; 1610 uint64_t* pCounters = (uint64_t*)pExtra;
1611 char sBuffer[SBUF_MAX]; 1611 char sBuffer[SBUF_MAX];
@@ -1613,7 +1613,7 @@ void MicroProfileDrawBarMetaCountCallback(uint32_t nTimer, uint32_t nIdx, uint64
1613 MicroProfileDrawText(nX - nLen * (MICROPROFILE_TEXT_WIDTH+1), nY, (uint32_t)-1, sBuffer, nLen); 1613 MicroProfileDrawText(nX - nLen * (MICROPROFILE_TEXT_WIDTH+1), nY, (uint32_t)-1, sBuffer, nLen);
1614} 1614}
1615 1615
1616uint32_t MicroProfileDrawBarMetaCount(int32_t nX, int32_t nY, uint64_t* pCounters, const char* pName, uint32_t nTotalHeight) 1616inline uint32_t MicroProfileDrawBarMetaCount(int32_t nX, int32_t nY, uint64_t* pCounters, const char* pName, uint32_t nTotalHeight)
1617{ 1617{
1618 if(!pName) 1618 if(!pName)
1619 return 0; 1619 return 0;
@@ -1625,7 +1625,7 @@ uint32_t MicroProfileDrawBarMetaCount(int32_t nX, int32_t nY, uint64_t* pCounter
1625 return 5 + nTextWidth; 1625 return 5 + nTextWidth;
1626} 1626}
1627 1627
1628void MicroProfileDrawBarLegendCallback(uint32_t nTimer, uint32_t nIdx, uint64_t nGroupMask, uint32_t nX, uint32_t nY, void* pExtra) 1628inline void MicroProfileDrawBarLegendCallback(uint32_t nTimer, uint32_t nIdx, uint64_t nGroupMask, uint32_t nX, uint32_t nY, void* pExtra)
1629{ 1629{
1630 MicroProfile& S = *MicroProfileGet(); 1630 MicroProfile& S = *MicroProfileGet();
1631 if (S.TimerInfo[nTimer].bGraph) 1631 if (S.TimerInfo[nTimer].bGraph)
@@ -1640,7 +1640,7 @@ void MicroProfileDrawBarLegendCallback(uint32_t nTimer, uint32_t nIdx, uint64_t
1640 } 1640 }
1641} 1641}
1642 1642
1643uint32_t MicroProfileDrawBarLegend(int32_t nX, int32_t nY, uint32_t nTotalHeight, uint32_t nMaxWidth) 1643inline uint32_t MicroProfileDrawBarLegend(int32_t nX, int32_t nY, uint32_t nTotalHeight, uint32_t nMaxWidth)
1644{ 1644{
1645 MicroProfileDrawLineVertical(nX-5, nY, nTotalHeight, UI.nOpacityBackground | g_nMicroProfileBackColors[0]|g_nMicroProfileBackColors[1]); 1645 MicroProfileDrawLineVertical(nX-5, nY, nTotalHeight, UI.nOpacityBackground | g_nMicroProfileBackColors[0]|g_nMicroProfileBackColors[1]);
1646 MicroProfileLoopActiveGroupsDraw(nMaxWidth, nY, 0, MicroProfileDrawBarLegendCallback, 0); 1646 MicroProfileLoopActiveGroupsDraw(nMaxWidth, nY, 0, MicroProfileDrawBarLegendCallback, 0);
@@ -1807,7 +1807,7 @@ void MicroProfileDumpTimers()
1807 } 1807 }
1808} 1808}
1809 1809
1810void MicroProfileDrawBarView(uint32_t nScreenWidth, uint32_t nScreenHeight) 1810inline void MicroProfileDrawBarView(uint32_t nScreenWidth, uint32_t nScreenHeight)
1811{ 1811{
1812 MicroProfile& S = *MicroProfileGet(); 1812 MicroProfile& S = *MicroProfileGet();
1813 1813
@@ -1951,7 +1951,7 @@ typedef const char* (*MicroProfileSubmenuCallback)(int, bool* bSelected);
1951typedef void (*MicroProfileClickCallback)(int); 1951typedef void (*MicroProfileClickCallback)(int);
1952 1952
1953 1953
1954const char* MicroProfileUIMenuMode(int nIndex, bool* bSelected) 1954inline const char* MicroProfileUIMenuMode(int nIndex, bool* bSelected)
1955{ 1955{
1956 MicroProfile& S = *MicroProfileGet(); 1956 MicroProfile& S = *MicroProfileGet();
1957 switch(nIndex) 1957 switch(nIndex)
@@ -1979,7 +1979,7 @@ const char* MicroProfileUIMenuMode(int nIndex, bool* bSelected)
1979 } 1979 }
1980} 1980}
1981 1981
1982const char* MicroProfileUIMenuGroups(int nIndex, bool* bSelected) 1982inline const char* MicroProfileUIMenuGroups(int nIndex, bool* bSelected)
1983{ 1983{
1984 MicroProfile& S = *MicroProfileGet(); 1984 MicroProfile& S = *MicroProfileGet();
1985 *bSelected = false; 1985 *bSelected = false;
@@ -2012,7 +2012,7 @@ const char* MicroProfileUIMenuGroups(int nIndex, bool* bSelected)
2012 } 2012 }
2013} 2013}
2014 2014
2015const char* MicroProfileUIMenuAggregate(int nIndex, bool* bSelected) 2015inline const char* MicroProfileUIMenuAggregate(int nIndex, bool* bSelected)
2016{ 2016{
2017 MicroProfile& S = *MicroProfileGet(); 2017 MicroProfile& S = *MicroProfileGet();
2018 if(nIndex < sizeof(g_MicroProfileAggregatePresets)/sizeof(g_MicroProfileAggregatePresets[0])) 2018 if(nIndex < sizeof(g_MicroProfileAggregatePresets)/sizeof(g_MicroProfileAggregatePresets[0]))
@@ -2032,7 +2032,7 @@ const char* MicroProfileUIMenuAggregate(int nIndex, bool* bSelected)
2032 2032
2033} 2033}
2034 2034
2035const char* MicroProfileUIMenuTimers(int nIndex, bool* bSelected) 2035inline const char* MicroProfileUIMenuTimers(int nIndex, bool* bSelected)
2036{ 2036{
2037 MicroProfile& S = *MicroProfileGet(); 2037 MicroProfile& S = *MicroProfileGet();
2038 *bSelected = 0 != (S.nBars & (1 << nIndex)); 2038 *bSelected = 0 != (S.nBars & (1 << nIndex));
@@ -2054,7 +2054,7 @@ const char* MicroProfileUIMenuTimers(int nIndex, bool* bSelected)
2054 return 0; 2054 return 0;
2055} 2055}
2056 2056
2057const char* MicroProfileUIMenuOptions(int nIndex, bool* bSelected) 2057inline const char* MicroProfileUIMenuOptions(int nIndex, bool* bSelected)
2058{ 2058{
2059 MicroProfile& S = *MicroProfileGet(); 2059 MicroProfile& S = *MicroProfileGet();
2060 if(nIndex >= MICROPROFILE_OPTION_SIZE) return 0; 2060 if(nIndex >= MICROPROFILE_OPTION_SIZE) return 0;
@@ -2094,7 +2094,7 @@ const char* MicroProfileUIMenuOptions(int nIndex, bool* bSelected)
2094 return UI.Options[nIndex].Text; 2094 return UI.Options[nIndex].Text;
2095} 2095}
2096 2096
2097const char* MicroProfileUIMenuPreset(int nIndex, bool* bSelected) 2097inline const char* MicroProfileUIMenuPreset(int nIndex, bool* bSelected)
2098{ 2098{
2099 static char buf[128]; 2099 static char buf[128];
2100 *bSelected = false; 2100 *bSelected = false;
@@ -2118,7 +2118,7 @@ const char* MicroProfileUIMenuPreset(int nIndex, bool* bSelected)
2118 } 2118 }
2119} 2119}
2120 2120
2121const char* MicroProfileUIMenuCustom(int nIndex, bool* bSelected) 2121inline const char* MicroProfileUIMenuCustom(int nIndex, bool* bSelected)
2122{ 2122{
2123 if((uint32_t)-1 == UI.nCustomActive) 2123 if((uint32_t)-1 == UI.nCustomActive)
2124 { 2124 {
@@ -2145,13 +2145,13 @@ const char* MicroProfileUIMenuCustom(int nIndex, bool* bSelected)
2145 } 2145 }
2146} 2146}
2147 2147
2148const char* MicroProfileUIMenuEmpty(int nIndex, bool* bSelected) 2148inline const char* MicroProfileUIMenuEmpty(int nIndex, bool* bSelected)
2149{ 2149{
2150 return 0; 2150 return 0;
2151} 2151}
2152 2152
2153 2153
2154void MicroProfileUIClickMode(int nIndex) 2154inline void MicroProfileUIClickMode(int nIndex)
2155{ 2155{
2156 MicroProfile& S = *MicroProfileGet(); 2156 MicroProfile& S = *MicroProfileGet();
2157 switch(nIndex) 2157 switch(nIndex)
@@ -2176,7 +2176,7 @@ void MicroProfileUIClickMode(int nIndex)
2176 } 2176 }
2177} 2177}
2178 2178
2179void MicroProfileUIClickGroups(int nIndex) 2179inline void MicroProfileUIClickGroups(int nIndex)
2180{ 2180{
2181 MicroProfile& S = *MicroProfileGet(); 2181 MicroProfile& S = *MicroProfileGet();
2182 if(nIndex == 0) 2182 if(nIndex == 0)
@@ -2208,7 +2208,7 @@ void MicroProfileUIClickGroups(int nIndex)
2208 } 2208 }
2209} 2209}
2210 2210
2211void MicroProfileUIClickAggregate(int nIndex) 2211inline void MicroProfileUIClickAggregate(int nIndex)
2212{ 2212{
2213 MicroProfile& S = *MicroProfileGet(); 2213 MicroProfile& S = *MicroProfileGet();
2214 S.nAggregateFlip = g_MicroProfileAggregatePresets[nIndex]; 2214 S.nAggregateFlip = g_MicroProfileAggregatePresets[nIndex];
@@ -2218,13 +2218,13 @@ void MicroProfileUIClickAggregate(int nIndex)
2218 } 2218 }
2219} 2219}
2220 2220
2221void MicroProfileUIClickTimers(int nIndex) 2221inline void MicroProfileUIClickTimers(int nIndex)
2222{ 2222{
2223 MicroProfile& S = *MicroProfileGet(); 2223 MicroProfile& S = *MicroProfileGet();
2224 S.nBars ^= (1 << nIndex); 2224 S.nBars ^= (1 << nIndex);
2225} 2225}
2226 2226
2227void MicroProfileUIClickOptions(int nIndex) 2227inline void MicroProfileUIClickOptions(int nIndex)
2228{ 2228{
2229 MicroProfile& S = *MicroProfileGet(); 2229 MicroProfile& S = *MicroProfileGet();
2230 switch(UI.Options[nIndex].nSubType) 2230 switch(UI.Options[nIndex].nSubType)
@@ -2271,7 +2271,7 @@ void MicroProfileUIClickOptions(int nIndex)
2271 } 2271 }
2272} 2272}
2273 2273
2274void MicroProfileUIClickPreset(int nIndex) 2274inline void MicroProfileUIClickPreset(int nIndex)
2275{ 2275{
2276 int nNumPresets = sizeof(g_MicroProfilePresetNames) / sizeof(g_MicroProfilePresetNames[0]); 2276 int nNumPresets = sizeof(g_MicroProfilePresetNames) / sizeof(g_MicroProfilePresetNames[0]);
2277 int nIndexSave = nIndex - nNumPresets - 1; 2277 int nIndexSave = nIndex - nNumPresets - 1;
@@ -2285,7 +2285,7 @@ void MicroProfileUIClickPreset(int nIndex)
2285 } 2285 }
2286} 2286}
2287 2287
2288void MicroProfileUIClickCustom(int nIndex) 2288inline void MicroProfileUIClickCustom(int nIndex)
2289{ 2289{
2290 if(nIndex == 0) 2290 if(nIndex == 0)
2291 { 2291 {
@@ -2298,13 +2298,13 @@ void MicroProfileUIClickCustom(int nIndex)
2298 2298
2299} 2299}
2300 2300
2301void MicroProfileUIClickEmpty(int nIndex) 2301inline void MicroProfileUIClickEmpty(int nIndex)
2302{ 2302{
2303 2303
2304} 2304}
2305 2305
2306 2306
2307void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight) 2307inline void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
2308{ 2308{
2309 MicroProfile& S = *MicroProfileGet(); 2309 MicroProfile& S = *MicroProfileGet();
2310 2310
@@ -2489,7 +2489,7 @@ void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
2489} 2489}
2490 2490
2491 2491
2492void MicroProfileMoveGraph() 2492inline void MicroProfileMoveGraph()
2493{ 2493{
2494 2494
2495 int nZoom = UI.nMouseWheelDelta; 2495 int nZoom = UI.nMouseWheelDelta;
@@ -2536,7 +2536,7 @@ void MicroProfileMoveGraph()
2536 UI.nOffsetY = 0; 2536 UI.nOffsetY = 0;
2537} 2537}
2538 2538
2539void MicroProfileDrawCustom(uint32_t nWidth, uint32_t nHeight) 2539inline void MicroProfileDrawCustom(uint32_t nWidth, uint32_t nHeight)
2540{ 2540{
2541 if((uint32_t)-1 != UI.nCustomActive) 2541 if((uint32_t)-1 != UI.nCustomActive)
2542 { 2542 {
@@ -2633,7 +2633,7 @@ void MicroProfileDrawCustom(uint32_t nWidth, uint32_t nHeight)
2633 } 2633 }
2634 } 2634 }
2635} 2635}
2636void MicroProfileDraw(uint32_t nWidth, uint32_t nHeight) 2636inline void MicroProfileDraw(uint32_t nWidth, uint32_t nHeight)
2637{ 2637{
2638 MICROPROFILE_SCOPE(g_MicroProfileDraw); 2638 MICROPROFILE_SCOPE(g_MicroProfileDraw);
2639 MicroProfile& S = *MicroProfileGet(); 2639 MicroProfile& S = *MicroProfileGet();
@@ -3226,7 +3226,7 @@ void MicroProfileLoadPreset(const char* pSuffix)
3226 } 3226 }
3227} 3227}
3228 3228
3229uint32_t MicroProfileCustomGroupFind(const char* pCustomName) 3229inline uint32_t MicroProfileCustomGroupFind(const char* pCustomName)
3230{ 3230{
3231 for(uint32_t i = 0; i < UI.nCustomCount; ++i) 3231 for(uint32_t i = 0; i < UI.nCustomCount; ++i)
3232 { 3232 {
@@ -3238,7 +3238,7 @@ uint32_t MicroProfileCustomGroupFind(const char* pCustomName)
3238 return (uint32_t)-1; 3238 return (uint32_t)-1;
3239} 3239}
3240 3240
3241uint32_t MicroProfileCustomGroup(const char* pCustomName) 3241inline uint32_t MicroProfileCustomGroup(const char* pCustomName)
3242{ 3242{
3243 for(uint32_t i = 0; i < UI.nCustomCount; ++i) 3243 for(uint32_t i = 0; i < UI.nCustomCount; ++i)
3244 { 3244 {
@@ -3271,7 +3271,7 @@ void MicroProfileCustomGroup(const char* pCustomName, uint32_t nMaxTimers, uint3
3271 UI.Custom[nIndex].nAggregateFlip = nAggregateFlip; 3271 UI.Custom[nIndex].nAggregateFlip = nAggregateFlip;
3272} 3272}
3273 3273
3274void MicroProfileCustomGroupEnable(uint32_t nIndex) 3274inline void MicroProfileCustomGroupEnable(uint32_t nIndex)
3275{ 3275{
3276 if(nIndex < UI.nCustomCount) 3276 if(nIndex < UI.nCustomCount)
3277 { 3277 {
diff --git a/externals/opus/CMakeLists.txt b/externals/opus/CMakeLists.txt
index cbb393272..94a86551f 100644
--- a/externals/opus/CMakeLists.txt
+++ b/externals/opus/CMakeLists.txt
@@ -203,7 +203,11 @@ endif()
203target_compile_definitions(opus PRIVATE OPUS_BUILD ENABLE_HARDENING) 203target_compile_definitions(opus PRIVATE OPUS_BUILD ENABLE_HARDENING)
204 204
205if(NOT MSVC) 205if(NOT MSVC)
206 target_compile_definitions(opus PRIVATE _FORTIFY_SOURCE=2) 206 if(MINGW)
207 target_compile_definitions(opus PRIVATE _FORTIFY_SOURCE=0)
208 else()
209 target_compile_definitions(opus PRIVATE _FORTIFY_SOURCE=2)
210 endif()
207endif() 211endif()
208 212
209# It is strongly recommended to uncomment one of these VAR_ARRAYS: Use C99 213# It is strongly recommended to uncomment one of these VAR_ARRAYS: Use C99
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 0913be72c..3a57356ab 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -54,8 +54,10 @@ else()
54 add_compile_options( 54 add_compile_options(
55 -Wall 55 -Wall
56 -Werror=implicit-fallthrough 56 -Werror=implicit-fallthrough
57 -Werror=missing-declarations
57 -Werror=reorder 58 -Werror=reorder
58 -Wextra 59 -Wextra
60 -Wmissing-declarations
59 -Wno-attributes 61 -Wno-attributes
60 -Wno-unused-parameter 62 -Wno-unused-parameter
61 ) 63 )
diff --git a/src/common/bit_field.h b/src/common/bit_field.h
index fd2bbbd99..26ae6c7fc 100644
--- a/src/common/bit_field.h
+++ b/src/common/bit_field.h
@@ -180,7 +180,7 @@ public:
180 } 180 }
181 181
182 constexpr void Assign(const T& value) { 182 constexpr void Assign(const T& value) {
183 storage = (static_cast<StorageType>(storage) & ~mask) | FormatValue(value); 183 storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value));
184 } 184 }
185 185
186 constexpr T Value() const { 186 constexpr T Value() const {
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp
index d64302f2e..7ed71ac3a 100644
--- a/src/core/crypto/partition_data_manager.cpp
+++ b/src/core/crypto/partition_data_manager.cpp
@@ -202,8 +202,8 @@ static std::array<Key128, 0x20> FindEncryptedMasterKeyFromHex(const std::vector<
202 return out; 202 return out;
203} 203}
204 204
205FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir, 205static FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir,
206 const std::string& name) { 206 const std::string& name) {
207 const auto upper = Common::ToUpper(name); 207 const auto upper = Common::ToUpper(name);
208 208
209 for (const auto& fname : {name, name + ".bin", upper, upper + ".BIN"}) { 209 for (const auto& fname : {name, name + ".bin", upper, upper + ".BIN"}) {
@@ -345,8 +345,7 @@ FileSys::VirtualFile PartitionDataManager::GetPackage2Raw(Package2Type type) con
345 return package2.at(static_cast<size_t>(type)); 345 return package2.at(static_cast<size_t>(type));
346} 346}
347 347
348bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header) { 348static bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header) {
349
350 const std::vector<u8> iv(header.header_ctr.begin(), header.header_ctr.end()); 349 const std::vector<u8> iv(header.header_ctr.begin(), header.header_ctr.end());
351 Package2Header temp = header; 350 Package2Header temp = header;
352 AESCipher<Key128> cipher(key, Mode::CTR); 351 AESCipher<Key128> cipher(key, Mode::CTR);
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index 2f15635c5..70c0f8b80 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -1389,10 +1389,9 @@ void SendTrap(Kernel::Thread* thread, int trap) {
1389 return; 1389 return;
1390 } 1390 }
1391 1391
1392 if (!halt_loop || current_thread == thread) { 1392 current_thread = thread;
1393 current_thread = thread; 1393 SendSignal(thread, trap);
1394 SendSignal(thread, trap); 1394
1395 }
1396 halt_loop = true; 1395 halt_loop = true;
1397 send_trap = false; 1396 send_trap = false;
1398} 1397}
diff --git a/src/core/hle/kernel/memory/memory_block.h b/src/core/hle/kernel/memory/memory_block.h
index e11043b60..9db1f7b39 100644
--- a/src/core/hle/kernel/memory/memory_block.h
+++ b/src/core/hle/kernel/memory/memory_block.h
@@ -17,7 +17,7 @@ namespace Kernel::Memory {
17 17
18enum class MemoryState : u32 { 18enum class MemoryState : u32 {
19 None = 0, 19 None = 0,
20 Mask = 0xFFFFFFFF, // TODO(bunnei): This should probable be 0xFF 20 Mask = 0xFF,
21 All = ~None, 21 All = ~None,
22 22
23 FlagCanReprotect = (1 << 8), 23 FlagCanReprotect = (1 << 8),
@@ -253,6 +253,23 @@ public:
253 }; 253 };
254 } 254 }
255 255
256 void ShareToDevice(MemoryPermission /*new_perm*/) {
257 ASSERT((attribute & MemoryAttribute::DeviceShared) == MemoryAttribute::DeviceShared ||
258 device_use_count == 0);
259 attribute |= MemoryAttribute::DeviceShared;
260 const u16 new_use_count{++device_use_count};
261 ASSERT(new_use_count > 0);
262 }
263
264 void UnshareToDevice(MemoryPermission /*new_perm*/) {
265 ASSERT((attribute & MemoryAttribute::DeviceShared) == MemoryAttribute::DeviceShared);
266 const u16 prev_use_count{device_use_count--};
267 ASSERT(prev_use_count > 0);
268 if (prev_use_count == 1) {
269 attribute &= ~MemoryAttribute::DeviceShared;
270 }
271 }
272
256private: 273private:
257 constexpr bool HasProperties(MemoryState s, MemoryPermission p, MemoryAttribute a) const { 274 constexpr bool HasProperties(MemoryState s, MemoryPermission p, MemoryAttribute a) const {
258 constexpr MemoryAttribute AttributeIgnoreMask{MemoryAttribute::DontCareMask | 275 constexpr MemoryAttribute AttributeIgnoreMask{MemoryAttribute::DontCareMask |
@@ -287,9 +304,9 @@ private:
287 state = new_state; 304 state = new_state;
288 perm = new_perm; 305 perm = new_perm;
289 306
290 // TODO(bunnei): Is this right?
291 attribute = static_cast<MemoryAttribute>( 307 attribute = static_cast<MemoryAttribute>(
292 new_attribute /*| (attribute & (MemoryAttribute::IpcLocked | MemoryAttribute::DeviceShared))*/); 308 new_attribute |
309 (attribute & (MemoryAttribute::IpcLocked | MemoryAttribute::DeviceShared)));
293 } 310 }
294 311
295 constexpr MemoryBlock Split(VAddr split_addr) { 312 constexpr MemoryBlock Split(VAddr split_addr) {
diff --git a/src/core/hle/kernel/memory/memory_block_manager.cpp b/src/core/hle/kernel/memory/memory_block_manager.cpp
index 1ebc126c0..900395c37 100644
--- a/src/core/hle/kernel/memory/memory_block_manager.cpp
+++ b/src/core/hle/kernel/memory/memory_block_manager.cpp
@@ -143,6 +143,42 @@ void MemoryBlockManager::Update(VAddr addr, std::size_t num_pages, MemoryState s
143 } 143 }
144} 144}
145 145
146void MemoryBlockManager::UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&& lock_func,
147 MemoryPermission perm) {
148 const std::size_t prev_count{memory_block_tree.size()};
149 const VAddr end_addr{addr + num_pages * PageSize};
150 iterator node{memory_block_tree.begin()};
151
152 while (node != memory_block_tree.end()) {
153 MemoryBlock* block{&(*node)};
154 iterator next_node{std::next(node)};
155 const VAddr cur_addr{block->GetAddress()};
156 const VAddr cur_end_addr{block->GetNumPages() * PageSize + cur_addr};
157
158 if (addr < cur_end_addr && cur_addr < end_addr) {
159 iterator new_node{node};
160
161 if (addr > cur_addr) {
162 memory_block_tree.insert(node, block->Split(addr));
163 }
164
165 if (end_addr < cur_end_addr) {
166 new_node = memory_block_tree.insert(node, block->Split(end_addr));
167 }
168
169 lock_func(new_node, perm);
170
171 MergeAdjacent(new_node, next_node);
172 }
173
174 if (cur_end_addr - 1 >= end_addr - 1) {
175 break;
176 }
177
178 node = next_node;
179 }
180}
181
146void MemoryBlockManager::IterateForRange(VAddr start, VAddr end, IterateFunc&& func) { 182void MemoryBlockManager::IterateForRange(VAddr start, VAddr end, IterateFunc&& func) {
147 const_iterator it{FindIterator(start)}; 183 const_iterator it{FindIterator(start)};
148 MemoryInfo info{}; 184 MemoryInfo info{};
diff --git a/src/core/hle/kernel/memory/memory_block_manager.h b/src/core/hle/kernel/memory/memory_block_manager.h
index 0f2270f0f..9451b5df6 100644
--- a/src/core/hle/kernel/memory/memory_block_manager.h
+++ b/src/core/hle/kernel/memory/memory_block_manager.h
@@ -45,6 +45,9 @@ public:
45 MemoryPermission perm = MemoryPermission::None, 45 MemoryPermission perm = MemoryPermission::None,
46 MemoryAttribute attribute = MemoryAttribute::None); 46 MemoryAttribute attribute = MemoryAttribute::None);
47 47
48 using LockFunc = std::function<void(iterator, MemoryPermission)>;
49 void UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&& lock_func, MemoryPermission perm);
50
48 using IterateFunc = std::function<void(const MemoryInfo&)>; 51 using IterateFunc = std::function<void(const MemoryInfo&)>;
49 void IterateForRange(VAddr start, VAddr end, IterateFunc&& func); 52 void IterateForRange(VAddr start, VAddr end, IterateFunc&& func);
50 53
diff --git a/src/core/hle/kernel/memory/page_table.cpp b/src/core/hle/kernel/memory/page_table.cpp
index 091e52ca4..3281611f8 100644
--- a/src/core/hle/kernel/memory/page_table.cpp
+++ b/src/core/hle/kernel/memory/page_table.cpp
@@ -840,6 +840,50 @@ ResultVal<VAddr> PageTable::AllocateAndMapMemory(std::size_t needed_num_pages, s
840 return MakeResult<VAddr>(addr); 840 return MakeResult<VAddr>(addr);
841} 841}
842 842
843ResultCode PageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
844 std::lock_guard lock{page_table_lock};
845
846 MemoryPermission perm{};
847 if (const ResultCode result{CheckMemoryState(
848 nullptr, &perm, nullptr, addr, size, MemoryState::FlagCanChangeAttribute,
849 MemoryState::FlagCanChangeAttribute, MemoryPermission::None, MemoryPermission::None,
850 MemoryAttribute::LockedAndIpcLocked, MemoryAttribute::None,
851 MemoryAttribute::DeviceSharedAndUncached)};
852 result.IsError()) {
853 return result;
854 }
855
856 block_manager->UpdateLock(addr, size / PageSize,
857 [](MemoryBlockManager::iterator block, MemoryPermission perm) {
858 block->ShareToDevice(perm);
859 },
860 perm);
861
862 return RESULT_SUCCESS;
863}
864
865ResultCode PageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) {
866 std::lock_guard lock{page_table_lock};
867
868 MemoryPermission perm{};
869 if (const ResultCode result{CheckMemoryState(
870 nullptr, &perm, nullptr, addr, size, MemoryState::FlagCanChangeAttribute,
871 MemoryState::FlagCanChangeAttribute, MemoryPermission::None, MemoryPermission::None,
872 MemoryAttribute::LockedAndIpcLocked, MemoryAttribute::None,
873 MemoryAttribute::DeviceSharedAndUncached)};
874 result.IsError()) {
875 return result;
876 }
877
878 block_manager->UpdateLock(addr, size / PageSize,
879 [](MemoryBlockManager::iterator block, MemoryPermission perm) {
880 block->UnshareToDevice(perm);
881 },
882 perm);
883
884 return RESULT_SUCCESS;
885}
886
843ResultCode PageTable::InitializeMemoryLayout(VAddr start, VAddr end) { 887ResultCode PageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
844 block_manager = std::make_unique<MemoryBlockManager>(start, end); 888 block_manager = std::make_unique<MemoryBlockManager>(start, end);
845 889
diff --git a/src/core/hle/kernel/memory/page_table.h b/src/core/hle/kernel/memory/page_table.h
index 80384ab0f..a867aa050 100644
--- a/src/core/hle/kernel/memory/page_table.h
+++ b/src/core/hle/kernel/memory/page_table.h
@@ -53,6 +53,8 @@ public:
53 bool is_map_only, VAddr region_start, 53 bool is_map_only, VAddr region_start,
54 std::size_t region_num_pages, MemoryState state, 54 std::size_t region_num_pages, MemoryState state,
55 MemoryPermission perm, PAddr map_addr = 0); 55 MemoryPermission perm, PAddr map_addr = 0);
56 ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size);
57 ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size);
56 58
57 Common::PageTable& PageTableImpl() { 59 Common::PageTable& PageTableImpl() {
58 return page_table_impl; 60 return page_table_impl;
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index c67696757..0cd467110 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -36,22 +36,22 @@ std::shared_ptr<SharedMemory> SharedMemory::Create(
36} 36}
37 37
38ResultCode SharedMemory::Map(Process& target_process, VAddr address, std::size_t size, 38ResultCode SharedMemory::Map(Process& target_process, VAddr address, std::size_t size,
39 Memory::MemoryPermission permission) { 39 Memory::MemoryPermission permissions) {
40 const u64 page_count{(size + Memory::PageSize - 1) / Memory::PageSize}; 40 const u64 page_count{(size + Memory::PageSize - 1) / Memory::PageSize};
41 41
42 if (page_list.GetNumPages() != page_count) { 42 if (page_list.GetNumPages() != page_count) {
43 UNIMPLEMENTED_MSG("Page count does not match"); 43 UNIMPLEMENTED_MSG("Page count does not match");
44 } 44 }
45 45
46 Memory::MemoryPermission expected = 46 const Memory::MemoryPermission expected =
47 &target_process == owner_process ? owner_permission : user_permission; 47 &target_process == owner_process ? owner_permission : user_permission;
48 48
49 if (permission != expected) { 49 if (permissions != expected) {
50 UNIMPLEMENTED_MSG("Permission does not match"); 50 UNIMPLEMENTED_MSG("Permission does not match");
51 } 51 }
52 52
53 return target_process.PageTable().MapPages(address, page_list, Memory::MemoryState::Shared, 53 return target_process.PageTable().MapPages(address, page_list, Memory::MemoryState::Shared,
54 permission); 54 permissions);
55} 55}
56 56
57} // namespace Kernel 57} // namespace Kernel
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index cd16d6412..0ef87235c 100644
--- a/src/core/hle/kernel/shared_memory.h
+++ b/src/core/hle/kernel/shared_memory.h
@@ -51,7 +51,7 @@ public:
51 * @param permissions Memory block map permissions (specified by SVC field) 51 * @param permissions Memory block map permissions (specified by SVC field)
52 */ 52 */
53 ResultCode Map(Process& target_process, VAddr address, std::size_t size, 53 ResultCode Map(Process& target_process, VAddr address, std::size_t size,
54 Memory::MemoryPermission permission); 54 Memory::MemoryPermission permissions);
55 55
56 /** 56 /**
57 * Gets a pointer to the shared memory block 57 * Gets a pointer to the shared memory block
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 4134acf65..25b4a23b4 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -55,9 +55,6 @@ constexpr bool IsValidAddressRange(VAddr address, u64 size) {
55 return address + size > address; 55 return address + size > address;
56} 56}
57 57
58// 8 GiB
59constexpr u64 MAIN_MEMORY_SIZE = 0x200000000;
60
61// Helper function that performs the common sanity checks for svcMapMemory 58// Helper function that performs the common sanity checks for svcMapMemory
62// and svcUnmapMemory. This is doable, as both functions perform their sanitizing 59// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
63// in the same order. 60// in the same order.
@@ -1229,6 +1226,142 @@ static ResultCode QueryMemory32(Core::System& system, u32 memory_info_address,
1229 return QueryMemory(system, memory_info_address, page_info_address, query_address); 1226 return QueryMemory(system, memory_info_address, page_info_address, query_address);
1230} 1227}
1231 1228
1229static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
1230 u64 src_address, u64 size) {
1231 LOG_DEBUG(Kernel_SVC,
1232 "called. process_handle=0x{:08X}, dst_address=0x{:016X}, "
1233 "src_address=0x{:016X}, size=0x{:016X}",
1234 process_handle, dst_address, src_address, size);
1235
1236 if (!Common::Is4KBAligned(src_address)) {
1237 LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
1238 src_address);
1239 return ERR_INVALID_ADDRESS;
1240 }
1241
1242 if (!Common::Is4KBAligned(dst_address)) {
1243 LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
1244 dst_address);
1245 return ERR_INVALID_ADDRESS;
1246 }
1247
1248 if (size == 0 || !Common::Is4KBAligned(size)) {
1249 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size);
1250 return ERR_INVALID_SIZE;
1251 }
1252
1253 if (!IsValidAddressRange(dst_address, size)) {
1254 LOG_ERROR(Kernel_SVC,
1255 "Destination address range overflows the address space (dst_address=0x{:016X}, "
1256 "size=0x{:016X}).",
1257 dst_address, size);
1258 return ERR_INVALID_ADDRESS_STATE;
1259 }
1260
1261 if (!IsValidAddressRange(src_address, size)) {
1262 LOG_ERROR(Kernel_SVC,
1263 "Source address range overflows the address space (src_address=0x{:016X}, "
1264 "size=0x{:016X}).",
1265 src_address, size);
1266 return ERR_INVALID_ADDRESS_STATE;
1267 }
1268
1269 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1270 auto process = handle_table.Get<Process>(process_handle);
1271 if (!process) {
1272 LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
1273 process_handle);
1274 return ERR_INVALID_HANDLE;
1275 }
1276
1277 auto& page_table = process->PageTable();
1278 if (!page_table.IsInsideAddressSpace(src_address, size)) {
1279 LOG_ERROR(Kernel_SVC,
1280 "Source address range is not within the address space (src_address=0x{:016X}, "
1281 "size=0x{:016X}).",
1282 src_address, size);
1283 return ERR_INVALID_ADDRESS_STATE;
1284 }
1285
1286 if (!page_table.IsInsideASLRRegion(dst_address, size)) {
1287 LOG_ERROR(Kernel_SVC,
1288 "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
1289 "size=0x{:016X}).",
1290 dst_address, size);
1291 return ERR_INVALID_MEMORY_RANGE;
1292 }
1293
1294 return page_table.MapProcessCodeMemory(dst_address, src_address, size);
1295}
1296
1297static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle,
1298 u64 dst_address, u64 src_address, u64 size) {
1299 LOG_DEBUG(Kernel_SVC,
1300 "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, "
1301 "size=0x{:016X}",
1302 process_handle, dst_address, src_address, size);
1303
1304 if (!Common::Is4KBAligned(dst_address)) {
1305 LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
1306 dst_address);
1307 return ERR_INVALID_ADDRESS;
1308 }
1309
1310 if (!Common::Is4KBAligned(src_address)) {
1311 LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
1312 src_address);
1313 return ERR_INVALID_ADDRESS;
1314 }
1315
1316 if (size == 0 || Common::Is4KBAligned(size)) {
1317 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
1318 return ERR_INVALID_SIZE;
1319 }
1320
1321 if (!IsValidAddressRange(dst_address, size)) {
1322 LOG_ERROR(Kernel_SVC,
1323 "Destination address range overflows the address space (dst_address=0x{:016X}, "
1324 "size=0x{:016X}).",
1325 dst_address, size);
1326 return ERR_INVALID_ADDRESS_STATE;
1327 }
1328
1329 if (!IsValidAddressRange(src_address, size)) {
1330 LOG_ERROR(Kernel_SVC,
1331 "Source address range overflows the address space (src_address=0x{:016X}, "
1332 "size=0x{:016X}).",
1333 src_address, size);
1334 return ERR_INVALID_ADDRESS_STATE;
1335 }
1336
1337 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1338 auto process = handle_table.Get<Process>(process_handle);
1339 if (!process) {
1340 LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
1341 process_handle);
1342 return ERR_INVALID_HANDLE;
1343 }
1344
1345 auto& page_table = process->PageTable();
1346 if (!page_table.IsInsideAddressSpace(src_address, size)) {
1347 LOG_ERROR(Kernel_SVC,
1348 "Source address range is not within the address space (src_address=0x{:016X}, "
1349 "size=0x{:016X}).",
1350 src_address, size);
1351 return ERR_INVALID_ADDRESS_STATE;
1352 }
1353
1354 if (!page_table.IsInsideASLRRegion(dst_address, size)) {
1355 LOG_ERROR(Kernel_SVC,
1356 "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
1357 "size=0x{:016X}).",
1358 dst_address, size);
1359 return ERR_INVALID_MEMORY_RANGE;
1360 }
1361
1362 return page_table.UnmapProcessCodeMemory(dst_address, src_address, size);
1363}
1364
1232/// Exits the current process 1365/// Exits the current process
1233static void ExitProcess(Core::System& system) { 1366static void ExitProcess(Core::System& system) {
1234 auto* current_process = system.Kernel().CurrentProcess(); 1367 auto* current_process = system.Kernel().CurrentProcess();
@@ -2256,8 +2389,8 @@ static const FunctionDef SVC_Table_64[] = {
2256 {0x74, nullptr, "MapProcessMemory"}, 2389 {0x74, nullptr, "MapProcessMemory"},
2257 {0x75, nullptr, "UnmapProcessMemory"}, 2390 {0x75, nullptr, "UnmapProcessMemory"},
2258 {0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"}, 2391 {0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"},
2259 {0x77, nullptr, "MapProcessCodeMemory"}, 2392 {0x77, SvcWrap64<MapProcessCodeMemory>, "MapProcessCodeMemory"},
2260 {0x78, nullptr, "UnmapProcessCodeMemory"}, 2393 {0x78, SvcWrap64<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"},
2261 {0x79, nullptr, "CreateProcess"}, 2394 {0x79, nullptr, "CreateProcess"},
2262 {0x7A, nullptr, "StartProcess"}, 2395 {0x7A, nullptr, "StartProcess"},
2263 {0x7B, nullptr, "TerminateProcess"}, 2396 {0x7B, nullptr, "TerminateProcess"},
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 4c0451c01..a919750a6 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -150,8 +150,7 @@ static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context,
150 context.pc = entry_point; 150 context.pc = entry_point;
151 context.sp = stack_top; 151 context.sp = stack_top;
152 // TODO(merry): Perform a hardware test to determine the below value. 152 // TODO(merry): Perform a hardware test to determine the below value.
153 // AHP = 0, DN = 1, FTZ = 1, RMode = Round towards zero 153 context.fpcr = 0;
154 context.fpcr = 0x03C00000;
155} 154}
156 155
157ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::string name, 156ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::string name,
diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp
index b941c260b..ae88deda5 100644
--- a/src/core/hle/service/acc/acc_su.cpp
+++ b/src/core/hle/service/acc/acc_su.cpp
@@ -33,8 +33,10 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
33 {111, nullptr, "ClearSaveDataThumbnail"}, 33 {111, nullptr, "ClearSaveDataThumbnail"},
34 {112, nullptr, "LoadSaveDataThumbnail"}, 34 {112, nullptr, "LoadSaveDataThumbnail"},
35 {113, nullptr, "GetSaveDataThumbnailExistence"}, 35 {113, nullptr, "GetSaveDataThumbnailExistence"},
36 {120, nullptr, "ListOpenUsersInApplication"},
36 {130, nullptr, "ActivateOpenContextRetention"}, 37 {130, nullptr, "ActivateOpenContextRetention"},
37 {140, nullptr, "ListQualifiedUsers"}, 38 {140, nullptr, "ListQualifiedUsers"},
39 {150, nullptr, "AuthenticateApplicationAsync"},
38 {190, nullptr, "GetUserLastOpenedApplication"}, 40 {190, nullptr, "GetUserLastOpenedApplication"},
39 {191, nullptr, "ActivateOpenContextHolder"}, 41 {191, nullptr, "ActivateOpenContextHolder"},
40 {200, nullptr, "BeginUserRegistration"}, 42 {200, nullptr, "BeginUserRegistration"},
diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp
index 858e91dde..2b9c11928 100644
--- a/src/core/hle/service/acc/acc_u1.cpp
+++ b/src/core/hle/service/acc/acc_u1.cpp
@@ -35,6 +35,7 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
35 {113, nullptr, "GetSaveDataThumbnailExistence"}, 35 {113, nullptr, "GetSaveDataThumbnailExistence"},
36 {130, nullptr, "ActivateOpenContextRetention"}, 36 {130, nullptr, "ActivateOpenContextRetention"},
37 {140, nullptr, "ListQualifiedUsers"}, 37 {140, nullptr, "ListQualifiedUsers"},
38 {150, nullptr, "AuthenticateApplicationAsync"},
38 {190, nullptr, "GetUserLastOpenedApplication"}, 39 {190, nullptr, "GetUserLastOpenedApplication"},
39 {191, nullptr, "ActivateOpenContextHolder"}, 40 {191, nullptr, "ActivateOpenContextHolder"},
40 {997, nullptr, "DebugInvalidateTokenCacheForUser"}, 41 {997, nullptr, "DebugInvalidateTokenCacheForUser"},
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 3ece2cf3c..bee4a9d3f 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -235,6 +235,7 @@ IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} {
235 {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"}, 235 {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"},
236 {40, nullptr, "GetAppletResourceUsageInfo"}, 236 {40, nullptr, "GetAppletResourceUsageInfo"},
237 {100, nullptr, "SetCpuBoostModeForApplet"}, 237 {100, nullptr, "SetCpuBoostModeForApplet"},
238 {101, nullptr, "CancelCpuBoostModeForApplet"},
238 {110, nullptr, "PushToAppletBoundChannelForDebug"}, 239 {110, nullptr, "PushToAppletBoundChannelForDebug"},
239 {111, nullptr, "TryPopFromAppletBoundChannelForDebug"}, 240 {111, nullptr, "TryPopFromAppletBoundChannelForDebug"},
240 {120, nullptr, "AlarmSettingNotificationEnableAppEventReserve"}, 241 {120, nullptr, "AlarmSettingNotificationEnableAppEventReserve"},
@@ -277,6 +278,8 @@ ISelfController::ISelfController(Core::System& system,
277 {41, nullptr, "IsSystemBufferSharingEnabled"}, 278 {41, nullptr, "IsSystemBufferSharingEnabled"},
278 {42, nullptr, "GetSystemSharedLayerHandle"}, 279 {42, nullptr, "GetSystemSharedLayerHandle"},
279 {43, nullptr, "GetSystemSharedBufferHandle"}, 280 {43, nullptr, "GetSystemSharedBufferHandle"},
281 {44, nullptr, "CreateManagedDisplaySeparableLayer"},
282 {45, nullptr, "SetManagedDisplayLayerSeparationMode"},
280 {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"}, 283 {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"},
281 {51, nullptr, "ApproveToDisplay"}, 284 {51, nullptr, "ApproveToDisplay"},
282 {60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"}, 285 {60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"},
@@ -623,11 +626,15 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system,
623 {64, nullptr, "SetTvPowerStateMatchingMode"}, 626 {64, nullptr, "SetTvPowerStateMatchingMode"},
624 {65, nullptr, "GetApplicationIdByContentActionName"}, 627 {65, nullptr, "GetApplicationIdByContentActionName"},
625 {66, &ICommonStateGetter::SetCpuBoostMode, "SetCpuBoostMode"}, 628 {66, &ICommonStateGetter::SetCpuBoostMode, "SetCpuBoostMode"},
629 {67, nullptr, "CancelCpuBoostMode"},
626 {80, nullptr, "PerformSystemButtonPressingIfInFocus"}, 630 {80, nullptr, "PerformSystemButtonPressingIfInFocus"},
627 {90, nullptr, "SetPerformanceConfigurationChangedNotification"}, 631 {90, nullptr, "SetPerformanceConfigurationChangedNotification"},
628 {91, nullptr, "GetCurrentPerformanceConfiguration"}, 632 {91, nullptr, "GetCurrentPerformanceConfiguration"},
633 {100, nullptr, "SetHandlingHomeButtonShortPressedEnabled"},
629 {200, nullptr, "GetOperationModeSystemInfo"}, 634 {200, nullptr, "GetOperationModeSystemInfo"},
630 {300, nullptr, "GetSettingsPlatformRegion"}, 635 {300, nullptr, "GetSettingsPlatformRegion"},
636 {400, nullptr, "ActivateMigrationService"},
637 {401, nullptr, "DeactivateMigrationService"},
631 }; 638 };
632 // clang-format on 639 // clang-format on
633 640
@@ -835,6 +842,7 @@ public:
835 {25, nullptr, "Terminate"}, 842 {25, nullptr, "Terminate"},
836 {30, &ILibraryAppletAccessor::GetResult, "GetResult"}, 843 {30, &ILibraryAppletAccessor::GetResult, "GetResult"},
837 {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"}, 844 {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"},
845 {60, nullptr, "PresetLibraryAppletGpuTimeSliceZero"},
838 {100, &ILibraryAppletAccessor::PushInData, "PushInData"}, 846 {100, &ILibraryAppletAccessor::PushInData, "PushInData"},
839 {101, &ILibraryAppletAccessor::PopOutData, "PopOutData"}, 847 {101, &ILibraryAppletAccessor::PopOutData, "PopOutData"},
840 {102, nullptr, "PushExtraStorage"}, 848 {102, nullptr, "PushExtraStorage"},
@@ -1139,6 +1147,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
1139 {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"}, 1147 {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"},
1140 {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"}, 1148 {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"},
1141 {33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"}, 1149 {33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"},
1150 {34, nullptr, "SelectApplicationLicense"},
1142 {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"}, 1151 {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"},
1143 {50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"}, 1152 {50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"},
1144 {60, nullptr, "SetMediaPlaybackStateForApplication"}, 1153 {60, nullptr, "SetMediaPlaybackStateForApplication"},
@@ -1148,6 +1157,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
1148 {68, nullptr, "RequestFlushGamePlayingMovieForDebug"}, 1157 {68, nullptr, "RequestFlushGamePlayingMovieForDebug"},
1149 {70, nullptr, "RequestToShutdown"}, 1158 {70, nullptr, "RequestToShutdown"},
1150 {71, nullptr, "RequestToReboot"}, 1159 {71, nullptr, "RequestToReboot"},
1160 {72, nullptr, "RequestToSleep"},
1151 {80, nullptr, "ExitAndRequestToShowThanksMessage"}, 1161 {80, nullptr, "ExitAndRequestToShowThanksMessage"},
1152 {90, &IApplicationFunctions::EnableApplicationCrashReport, "EnableApplicationCrashReport"}, 1162 {90, &IApplicationFunctions::EnableApplicationCrashReport, "EnableApplicationCrashReport"},
1153 {100, &IApplicationFunctions::InitializeApplicationCopyrightFrameBuffer, "InitializeApplicationCopyrightFrameBuffer"}, 1163 {100, &IApplicationFunctions::InitializeApplicationCopyrightFrameBuffer, "InitializeApplicationCopyrightFrameBuffer"},
diff --git a/src/core/hle/service/audio/audctl.cpp b/src/core/hle/service/audio/audctl.cpp
index 9e08e5346..6ddb547fb 100644
--- a/src/core/hle/service/audio/audctl.cpp
+++ b/src/core/hle/service/audio/audctl.cpp
@@ -39,6 +39,8 @@ AudCtl::AudCtl() : ServiceFramework{"audctl"} {
39 {25, nullptr, "GetAudioVolumeDataForPlayReport"}, 39 {25, nullptr, "GetAudioVolumeDataForPlayReport"},
40 {26, nullptr, "UpdateHeadphoneSettings"}, 40 {26, nullptr, "UpdateHeadphoneSettings"},
41 {27, nullptr, "SetVolumeMappingTableForDev"}, 41 {27, nullptr, "SetVolumeMappingTableForDev"},
42 {28, nullptr, "GetAudioOutputChannelCountForPlayReport"},
43 {29, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"},
42 }; 44 };
43 // clang-format on 45 // clang-format on
44 46
diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp
index f589864ee..5febe8fc1 100644
--- a/src/core/hle/service/bcat/backend/boxcat.cpp
+++ b/src/core/hle/service/bcat/backend/boxcat.cpp
@@ -18,6 +18,7 @@
18#include "core/hle/service/bcat/backend/boxcat.h" 18#include "core/hle/service/bcat/backend/boxcat.h"
19#include "core/settings.h" 19#include "core/settings.h"
20 20
21namespace Service::BCAT {
21namespace { 22namespace {
22 23
23// Prevents conflicts with windows macro called CreateFile 24// Prevents conflicts with windows macro called CreateFile
@@ -30,10 +31,6 @@ bool VfsDeleteFileWrap(FileSys::VirtualDir dir, std::string_view name) {
30 return dir->DeleteFile(name); 31 return dir->DeleteFile(name);
31} 32}
32 33
33} // Anonymous namespace
34
35namespace Service::BCAT {
36
37constexpr ResultCode ERROR_GENERAL_BCAT_FAILURE{ErrorModule::BCAT, 1}; 34constexpr ResultCode ERROR_GENERAL_BCAT_FAILURE{ErrorModule::BCAT, 1};
38 35
39constexpr char BOXCAT_HOSTNAME[] = "api.yuzu-emu.org"; 36constexpr char BOXCAT_HOSTNAME[] = "api.yuzu-emu.org";
@@ -90,8 +87,6 @@ constexpr u32 PORT = 443;
90constexpr u32 TIMEOUT_SECONDS = 30; 87constexpr u32 TIMEOUT_SECONDS = 30;
91[[maybe_unused]] constexpr u64 VFS_COPY_BLOCK_SIZE = 1ULL << 24; // 4MB 88[[maybe_unused]] constexpr u64 VFS_COPY_BLOCK_SIZE = 1ULL << 24; // 4MB
92 89
93namespace {
94
95std::string GetBINFilePath(u64 title_id) { 90std::string GetBINFilePath(u64 title_id) {
96 return fmt::format("{}bcat/{:016X}/launchparam.bin", 91 return fmt::format("{}bcat/{:016X}/launchparam.bin",
97 FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id); 92 FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id);
diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp
index 7ada67130..34aba7a27 100644
--- a/src/core/hle/service/bcat/module.cpp
+++ b/src/core/hle/service/bcat/module.cpp
@@ -141,6 +141,7 @@ public:
141 {20301, nullptr, "RequestSuspendDeliveryTask"}, 141 {20301, nullptr, "RequestSuspendDeliveryTask"},
142 {20400, nullptr, "RegisterSystemApplicationDeliveryTask"}, 142 {20400, nullptr, "RegisterSystemApplicationDeliveryTask"},
143 {20401, nullptr, "UnregisterSystemApplicationDeliveryTask"}, 143 {20401, nullptr, "UnregisterSystemApplicationDeliveryTask"},
144 {20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"},
144 {30100, &IBcatService::SetPassphrase, "SetPassphrase"}, 145 {30100, &IBcatService::SetPassphrase, "SetPassphrase"},
145 {30200, nullptr, "RegisterBackgroundDeliveryTask"}, 146 {30200, nullptr, "RegisterBackgroundDeliveryTask"},
146 {30201, nullptr, "UnregisterBackgroundDeliveryTask"}, 147 {30201, nullptr, "UnregisterBackgroundDeliveryTask"},
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp
index 86f36915a..f8e9df4b1 100644
--- a/src/core/hle/service/es/es.cpp
+++ b/src/core/hle/service/es/es.cpp
@@ -4,6 +4,7 @@
4 4
5#include "core/crypto/key_manager.h" 5#include "core/crypto/key_manager.h"
6#include "core/hle/ipc_helpers.h" 6#include "core/hle/ipc_helpers.h"
7#include "core/hle/service/es/es.h"
7#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
8 9
9namespace Service::ES { 10namespace Service::ES {
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index 61045c75c..6b9b4f3b9 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -697,12 +697,14 @@ FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter)
697 {68, nullptr, "OpenSaveDataInfoReaderBySaveDataFilter"}, 697 {68, nullptr, "OpenSaveDataInfoReaderBySaveDataFilter"},
698 {69, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataAttribute"}, 698 {69, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataAttribute"},
699 {70, nullptr, "WriteSaveDataFileSystemExtraDataBySaveDataAttribute"}, 699 {70, nullptr, "WriteSaveDataFileSystemExtraDataBySaveDataAttribute"},
700 {71, nullptr, "ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute"},
700 {80, nullptr, "OpenSaveDataMetaFile"}, 701 {80, nullptr, "OpenSaveDataMetaFile"},
701 {81, nullptr, "OpenSaveDataTransferManager"}, 702 {81, nullptr, "OpenSaveDataTransferManager"},
702 {82, nullptr, "OpenSaveDataTransferManagerVersion2"}, 703 {82, nullptr, "OpenSaveDataTransferManagerVersion2"},
703 {83, nullptr, "OpenSaveDataTransferProhibiterForCloudBackUp"}, 704 {83, nullptr, "OpenSaveDataTransferProhibiterForCloudBackUp"},
704 {84, nullptr, "ListApplicationAccessibleSaveDataOwnerId"}, 705 {84, nullptr, "ListApplicationAccessibleSaveDataOwnerId"},
705 {85, nullptr, "OpenSaveDataTransferManagerForSaveDataRepair"}, 706 {85, nullptr, "OpenSaveDataTransferManagerForSaveDataRepair"},
707 {86, nullptr, "OpenSaveDataMover"},
706 {100, nullptr, "OpenImageDirectoryFileSystem"}, 708 {100, nullptr, "OpenImageDirectoryFileSystem"},
707 {110, nullptr, "OpenContentStorageFileSystem"}, 709 {110, nullptr, "OpenContentStorageFileSystem"},
708 {120, nullptr, "OpenCloudBackupWorkStorageFileSystem"}, 710 {120, nullptr, "OpenCloudBackupWorkStorageFileSystem"},
@@ -762,9 +764,11 @@ FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter)
762 {1011, &FSP_SRV::GetAccessLogVersionInfo, "GetAccessLogVersionInfo"}, 764 {1011, &FSP_SRV::GetAccessLogVersionInfo, "GetAccessLogVersionInfo"},
763 {1012, nullptr, "GetFsStackUsage"}, 765 {1012, nullptr, "GetFsStackUsage"},
764 {1013, nullptr, "UnsetSaveDataRootPath"}, 766 {1013, nullptr, "UnsetSaveDataRootPath"},
767 {1014, nullptr, "OutputMultiProgramTagAccessLog"},
765 {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"}, 768 {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"},
766 {1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"}, 769 {1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"},
767 {1200, nullptr, "OpenMultiCommitManager"}, 770 {1200, nullptr, "OpenMultiCommitManager"},
771 {1300, nullptr, "OpenBisWiper"},
768 }; 772 };
769 // clang-format on 773 // clang-format on
770 RegisterHandlers(functions); 774 RegisterHandlers(functions);
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index 7938b4b80..68f259b70 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -96,6 +96,7 @@ public:
96 {30830, nullptr, "ClearPlayLog"}, 96 {30830, nullptr, "ClearPlayLog"},
97 {30900, nullptr, "SendFriendInvitation"}, 97 {30900, nullptr, "SendFriendInvitation"},
98 {30910, nullptr, "ReadFriendInvitation"}, 98 {30910, nullptr, "ReadFriendInvitation"},
99 {30911, nullptr, "ReadAllFriendInvitations"},
99 {49900, nullptr, "DeleteNetworkServiceAccountCache"}, 100 {49900, nullptr, "DeleteNetworkServiceAccountCache"},
100 }; 101 };
101 // clang-format on 102 // clang-format on
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index d6031a987..5559587e3 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -233,7 +233,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
233 {302, nullptr, "StopConsoleSixAxisSensor"}, 233 {302, nullptr, "StopConsoleSixAxisSensor"},
234 {303, nullptr, "ActivateSevenSixAxisSensor"}, 234 {303, nullptr, "ActivateSevenSixAxisSensor"},
235 {304, nullptr, "StartSevenSixAxisSensor"}, 235 {304, nullptr, "StartSevenSixAxisSensor"},
236 {305, nullptr, "StopSevenSixAxisSensor"}, 236 {305, &Hid::StopSevenSixAxisSensor, "StopSevenSixAxisSensor"},
237 {306, &Hid::InitializeSevenSixAxisSensor, "InitializeSevenSixAxisSensor"}, 237 {306, &Hid::InitializeSevenSixAxisSensor, "InitializeSevenSixAxisSensor"},
238 {307, nullptr, "FinalizeSevenSixAxisSensor"}, 238 {307, nullptr, "FinalizeSevenSixAxisSensor"},
239 {308, nullptr, "SetSevenSixAxisSensorFusionStrength"}, 239 {308, nullptr, "SetSevenSixAxisSensorFusionStrength"},
@@ -282,6 +282,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
282 {1001, nullptr, "GetNpadCommunicationMode"}, 282 {1001, nullptr, "GetNpadCommunicationMode"},
283 {1002, nullptr, "SetTouchScreenConfiguration"}, 283 {1002, nullptr, "SetTouchScreenConfiguration"},
284 {1003, nullptr, "IsFirmwareUpdateNeededForNotification"}, 284 {1003, nullptr, "IsFirmwareUpdateNeededForNotification"},
285 {2000, nullptr, "ActivateDigitizer"},
285 }; 286 };
286 // clang-format on 287 // clang-format on
287 288
@@ -852,6 +853,17 @@ void Hid::SetPalmaBoostMode(Kernel::HLERequestContext& ctx) {
852 rb.Push(RESULT_SUCCESS); 853 rb.Push(RESULT_SUCCESS);
853} 854}
854 855
856void Hid::StopSevenSixAxisSensor(Kernel::HLERequestContext& ctx) {
857 IPC::RequestParser rp{ctx};
858 const auto applet_resource_user_id{rp.Pop<u64>()};
859
860 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
861 applet_resource_user_id);
862
863 IPC::ResponseBuilder rb{ctx, 2};
864 rb.Push(RESULT_SUCCESS);
865}
866
855void Hid::InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx) { 867void Hid::InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx) {
856 LOG_WARNING(Service_HID, "(STUBBED) called"); 868 LOG_WARNING(Service_HID, "(STUBBED) called");
857 869
@@ -870,6 +882,7 @@ public:
870 {10, nullptr, "DeactivateTouchScreen"}, 882 {10, nullptr, "DeactivateTouchScreen"},
871 {11, nullptr, "SetTouchScreenAutoPilotState"}, 883 {11, nullptr, "SetTouchScreenAutoPilotState"},
872 {12, nullptr, "UnsetTouchScreenAutoPilotState"}, 884 {12, nullptr, "UnsetTouchScreenAutoPilotState"},
885 {13, nullptr, "GetTouchScreenConfiguration"},
873 {20, nullptr, "DeactivateMouse"}, 886 {20, nullptr, "DeactivateMouse"},
874 {21, nullptr, "SetMouseAutoPilotState"}, 887 {21, nullptr, "SetMouseAutoPilotState"},
875 {22, nullptr, "UnsetMouseAutoPilotState"}, 888 {22, nullptr, "UnsetMouseAutoPilotState"},
@@ -879,7 +892,9 @@ public:
879 {50, nullptr, "DeactivateXpad"}, 892 {50, nullptr, "DeactivateXpad"},
880 {51, nullptr, "SetXpadAutoPilotState"}, 893 {51, nullptr, "SetXpadAutoPilotState"},
881 {52, nullptr, "UnsetXpadAutoPilotState"}, 894 {52, nullptr, "UnsetXpadAutoPilotState"},
882 {60, nullptr, "DeactivateJoyXpad"}, 895 {60, nullptr, "ClearNpadSystemCommonPolicy"},
896 {61, nullptr, "DeactivateNpad"},
897 {62, nullptr, "ForceDisconnectNpad"},
883 {91, nullptr, "DeactivateGesture"}, 898 {91, nullptr, "DeactivateGesture"},
884 {110, nullptr, "DeactivateHomeButton"}, 899 {110, nullptr, "DeactivateHomeButton"},
885 {111, nullptr, "SetHomeButtonAutoPilotState"}, 900 {111, nullptr, "SetHomeButtonAutoPilotState"},
@@ -899,6 +914,15 @@ public:
899 {141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"}, 914 {141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"},
900 {142, nullptr, "DeactivateSevenSixAxisSensor"}, 915 {142, nullptr, "DeactivateSevenSixAxisSensor"},
901 {143, nullptr, "GetConsoleSixAxisSensorCountStates"}, 916 {143, nullptr, "GetConsoleSixAxisSensorCountStates"},
917 {144, nullptr, "GetAccelerometerFsr"},
918 {145, nullptr, "SetAccelerometerFsr"},
919 {146, nullptr, "GetAccelerometerOdr"},
920 {147, nullptr, "SetAccelerometerOdr"},
921 {148, nullptr, "GetGyroscopeFsr"},
922 {149, nullptr, "SetGyroscopeFsr"},
923 {150, nullptr, "GetGyroscopeOdr"},
924 {151, nullptr, "SetGyroscopeOdr"},
925 {152, nullptr, "GetWhoAmI"},
902 {201, nullptr, "ActivateFirmwareUpdate"}, 926 {201, nullptr, "ActivateFirmwareUpdate"},
903 {202, nullptr, "DeactivateFirmwareUpdate"}, 927 {202, nullptr, "DeactivateFirmwareUpdate"},
904 {203, nullptr, "StartFirmwareUpdate"}, 928 {203, nullptr, "StartFirmwareUpdate"},
@@ -927,6 +951,17 @@ public:
927 {233, nullptr, "ClearPairingInfo"}, 951 {233, nullptr, "ClearPairingInfo"},
928 {234, nullptr, "GetUniquePadDeviceTypeSetInternal"}, 952 {234, nullptr, "GetUniquePadDeviceTypeSetInternal"},
929 {235, nullptr, "EnableAnalogStickPower"}, 953 {235, nullptr, "EnableAnalogStickPower"},
954 {236, nullptr, "RequestKuinaUartClockCal"},
955 {237, nullptr, "GetKuinaUartClockCal"},
956 {238, nullptr, "SetKuinaUartClockTrim"},
957 {239, nullptr, "KuinaLoopbackTest"},
958 {240, nullptr, "RequestBatteryVoltage"},
959 {241, nullptr, "GetBatteryVoltage"},
960 {242, nullptr, "GetUniquePadPowerInfo"},
961 {243, nullptr, "RebootUniquePad"},
962 {244, nullptr, "RequestKuinaFirmwareVersion"},
963 {245, nullptr, "GetKuinaFirmwareVersion"},
964 {246, nullptr, "GetVidPid"},
930 {301, nullptr, "GetAbstractedPadHandles"}, 965 {301, nullptr, "GetAbstractedPadHandles"},
931 {302, nullptr, "GetAbstractedPadState"}, 966 {302, nullptr, "GetAbstractedPadState"},
932 {303, nullptr, "GetAbstractedPadsState"}, 967 {303, nullptr, "GetAbstractedPadsState"},
@@ -945,6 +980,17 @@ public:
945 {350, nullptr, "AddRegisteredDevice"}, 980 {350, nullptr, "AddRegisteredDevice"},
946 {400, nullptr, "DisableExternalMcuOnNxDevice"}, 981 {400, nullptr, "DisableExternalMcuOnNxDevice"},
947 {401, nullptr, "DisableRailDeviceFiltering"}, 982 {401, nullptr, "DisableRailDeviceFiltering"},
983 {402, nullptr, "EnableWiredPairing"},
984 {403, nullptr, "EnableShipmentModeAutoClear"},
985 {500, nullptr, "SetFactoryInt"},
986 {501, nullptr, "IsFactoryBootEnabled"},
987 {550, nullptr, "SetAnalogStickModelDataTemporarily"},
988 {551, nullptr, "GetAnalogStickModelData"},
989 {552, nullptr, "ResetAnalogStickModelData"},
990 {600, nullptr, "ConvertPadState"},
991 {2000, nullptr, "DeactivateDigitizer"},
992 {2001, nullptr, "SetDigitizerAutoPilotState"},
993 {2002, nullptr, "UnsetDigitizerAutoPilotState"},
948 }; 994 };
949 // clang-format on 995 // clang-format on
950 996
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 039c38b58..23552efb1 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -128,6 +128,7 @@ private:
128 void StopSixAxisSensor(Kernel::HLERequestContext& ctx); 128 void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
129 void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx); 129 void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx);
130 void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); 130 void SetPalmaBoostMode(Kernel::HLERequestContext& ctx);
131 void StopSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
131 void InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx); 132 void InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
132 133
133 std::shared_ptr<IAppletResource> applet_resource; 134 std::shared_ptr<IAppletResource> applet_resource;
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 0cde7a557..6ad3be1b3 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -116,6 +116,7 @@ public:
116 {1, nullptr, "GetProgramInfo"}, 116 {1, nullptr, "GetProgramInfo"},
117 {2, nullptr, "RegisterTitle"}, 117 {2, nullptr, "RegisterTitle"},
118 {3, nullptr, "UnregisterTitle"}, 118 {3, nullptr, "UnregisterTitle"},
119 {4, nullptr, "SetEnabledProgramVerification"},
119 }; 120 };
120 // clang-format on 121 // clang-format on
121 122
diff --git a/src/core/hle/service/ncm/ncm.cpp b/src/core/hle/service/ncm/ncm.cpp
index 89e283ca5..ec9aae04a 100644
--- a/src/core/hle/service/ncm/ncm.cpp
+++ b/src/core/hle/service/ncm/ncm.cpp
@@ -122,6 +122,7 @@ public:
122 {11, nullptr, "ActivateContentMetaDatabase"}, 122 {11, nullptr, "ActivateContentMetaDatabase"},
123 {12, nullptr, "InactivateContentMetaDatabase"}, 123 {12, nullptr, "InactivateContentMetaDatabase"},
124 {13, nullptr, "InvalidateRightsIdCache"}, 124 {13, nullptr, "InvalidateRightsIdCache"},
125 {14, nullptr, "GetMemoryReport"},
125 }; 126 };
126 // clang-format on 127 // clang-format on
127 128
diff --git a/src/core/hle/service/npns/npns.cpp b/src/core/hle/service/npns/npns.cpp
index aa171473b..f38d01084 100644
--- a/src/core/hle/service/npns/npns.cpp
+++ b/src/core/hle/service/npns/npns.cpp
@@ -48,6 +48,8 @@ public:
48 {151, nullptr, "GetStateWithHandover"}, 48 {151, nullptr, "GetStateWithHandover"},
49 {152, nullptr, "GetStateChangeEventWithHandover"}, 49 {152, nullptr, "GetStateChangeEventWithHandover"},
50 {153, nullptr, "GetDropEventWithHandover"}, 50 {153, nullptr, "GetDropEventWithHandover"},
51 {161, nullptr, "GetRequestChangeStateCancelEvent"},
52 {162, nullptr, "RequestChangeStateForceTimedWithCancelEvent"},
51 {201, nullptr, "RequestChangeStateForceTimed"}, 53 {201, nullptr, "RequestChangeStateForceTimed"},
52 {202, nullptr, "RequestChangeStateForceAsync"}, 54 {202, nullptr, "RequestChangeStateForceAsync"},
53 }; 55 };
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index fdab3cf78..8fb88990e 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -110,6 +110,10 @@ IApplicationManagerInterface::IApplicationManagerInterface()
110 {100, nullptr, "ResetToFactorySettings"}, 110 {100, nullptr, "ResetToFactorySettings"},
111 {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"}, 111 {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
112 {102, nullptr, "ResetToFactorySettingsForRefurbishment"}, 112 {102, nullptr, "ResetToFactorySettingsForRefurbishment"},
113 {103, nullptr, "ResetToFactorySettingsWithPlatformRegion"},
114 {104, nullptr, "ResetToFactorySettingsWithPlatformRegionAuthentication"},
115 {105, nullptr, "RequestResetToFactorySettingsSecurely"},
116 {106, nullptr, "RequestResetToFactorySettingsWithPlatformRegionAuthenticationSecurely"},
113 {200, nullptr, "CalculateUserSaveDataStatistics"}, 117 {200, nullptr, "CalculateUserSaveDataStatistics"},
114 {201, nullptr, "DeleteUserSaveDataAll"}, 118 {201, nullptr, "DeleteUserSaveDataAll"},
115 {210, nullptr, "DeleteUserSystemSaveData"}, 119 {210, nullptr, "DeleteUserSystemSaveData"},
@@ -191,6 +195,9 @@ IApplicationManagerInterface::IApplicationManagerInterface()
191 {1307, nullptr, "TryDeleteRunningApplicationContentEntities"}, 195 {1307, nullptr, "TryDeleteRunningApplicationContentEntities"},
192 {1308, nullptr, "DeleteApplicationCompletelyForDebug"}, 196 {1308, nullptr, "DeleteApplicationCompletelyForDebug"},
193 {1309, nullptr, "CleanupUnavailableAddOnContents"}, 197 {1309, nullptr, "CleanupUnavailableAddOnContents"},
198 {1310, nullptr, "RequestMoveApplicationEntity"},
199 {1311, nullptr, "EstimateSizeToMove"},
200 {1312, nullptr, "HasMovableEntity"},
194 {1400, nullptr, "PrepareShutdown"}, 201 {1400, nullptr, "PrepareShutdown"},
195 {1500, nullptr, "FormatSdCard"}, 202 {1500, nullptr, "FormatSdCard"},
196 {1501, nullptr, "NeedsSystemUpdateToFormatSdCard"}, 203 {1501, nullptr, "NeedsSystemUpdateToFormatSdCard"},
@@ -241,7 +248,7 @@ IApplicationManagerInterface::IApplicationManagerInterface()
241 {2153, nullptr, "DeactivateRightsEnvironment"}, 248 {2153, nullptr, "DeactivateRightsEnvironment"},
242 {2154, nullptr, "ForceActivateRightsContextForExit"}, 249 {2154, nullptr, "ForceActivateRightsContextForExit"},
243 {2155, nullptr, "UpdateRightsEnvironmentStatus"}, 250 {2155, nullptr, "UpdateRightsEnvironmentStatus"},
244 {2156, nullptr, "CreateRightsEnvironmentForPreomia"}, 251 {2156, nullptr, "CreateRightsEnvironmentForMicroApplication"},
245 {2160, nullptr, "AddTargetApplicationToRightsEnvironment"}, 252 {2160, nullptr, "AddTargetApplicationToRightsEnvironment"},
246 {2161, nullptr, "SetUsersToRightsEnvironment"}, 253 {2161, nullptr, "SetUsersToRightsEnvironment"},
247 {2170, nullptr, "GetRightsEnvironmentStatus"}, 254 {2170, nullptr, "GetRightsEnvironmentStatus"},
@@ -258,6 +265,7 @@ IApplicationManagerInterface::IApplicationManagerInterface()
258 {2350, nullptr, "PerformAutoUpdateByApplicationId"}, 265 {2350, nullptr, "PerformAutoUpdateByApplicationId"},
259 {2351, nullptr, "RequestNoDownloadRightsErrorResolution"}, 266 {2351, nullptr, "RequestNoDownloadRightsErrorResolution"},
260 {2352, nullptr, "RequestResolveNoDownloadRightsError"}, 267 {2352, nullptr, "RequestResolveNoDownloadRightsError"},
268 {2353, nullptr, "GetApplicationDownloadTaskInfo"},
261 {2400, nullptr, "GetPromotionInfo"}, 269 {2400, nullptr, "GetPromotionInfo"},
262 {2401, nullptr, "CountPromotionInfo"}, 270 {2401, nullptr, "CountPromotionInfo"},
263 {2402, nullptr, "ListPromotionInfo"}, 271 {2402, nullptr, "ListPromotionInfo"},
@@ -266,9 +274,12 @@ IApplicationManagerInterface::IApplicationManagerInterface()
266 {2500, nullptr, "ConfirmAvailableTime"}, 274 {2500, nullptr, "ConfirmAvailableTime"},
267 {2510, nullptr, "CreateApplicationResource"}, 275 {2510, nullptr, "CreateApplicationResource"},
268 {2511, nullptr, "GetApplicationResource"}, 276 {2511, nullptr, "GetApplicationResource"},
269 {2513, nullptr, "LaunchPreomia"}, 277 {2513, nullptr, "LaunchMicroApplication"},
270 {2514, nullptr, "ClearTaskOfAsyncTaskManager"}, 278 {2514, nullptr, "ClearTaskOfAsyncTaskManager"},
279 {2515, nullptr, "CleanupAllPlaceHolderAndFragmentsIfNoTask"},
280 {2516, nullptr, "EnsureApplicationCertificate"},
271 {2800, nullptr, "GetApplicationIdOfPreomia"}, 281 {2800, nullptr, "GetApplicationIdOfPreomia"},
282 {9999, nullptr, "GetApplicationCertificate"},
272 }; 283 };
273 // clang-format on 284 // clang-format on
274 285
@@ -505,6 +516,10 @@ IFactoryResetInterface::IFactoryResetInterface::IFactoryResetInterface()
505 {100, nullptr, "ResetToFactorySettings"}, 516 {100, nullptr, "ResetToFactorySettings"},
506 {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"}, 517 {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
507 {102, nullptr, "ResetToFactorySettingsForRefurbishment"}, 518 {102, nullptr, "ResetToFactorySettingsForRefurbishment"},
519 {103, nullptr, "ResetToFactorySettingsWithPlatformRegion"},
520 {104, nullptr, "ResetToFactorySettingsWithPlatformRegionAuthentication"},
521 {105, nullptr, "RequestResetToFactorySettingsSecurely"},
522 {106, nullptr, "RequestResetToFactorySettingsWithPlatformRegionAuthenticationSecurely"},
508 }; 523 };
509 // clang-format on 524 // clang-format on
510 525
@@ -553,6 +568,9 @@ public:
553 {10, nullptr, "TerminateApplication2"}, 568 {10, nullptr, "TerminateApplication2"},
554 {11, nullptr, "GetRunningApplicationProcessId"}, 569 {11, nullptr, "GetRunningApplicationProcessId"},
555 {12, nullptr, "SetCurrentApplicationRightsEnvironmentCanBeActive"}, 570 {12, nullptr, "SetCurrentApplicationRightsEnvironmentCanBeActive"},
571 {13, nullptr, "CreateApplicationResourceForDevelop"},
572 {14, nullptr, "IsPreomiaForDevelop"},
573 {15, nullptr, "GetApplicationProgramIdFromHost"},
556 }; 574 };
557 // clang-format on 575 // clang-format on
558 576
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
index ab1746d28..6efdf1606 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/pl_u.cpp
@@ -164,6 +164,7 @@ PL_U::PL_U(Core::System& system)
164 {6, nullptr, "GetSharedFontInOrderOfPriorityForSystem"}, 164 {6, nullptr, "GetSharedFontInOrderOfPriorityForSystem"},
165 {100, nullptr, "RequestApplicationFunctionAuthorization"}, 165 {100, nullptr, "RequestApplicationFunctionAuthorization"},
166 {101, nullptr, "RequestApplicationFunctionAuthorizationForSystem"}, 166 {101, nullptr, "RequestApplicationFunctionAuthorizationForSystem"},
167 {102, nullptr, "RequestApplicationFunctionAuthorizationByApplicationId"},
167 {1000, nullptr, "LoadNgWordDataForPlatformRegionChina"}, 168 {1000, nullptr, "LoadNgWordDataForPlatformRegionChina"},
168 {1001, nullptr, "GetNgWordDataSizeForPlatformRegionChina"}, 169 {1001, nullptr, "GetNgWordDataSizeForPlatformRegionChina"},
169 }; 170 };
diff --git a/src/core/hle/service/pctl/module.cpp b/src/core/hle/service/pctl/module.cpp
index c75b4ee34..caf14ed61 100644
--- a/src/core/hle/service/pctl/module.cpp
+++ b/src/core/hle/service/pctl/module.cpp
@@ -31,6 +31,8 @@ public:
31 {1014, nullptr, "ConfirmPlayableApplicationVideoOld"}, 31 {1014, nullptr, "ConfirmPlayableApplicationVideoOld"},
32 {1015, nullptr, "ConfirmPlayableApplicationVideo"}, 32 {1015, nullptr, "ConfirmPlayableApplicationVideo"},
33 {1016, nullptr, "ConfirmShowNewsPermission"}, 33 {1016, nullptr, "ConfirmShowNewsPermission"},
34 {1017, nullptr, "EndFreeCommunication"},
35 {1018, nullptr, "IsFreeCommunicationAvailable"},
34 {1031, nullptr, "IsRestrictionEnabled"}, 36 {1031, nullptr, "IsRestrictionEnabled"},
35 {1032, nullptr, "GetSafetyLevel"}, 37 {1032, nullptr, "GetSafetyLevel"},
36 {1033, nullptr, "SetSafetyLevel"}, 38 {1033, nullptr, "SetSafetyLevel"},
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp
index 8f1be0e48..14309c679 100644
--- a/src/core/hle/service/prepo/prepo.cpp
+++ b/src/core/hle/service/prepo/prepo.cpp
@@ -21,8 +21,10 @@ public:
21 static const FunctionInfo functions[] = { 21 static const FunctionInfo functions[] = {
22 {10100, &PlayReport::SaveReport<Core::Reporter::PlayReportType::Old>, "SaveReportOld"}, 22 {10100, &PlayReport::SaveReport<Core::Reporter::PlayReportType::Old>, "SaveReportOld"},
23 {10101, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::Old>, "SaveReportWithUserOld"}, 23 {10101, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::Old>, "SaveReportWithUserOld"},
24 {10102, &PlayReport::SaveReport<Core::Reporter::PlayReportType::New>, "SaveReport"}, 24 {10102, &PlayReport::SaveReport<Core::Reporter::PlayReportType::Old2>, "SaveReportOld2"},
25 {10103, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::New>, "SaveReportWithUser"}, 25 {10103, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::Old2>, "SaveReportWithUserOld2"},
26 {10104, nullptr, "SaveReport"},
27 {10105, nullptr, "SaveReportWithUser"},
26 {10200, nullptr, "RequestImmediateTransmission"}, 28 {10200, nullptr, "RequestImmediateTransmission"},
27 {10300, nullptr, "GetTransmissionStatus"}, 29 {10300, nullptr, "GetTransmissionStatus"},
28 {10400, nullptr, "GetSystemSessionId"}, 30 {10400, nullptr, "GetSystemSessionId"},
@@ -35,8 +37,10 @@ public:
35 {30400, nullptr, "GetStatistics"}, 37 {30400, nullptr, "GetStatistics"},
36 {30401, nullptr, "GetThroughputHistory"}, 38 {30401, nullptr, "GetThroughputHistory"},
37 {30500, nullptr, "GetLastUploadError"}, 39 {30500, nullptr, "GetLastUploadError"},
40 {30600, nullptr, "GetApplicationUploadSummary"},
38 {40100, nullptr, "IsUserAgreementCheckEnabled"}, 41 {40100, nullptr, "IsUserAgreementCheckEnabled"},
39 {40101, nullptr, "SetUserAgreementCheckEnabled"}, 42 {40101, nullptr, "SetUserAgreementCheckEnabled"},
43 {50100, nullptr, "ReadAllApplicationReportFiles"},
40 {90100, nullptr, "ReadAllReportFiles"}, 44 {90100, nullptr, "ReadAllReportFiles"},
41 }; 45 };
42 // clang-format on 46 // clang-format on
@@ -51,7 +55,7 @@ private:
51 const auto process_id = rp.PopRaw<u64>(); 55 const auto process_id = rp.PopRaw<u64>();
52 56
53 std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)}; 57 std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)};
54 if (Type == Core::Reporter::PlayReportType::New) { 58 if constexpr (Type == Core::Reporter::PlayReportType::Old2) {
55 data.emplace_back(ctx.ReadBuffer(1)); 59 data.emplace_back(ctx.ReadBuffer(1));
56 } 60 }
57 61
@@ -71,7 +75,7 @@ private:
71 const auto user_id = rp.PopRaw<u128>(); 75 const auto user_id = rp.PopRaw<u128>();
72 const auto process_id = rp.PopRaw<u64>(); 76 const auto process_id = rp.PopRaw<u64>();
73 std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)}; 77 std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)};
74 if (Type == Core::Reporter::PlayReportType::New) { 78 if constexpr (Type == Core::Reporter::PlayReportType::Old2) {
75 data.emplace_back(ctx.ReadBuffer(1)); 79 data.emplace_back(ctx.ReadBuffer(1));
76 } 80 }
77 81
diff --git a/src/core/hle/service/set/set_cal.cpp b/src/core/hle/service/set/set_cal.cpp
index 1398a4a48..3fbfecc9e 100644
--- a/src/core/hle/service/set/set_cal.cpp
+++ b/src/core/hle/service/set/set_cal.cpp
@@ -50,6 +50,8 @@ SET_CAL::SET_CAL() : ServiceFramework("set:cal") {
50 {39, nullptr, "GetConsoleSixAxisSensorModuleType"}, 50 {39, nullptr, "GetConsoleSixAxisSensorModuleType"},
51 {40, nullptr, "GetConsoleSixAxisSensorHorizontalOffset"}, 51 {40, nullptr, "GetConsoleSixAxisSensorHorizontalOffset"},
52 {41, nullptr, "GetBatteryVersion"}, 52 {41, nullptr, "GetBatteryVersion"},
53 {42, nullptr, "GetDeviceId"},
54 {43, nullptr, "GetConsoleSixAxisSensorMountType"},
53 }; 55 };
54 // clang-format on 56 // clang-format on
55 57
diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp
index b7c9ea74b..8bd4c7e79 100644
--- a/src/core/hle/service/set/set_sys.cpp
+++ b/src/core/hle/service/set/set_sys.cpp
@@ -288,6 +288,18 @@ SET_SYS::SET_SYS() : ServiceFramework("set:sys") {
288 {186, nullptr, "GetMemoryUsageRateFlag"}, 288 {186, nullptr, "GetMemoryUsageRateFlag"},
289 {187, nullptr, "GetTouchScreenMode"}, 289 {187, nullptr, "GetTouchScreenMode"},
290 {188, nullptr, "SetTouchScreenMode"}, 290 {188, nullptr, "SetTouchScreenMode"},
291 {189, nullptr, "GetButtonConfigSettingsFull"},
292 {190, nullptr, "SetButtonConfigSettingsFull"},
293 {191, nullptr, "GetButtonConfigSettingsEmbedded"},
294 {192, nullptr, "SetButtonConfigSettingsEmbedded"},
295 {193, nullptr, "GetButtonConfigSettingsLeft"},
296 {194, nullptr, "SetButtonConfigSettingsLeft"},
297 {195, nullptr, "GetButtonConfigSettingsRight"},
298 {196, nullptr, "SetButtonConfigSettingsRight"},
299 {197, nullptr, "GetButtonConfigRegisteredSettingsEmbedded"},
300 {198, nullptr, "SetButtonConfigRegisteredSettingsEmbedded"},
301 {199, nullptr, "GetButtonConfigRegisteredSettings"},
302 {200, nullptr, "SetButtonConfigRegisteredSettings"},
291 }; 303 };
292 // clang-format on 304 // clang-format on
293 305
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index f67fab2f9..8d4952c0e 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -148,6 +148,7 @@ BSD::BSD(const char* name) : ServiceFramework(name) {
148 {30, nullptr, "SendMMsg"}, 148 {30, nullptr, "SendMMsg"},
149 {31, nullptr, "EventFd"}, 149 {31, nullptr, "EventFd"},
150 {32, nullptr, "RegisterResourceStatisticsName"}, 150 {32, nullptr, "RegisterResourceStatisticsName"},
151 {33, nullptr, "Initialize2"},
151 }; 152 };
152 // clang-format on 153 // clang-format on
153 154
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index e722886de..67f1bbcf3 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -20,8 +20,8 @@ namespace Service::Time {
20 20
21class ISystemClock final : public ServiceFramework<ISystemClock> { 21class ISystemClock final : public ServiceFramework<ISystemClock> {
22public: 22public:
23 ISystemClock(Clock::SystemClockCore& clock_core) 23 explicit ISystemClock(Clock::SystemClockCore& clock_core, Core::System& system)
24 : ServiceFramework("ISystemClock"), clock_core{clock_core} { 24 : ServiceFramework("ISystemClock"), clock_core{clock_core}, system{system} {
25 // clang-format off 25 // clang-format off
26 static const FunctionInfo functions[] = { 26 static const FunctionInfo functions[] = {
27 {0, &ISystemClock::GetCurrentTime, "GetCurrentTime"}, 27 {0, &ISystemClock::GetCurrentTime, "GetCurrentTime"},
@@ -46,9 +46,8 @@ private:
46 } 46 }
47 47
48 s64 posix_time{}; 48 s64 posix_time{};
49 if (const ResultCode result{ 49 if (const ResultCode result{clock_core.GetCurrentTime(system, posix_time)};
50 clock_core.GetCurrentTime(Core::System::GetInstance(), posix_time)}; 50 result.IsError()) {
51 result != RESULT_SUCCESS) {
52 IPC::ResponseBuilder rb{ctx, 2}; 51 IPC::ResponseBuilder rb{ctx, 2};
53 rb.Push(result); 52 rb.Push(result);
54 return; 53 return;
@@ -69,9 +68,8 @@ private:
69 } 68 }
70 69
71 Clock::SystemClockContext system_clock_context{}; 70 Clock::SystemClockContext system_clock_context{};
72 if (const ResultCode result{ 71 if (const ResultCode result{clock_core.GetClockContext(system, system_clock_context)};
73 clock_core.GetClockContext(Core::System::GetInstance(), system_clock_context)}; 72 result.IsError()) {
74 result != RESULT_SUCCESS) {
75 IPC::ResponseBuilder rb{ctx, 2}; 73 IPC::ResponseBuilder rb{ctx, 2};
76 rb.Push(result); 74 rb.Push(result);
77 return; 75 return;
@@ -83,12 +81,13 @@ private:
83 } 81 }
84 82
85 Clock::SystemClockCore& clock_core; 83 Clock::SystemClockCore& clock_core;
84 Core::System& system;
86}; 85};
87 86
88class ISteadyClock final : public ServiceFramework<ISteadyClock> { 87class ISteadyClock final : public ServiceFramework<ISteadyClock> {
89public: 88public:
90 ISteadyClock(Clock::SteadyClockCore& clock_core) 89 explicit ISteadyClock(Clock::SteadyClockCore& clock_core, Core::System& system)
91 : ServiceFramework("ISteadyClock"), clock_core{clock_core} { 90 : ServiceFramework("ISteadyClock"), clock_core{clock_core}, system{system} {
92 static const FunctionInfo functions[] = { 91 static const FunctionInfo functions[] = {
93 {0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"}, 92 {0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"},
94 }; 93 };
@@ -105,14 +104,14 @@ private:
105 return; 104 return;
106 } 105 }
107 106
108 const Clock::SteadyClockTimePoint time_point{ 107 const Clock::SteadyClockTimePoint time_point{clock_core.GetCurrentTimePoint(system)};
109 clock_core.GetCurrentTimePoint(Core::System::GetInstance())};
110 IPC::ResponseBuilder rb{ctx, (sizeof(Clock::SteadyClockTimePoint) / 4) + 2}; 108 IPC::ResponseBuilder rb{ctx, (sizeof(Clock::SteadyClockTimePoint) / 4) + 2};
111 rb.Push(RESULT_SUCCESS); 109 rb.Push(RESULT_SUCCESS);
112 rb.PushRaw(time_point); 110 rb.PushRaw(time_point);
113 } 111 }
114 112
115 Clock::SteadyClockCore& clock_core; 113 Clock::SteadyClockCore& clock_core;
114 Core::System& system;
116}; 115};
117 116
118ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal( 117ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
@@ -134,7 +133,7 @@ ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
134 } 133 }
135 134
136 const auto current_time_point{ 135 const auto current_time_point{
137 time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(Core::System::GetInstance())}; 136 time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system)};
138 if (const ResultCode result{Clock::ClockSnapshot::GetCurrentTime( 137 if (const ResultCode result{Clock::ClockSnapshot::GetCurrentTime(
139 clock_snapshot.user_time, current_time_point, clock_snapshot.user_context)}; 138 clock_snapshot.user_time, current_time_point, clock_snapshot.user_context)};
140 result != RESULT_SUCCESS) { 139 result != RESULT_SUCCESS) {
@@ -176,21 +175,24 @@ void Module::Interface::GetStandardUserSystemClock(Kernel::HLERequestContext& ct
176 LOG_DEBUG(Service_Time, "called"); 175 LOG_DEBUG(Service_Time, "called");
177 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 176 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
178 rb.Push(RESULT_SUCCESS); 177 rb.Push(RESULT_SUCCESS);
179 rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardUserSystemClockCore()); 178 rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardUserSystemClockCore(),
179 system);
180} 180}
181 181
182void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx) { 182void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx) {
183 LOG_DEBUG(Service_Time, "called"); 183 LOG_DEBUG(Service_Time, "called");
184 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 184 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
185 rb.Push(RESULT_SUCCESS); 185 rb.Push(RESULT_SUCCESS);
186 rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardNetworkSystemClockCore()); 186 rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardNetworkSystemClockCore(),
187 system);
187} 188}
188 189
189void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) { 190void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) {
190 LOG_DEBUG(Service_Time, "called"); 191 LOG_DEBUG(Service_Time, "called");
191 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 192 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
192 rb.Push(RESULT_SUCCESS); 193 rb.Push(RESULT_SUCCESS);
193 rb.PushIpcInterface<ISteadyClock>(module->GetTimeManager().GetStandardSteadyClockCore()); 194 rb.PushIpcInterface<ISteadyClock>(module->GetTimeManager().GetStandardSteadyClockCore(),
195 system);
194} 196}
195 197
196void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) { 198void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) {
@@ -204,7 +206,8 @@ void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& c
204 LOG_DEBUG(Service_Time, "called"); 206 LOG_DEBUG(Service_Time, "called");
205 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 207 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
206 rb.Push(RESULT_SUCCESS); 208 rb.Push(RESULT_SUCCESS);
207 rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardLocalSystemClockCore()); 209 rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardLocalSystemClockCore(),
210 system);
208} 211}
209 212
210void Module::Interface::IsStandardNetworkSystemClockAccuracySufficient( 213void Module::Interface::IsStandardNetworkSystemClockAccuracySufficient(
@@ -228,8 +231,7 @@ void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERe
228 231
229 IPC::RequestParser rp{ctx}; 232 IPC::RequestParser rp{ctx};
230 const auto context{rp.PopRaw<Clock::SystemClockContext>()}; 233 const auto context{rp.PopRaw<Clock::SystemClockContext>()};
231 const auto current_time_point{ 234 const auto current_time_point{steady_clock_core.GetCurrentTimePoint(system)};
232 steady_clock_core.GetCurrentTimePoint(Core::System::GetInstance())};
233 235
234 if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) { 236 if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) {
235 const auto ticks{Clock::TimeSpanType::FromTicks( 237 const auto ticks{Clock::TimeSpanType::FromTicks(
@@ -255,8 +257,8 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
255 Clock::SystemClockContext user_context{}; 257 Clock::SystemClockContext user_context{};
256 if (const ResultCode result{ 258 if (const ResultCode result{
257 module->GetTimeManager().GetStandardUserSystemClockCore().GetClockContext( 259 module->GetTimeManager().GetStandardUserSystemClockCore().GetClockContext(
258 Core::System::GetInstance(), user_context)}; 260 system, user_context)};
259 result != RESULT_SUCCESS) { 261 result.IsError()) {
260 IPC::ResponseBuilder rb{ctx, 2}; 262 IPC::ResponseBuilder rb{ctx, 2};
261 rb.Push(result); 263 rb.Push(result);
262 return; 264 return;
@@ -264,8 +266,8 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
264 Clock::SystemClockContext network_context{}; 266 Clock::SystemClockContext network_context{};
265 if (const ResultCode result{ 267 if (const ResultCode result{
266 module->GetTimeManager().GetStandardNetworkSystemClockCore().GetClockContext( 268 module->GetTimeManager().GetStandardNetworkSystemClockCore().GetClockContext(
267 Core::System::GetInstance(), network_context)}; 269 system, network_context)};
268 result != RESULT_SUCCESS) { 270 result.IsError()) {
269 IPC::ResponseBuilder rb{ctx, 2}; 271 IPC::ResponseBuilder rb{ctx, 2};
270 rb.Push(result); 272 rb.Push(result);
271 return; 273 return;
@@ -274,7 +276,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
274 Clock::ClockSnapshot clock_snapshot{}; 276 Clock::ClockSnapshot clock_snapshot{};
275 if (const ResultCode result{GetClockSnapshotFromSystemClockContextInternal( 277 if (const ResultCode result{GetClockSnapshotFromSystemClockContextInternal(
276 &ctx.GetThread(), user_context, network_context, type, clock_snapshot)}; 278 &ctx.GetThread(), user_context, network_context, type, clock_snapshot)};
277 result != RESULT_SUCCESS) { 279 result.IsError()) {
278 IPC::ResponseBuilder rb{ctx, 2}; 280 IPC::ResponseBuilder rb{ctx, 2};
279 rb.Push(result); 281 rb.Push(result);
280 return; 282 return;
diff --git a/src/core/reporter.h b/src/core/reporter.h
index 380941b1b..86d760cf0 100644
--- a/src/core/reporter.h
+++ b/src/core/reporter.h
@@ -56,6 +56,7 @@ public:
56 56
57 enum class PlayReportType { 57 enum class PlayReportType {
58 Old, 58 Old,
59 Old2,
59 New, 60 New,
60 System, 61 System,
61 }; 62 };
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index c1282cb80..cd6c257f5 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -92,7 +92,7 @@ void LogSettings() {
92 LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit); 92 LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit);
93 LogSetting("Renderer_FrameLimit", Settings::values.frame_limit); 93 LogSetting("Renderer_FrameLimit", Settings::values.frame_limit);
94 LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache); 94 LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache);
95 LogSetting("Renderer_UseAccurateGpuEmulation", Settings::values.use_accurate_gpu_emulation); 95 LogSetting("Renderer_GPUAccuracyLevel", Settings::values.gpu_accuracy);
96 LogSetting("Renderer_UseAsynchronousGpuEmulation", 96 LogSetting("Renderer_UseAsynchronousGpuEmulation",
97 Settings::values.use_asynchronous_gpu_emulation); 97 Settings::values.use_asynchronous_gpu_emulation);
98 LogSetting("Renderer_UseVsync", Settings::values.use_vsync); 98 LogSetting("Renderer_UseVsync", Settings::values.use_vsync);
@@ -109,4 +109,12 @@ void LogSettings() {
109 LogSetting("Services_BCATBoxcatLocal", Settings::values.bcat_boxcat_local); 109 LogSetting("Services_BCATBoxcatLocal", Settings::values.bcat_boxcat_local);
110} 110}
111 111
112bool IsGPULevelExtreme() {
113 return values.gpu_accuracy == GPUAccuracy::Extreme;
114}
115
116bool IsGPULevelHigh() {
117 return values.gpu_accuracy == GPUAccuracy::Extreme || values.gpu_accuracy == GPUAccuracy::High;
118}
119
112} // namespace Settings 120} // namespace Settings
diff --git a/src/core/settings.h b/src/core/settings.h
index c73d1c596..163900f0b 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -376,6 +376,12 @@ enum class RendererBackend {
376 Vulkan = 1, 376 Vulkan = 1,
377}; 377};
378 378
379enum class GPUAccuracy : u32 {
380 Normal = 0,
381 High = 1,
382 Extreme = 2,
383};
384
379struct Values { 385struct Values {
380 // System 386 // System
381 bool use_docked_mode; 387 bool use_docked_mode;
@@ -436,10 +442,11 @@ struct Values {
436 bool use_frame_limit; 442 bool use_frame_limit;
437 u16 frame_limit; 443 u16 frame_limit;
438 bool use_disk_shader_cache; 444 bool use_disk_shader_cache;
439 bool use_accurate_gpu_emulation; 445 GPUAccuracy gpu_accuracy;
440 bool use_asynchronous_gpu_emulation; 446 bool use_asynchronous_gpu_emulation;
441 bool use_vsync; 447 bool use_vsync;
442 bool force_30fps_mode; 448 bool force_30fps_mode;
449 bool use_fast_gpu_time;
443 450
444 float bg_red; 451 float bg_red;
445 float bg_green; 452 float bg_green;
@@ -480,6 +487,9 @@ struct Values {
480 std::map<u64, std::vector<std::string>> disabled_addons; 487 std::map<u64, std::vector<std::string>> disabled_addons;
481} extern values; 488} extern values;
482 489
490bool IsGPULevelExtreme();
491bool IsGPULevelHigh();
492
483void Apply(); 493void Apply();
484void LogSettings(); 494void LogSettings();
485} // namespace Settings 495} // namespace Settings
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index fd5a3ee9f..1c3b03a1c 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -56,6 +56,18 @@ static const char* TranslateRenderer(Settings::RendererBackend backend) {
56 return "Unknown"; 56 return "Unknown";
57} 57}
58 58
59static const char* TranslateGPUAccuracyLevel(Settings::GPUAccuracy backend) {
60 switch (backend) {
61 case Settings::GPUAccuracy::Normal:
62 return "Normal";
63 case Settings::GPUAccuracy::High:
64 return "High";
65 case Settings::GPUAccuracy::Extreme:
66 return "Extreme";
67 }
68 return "Unknown";
69}
70
59u64 GetTelemetryId() { 71u64 GetTelemetryId() {
60 u64 telemetry_id{}; 72 u64 telemetry_id{};
61 const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + 73 const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) +
@@ -184,8 +196,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
184 AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit); 196 AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit);
185 AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit); 197 AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit);
186 AddField(field_type, "Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache); 198 AddField(field_type, "Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache);
187 AddField(field_type, "Renderer_UseAccurateGpuEmulation", 199 AddField(field_type, "Renderer_GPUAccuracyLevel",
188 Settings::values.use_accurate_gpu_emulation); 200 TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy));
189 AddField(field_type, "Renderer_UseAsynchronousGpuEmulation", 201 AddField(field_type, "Renderer_UseAsynchronousGpuEmulation",
190 Settings::values.use_asynchronous_gpu_emulation); 202 Settings::values.use_asynchronous_gpu_emulation);
191 AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync); 203 AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync);
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp
index 1e3940801..ff2d11cc8 100644
--- a/src/tests/core/core_timing.cpp
+++ b/src/tests/core/core_timing.cpp
@@ -14,13 +14,14 @@
14#include "core/core.h" 14#include "core/core.h"
15#include "core/core_timing.h" 15#include "core/core_timing.h"
16 16
17namespace {
17// Numbers are chosen randomly to make sure the correct one is given. 18// Numbers are chosen randomly to make sure the correct one is given.
18static constexpr std::array<u64, 5> CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}}; 19constexpr std::array<u64, 5> CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}};
19static constexpr int MAX_SLICE_LENGTH = 10000; // Copied from CoreTiming internals 20constexpr int MAX_SLICE_LENGTH = 10000; // Copied from CoreTiming internals
20 21
21static std::bitset<CB_IDS.size()> callbacks_ran_flags; 22std::bitset<CB_IDS.size()> callbacks_ran_flags;
22static u64 expected_callback = 0; 23u64 expected_callback = 0;
23static s64 lateness = 0; 24s64 lateness = 0;
24 25
25template <unsigned int IDX> 26template <unsigned int IDX>
26void CallbackTemplate(u64 userdata, s64 cycles_late) { 27void CallbackTemplate(u64 userdata, s64 cycles_late) {
@@ -31,7 +32,7 @@ void CallbackTemplate(u64 userdata, s64 cycles_late) {
31 REQUIRE(lateness == cycles_late); 32 REQUIRE(lateness == cycles_late);
32} 33}
33 34
34static u64 callbacks_done = 0; 35u64 callbacks_done = 0;
35 36
36void EmptyCallback(u64 userdata, s64 cycles_late) { 37void EmptyCallback(u64 userdata, s64 cycles_late) {
37 ++callbacks_done; 38 ++callbacks_done;
@@ -48,8 +49,8 @@ struct ScopeInit final {
48 Core::Timing::CoreTiming core_timing; 49 Core::Timing::CoreTiming core_timing;
49}; 50};
50 51
51static void AdvanceAndCheck(Core::Timing::CoreTiming& core_timing, u32 idx, u32 context = 0, 52void AdvanceAndCheck(Core::Timing::CoreTiming& core_timing, u32 idx, u32 context = 0,
52 int expected_lateness = 0, int cpu_downcount = 0) { 53 int expected_lateness = 0, int cpu_downcount = 0) {
53 callbacks_ran_flags = 0; 54 callbacks_ran_flags = 0;
54 expected_callback = CB_IDS[idx]; 55 expected_callback = CB_IDS[idx];
55 lateness = expected_lateness; 56 lateness = expected_lateness;
@@ -62,6 +63,7 @@ static void AdvanceAndCheck(Core::Timing::CoreTiming& core_timing, u32 idx, u32
62 63
63 REQUIRE(decltype(callbacks_ran_flags)().set(idx) == callbacks_ran_flags); 64 REQUIRE(decltype(callbacks_ran_flags)().set(idx) == callbacks_ran_flags);
64} 65}
66} // Anonymous namespace
65 67
66TEST_CASE("CoreTiming[BasicOrder]", "[core]") { 68TEST_CASE("CoreTiming[BasicOrder]", "[core]") {
67 ScopeInit guard; 69 ScopeInit guard;
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 258d58eba..8ede4ba9b 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -23,6 +23,7 @@ add_library(video_core STATIC
23 engines/shader_bytecode.h 23 engines/shader_bytecode.h
24 engines/shader_header.h 24 engines/shader_header.h
25 engines/shader_type.h 25 engines/shader_type.h
26 fence_manager.h
26 gpu.cpp 27 gpu.cpp
27 gpu.h 28 gpu.h
28 gpu_asynch.cpp 29 gpu_asynch.cpp
@@ -51,6 +52,8 @@ add_library(video_core STATIC
51 renderer_opengl/gl_buffer_cache.h 52 renderer_opengl/gl_buffer_cache.h
52 renderer_opengl/gl_device.cpp 53 renderer_opengl/gl_device.cpp
53 renderer_opengl/gl_device.h 54 renderer_opengl/gl_device.h
55 renderer_opengl/gl_fence_manager.cpp
56 renderer_opengl/gl_fence_manager.h
54 renderer_opengl/gl_framebuffer_cache.cpp 57 renderer_opengl/gl_framebuffer_cache.cpp
55 renderer_opengl/gl_framebuffer_cache.h 58 renderer_opengl/gl_framebuffer_cache.h
56 renderer_opengl/gl_rasterizer.cpp 59 renderer_opengl/gl_rasterizer.cpp
@@ -160,6 +163,8 @@ if (ENABLE_VULKAN)
160 renderer_vulkan/fixed_pipeline_state.h 163 renderer_vulkan/fixed_pipeline_state.h
161 renderer_vulkan/maxwell_to_vk.cpp 164 renderer_vulkan/maxwell_to_vk.cpp
162 renderer_vulkan/maxwell_to_vk.h 165 renderer_vulkan/maxwell_to_vk.h
166 renderer_vulkan/nsight_aftermath_tracker.cpp
167 renderer_vulkan/nsight_aftermath_tracker.h
163 renderer_vulkan/renderer_vulkan.h 168 renderer_vulkan/renderer_vulkan.h
164 renderer_vulkan/renderer_vulkan.cpp 169 renderer_vulkan/renderer_vulkan.cpp
165 renderer_vulkan/vk_blit_screen.cpp 170 renderer_vulkan/vk_blit_screen.cpp
@@ -174,6 +179,8 @@ if (ENABLE_VULKAN)
174 renderer_vulkan/vk_descriptor_pool.h 179 renderer_vulkan/vk_descriptor_pool.h
175 renderer_vulkan/vk_device.cpp 180 renderer_vulkan/vk_device.cpp
176 renderer_vulkan/vk_device.h 181 renderer_vulkan/vk_device.h
182 renderer_vulkan/vk_fence_manager.cpp
183 renderer_vulkan/vk_fence_manager.h
177 renderer_vulkan/vk_graphics_pipeline.cpp 184 renderer_vulkan/vk_graphics_pipeline.cpp
178 renderer_vulkan/vk_graphics_pipeline.h 185 renderer_vulkan/vk_graphics_pipeline.h
179 renderer_vulkan/vk_image.cpp 186 renderer_vulkan/vk_image.cpp
@@ -213,19 +220,30 @@ if (ENABLE_VULKAN)
213 renderer_vulkan/wrapper.cpp 220 renderer_vulkan/wrapper.cpp
214 renderer_vulkan/wrapper.h 221 renderer_vulkan/wrapper.h
215 ) 222 )
216
217 target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include)
218 target_compile_definitions(video_core PRIVATE HAS_VULKAN)
219endif() 223endif()
220 224
221create_target_directory_groups(video_core) 225create_target_directory_groups(video_core)
222 226
223target_link_libraries(video_core PUBLIC common core) 227target_link_libraries(video_core PUBLIC common core)
224target_link_libraries(video_core PRIVATE glad) 228target_link_libraries(video_core PRIVATE glad)
229
225if (ENABLE_VULKAN) 230if (ENABLE_VULKAN)
231 target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include)
232 target_compile_definitions(video_core PRIVATE HAS_VULKAN)
226 target_link_libraries(video_core PRIVATE sirit) 233 target_link_libraries(video_core PRIVATE sirit)
227endif() 234endif()
228 235
236if (ENABLE_NSIGHT_AFTERMATH)
237 if (NOT DEFINED ENV{NSIGHT_AFTERMATH_SDK})
238 message(ERROR "Environment variable NSIGHT_AFTERMATH_SDK has to be provided")
239 endif()
240 if (NOT WIN32)
241 message(ERROR "Nsight Aftermath doesn't support non-Windows platforms")
242 endif()
243 target_compile_definitions(video_core PRIVATE HAS_NSIGHT_AFTERMATH)
244 target_include_directories(video_core PRIVATE "$ENV{NSIGHT_AFTERMATH_SDK}/include")
245endif()
246
229if (MSVC) 247if (MSVC)
230 target_compile_options(video_core PRIVATE /we4267) 248 target_compile_options(video_core PRIVATE /we4267)
231else() 249else()
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 83e7a1cde..510f11089 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <list>
8#include <memory> 9#include <memory>
9#include <mutex> 10#include <mutex>
10#include <unordered_map> 11#include <unordered_map>
@@ -18,8 +19,10 @@
18 19
19#include "common/alignment.h" 20#include "common/alignment.h"
20#include "common/common_types.h" 21#include "common/common_types.h"
22#include "common/logging/log.h"
21#include "core/core.h" 23#include "core/core.h"
22#include "core/memory.h" 24#include "core/memory.h"
25#include "core/settings.h"
23#include "video_core/buffer_cache/buffer_block.h" 26#include "video_core/buffer_cache/buffer_block.h"
24#include "video_core/buffer_cache/map_interval.h" 27#include "video_core/buffer_cache/map_interval.h"
25#include "video_core/memory_manager.h" 28#include "video_core/memory_manager.h"
@@ -79,6 +82,9 @@ public:
79 auto map = MapAddress(block, gpu_addr, cpu_addr, size); 82 auto map = MapAddress(block, gpu_addr, cpu_addr, size);
80 if (is_written) { 83 if (is_written) {
81 map->MarkAsModified(true, GetModifiedTicks()); 84 map->MarkAsModified(true, GetModifiedTicks());
85 if (Settings::IsGPULevelHigh() && Settings::values.use_asynchronous_gpu_emulation) {
86 MarkForAsyncFlush(map);
87 }
82 if (!map->IsWritten()) { 88 if (!map->IsWritten()) {
83 map->MarkAsWritten(true); 89 map->MarkAsWritten(true);
84 MarkRegionAsWritten(map->GetStart(), map->GetEnd() - 1); 90 MarkRegionAsWritten(map->GetStart(), map->GetEnd() - 1);
@@ -137,11 +143,22 @@ public:
137 }); 143 });
138 for (auto& object : objects) { 144 for (auto& object : objects) {
139 if (object->IsModified() && object->IsRegistered()) { 145 if (object->IsModified() && object->IsRegistered()) {
146 mutex.unlock();
140 FlushMap(object); 147 FlushMap(object);
148 mutex.lock();
141 } 149 }
142 } 150 }
143 } 151 }
144 152
153 bool MustFlushRegion(VAddr addr, std::size_t size) {
154 std::lock_guard lock{mutex};
155
156 const std::vector<MapInterval> objects = GetMapsInRange(addr, size);
157 return std::any_of(objects.cbegin(), objects.cend(), [](const MapInterval& map) {
158 return map->IsModified() && map->IsRegistered();
159 });
160 }
161
145 /// Mark the specified region as being invalidated 162 /// Mark the specified region as being invalidated
146 void InvalidateRegion(VAddr addr, u64 size) { 163 void InvalidateRegion(VAddr addr, u64 size) {
147 std::lock_guard lock{mutex}; 164 std::lock_guard lock{mutex};
@@ -154,6 +171,77 @@ public:
154 } 171 }
155 } 172 }
156 173
174 void OnCPUWrite(VAddr addr, std::size_t size) {
175 std::lock_guard lock{mutex};
176
177 for (const auto& object : GetMapsInRange(addr, size)) {
178 if (object->IsMemoryMarked() && object->IsRegistered()) {
179 UnmarkMemory(object);
180 object->SetSyncPending(true);
181 marked_for_unregister.emplace_back(object);
182 }
183 }
184 }
185
186 void SyncGuestHost() {
187 std::lock_guard lock{mutex};
188
189 for (const auto& object : marked_for_unregister) {
190 if (object->IsRegistered()) {
191 object->SetSyncPending(false);
192 Unregister(object);
193 }
194 }
195 marked_for_unregister.clear();
196 }
197
198 void CommitAsyncFlushes() {
199 if (uncommitted_flushes) {
200 auto commit_list = std::make_shared<std::list<MapInterval>>();
201 for (auto& map : *uncommitted_flushes) {
202 if (map->IsRegistered() && map->IsModified()) {
203 // TODO(Blinkhawk): Implement backend asynchronous flushing
204 // AsyncFlushMap(map)
205 commit_list->push_back(map);
206 }
207 }
208 if (!commit_list->empty()) {
209 committed_flushes.push_back(commit_list);
210 } else {
211 committed_flushes.emplace_back();
212 }
213 } else {
214 committed_flushes.emplace_back();
215 }
216 uncommitted_flushes.reset();
217 }
218
219 bool ShouldWaitAsyncFlushes() const {
220 return !committed_flushes.empty() && committed_flushes.front() != nullptr;
221 }
222
223 bool HasUncommittedFlushes() const {
224 return uncommitted_flushes != nullptr;
225 }
226
227 void PopAsyncFlushes() {
228 if (committed_flushes.empty()) {
229 return;
230 }
231 auto& flush_list = committed_flushes.front();
232 if (!flush_list) {
233 committed_flushes.pop_front();
234 return;
235 }
236 for (MapInterval& map : *flush_list) {
237 if (map->IsRegistered()) {
238 // TODO(Blinkhawk): Replace this for reading the asynchronous flush
239 FlushMap(map);
240 }
241 }
242 committed_flushes.pop_front();
243 }
244
157 virtual BufferType GetEmptyBuffer(std::size_t size) = 0; 245 virtual BufferType GetEmptyBuffer(std::size_t size) = 0;
158 246
159protected: 247protected:
@@ -196,17 +284,30 @@ protected:
196 const IntervalType interval{new_map->GetStart(), new_map->GetEnd()}; 284 const IntervalType interval{new_map->GetStart(), new_map->GetEnd()};
197 mapped_addresses.insert({interval, new_map}); 285 mapped_addresses.insert({interval, new_map});
198 rasterizer.UpdatePagesCachedCount(cpu_addr, size, 1); 286 rasterizer.UpdatePagesCachedCount(cpu_addr, size, 1);
287 new_map->SetMemoryMarked(true);
199 if (inherit_written) { 288 if (inherit_written) {
200 MarkRegionAsWritten(new_map->GetStart(), new_map->GetEnd() - 1); 289 MarkRegionAsWritten(new_map->GetStart(), new_map->GetEnd() - 1);
201 new_map->MarkAsWritten(true); 290 new_map->MarkAsWritten(true);
202 } 291 }
203 } 292 }
204 293
205 /// Unregisters an object from the cache 294 void UnmarkMemory(const MapInterval& map) {
206 void Unregister(MapInterval& map) { 295 if (!map->IsMemoryMarked()) {
296 return;
297 }
207 const std::size_t size = map->GetEnd() - map->GetStart(); 298 const std::size_t size = map->GetEnd() - map->GetStart();
208 rasterizer.UpdatePagesCachedCount(map->GetStart(), size, -1); 299 rasterizer.UpdatePagesCachedCount(map->GetStart(), size, -1);
300 map->SetMemoryMarked(false);
301 }
302
303 /// Unregisters an object from the cache
304 void Unregister(const MapInterval& map) {
305 UnmarkMemory(map);
209 map->MarkAsRegistered(false); 306 map->MarkAsRegistered(false);
307 if (map->IsSyncPending()) {
308 marked_for_unregister.remove(map);
309 map->SetSyncPending(false);
310 }
210 if (map->IsWritten()) { 311 if (map->IsWritten()) {
211 UnmarkRegionAsWritten(map->GetStart(), map->GetEnd() - 1); 312 UnmarkRegionAsWritten(map->GetStart(), map->GetEnd() - 1);
212 } 313 }
@@ -264,6 +365,9 @@ private:
264 MapInterval new_map = CreateMap(new_start, new_end, new_gpu_addr); 365 MapInterval new_map = CreateMap(new_start, new_end, new_gpu_addr);
265 if (modified_inheritance) { 366 if (modified_inheritance) {
266 new_map->MarkAsModified(true, GetModifiedTicks()); 367 new_map->MarkAsModified(true, GetModifiedTicks());
368 if (Settings::IsGPULevelHigh() && Settings::values.use_asynchronous_gpu_emulation) {
369 MarkForAsyncFlush(new_map);
370 }
267 } 371 }
268 Register(new_map, write_inheritance); 372 Register(new_map, write_inheritance);
269 return new_map; 373 return new_map;
@@ -450,6 +554,13 @@ private:
450 return false; 554 return false;
451 } 555 }
452 556
557 void MarkForAsyncFlush(MapInterval& map) {
558 if (!uncommitted_flushes) {
559 uncommitted_flushes = std::make_shared<std::unordered_set<MapInterval>>();
560 }
561 uncommitted_flushes->insert(map);
562 }
563
453 VideoCore::RasterizerInterface& rasterizer; 564 VideoCore::RasterizerInterface& rasterizer;
454 Core::System& system; 565 Core::System& system;
455 566
@@ -479,6 +590,10 @@ private:
479 u64 modified_ticks = 0; 590 u64 modified_ticks = 0;
480 591
481 std::vector<u8> staging_buffer; 592 std::vector<u8> staging_buffer;
593 std::list<MapInterval> marked_for_unregister;
594
595 std::shared_ptr<std::unordered_set<MapInterval>> uncommitted_flushes{};
596 std::list<std::shared_ptr<std::list<MapInterval>>> committed_flushes;
482 597
483 std::recursive_mutex mutex; 598 std::recursive_mutex mutex;
484}; 599};
diff --git a/src/video_core/buffer_cache/map_interval.h b/src/video_core/buffer_cache/map_interval.h
index b0956029d..29d8b26f3 100644
--- a/src/video_core/buffer_cache/map_interval.h
+++ b/src/video_core/buffer_cache/map_interval.h
@@ -46,6 +46,22 @@ public:
46 return is_registered; 46 return is_registered;
47 } 47 }
48 48
49 void SetMemoryMarked(bool is_memory_marked_) {
50 is_memory_marked = is_memory_marked_;
51 }
52
53 bool IsMemoryMarked() const {
54 return is_memory_marked;
55 }
56
57 void SetSyncPending(bool is_sync_pending_) {
58 is_sync_pending = is_sync_pending_;
59 }
60
61 bool IsSyncPending() const {
62 return is_sync_pending;
63 }
64
49 VAddr GetStart() const { 65 VAddr GetStart() const {
50 return start; 66 return start;
51 } 67 }
@@ -83,6 +99,8 @@ private:
83 bool is_written{}; 99 bool is_written{};
84 bool is_modified{}; 100 bool is_modified{};
85 bool is_registered{}; 101 bool is_registered{};
102 bool is_memory_marked{};
103 bool is_sync_pending{};
86 u64 ticks{}; 104 u64 ticks{};
87}; 105};
88 106
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
index 0b77afc71..16311f05e 100644
--- a/src/video_core/dma_pusher.cpp
+++ b/src/video_core/dma_pusher.cpp
@@ -21,6 +21,7 @@ MICROPROFILE_DEFINE(DispatchCalls, "GPU", "Execute command buffer", MP_RGB(128,
21void DmaPusher::DispatchCalls() { 21void DmaPusher::DispatchCalls() {
22 MICROPROFILE_SCOPE(DispatchCalls); 22 MICROPROFILE_SCOPE(DispatchCalls);
23 23
24 gpu.SyncGuestHost();
24 // On entering GPU code, assume all memory may be touched by the ARM core. 25 // On entering GPU code, assume all memory may be touched by the ARM core.
25 gpu.Maxwell3D().OnMemoryWrite(); 26 gpu.Maxwell3D().OnMemoryWrite();
26 27
@@ -32,6 +33,8 @@ void DmaPusher::DispatchCalls() {
32 } 33 }
33 } 34 }
34 gpu.FlushCommands(); 35 gpu.FlushCommands();
36 gpu.SyncGuestHost();
37 gpu.OnCommandListEnd();
35} 38}
36 39
37bool DmaPusher::Step() { 40bool DmaPusher::Step() {
@@ -68,16 +71,22 @@ bool DmaPusher::Step() {
68 gpu.MemoryManager().ReadBlockUnsafe(dma_get, command_headers.data(), 71 gpu.MemoryManager().ReadBlockUnsafe(dma_get, command_headers.data(),
69 command_list_header.size * sizeof(u32)); 72 command_list_header.size * sizeof(u32));
70 73
71 for (const CommandHeader& command_header : command_headers) { 74 for (std::size_t index = 0; index < command_headers.size();) {
75 const CommandHeader& command_header = command_headers[index];
72 76
73 // now, see if we're in the middle of a command 77 if (dma_state.method_count) {
74 if (dma_state.length_pending) {
75 // Second word of long non-inc methods command - method count
76 dma_state.length_pending = 0;
77 dma_state.method_count = command_header.method_count_;
78 } else if (dma_state.method_count) {
79 // Data word of methods command 78 // Data word of methods command
80 CallMethod(command_header.argument); 79 if (dma_state.non_incrementing) {
80 const u32 max_write = static_cast<u32>(
81 std::min<std::size_t>(index + dma_state.method_count, command_headers.size()) -
82 index);
83 CallMultiMethod(&command_header.argument, max_write);
84 dma_state.method_count -= max_write;
85 index += max_write;
86 continue;
87 } else {
88 CallMethod(command_header.argument);
89 }
81 90
82 if (!dma_state.non_incrementing) { 91 if (!dma_state.non_incrementing) {
83 dma_state.method++; 92 dma_state.method++;
@@ -117,6 +126,7 @@ bool DmaPusher::Step() {
117 break; 126 break;
118 } 127 }
119 } 128 }
129 index++;
120 } 130 }
121 131
122 if (!non_main) { 132 if (!non_main) {
@@ -137,4 +147,9 @@ void DmaPusher::CallMethod(u32 argument) const {
137 gpu.CallMethod({dma_state.method, argument, dma_state.subchannel, dma_state.method_count}); 147 gpu.CallMethod({dma_state.method, argument, dma_state.subchannel, dma_state.method_count});
138} 148}
139 149
150void DmaPusher::CallMultiMethod(const u32* base_start, u32 num_methods) const {
151 gpu.CallMultiMethod(dma_state.method, dma_state.subchannel, base_start, num_methods,
152 dma_state.method_count);
153}
154
140} // namespace Tegra 155} // namespace Tegra
diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h
index d6188614a..6cef71306 100644
--- a/src/video_core/dma_pusher.h
+++ b/src/video_core/dma_pusher.h
@@ -75,6 +75,7 @@ private:
75 void SetState(const CommandHeader& command_header); 75 void SetState(const CommandHeader& command_header);
76 76
77 void CallMethod(u32 argument) const; 77 void CallMethod(u32 argument) const;
78 void CallMultiMethod(const u32* base_start, u32 num_methods) const;
78 79
79 std::vector<CommandHeader> command_headers; ///< Buffer for list of commands fetched at once 80 std::vector<CommandHeader> command_headers; ///< Buffer for list of commands fetched at once
80 81
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp
index 85d308e26..8a47614d2 100644
--- a/src/video_core/engines/fermi_2d.cpp
+++ b/src/video_core/engines/fermi_2d.cpp
@@ -28,7 +28,13 @@ void Fermi2D::CallMethod(const GPU::MethodCall& method_call) {
28 } 28 }
29} 29}
30 30
31std::pair<u32, u32> DelimitLine(u32 src_1, u32 src_2, u32 dst_1, u32 dst_2, u32 src_line) { 31void Fermi2D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 methods_pending) {
32 for (std::size_t i = 0; i < amount; i++) {
33 CallMethod({method, base_start[i], 0, methods_pending - static_cast<u32>(i)});
34 }
35}
36
37static std::pair<u32, u32> DelimitLine(u32 src_1, u32 src_2, u32 dst_1, u32 dst_2, u32 src_line) {
32 const u32 line_a = src_2 - src_1; 38 const u32 line_a = src_2 - src_1;
33 const u32 line_b = dst_2 - dst_1; 39 const u32 line_b = dst_2 - dst_1;
34 const u32 excess = std::max<s32>(0, line_a - src_line + src_1); 40 const u32 excess = std::max<s32>(0, line_a - src_line + src_1);
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h
index dba342c70..939a5966d 100644
--- a/src/video_core/engines/fermi_2d.h
+++ b/src/video_core/engines/fermi_2d.h
@@ -39,6 +39,9 @@ public:
39 /// Write the value to the register identified by method. 39 /// Write the value to the register identified by method.
40 void CallMethod(const GPU::MethodCall& method_call); 40 void CallMethod(const GPU::MethodCall& method_call);
41 41
42 /// Write multiple values to the register identified by method.
43 void CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 methods_pending);
44
42 enum class Origin : u32 { 45 enum class Origin : u32 {
43 Center = 0, 46 Center = 0,
44 Corner = 1, 47 Corner = 1,
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp
index 368c75a66..00a12175f 100644
--- a/src/video_core/engines/kepler_compute.cpp
+++ b/src/video_core/engines/kepler_compute.cpp
@@ -51,6 +51,13 @@ void KeplerCompute::CallMethod(const GPU::MethodCall& method_call) {
51 } 51 }
52} 52}
53 53
54void KeplerCompute::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
55 u32 methods_pending) {
56 for (std::size_t i = 0; i < amount; i++) {
57 CallMethod({method, base_start[i], 0, methods_pending - static_cast<u32>(i)});
58 }
59}
60
54Texture::FullTextureInfo KeplerCompute::GetTexture(std::size_t offset) const { 61Texture::FullTextureInfo KeplerCompute::GetTexture(std::size_t offset) const {
55 const std::bitset<8> cbuf_mask = launch_description.const_buffer_enable_mask.Value(); 62 const std::bitset<8> cbuf_mask = launch_description.const_buffer_enable_mask.Value();
56 ASSERT(cbuf_mask[regs.tex_cb_index]); 63 ASSERT(cbuf_mask[regs.tex_cb_index]);
diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h
index eeb79c56f..fe55fdfd0 100644
--- a/src/video_core/engines/kepler_compute.h
+++ b/src/video_core/engines/kepler_compute.h
@@ -202,6 +202,9 @@ public:
202 /// Write the value to the register identified by method. 202 /// Write the value to the register identified by method.
203 void CallMethod(const GPU::MethodCall& method_call); 203 void CallMethod(const GPU::MethodCall& method_call);
204 204
205 /// Write multiple values to the register identified by method.
206 void CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 methods_pending);
207
205 Texture::FullTextureInfo GetTexture(std::size_t offset) const; 208 Texture::FullTextureInfo GetTexture(std::size_t offset) const;
206 209
207 /// Given a texture handle, returns the TSC and TIC entries. 210 /// Given a texture handle, returns the TSC and TIC entries.
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp
index 597872e43..586ff15dc 100644
--- a/src/video_core/engines/kepler_memory.cpp
+++ b/src/video_core/engines/kepler_memory.cpp
@@ -41,4 +41,11 @@ void KeplerMemory::CallMethod(const GPU::MethodCall& method_call) {
41 } 41 }
42} 42}
43 43
44void KeplerMemory::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
45 u32 methods_pending) {
46 for (std::size_t i = 0; i < amount; i++) {
47 CallMethod({method, base_start[i], 0, methods_pending - static_cast<u32>(i)});
48 }
49}
50
44} // namespace Tegra::Engines 51} // namespace Tegra::Engines
diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h
index 396fb6e86..bb26fb030 100644
--- a/src/video_core/engines/kepler_memory.h
+++ b/src/video_core/engines/kepler_memory.h
@@ -40,6 +40,9 @@ public:
40 /// Write the value to the register identified by method. 40 /// Write the value to the register identified by method.
41 void CallMethod(const GPU::MethodCall& method_call); 41 void CallMethod(const GPU::MethodCall& method_call);
42 42
43 /// Write multiple values to the register identified by method.
44 void CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 methods_pending);
45
43 struct Regs { 46 struct Regs {
44 static constexpr size_t NUM_REGS = 0x7F; 47 static constexpr size_t NUM_REGS = 0x7F;
45 48
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index baa74ad4c..39e3b66a2 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -280,6 +280,58 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
280 } 280 }
281} 281}
282 282
283void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
284 u32 methods_pending) {
285 // Methods after 0xE00 are special, they're actually triggers for some microcode that was
286 // uploaded to the GPU during initialization.
287 if (method >= MacroRegistersStart) {
288 // We're trying to execute a macro
289 if (executing_macro == 0) {
290 // A macro call must begin by writing the macro method's register, not its argument.
291 ASSERT_MSG((method % 2) == 0,
292 "Can't start macro execution by writing to the ARGS register");
293 executing_macro = method;
294 }
295
296 for (std::size_t i = 0; i < amount; i++) {
297 macro_params.push_back(base_start[i]);
298 }
299
300 // Call the macro when there are no more parameters in the command buffer
301 if (amount == methods_pending) {
302 CallMacroMethod(executing_macro, macro_params.size(), macro_params.data());
303 macro_params.clear();
304 }
305 return;
306 }
307 switch (method) {
308 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]):
309 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[1]):
310 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[2]):
311 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[3]):
312 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[4]):
313 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[5]):
314 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[6]):
315 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[7]):
316 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[8]):
317 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[9]):
318 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[10]):
319 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[11]):
320 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[12]):
321 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[13]):
322 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[14]):
323 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[15]): {
324 ProcessCBMultiData(method, base_start, amount);
325 break;
326 }
327 default: {
328 for (std::size_t i = 0; i < amount; i++) {
329 CallMethod({method, base_start[i], 0, methods_pending - static_cast<u32>(i)});
330 }
331 }
332 }
333}
334
283void Maxwell3D::StepInstance(const MMEDrawMode expected_mode, const u32 count) { 335void Maxwell3D::StepInstance(const MMEDrawMode expected_mode, const u32 count) {
284 if (mme_draw.current_mode == MMEDrawMode::Undefined) { 336 if (mme_draw.current_mode == MMEDrawMode::Undefined) {
285 if (mme_draw.gl_begin_consume) { 337 if (mme_draw.gl_begin_consume) {
@@ -404,7 +456,11 @@ void Maxwell3D::ProcessQueryGet() {
404 456
405 switch (regs.query.query_get.operation) { 457 switch (regs.query.query_get.operation) {
406 case Regs::QueryOperation::Release: 458 case Regs::QueryOperation::Release:
407 StampQueryResult(regs.query.query_sequence, regs.query.query_get.short_query == 0); 459 if (regs.query.query_get.fence == 1) {
460 rasterizer.SignalSemaphore(regs.query.QueryAddress(), regs.query.query_sequence);
461 } else {
462 StampQueryResult(regs.query.query_sequence, regs.query.query_get.short_query == 0);
463 }
408 break; 464 break;
409 case Regs::QueryOperation::Acquire: 465 case Regs::QueryOperation::Acquire:
410 // TODO(Blinkhawk): Under this operation, the GPU waits for the CPU to write a value that 466 // TODO(Blinkhawk): Under this operation, the GPU waits for the CPU to write a value that
@@ -483,7 +539,7 @@ void Maxwell3D::ProcessSyncPoint() {
483 const u32 increment = regs.sync_info.increment.Value(); 539 const u32 increment = regs.sync_info.increment.Value();
484 [[maybe_unused]] const u32 cache_flush = regs.sync_info.unknown.Value(); 540 [[maybe_unused]] const u32 cache_flush = regs.sync_info.unknown.Value();
485 if (increment) { 541 if (increment) {
486 system.GPU().IncrementSyncPoint(sync_point); 542 rasterizer.SignalSyncPoint(sync_point);
487 } 543 }
488} 544}
489 545
@@ -566,6 +622,28 @@ void Maxwell3D::StartCBData(u32 method) {
566 ProcessCBData(regs.const_buffer.cb_data[cb_data_state.id]); 622 ProcessCBData(regs.const_buffer.cb_data[cb_data_state.id]);
567} 623}
568 624
625void Maxwell3D::ProcessCBMultiData(u32 method, const u32* start_base, u32 amount) {
626 if (cb_data_state.current != method) {
627 if (cb_data_state.current != null_cb_data) {
628 FinishCBData();
629 }
630 constexpr u32 first_cb_data = MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]);
631 cb_data_state.start_pos = regs.const_buffer.cb_pos;
632 cb_data_state.id = method - first_cb_data;
633 cb_data_state.current = method;
634 cb_data_state.counter = 0;
635 }
636 const std::size_t id = cb_data_state.id;
637 const std::size_t size = amount;
638 std::size_t i = 0;
639 for (; i < size; i++) {
640 cb_data_state.buffer[id][cb_data_state.counter] = start_base[i];
641 cb_data_state.counter++;
642 }
643 // Increment the current buffer position.
644 regs.const_buffer.cb_pos = regs.const_buffer.cb_pos + 4 * amount;
645}
646
569void Maxwell3D::FinishCBData() { 647void Maxwell3D::FinishCBData() {
570 // Write the input value to the current const buffer at the current position. 648 // Write the input value to the current const buffer at the current position.
571 const GPUVAddr buffer_address = regs.const_buffer.BufferAddress(); 649 const GPUVAddr buffer_address = regs.const_buffer.BufferAddress();
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 59d5752d2..3dfba8197 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -1259,7 +1259,8 @@ public:
1259 1259
1260 GPUVAddr LimitAddress() const { 1260 GPUVAddr LimitAddress() const {
1261 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(limit_high) << 32) | 1261 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(limit_high) << 32) |
1262 limit_low); 1262 limit_low) +
1263 1;
1263 } 1264 }
1264 } vertex_array_limit[NumVertexArrays]; 1265 } vertex_array_limit[NumVertexArrays];
1265 1266
@@ -1358,6 +1359,9 @@ public:
1358 /// Write the value to the register identified by method. 1359 /// Write the value to the register identified by method.
1359 void CallMethod(const GPU::MethodCall& method_call); 1360 void CallMethod(const GPU::MethodCall& method_call);
1360 1361
1362 /// Write multiple values to the register identified by method.
1363 void CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 methods_pending);
1364
1361 /// Write the value to the register identified by method. 1365 /// Write the value to the register identified by method.
1362 void CallMethodFromMME(const GPU::MethodCall& method_call); 1366 void CallMethodFromMME(const GPU::MethodCall& method_call);
1363 1367
@@ -1511,6 +1515,7 @@ private:
1511 /// Handles a write to the CB_DATA[i] register. 1515 /// Handles a write to the CB_DATA[i] register.
1512 void StartCBData(u32 method); 1516 void StartCBData(u32 method);
1513 void ProcessCBData(u32 value); 1517 void ProcessCBData(u32 value);
1518 void ProcessCBMultiData(u32 method, const u32* start_base, u32 amount);
1514 void FinishCBData(); 1519 void FinishCBData();
1515 1520
1516 /// Handles a write to the CB_BIND register. 1521 /// Handles a write to the CB_BIND register.
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index c2610f992..6630005b0 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -36,6 +36,13 @@ void MaxwellDMA::CallMethod(const GPU::MethodCall& method_call) {
36#undef MAXWELLDMA_REG_INDEX 36#undef MAXWELLDMA_REG_INDEX
37} 37}
38 38
39void MaxwellDMA::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
40 u32 methods_pending) {
41 for (std::size_t i = 0; i < amount; i++) {
42 CallMethod({method, base_start[i], 0, methods_pending - static_cast<u32>(i)});
43 }
44}
45
39void MaxwellDMA::HandleCopy() { 46void MaxwellDMA::HandleCopy() {
40 LOG_TRACE(HW_GPU, "Requested a DMA copy"); 47 LOG_TRACE(HW_GPU, "Requested a DMA copy");
41 48
@@ -104,8 +111,13 @@ void MaxwellDMA::HandleCopy() {
104 write_buffer.resize(dst_size); 111 write_buffer.resize(dst_size);
105 } 112 }
106 113
107 memory_manager.ReadBlock(source, read_buffer.data(), src_size); 114 if (Settings::IsGPULevelExtreme()) {
108 memory_manager.ReadBlock(dest, write_buffer.data(), dst_size); 115 memory_manager.ReadBlock(source, read_buffer.data(), src_size);
116 memory_manager.ReadBlock(dest, write_buffer.data(), dst_size);
117 } else {
118 memory_manager.ReadBlockUnsafe(source, read_buffer.data(), src_size);
119 memory_manager.ReadBlockUnsafe(dest, write_buffer.data(), dst_size);
120 }
109 121
110 Texture::UnswizzleSubrect( 122 Texture::UnswizzleSubrect(
111 regs.x_count, regs.y_count, regs.dst_pitch, regs.src_params.size_x, bytes_per_pixel, 123 regs.x_count, regs.y_count, regs.dst_pitch, regs.src_params.size_x, bytes_per_pixel,
@@ -136,7 +148,7 @@ void MaxwellDMA::HandleCopy() {
136 write_buffer.resize(dst_size); 148 write_buffer.resize(dst_size);
137 } 149 }
138 150
139 if (Settings::values.use_accurate_gpu_emulation) { 151 if (Settings::IsGPULevelExtreme()) {
140 memory_manager.ReadBlock(source, read_buffer.data(), src_size); 152 memory_manager.ReadBlock(source, read_buffer.data(), src_size);
141 memory_manager.ReadBlock(dest, write_buffer.data(), dst_size); 153 memory_manager.ReadBlock(dest, write_buffer.data(), dst_size);
142 } else { 154 } else {
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h
index 4f40d1d1f..c43ed8194 100644
--- a/src/video_core/engines/maxwell_dma.h
+++ b/src/video_core/engines/maxwell_dma.h
@@ -35,6 +35,9 @@ public:
35 /// Write the value to the register identified by method. 35 /// Write the value to the register identified by method.
36 void CallMethod(const GPU::MethodCall& method_call); 36 void CallMethod(const GPU::MethodCall& method_call);
37 37
38 /// Write multiple values to the register identified by method.
39 void CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 methods_pending);
40
38 struct Regs { 41 struct Regs {
39 static constexpr std::size_t NUM_REGS = 0x1D6; 42 static constexpr std::size_t NUM_REGS = 0x1D6;
40 43
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 7231597d4..cde3a26b9 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -655,6 +655,7 @@ union Instruction {
655 } 655 }
656 656
657 constexpr Instruction(u64 value) : value{value} {} 657 constexpr Instruction(u64 value) : value{value} {}
658 constexpr Instruction(const Instruction& instr) : value(instr.value) {}
658 659
659 BitField<0, 8, Register> gpr0; 660 BitField<0, 8, Register> gpr0;
660 BitField<8, 8, Register> gpr8; 661 BitField<8, 8, Register> gpr8;
@@ -817,11 +818,9 @@ union Instruction {
817 BitField<32, 1, u64> saturate; 818 BitField<32, 1, u64> saturate;
818 BitField<49, 2, HalfMerge> merge; 819 BitField<49, 2, HalfMerge> merge;
819 820
820 BitField<43, 1, u64> negate_a;
821 BitField<44, 1, u64> abs_a; 821 BitField<44, 1, u64> abs_a;
822 BitField<47, 2, HalfType> type_a; 822 BitField<47, 2, HalfType> type_a;
823 823
824 BitField<31, 1, u64> negate_b;
825 BitField<30, 1, u64> abs_b; 824 BitField<30, 1, u64> abs_b;
826 BitField<28, 2, HalfType> type_b; 825 BitField<28, 2, HalfType> type_b;
827 826
diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h
new file mode 100644
index 000000000..dabd1588c
--- /dev/null
+++ b/src/video_core/fence_manager.h
@@ -0,0 +1,170 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <algorithm>
8#include <array>
9#include <memory>
10#include <queue>
11
12#include "common/assert.h"
13#include "common/common_types.h"
14#include "core/core.h"
15#include "core/memory.h"
16#include "core/settings.h"
17#include "video_core/gpu.h"
18#include "video_core/memory_manager.h"
19#include "video_core/rasterizer_interface.h"
20
21namespace VideoCommon {
22
23class FenceBase {
24public:
25 FenceBase(u32 payload, bool is_stubbed)
26 : address{}, payload{payload}, is_semaphore{false}, is_stubbed{is_stubbed} {}
27
28 FenceBase(GPUVAddr address, u32 payload, bool is_stubbed)
29 : address{address}, payload{payload}, is_semaphore{true}, is_stubbed{is_stubbed} {}
30
31 GPUVAddr GetAddress() const {
32 return address;
33 }
34
35 u32 GetPayload() const {
36 return payload;
37 }
38
39 bool IsSemaphore() const {
40 return is_semaphore;
41 }
42
43private:
44 GPUVAddr address;
45 u32 payload;
46 bool is_semaphore;
47
48protected:
49 bool is_stubbed;
50};
51
52template <typename TFence, typename TTextureCache, typename TTBufferCache, typename TQueryCache>
53class FenceManager {
54public:
55 void SignalSemaphore(GPUVAddr addr, u32 value) {
56 TryReleasePendingFences();
57 const bool should_flush = ShouldFlush();
58 CommitAsyncFlushes();
59 TFence new_fence = CreateFence(addr, value, !should_flush);
60 fences.push(new_fence);
61 QueueFence(new_fence);
62 if (should_flush) {
63 rasterizer.FlushCommands();
64 }
65 rasterizer.SyncGuestHost();
66 }
67
68 void SignalSyncPoint(u32 value) {
69 TryReleasePendingFences();
70 const bool should_flush = ShouldFlush();
71 CommitAsyncFlushes();
72 TFence new_fence = CreateFence(value, !should_flush);
73 fences.push(new_fence);
74 QueueFence(new_fence);
75 if (should_flush) {
76 rasterizer.FlushCommands();
77 }
78 rasterizer.SyncGuestHost();
79 }
80
81 void WaitPendingFences() {
82 auto& gpu{system.GPU()};
83 auto& memory_manager{gpu.MemoryManager()};
84 while (!fences.empty()) {
85 TFence& current_fence = fences.front();
86 if (ShouldWait()) {
87 WaitFence(current_fence);
88 }
89 PopAsyncFlushes();
90 if (current_fence->IsSemaphore()) {
91 memory_manager.Write<u32>(current_fence->GetAddress(), current_fence->GetPayload());
92 } else {
93 gpu.IncrementSyncPoint(current_fence->GetPayload());
94 }
95 fences.pop();
96 }
97 }
98
99protected:
100 FenceManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
101 TTextureCache& texture_cache, TTBufferCache& buffer_cache,
102 TQueryCache& query_cache)
103 : system{system}, rasterizer{rasterizer}, texture_cache{texture_cache},
104 buffer_cache{buffer_cache}, query_cache{query_cache} {}
105
106 virtual ~FenceManager() {}
107
108 /// Creates a Sync Point Fence Interface, does not create a backend fence if 'is_stubbed' is
109 /// true
110 virtual TFence CreateFence(u32 value, bool is_stubbed) = 0;
111 /// Creates a Semaphore Fence Interface, does not create a backend fence if 'is_stubbed' is true
112 virtual TFence CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) = 0;
113 /// Queues a fence into the backend if the fence isn't stubbed.
114 virtual void QueueFence(TFence& fence) = 0;
115 /// Notifies that the backend fence has been signaled/reached in host GPU.
116 virtual bool IsFenceSignaled(TFence& fence) const = 0;
117 /// Waits until a fence has been signalled by the host GPU.
118 virtual void WaitFence(TFence& fence) = 0;
119
120 Core::System& system;
121 VideoCore::RasterizerInterface& rasterizer;
122 TTextureCache& texture_cache;
123 TTBufferCache& buffer_cache;
124 TQueryCache& query_cache;
125
126private:
127 void TryReleasePendingFences() {
128 auto& gpu{system.GPU()};
129 auto& memory_manager{gpu.MemoryManager()};
130 while (!fences.empty()) {
131 TFence& current_fence = fences.front();
132 if (ShouldWait() && !IsFenceSignaled(current_fence)) {
133 return;
134 }
135 PopAsyncFlushes();
136 if (current_fence->IsSemaphore()) {
137 memory_manager.Write<u32>(current_fence->GetAddress(), current_fence->GetPayload());
138 } else {
139 gpu.IncrementSyncPoint(current_fence->GetPayload());
140 }
141 fences.pop();
142 }
143 }
144
145 bool ShouldWait() const {
146 return texture_cache.ShouldWaitAsyncFlushes() || buffer_cache.ShouldWaitAsyncFlushes() ||
147 query_cache.ShouldWaitAsyncFlushes();
148 }
149
150 bool ShouldFlush() const {
151 return texture_cache.HasUncommittedFlushes() || buffer_cache.HasUncommittedFlushes() ||
152 query_cache.HasUncommittedFlushes();
153 }
154
155 void PopAsyncFlushes() {
156 texture_cache.PopAsyncFlushes();
157 buffer_cache.PopAsyncFlushes();
158 query_cache.PopAsyncFlushes();
159 }
160
161 void CommitAsyncFlushes() {
162 texture_cache.CommitAsyncFlushes();
163 buffer_cache.CommitAsyncFlushes();
164 query_cache.CommitAsyncFlushes();
165 }
166
167 std::queue<TFence> fences;
168};
169
170} // namespace VideoCommon
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index a606f4abd..b87fd873d 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -9,6 +9,7 @@
9#include "core/core_timing_util.h" 9#include "core/core_timing_util.h"
10#include "core/frontend/emu_window.h" 10#include "core/frontend/emu_window.h"
11#include "core/memory.h" 11#include "core/memory.h"
12#include "core/settings.h"
12#include "video_core/engines/fermi_2d.h" 13#include "video_core/engines/fermi_2d.h"
13#include "video_core/engines/kepler_compute.h" 14#include "video_core/engines/kepler_compute.h"
14#include "video_core/engines/kepler_memory.h" 15#include "video_core/engines/kepler_memory.h"
@@ -125,6 +126,28 @@ bool GPU::CancelSyncptInterrupt(const u32 syncpoint_id, const u32 value) {
125 return true; 126 return true;
126} 127}
127 128
129u64 GPU::RequestFlush(VAddr addr, std::size_t size) {
130 std::unique_lock lck{flush_request_mutex};
131 const u64 fence = ++last_flush_fence;
132 flush_requests.emplace_back(fence, addr, size);
133 return fence;
134}
135
136void GPU::TickWork() {
137 std::unique_lock lck{flush_request_mutex};
138 while (!flush_requests.empty()) {
139 auto& request = flush_requests.front();
140 const u64 fence = request.fence;
141 const VAddr addr = request.addr;
142 const std::size_t size = request.size;
143 flush_requests.pop_front();
144 flush_request_mutex.unlock();
145 renderer->Rasterizer().FlushRegion(addr, size);
146 current_flush_fence.store(fence);
147 flush_request_mutex.lock();
148 }
149}
150
128u64 GPU::GetTicks() const { 151u64 GPU::GetTicks() const {
129 // This values were reversed engineered by fincs from NVN 152 // This values were reversed engineered by fincs from NVN
130 // The gpu clock is reported in units of 385/625 nanoseconds 153 // The gpu clock is reported in units of 385/625 nanoseconds
@@ -132,7 +155,10 @@ u64 GPU::GetTicks() const {
132 constexpr u64 gpu_ticks_den = 625; 155 constexpr u64 gpu_ticks_den = 625;
133 156
134 const u64 cpu_ticks = system.CoreTiming().GetTicks(); 157 const u64 cpu_ticks = system.CoreTiming().GetTicks();
135 const u64 nanoseconds = Core::Timing::CyclesToNs(cpu_ticks).count(); 158 u64 nanoseconds = Core::Timing::CyclesToNs(cpu_ticks).count();
159 if (Settings::values.use_fast_gpu_time) {
160 nanoseconds /= 256;
161 }
136 const u64 nanoseconds_num = nanoseconds / gpu_ticks_den; 162 const u64 nanoseconds_num = nanoseconds / gpu_ticks_den;
137 const u64 nanoseconds_rem = nanoseconds % gpu_ticks_den; 163 const u64 nanoseconds_rem = nanoseconds % gpu_ticks_den;
138 return nanoseconds_num * gpu_ticks_num + (nanoseconds_rem * gpu_ticks_num) / gpu_ticks_den; 164 return nanoseconds_num * gpu_ticks_num + (nanoseconds_rem * gpu_ticks_num) / gpu_ticks_den;
@@ -142,6 +168,13 @@ void GPU::FlushCommands() {
142 renderer->Rasterizer().FlushCommands(); 168 renderer->Rasterizer().FlushCommands();
143} 169}
144 170
171void GPU::SyncGuestHost() {
172 renderer->Rasterizer().SyncGuestHost();
173}
174
175void GPU::OnCommandListEnd() {
176 renderer->Rasterizer().ReleaseFences();
177}
145// Note that, traditionally, methods are treated as 4-byte addressable locations, and hence 178// Note that, traditionally, methods are treated as 4-byte addressable locations, and hence
146// their numbers are written down multiplied by 4 in Docs. Here we are not multiply by 4. 179// their numbers are written down multiplied by 4 in Docs. Here we are not multiply by 4.
147// So the values you see in docs might be multiplied by 4. 180// So the values you see in docs might be multiplied by 4.
@@ -180,16 +213,32 @@ void GPU::CallMethod(const MethodCall& method_call) {
180 213
181 ASSERT(method_call.subchannel < bound_engines.size()); 214 ASSERT(method_call.subchannel < bound_engines.size());
182 215
183 if (ExecuteMethodOnEngine(method_call)) { 216 if (ExecuteMethodOnEngine(method_call.method)) {
184 CallEngineMethod(method_call); 217 CallEngineMethod(method_call);
185 } else { 218 } else {
186 CallPullerMethod(method_call); 219 CallPullerMethod(method_call);
187 } 220 }
188} 221}
189 222
190bool GPU::ExecuteMethodOnEngine(const MethodCall& method_call) { 223void GPU::CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount,
191 const auto method = static_cast<BufferMethods>(method_call.method); 224 u32 methods_pending) {
192 return method >= BufferMethods::NonPullerMethods; 225 LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method, subchannel);
226
227 ASSERT(subchannel < bound_engines.size());
228
229 if (ExecuteMethodOnEngine(method)) {
230 CallEngineMultiMethod(method, subchannel, base_start, amount, methods_pending);
231 } else {
232 for (std::size_t i = 0; i < amount; i++) {
233 CallPullerMethod(
234 {method, base_start[i], subchannel, methods_pending - static_cast<u32>(i)});
235 }
236 }
237}
238
239bool GPU::ExecuteMethodOnEngine(u32 method) {
240 const auto buffer_method = static_cast<BufferMethods>(method);
241 return buffer_method >= BufferMethods::NonPullerMethods;
193} 242}
194 243
195void GPU::CallPullerMethod(const MethodCall& method_call) { 244void GPU::CallPullerMethod(const MethodCall& method_call) {
@@ -269,6 +318,31 @@ void GPU::CallEngineMethod(const MethodCall& method_call) {
269 } 318 }
270} 319}
271 320
321void GPU::CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount,
322 u32 methods_pending) {
323 const EngineID engine = bound_engines[subchannel];
324
325 switch (engine) {
326 case EngineID::FERMI_TWOD_A:
327 fermi_2d->CallMultiMethod(method, base_start, amount, methods_pending);
328 break;
329 case EngineID::MAXWELL_B:
330 maxwell_3d->CallMultiMethod(method, base_start, amount, methods_pending);
331 break;
332 case EngineID::KEPLER_COMPUTE_B:
333 kepler_compute->CallMultiMethod(method, base_start, amount, methods_pending);
334 break;
335 case EngineID::MAXWELL_DMA_COPY_A:
336 maxwell_dma->CallMultiMethod(method, base_start, amount, methods_pending);
337 break;
338 case EngineID::KEPLER_INLINE_TO_MEMORY_B:
339 kepler_memory->CallMultiMethod(method, base_start, amount, methods_pending);
340 break;
341 default:
342 UNIMPLEMENTED_MSG("Unimplemented engine");
343 }
344}
345
272void GPU::ProcessBindMethod(const MethodCall& method_call) { 346void GPU::ProcessBindMethod(const MethodCall& method_call) {
273 // Bind the current subchannel to the desired engine id. 347 // Bind the current subchannel to the desired engine id.
274 LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel, 348 LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel,
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 1a2d747be..dd51c95b7 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -155,7 +155,27 @@ public:
155 /// Calls a GPU method. 155 /// Calls a GPU method.
156 void CallMethod(const MethodCall& method_call); 156 void CallMethod(const MethodCall& method_call);
157 157
158 /// Calls a GPU multivalue method.
159 void CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount,
160 u32 methods_pending);
161
162 /// Flush all current written commands into the host GPU for execution.
158 void FlushCommands(); 163 void FlushCommands();
164 /// Synchronizes CPU writes with Host GPU memory.
165 void SyncGuestHost();
166 /// Signal the ending of command list.
167 virtual void OnCommandListEnd();
168
169 /// Request a host GPU memory flush from the CPU.
170 u64 RequestFlush(VAddr addr, std::size_t size);
171
172 /// Obtains current flush request fence id.
173 u64 CurrentFlushRequestFence() const {
174 return current_flush_fence.load(std::memory_order_relaxed);
175 }
176
177 /// Tick pending requests within the GPU.
178 void TickWork();
159 179
160 /// Returns a reference to the Maxwell3D GPU engine. 180 /// Returns a reference to the Maxwell3D GPU engine.
161 Engines::Maxwell3D& Maxwell3D(); 181 Engines::Maxwell3D& Maxwell3D();
@@ -293,8 +313,12 @@ private:
293 /// Calls a GPU engine method. 313 /// Calls a GPU engine method.
294 void CallEngineMethod(const MethodCall& method_call); 314 void CallEngineMethod(const MethodCall& method_call);
295 315
316 /// Calls a GPU engine multivalue method.
317 void CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount,
318 u32 methods_pending);
319
296 /// Determines where the method should be executed. 320 /// Determines where the method should be executed.
297 bool ExecuteMethodOnEngine(const MethodCall& method_call); 321 bool ExecuteMethodOnEngine(u32 method);
298 322
299protected: 323protected:
300 std::unique_ptr<Tegra::DmaPusher> dma_pusher; 324 std::unique_ptr<Tegra::DmaPusher> dma_pusher;
@@ -325,6 +349,19 @@ private:
325 349
326 std::condition_variable sync_cv; 350 std::condition_variable sync_cv;
327 351
352 struct FlushRequest {
353 FlushRequest(u64 fence, VAddr addr, std::size_t size)
354 : fence{fence}, addr{addr}, size{size} {}
355 u64 fence;
356 VAddr addr;
357 std::size_t size;
358 };
359
360 std::list<FlushRequest> flush_requests;
361 std::atomic<u64> current_flush_fence{};
362 u64 last_flush_fence{};
363 std::mutex flush_request_mutex;
364
328 const bool is_async; 365 const bool is_async;
329}; 366};
330 367
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp
index 20e73a37e..53305ab43 100644
--- a/src/video_core/gpu_asynch.cpp
+++ b/src/video_core/gpu_asynch.cpp
@@ -52,4 +52,8 @@ void GPUAsynch::WaitIdle() const {
52 gpu_thread.WaitIdle(); 52 gpu_thread.WaitIdle();
53} 53}
54 54
55void GPUAsynch::OnCommandListEnd() {
56 gpu_thread.OnCommandListEnd();
57}
58
55} // namespace VideoCommon 59} // namespace VideoCommon
diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h
index 03fd0eef0..517658612 100644
--- a/src/video_core/gpu_asynch.h
+++ b/src/video_core/gpu_asynch.h
@@ -32,6 +32,8 @@ public:
32 void FlushAndInvalidateRegion(VAddr addr, u64 size) override; 32 void FlushAndInvalidateRegion(VAddr addr, u64 size) override;
33 void WaitIdle() const override; 33 void WaitIdle() const override;
34 34
35 void OnCommandListEnd() override;
36
35protected: 37protected:
36 void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const override; 38 void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const override;
37 39
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index 10cda686b..c3bb4fe06 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -6,6 +6,7 @@
6#include "common/microprofile.h" 6#include "common/microprofile.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/frontend/emu_window.h" 8#include "core/frontend/emu_window.h"
9#include "core/settings.h"
9#include "video_core/dma_pusher.h" 10#include "video_core/dma_pusher.h"
10#include "video_core/gpu.h" 11#include "video_core/gpu.h"
11#include "video_core/gpu_thread.h" 12#include "video_core/gpu_thread.h"
@@ -14,8 +15,9 @@
14namespace VideoCommon::GPUThread { 15namespace VideoCommon::GPUThread {
15 16
16/// Runs the GPU thread 17/// Runs the GPU thread
17static void RunThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context, 18static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
18 Tegra::DmaPusher& dma_pusher, SynchState& state) { 19 Core::Frontend::GraphicsContext& context, Tegra::DmaPusher& dma_pusher,
20 SynchState& state) {
19 MicroProfileOnThreadCreate("GpuThread"); 21 MicroProfileOnThreadCreate("GpuThread");
20 22
21 // Wait for first GPU command before acquiring the window context 23 // Wait for first GPU command before acquiring the window context
@@ -37,10 +39,14 @@ static void RunThread(VideoCore::RendererBase& renderer, Core::Frontend::Graphic
37 dma_pusher.DispatchCalls(); 39 dma_pusher.DispatchCalls();
38 } else if (const auto data = std::get_if<SwapBuffersCommand>(&next.data)) { 40 } else if (const auto data = std::get_if<SwapBuffersCommand>(&next.data)) {
39 renderer.SwapBuffers(data->framebuffer ? &*data->framebuffer : nullptr); 41 renderer.SwapBuffers(data->framebuffer ? &*data->framebuffer : nullptr);
42 } else if (const auto data = std::get_if<OnCommandListEndCommand>(&next.data)) {
43 renderer.Rasterizer().ReleaseFences();
44 } else if (const auto data = std::get_if<GPUTickCommand>(&next.data)) {
45 system.GPU().TickWork();
40 } else if (const auto data = std::get_if<FlushRegionCommand>(&next.data)) { 46 } else if (const auto data = std::get_if<FlushRegionCommand>(&next.data)) {
41 renderer.Rasterizer().FlushRegion(data->addr, data->size); 47 renderer.Rasterizer().FlushRegion(data->addr, data->size);
42 } else if (const auto data = std::get_if<InvalidateRegionCommand>(&next.data)) { 48 } else if (const auto data = std::get_if<InvalidateRegionCommand>(&next.data)) {
43 renderer.Rasterizer().InvalidateRegion(data->addr, data->size); 49 renderer.Rasterizer().OnCPUWrite(data->addr, data->size);
44 } else if (std::holds_alternative<EndProcessingCommand>(next.data)) { 50 } else if (std::holds_alternative<EndProcessingCommand>(next.data)) {
45 return; 51 return;
46 } else { 52 } else {
@@ -65,8 +71,8 @@ ThreadManager::~ThreadManager() {
65void ThreadManager::StartThread(VideoCore::RendererBase& renderer, 71void ThreadManager::StartThread(VideoCore::RendererBase& renderer,
66 Core::Frontend::GraphicsContext& context, 72 Core::Frontend::GraphicsContext& context,
67 Tegra::DmaPusher& dma_pusher) { 73 Tegra::DmaPusher& dma_pusher) {
68 thread = std::thread{RunThread, std::ref(renderer), std::ref(context), std::ref(dma_pusher), 74 thread = std::thread{RunThread, std::ref(system), std::ref(renderer),
69 std::ref(state)}; 75 std::ref(context), std::ref(dma_pusher), std::ref(state)};
70} 76}
71 77
72void ThreadManager::SubmitList(Tegra::CommandList&& entries) { 78void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
@@ -78,16 +84,29 @@ void ThreadManager::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
78} 84}
79 85
80void ThreadManager::FlushRegion(VAddr addr, u64 size) { 86void ThreadManager::FlushRegion(VAddr addr, u64 size) {
81 PushCommand(FlushRegionCommand(addr, size)); 87 if (!Settings::IsGPULevelHigh()) {
88 PushCommand(FlushRegionCommand(addr, size));
89 return;
90 }
91 if (!Settings::IsGPULevelExtreme()) {
92 return;
93 }
94 if (system.Renderer().Rasterizer().MustFlushRegion(addr, size)) {
95 auto& gpu = system.GPU();
96 u64 fence = gpu.RequestFlush(addr, size);
97 PushCommand(GPUTickCommand());
98 while (fence > gpu.CurrentFlushRequestFence()) {
99 }
100 }
82} 101}
83 102
84void ThreadManager::InvalidateRegion(VAddr addr, u64 size) { 103void ThreadManager::InvalidateRegion(VAddr addr, u64 size) {
85 system.Renderer().Rasterizer().InvalidateRegion(addr, size); 104 system.Renderer().Rasterizer().OnCPUWrite(addr, size);
86} 105}
87 106
88void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) { 107void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) {
89 // Skip flush on asynch mode, as FlushAndInvalidateRegion is not used for anything too important 108 // Skip flush on asynch mode, as FlushAndInvalidateRegion is not used for anything too important
90 InvalidateRegion(addr, size); 109 system.Renderer().Rasterizer().OnCPUWrite(addr, size);
91} 110}
92 111
93void ThreadManager::WaitIdle() const { 112void ThreadManager::WaitIdle() const {
@@ -95,6 +114,10 @@ void ThreadManager::WaitIdle() const {
95 } 114 }
96} 115}
97 116
117void ThreadManager::OnCommandListEnd() {
118 PushCommand(OnCommandListEndCommand());
119}
120
98u64 ThreadManager::PushCommand(CommandData&& command_data) { 121u64 ThreadManager::PushCommand(CommandData&& command_data) {
99 const u64 fence{++state.last_fence}; 122 const u64 fence{++state.last_fence};
100 state.queue.Push(CommandDataContainer(std::move(command_data), fence)); 123 state.queue.Push(CommandDataContainer(std::move(command_data), fence));
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index cd74ad330..5a28335d6 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -70,9 +70,16 @@ struct FlushAndInvalidateRegionCommand final {
70 u64 size; 70 u64 size;
71}; 71};
72 72
73/// Command called within the gpu, to schedule actions after a command list end
74struct OnCommandListEndCommand final {};
75
76/// Command to make the gpu look into pending requests
77struct GPUTickCommand final {};
78
73using CommandData = 79using CommandData =
74 std::variant<EndProcessingCommand, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand, 80 std::variant<EndProcessingCommand, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand,
75 InvalidateRegionCommand, FlushAndInvalidateRegionCommand>; 81 InvalidateRegionCommand, FlushAndInvalidateRegionCommand, OnCommandListEndCommand,
82 GPUTickCommand>;
76 83
77struct CommandDataContainer { 84struct CommandDataContainer {
78 CommandDataContainer() = default; 85 CommandDataContainer() = default;
@@ -122,6 +129,8 @@ public:
122 // Wait until the gpu thread is idle. 129 // Wait until the gpu thread is idle.
123 void WaitIdle() const; 130 void WaitIdle() const;
124 131
132 void OnCommandListEnd();
133
125private: 134private:
126 /// Pushes a command to be executed by the GPU thread 135 /// Pushes a command to be executed by the GPU thread
127 u64 PushCommand(CommandData&& command_data); 136 u64 PushCommand(CommandData&& command_data);
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index fd49bc2a9..dbee9f634 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -51,11 +51,8 @@ GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, u64 size) {
51 const GPUVAddr gpu_addr{FindFreeRegion(address_space_base, aligned_size)}; 51 const GPUVAddr gpu_addr{FindFreeRegion(address_space_base, aligned_size)};
52 52
53 MapBackingMemory(gpu_addr, system.Memory().GetPointer(cpu_addr), aligned_size, cpu_addr); 53 MapBackingMemory(gpu_addr, system.Memory().GetPointer(cpu_addr), aligned_size, cpu_addr);
54 ASSERT(system.CurrentProcess() 54 ASSERT(
55 ->PageTable() 55 system.CurrentProcess()->PageTable().LockForDeviceAddressSpace(cpu_addr, size).IsSuccess());
56 .SetMemoryAttribute(cpu_addr, size, Kernel::Memory::MemoryAttribute::DeviceShared,
57 Kernel::Memory::MemoryAttribute::DeviceShared)
58 .IsSuccess());
59 56
60 return gpu_addr; 57 return gpu_addr;
61} 58}
@@ -66,11 +63,8 @@ GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size)
66 const u64 aligned_size{Common::AlignUp(size, page_size)}; 63 const u64 aligned_size{Common::AlignUp(size, page_size)};
67 64
68 MapBackingMemory(gpu_addr, system.Memory().GetPointer(cpu_addr), aligned_size, cpu_addr); 65 MapBackingMemory(gpu_addr, system.Memory().GetPointer(cpu_addr), aligned_size, cpu_addr);
69 ASSERT(system.CurrentProcess() 66 ASSERT(
70 ->PageTable() 67 system.CurrentProcess()->PageTable().LockForDeviceAddressSpace(cpu_addr, size).IsSuccess());
71 .SetMemoryAttribute(cpu_addr, size, Kernel::Memory::MemoryAttribute::DeviceShared,
72 Kernel::Memory::MemoryAttribute::DeviceShared)
73 .IsSuccess());
74 return gpu_addr; 68 return gpu_addr;
75} 69}
76 70
@@ -87,9 +81,7 @@ GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) {
87 UnmapRange(gpu_addr, aligned_size); 81 UnmapRange(gpu_addr, aligned_size);
88 ASSERT(system.CurrentProcess() 82 ASSERT(system.CurrentProcess()
89 ->PageTable() 83 ->PageTable()
90 .SetMemoryAttribute(cpu_addr.value(), size, 84 .UnlockForDeviceAddressSpace(cpu_addr.value(), size)
91 Kernel::Memory::MemoryAttribute::DeviceShared,
92 Kernel::Memory::MemoryAttribute::None)
93 .IsSuccess()); 85 .IsSuccess());
94 86
95 return gpu_addr; 87 return gpu_addr;
diff --git a/src/video_core/query_cache.h b/src/video_core/query_cache.h
index 5ea2b01f2..2f75f8801 100644
--- a/src/video_core/query_cache.h
+++ b/src/video_core/query_cache.h
@@ -12,10 +12,12 @@
12#include <mutex> 12#include <mutex>
13#include <optional> 13#include <optional>
14#include <unordered_map> 14#include <unordered_map>
15#include <unordered_set>
15#include <vector> 16#include <vector>
16 17
17#include "common/assert.h" 18#include "common/assert.h"
18#include "core/core.h" 19#include "core/core.h"
20#include "core/settings.h"
19#include "video_core/engines/maxwell_3d.h" 21#include "video_core/engines/maxwell_3d.h"
20#include "video_core/gpu.h" 22#include "video_core/gpu.h"
21#include "video_core/memory_manager.h" 23#include "video_core/memory_manager.h"
@@ -130,6 +132,9 @@ public:
130 } 132 }
131 133
132 query->BindCounter(Stream(type).Current(), timestamp); 134 query->BindCounter(Stream(type).Current(), timestamp);
135 if (Settings::values.use_asynchronous_gpu_emulation) {
136 AsyncFlushQuery(cpu_addr);
137 }
133 } 138 }
134 139
135 /// Updates counters from GPU state. Expected to be called once per draw, clear or dispatch. 140 /// Updates counters from GPU state. Expected to be called once per draw, clear or dispatch.
@@ -170,6 +175,37 @@ public:
170 return streams[static_cast<std::size_t>(type)]; 175 return streams[static_cast<std::size_t>(type)];
171 } 176 }
172 177
178 void CommitAsyncFlushes() {
179 committed_flushes.push_back(uncommitted_flushes);
180 uncommitted_flushes.reset();
181 }
182
183 bool HasUncommittedFlushes() const {
184 return uncommitted_flushes != nullptr;
185 }
186
187 bool ShouldWaitAsyncFlushes() const {
188 if (committed_flushes.empty()) {
189 return false;
190 }
191 return committed_flushes.front() != nullptr;
192 }
193
194 void PopAsyncFlushes() {
195 if (committed_flushes.empty()) {
196 return;
197 }
198 auto& flush_list = committed_flushes.front();
199 if (!flush_list) {
200 committed_flushes.pop_front();
201 return;
202 }
203 for (VAddr query_address : *flush_list) {
204 FlushAndRemoveRegion(query_address, 4);
205 }
206 committed_flushes.pop_front();
207 }
208
173protected: 209protected:
174 std::array<QueryPool, VideoCore::NumQueryTypes> query_pools; 210 std::array<QueryPool, VideoCore::NumQueryTypes> query_pools;
175 211
@@ -224,6 +260,13 @@ private:
224 return found != std::end(contents) ? &*found : nullptr; 260 return found != std::end(contents) ? &*found : nullptr;
225 } 261 }
226 262
263 void AsyncFlushQuery(VAddr addr) {
264 if (!uncommitted_flushes) {
265 uncommitted_flushes = std::make_shared<std::unordered_set<VAddr>>();
266 }
267 uncommitted_flushes->insert(addr);
268 }
269
227 static constexpr std::uintptr_t PAGE_SIZE = 4096; 270 static constexpr std::uintptr_t PAGE_SIZE = 4096;
228 static constexpr unsigned PAGE_SHIFT = 12; 271 static constexpr unsigned PAGE_SHIFT = 12;
229 272
@@ -235,6 +278,9 @@ private:
235 std::unordered_map<u64, std::vector<CachedQuery>> cached_queries; 278 std::unordered_map<u64, std::vector<CachedQuery>> cached_queries;
236 279
237 std::array<CounterStream, VideoCore::NumQueryTypes> streams; 280 std::array<CounterStream, VideoCore::NumQueryTypes> streams;
281
282 std::shared_ptr<std::unordered_set<VAddr>> uncommitted_flushes{};
283 std::list<std::shared_ptr<std::unordered_set<VAddr>>> committed_flushes;
238}; 284};
239 285
240template <class QueryCache, class HostCounter> 286template <class QueryCache, class HostCounter>
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index 8ae5b9c4e..603f61952 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -49,15 +49,33 @@ public:
49 /// Records a GPU query and caches it 49 /// Records a GPU query and caches it
50 virtual void Query(GPUVAddr gpu_addr, QueryType type, std::optional<u64> timestamp) = 0; 50 virtual void Query(GPUVAddr gpu_addr, QueryType type, std::optional<u64> timestamp) = 0;
51 51
52 /// Signal a GPU based semaphore as a fence
53 virtual void SignalSemaphore(GPUVAddr addr, u32 value) = 0;
54
55 /// Signal a GPU based syncpoint as a fence
56 virtual void SignalSyncPoint(u32 value) = 0;
57
58 /// Release all pending fences.
59 virtual void ReleaseFences() = 0;
60
52 /// Notify rasterizer that all caches should be flushed to Switch memory 61 /// Notify rasterizer that all caches should be flushed to Switch memory
53 virtual void FlushAll() = 0; 62 virtual void FlushAll() = 0;
54 63
55 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory 64 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
56 virtual void FlushRegion(VAddr addr, u64 size) = 0; 65 virtual void FlushRegion(VAddr addr, u64 size) = 0;
57 66
67 /// Check if the the specified memory area requires flushing to CPU Memory.
68 virtual bool MustFlushRegion(VAddr addr, u64 size) = 0;
69
58 /// Notify rasterizer that any caches of the specified region should be invalidated 70 /// Notify rasterizer that any caches of the specified region should be invalidated
59 virtual void InvalidateRegion(VAddr addr, u64 size) = 0; 71 virtual void InvalidateRegion(VAddr addr, u64 size) = 0;
60 72
73 /// Notify rasterizer that any caches of the specified region are desync with guest
74 virtual void OnCPUWrite(VAddr addr, u64 size) = 0;
75
76 /// Sync memory between guest and host.
77 virtual void SyncGuestHost() = 0;
78
61 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory 79 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
62 /// and invalidated 80 /// and invalidated
63 virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0; 81 virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0;
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index cb5792407..4efce0de7 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -52,7 +52,7 @@ Buffer OGLBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) {
52} 52}
53 53
54void OGLBufferCache::WriteBarrier() { 54void OGLBufferCache::WriteBarrier() {
55 glMemoryBarrier(GL_ALL_BARRIER_BITS); 55 glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
56} 56}
57 57
58GLuint OGLBufferCache::ToHandle(const Buffer& buffer) { 58GLuint OGLBufferCache::ToHandle(const Buffer& buffer) {
@@ -72,6 +72,7 @@ void OGLBufferCache::UploadBlockData(const Buffer& buffer, std::size_t offset, s
72void OGLBufferCache::DownloadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, 72void OGLBufferCache::DownloadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size,
73 u8* data) { 73 u8* data) {
74 MICROPROFILE_SCOPE(OpenGL_Buffer_Download); 74 MICROPROFILE_SCOPE(OpenGL_Buffer_Download);
75 glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
75 glGetNamedBufferSubData(buffer->GetHandle(), static_cast<GLintptr>(offset), 76 glGetNamedBufferSubData(buffer->GetHandle(), static_cast<GLintptr>(offset),
76 static_cast<GLsizeiptr>(size), data); 77 static_cast<GLsizeiptr>(size), data);
77} 78}
diff --git a/src/video_core/renderer_opengl/gl_fence_manager.cpp b/src/video_core/renderer_opengl/gl_fence_manager.cpp
new file mode 100644
index 000000000..99ddcb3f8
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_fence_manager.cpp
@@ -0,0 +1,72 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6
7#include "video_core/renderer_opengl/gl_fence_manager.h"
8
9namespace OpenGL {
10
11GLInnerFence::GLInnerFence(u32 payload, bool is_stubbed)
12 : VideoCommon::FenceBase(payload, is_stubbed), sync_object{} {}
13
14GLInnerFence::GLInnerFence(GPUVAddr address, u32 payload, bool is_stubbed)
15 : VideoCommon::FenceBase(address, payload, is_stubbed), sync_object{} {}
16
17GLInnerFence::~GLInnerFence() = default;
18
19void GLInnerFence::Queue() {
20 if (is_stubbed) {
21 return;
22 }
23 ASSERT(sync_object.handle == 0);
24 sync_object.Create();
25}
26
27bool GLInnerFence::IsSignaled() const {
28 if (is_stubbed) {
29 return true;
30 }
31 ASSERT(sync_object.handle != 0);
32 GLsizei length;
33 GLint sync_status;
34 glGetSynciv(sync_object.handle, GL_SYNC_STATUS, sizeof(GLint), &length, &sync_status);
35 return sync_status == GL_SIGNALED;
36}
37
38void GLInnerFence::Wait() {
39 if (is_stubbed) {
40 return;
41 }
42 ASSERT(sync_object.handle != 0);
43 glClientWaitSync(sync_object.handle, 0, GL_TIMEOUT_IGNORED);
44}
45
46FenceManagerOpenGL::FenceManagerOpenGL(Core::System& system,
47 VideoCore::RasterizerInterface& rasterizer,
48 TextureCacheOpenGL& texture_cache,
49 OGLBufferCache& buffer_cache, QueryCache& query_cache)
50 : GenericFenceManager(system, rasterizer, texture_cache, buffer_cache, query_cache) {}
51
52Fence FenceManagerOpenGL::CreateFence(u32 value, bool is_stubbed) {
53 return std::make_shared<GLInnerFence>(value, is_stubbed);
54}
55
56Fence FenceManagerOpenGL::CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) {
57 return std::make_shared<GLInnerFence>(addr, value, is_stubbed);
58}
59
60void FenceManagerOpenGL::QueueFence(Fence& fence) {
61 fence->Queue();
62}
63
64bool FenceManagerOpenGL::IsFenceSignaled(Fence& fence) const {
65 return fence->IsSignaled();
66}
67
68void FenceManagerOpenGL::WaitFence(Fence& fence) {
69 fence->Wait();
70}
71
72} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_fence_manager.h b/src/video_core/renderer_opengl/gl_fence_manager.h
new file mode 100644
index 000000000..c917b3343
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_fence_manager.h
@@ -0,0 +1,53 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <glad/glad.h>
9
10#include "common/common_types.h"
11#include "video_core/fence_manager.h"
12#include "video_core/renderer_opengl/gl_buffer_cache.h"
13#include "video_core/renderer_opengl/gl_query_cache.h"
14#include "video_core/renderer_opengl/gl_resource_manager.h"
15#include "video_core/renderer_opengl/gl_texture_cache.h"
16
17namespace OpenGL {
18
19class GLInnerFence : public VideoCommon::FenceBase {
20public:
21 GLInnerFence(u32 payload, bool is_stubbed);
22 GLInnerFence(GPUVAddr address, u32 payload, bool is_stubbed);
23 ~GLInnerFence();
24
25 void Queue();
26
27 bool IsSignaled() const;
28
29 void Wait();
30
31private:
32 OGLSync sync_object;
33};
34
35using Fence = std::shared_ptr<GLInnerFence>;
36using GenericFenceManager =
37 VideoCommon::FenceManager<Fence, TextureCacheOpenGL, OGLBufferCache, QueryCache>;
38
39class FenceManagerOpenGL final : public GenericFenceManager {
40public:
41 FenceManagerOpenGL(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
42 TextureCacheOpenGL& texture_cache, OGLBufferCache& buffer_cache,
43 QueryCache& query_cache);
44
45protected:
46 Fence CreateFence(u32 value, bool is_stubbed) override;
47 Fence CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) override;
48 void QueueFence(Fence& fence) override;
49 bool IsFenceSignaled(Fence& fence) const override;
50 void WaitFence(Fence& fence) override;
51};
52
53} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 175374f0d..6fe155bcc 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -99,9 +99,10 @@ RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWind
99 ScreenInfo& info, GLShader::ProgramManager& program_manager, 99 ScreenInfo& info, GLShader::ProgramManager& program_manager,
100 StateTracker& state_tracker) 100 StateTracker& state_tracker)
101 : RasterizerAccelerated{system.Memory()}, texture_cache{system, *this, device, state_tracker}, 101 : RasterizerAccelerated{system.Memory()}, texture_cache{system, *this, device, state_tracker},
102 shader_cache{*this, system, emu_window, device}, query_cache{system, *this}, system{system}, 102 shader_cache{*this, system, emu_window, device}, query_cache{system, *this},
103 screen_info{info}, program_manager{program_manager}, state_tracker{state_tracker}, 103 buffer_cache{*this, system, device, STREAM_BUFFER_SIZE},
104 buffer_cache{*this, system, device, STREAM_BUFFER_SIZE} { 104 fence_manager{system, *this, texture_cache, buffer_cache, query_cache}, system{system},
105 screen_info{info}, program_manager{program_manager}, state_tracker{state_tracker} {
105 CheckExtensions(); 106 CheckExtensions();
106} 107}
107 108
@@ -185,8 +186,12 @@ void RasterizerOpenGL::SetupVertexBuffer() {
185 const GPUVAddr start = vertex_array.StartAddress(); 186 const GPUVAddr start = vertex_array.StartAddress();
186 const GPUVAddr end = regs.vertex_array_limit[index].LimitAddress(); 187 const GPUVAddr end = regs.vertex_array_limit[index].LimitAddress();
187 188
188 ASSERT(end > start); 189 ASSERT(end >= start);
189 const u64 size = end - start + 1; 190 const u64 size = end - start;
191 if (size == 0) {
192 glBindVertexBuffer(static_cast<GLuint>(index), 0, 0, vertex_array.stride);
193 continue;
194 }
190 const auto [vertex_buffer, vertex_buffer_offset] = buffer_cache.UploadMemory(start, size); 195 const auto [vertex_buffer, vertex_buffer_offset] = buffer_cache.UploadMemory(start, size);
191 glBindVertexBuffer(static_cast<GLuint>(index), vertex_buffer, vertex_buffer_offset, 196 glBindVertexBuffer(static_cast<GLuint>(index), vertex_buffer, vertex_buffer_offset,
192 vertex_array.stride); 197 vertex_array.stride);
@@ -310,8 +315,8 @@ std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
310 const GPUVAddr start = regs.vertex_array[index].StartAddress(); 315 const GPUVAddr start = regs.vertex_array[index].StartAddress();
311 const GPUVAddr end = regs.vertex_array_limit[index].LimitAddress(); 316 const GPUVAddr end = regs.vertex_array_limit[index].LimitAddress();
312 317
313 ASSERT(end > start); 318 size += end - start;
314 size += end - start + 1; 319 ASSERT(end >= start);
315 } 320 }
316 321
317 return size; 322 return size;
@@ -599,6 +604,8 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
599 EndTransformFeedback(); 604 EndTransformFeedback();
600 605
601 ++num_queued_commands; 606 ++num_queued_commands;
607
608 system.GPU().TickWork();
602} 609}
603 610
604void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) { 611void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
@@ -649,6 +656,13 @@ void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) {
649 query_cache.FlushRegion(addr, size); 656 query_cache.FlushRegion(addr, size);
650} 657}
651 658
659bool RasterizerOpenGL::MustFlushRegion(VAddr addr, u64 size) {
660 if (!Settings::IsGPULevelHigh()) {
661 return buffer_cache.MustFlushRegion(addr, size);
662 }
663 return texture_cache.MustFlushRegion(addr, size) || buffer_cache.MustFlushRegion(addr, size);
664}
665
652void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) { 666void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
653 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 667 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
654 if (addr == 0 || size == 0) { 668 if (addr == 0 || size == 0) {
@@ -660,8 +674,52 @@ void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
660 query_cache.InvalidateRegion(addr, size); 674 query_cache.InvalidateRegion(addr, size);
661} 675}
662 676
677void RasterizerOpenGL::OnCPUWrite(VAddr addr, u64 size) {
678 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
679 if (addr == 0 || size == 0) {
680 return;
681 }
682 texture_cache.OnCPUWrite(addr, size);
683 shader_cache.InvalidateRegion(addr, size);
684 buffer_cache.OnCPUWrite(addr, size);
685 query_cache.InvalidateRegion(addr, size);
686}
687
688void RasterizerOpenGL::SyncGuestHost() {
689 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
690 texture_cache.SyncGuestHost();
691 buffer_cache.SyncGuestHost();
692}
693
694void RasterizerOpenGL::SignalSemaphore(GPUVAddr addr, u32 value) {
695 auto& gpu{system.GPU()};
696 if (!gpu.IsAsync()) {
697 auto& memory_manager{gpu.MemoryManager()};
698 memory_manager.Write<u32>(addr, value);
699 return;
700 }
701 fence_manager.SignalSemaphore(addr, value);
702}
703
704void RasterizerOpenGL::SignalSyncPoint(u32 value) {
705 auto& gpu{system.GPU()};
706 if (!gpu.IsAsync()) {
707 gpu.IncrementSyncPoint(value);
708 return;
709 }
710 fence_manager.SignalSyncPoint(value);
711}
712
713void RasterizerOpenGL::ReleaseFences() {
714 auto& gpu{system.GPU()};
715 if (!gpu.IsAsync()) {
716 return;
717 }
718 fence_manager.WaitPendingFences();
719}
720
663void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) { 721void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) {
664 if (Settings::values.use_accurate_gpu_emulation) { 722 if (Settings::IsGPULevelExtreme()) {
665 FlushRegion(addr, size); 723 FlushRegion(addr, size);
666 } 724 }
667 InvalidateRegion(addr, size); 725 InvalidateRegion(addr, size);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index caea174d2..ebd2173eb 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -23,6 +23,7 @@
23#include "video_core/rasterizer_interface.h" 23#include "video_core/rasterizer_interface.h"
24#include "video_core/renderer_opengl/gl_buffer_cache.h" 24#include "video_core/renderer_opengl/gl_buffer_cache.h"
25#include "video_core/renderer_opengl/gl_device.h" 25#include "video_core/renderer_opengl/gl_device.h"
26#include "video_core/renderer_opengl/gl_fence_manager.h"
26#include "video_core/renderer_opengl/gl_framebuffer_cache.h" 27#include "video_core/renderer_opengl/gl_framebuffer_cache.h"
27#include "video_core/renderer_opengl/gl_query_cache.h" 28#include "video_core/renderer_opengl/gl_query_cache.h"
28#include "video_core/renderer_opengl/gl_resource_manager.h" 29#include "video_core/renderer_opengl/gl_resource_manager.h"
@@ -66,7 +67,13 @@ public:
66 void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override; 67 void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override;
67 void FlushAll() override; 68 void FlushAll() override;
68 void FlushRegion(VAddr addr, u64 size) override; 69 void FlushRegion(VAddr addr, u64 size) override;
70 bool MustFlushRegion(VAddr addr, u64 size) override;
69 void InvalidateRegion(VAddr addr, u64 size) override; 71 void InvalidateRegion(VAddr addr, u64 size) override;
72 void OnCPUWrite(VAddr addr, u64 size) override;
73 void SyncGuestHost() override;
74 void SignalSemaphore(GPUVAddr addr, u32 value) override;
75 void SignalSyncPoint(u32 value) override;
76 void ReleaseFences() override;
70 void FlushAndInvalidateRegion(VAddr addr, u64 size) override; 77 void FlushAndInvalidateRegion(VAddr addr, u64 size) override;
71 void FlushCommands() override; 78 void FlushCommands() override;
72 void TickFrame() override; 79 void TickFrame() override;
@@ -222,6 +229,8 @@ private:
222 SamplerCacheOpenGL sampler_cache; 229 SamplerCacheOpenGL sampler_cache;
223 FramebufferCacheOpenGL framebuffer_cache; 230 FramebufferCacheOpenGL framebuffer_cache;
224 QueryCache query_cache; 231 QueryCache query_cache;
232 OGLBufferCache buffer_cache;
233 FenceManagerOpenGL fence_manager;
225 234
226 Core::System& system; 235 Core::System& system;
227 ScreenInfo& screen_info; 236 ScreenInfo& screen_info;
@@ -229,7 +238,6 @@ private:
229 StateTracker& state_tracker; 238 StateTracker& state_tracker;
230 239
231 static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; 240 static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
232 OGLBufferCache buffer_cache;
233 241
234 GLint vertex_binding = 0; 242 GLint vertex_binding = 0;
235 243
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 6d2ff20f9..f63156b8d 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -448,7 +448,7 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
448 448
449 // Look up shader in the cache based on address 449 // Look up shader in the cache based on address
450 const auto cpu_addr{memory_manager.GpuToCpuAddress(address)}; 450 const auto cpu_addr{memory_manager.GpuToCpuAddress(address)};
451 Shader shader{cpu_addr ? TryGet(*cpu_addr) : nullptr}; 451 Shader shader{cpu_addr ? TryGet(*cpu_addr) : null_shader};
452 if (shader) { 452 if (shader) {
453 return last_shaders[static_cast<std::size_t>(program)] = shader; 453 return last_shaders[static_cast<std::size_t>(program)] = shader;
454 } 454 }
@@ -477,7 +477,12 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
477 const std::size_t size_in_bytes = code.size() * sizeof(u64); 477 const std::size_t size_in_bytes = code.size() * sizeof(u64);
478 shader = CachedShader::CreateFromCache(params, found->second, size_in_bytes); 478 shader = CachedShader::CreateFromCache(params, found->second, size_in_bytes);
479 } 479 }
480 Register(shader); 480
481 if (cpu_addr) {
482 Register(shader);
483 } else {
484 null_shader = shader;
485 }
481 486
482 return last_shaders[static_cast<std::size_t>(program)] = shader; 487 return last_shaders[static_cast<std::size_t>(program)] = shader;
483} 488}
@@ -486,7 +491,7 @@ Shader ShaderCacheOpenGL::GetComputeKernel(GPUVAddr code_addr) {
486 auto& memory_manager{system.GPU().MemoryManager()}; 491 auto& memory_manager{system.GPU().MemoryManager()};
487 const auto cpu_addr{memory_manager.GpuToCpuAddress(code_addr)}; 492 const auto cpu_addr{memory_manager.GpuToCpuAddress(code_addr)};
488 493
489 auto kernel = cpu_addr ? TryGet(*cpu_addr) : nullptr; 494 auto kernel = cpu_addr ? TryGet(*cpu_addr) : null_kernel;
490 if (kernel) { 495 if (kernel) {
491 return kernel; 496 return kernel;
492 } 497 }
@@ -507,7 +512,11 @@ Shader ShaderCacheOpenGL::GetComputeKernel(GPUVAddr code_addr) {
507 kernel = CachedShader::CreateFromCache(params, found->second, size_in_bytes); 512 kernel = CachedShader::CreateFromCache(params, found->second, size_in_bytes);
508 } 513 }
509 514
510 Register(kernel); 515 if (cpu_addr) {
516 Register(kernel);
517 } else {
518 null_kernel = kernel;
519 }
511 return kernel; 520 return kernel;
512} 521}
513 522
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index c836df5bd..91690b470 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -125,6 +125,9 @@ private:
125 ShaderDiskCacheOpenGL disk_cache; 125 ShaderDiskCacheOpenGL disk_cache;
126 std::unordered_map<u64, PrecompiledShader> runtime_cache; 126 std::unordered_map<u64, PrecompiledShader> runtime_cache;
127 127
128 Shader null_shader{};
129 Shader null_kernel{};
130
128 std::array<Shader, Maxwell::MaxShaderProgram> last_shaders; 131 std::array<Shader, Maxwell::MaxShaderProgram> last_shaders;
129}; 132};
130 133
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
index 9fe6bdbf9..9a950f4de 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
@@ -129,7 +129,7 @@ struct alignas(32) FixedPipelineState {
129 auto& binding = bindings[index]; 129 auto& binding = bindings[index];
130 binding.raw = 0; 130 binding.raw = 0;
131 binding.enabled.Assign(enabled ? 1 : 0); 131 binding.enabled.Assign(enabled ? 1 : 0);
132 binding.stride.Assign(stride); 132 binding.stride.Assign(static_cast<u16>(stride));
133 binding_divisors[index] = divisor; 133 binding_divisors[index] = divisor;
134 } 134 }
135 135
diff --git a/src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp b/src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp
new file mode 100644
index 000000000..435c8c1b8
--- /dev/null
+++ b/src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp
@@ -0,0 +1,220 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#ifdef HAS_NSIGHT_AFTERMATH
6
7#include <mutex>
8#include <string>
9#include <string_view>
10#include <utility>
11#include <vector>
12
13#include <fmt/format.h>
14
15#define VK_NO_PROTOTYPES
16#include <vulkan/vulkan.h>
17
18#include <GFSDK_Aftermath.h>
19#include <GFSDK_Aftermath_Defines.h>
20#include <GFSDK_Aftermath_GpuCrashDump.h>
21#include <GFSDK_Aftermath_GpuCrashDumpDecoding.h>
22
23#include "common/common_paths.h"
24#include "common/common_types.h"
25#include "common/file_util.h"
26#include "common/logging/log.h"
27#include "common/scope_exit.h"
28
29#include "video_core/renderer_vulkan/nsight_aftermath_tracker.h"
30
31namespace Vulkan {
32
33static constexpr char AFTERMATH_LIB_NAME[] = "GFSDK_Aftermath_Lib.x64.dll";
34
35NsightAftermathTracker::NsightAftermathTracker() = default;
36
37NsightAftermathTracker::~NsightAftermathTracker() {
38 if (initialized) {
39 (void)GFSDK_Aftermath_DisableGpuCrashDumps();
40 }
41}
42
43bool NsightAftermathTracker::Initialize() {
44 if (!dl.Open(AFTERMATH_LIB_NAME)) {
45 LOG_ERROR(Render_Vulkan, "Failed to load Nsight Aftermath DLL");
46 return false;
47 }
48
49 if (!dl.GetSymbol("GFSDK_Aftermath_DisableGpuCrashDumps",
50 &GFSDK_Aftermath_DisableGpuCrashDumps) ||
51 !dl.GetSymbol("GFSDK_Aftermath_EnableGpuCrashDumps",
52 &GFSDK_Aftermath_EnableGpuCrashDumps) ||
53 !dl.GetSymbol("GFSDK_Aftermath_GetShaderDebugInfoIdentifier",
54 &GFSDK_Aftermath_GetShaderDebugInfoIdentifier) ||
55 !dl.GetSymbol("GFSDK_Aftermath_GetShaderHashSpirv", &GFSDK_Aftermath_GetShaderHashSpirv) ||
56 !dl.GetSymbol("GFSDK_Aftermath_GpuCrashDump_CreateDecoder",
57 &GFSDK_Aftermath_GpuCrashDump_CreateDecoder) ||
58 !dl.GetSymbol("GFSDK_Aftermath_GpuCrashDump_DestroyDecoder",
59 &GFSDK_Aftermath_GpuCrashDump_DestroyDecoder) ||
60 !dl.GetSymbol("GFSDK_Aftermath_GpuCrashDump_GenerateJSON",
61 &GFSDK_Aftermath_GpuCrashDump_GenerateJSON) ||
62 !dl.GetSymbol("GFSDK_Aftermath_GpuCrashDump_GetJSON",
63 &GFSDK_Aftermath_GpuCrashDump_GetJSON)) {
64 LOG_ERROR(Render_Vulkan, "Failed to load Nsight Aftermath function pointers");
65 return false;
66 }
67
68 dump_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir) + "gpucrash";
69
70 (void)FileUtil::DeleteDirRecursively(dump_dir);
71 if (!FileUtil::CreateDir(dump_dir)) {
72 LOG_ERROR(Render_Vulkan, "Failed to create Nsight Aftermath dump directory");
73 return false;
74 }
75
76 if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_EnableGpuCrashDumps(
77 GFSDK_Aftermath_Version_API, GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_Vulkan,
78 GFSDK_Aftermath_GpuCrashDumpFeatureFlags_Default, GpuCrashDumpCallback,
79 ShaderDebugInfoCallback, CrashDumpDescriptionCallback, this))) {
80 LOG_ERROR(Render_Vulkan, "GFSDK_Aftermath_EnableGpuCrashDumps failed");
81 return false;
82 }
83
84 LOG_INFO(Render_Vulkan, "Nsight Aftermath dump directory is \"{}\"", dump_dir);
85
86 initialized = true;
87 return true;
88}
89
90void NsightAftermathTracker::SaveShader(const std::vector<u32>& spirv) const {
91 if (!initialized) {
92 return;
93 }
94
95 std::vector<u32> spirv_copy = spirv;
96 GFSDK_Aftermath_SpirvCode shader;
97 shader.pData = spirv_copy.data();
98 shader.size = static_cast<u32>(spirv_copy.size() * 4);
99
100 std::scoped_lock lock{mutex};
101
102 GFSDK_Aftermath_ShaderHash hash;
103 if (!GFSDK_Aftermath_SUCCEED(
104 GFSDK_Aftermath_GetShaderHashSpirv(GFSDK_Aftermath_Version_API, &shader, &hash))) {
105 LOG_ERROR(Render_Vulkan, "Failed to hash SPIR-V module");
106 return;
107 }
108
109 FileUtil::IOFile file(fmt::format("{}/source_{:016x}.spv", dump_dir, hash.hash), "wb");
110 if (!file.IsOpen()) {
111 LOG_ERROR(Render_Vulkan, "Failed to dump SPIR-V module with hash={:016x}", hash.hash);
112 return;
113 }
114 if (file.WriteArray(spirv.data(), spirv.size()) != spirv.size()) {
115 LOG_ERROR(Render_Vulkan, "Failed to write SPIR-V module with hash={:016x}", hash.hash);
116 return;
117 }
118}
119
120void NsightAftermathTracker::OnGpuCrashDumpCallback(const void* gpu_crash_dump,
121 u32 gpu_crash_dump_size) {
122 std::scoped_lock lock{mutex};
123
124 LOG_CRITICAL(Render_Vulkan, "called");
125
126 GFSDK_Aftermath_GpuCrashDump_Decoder decoder;
127 if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GpuCrashDump_CreateDecoder(
128 GFSDK_Aftermath_Version_API, gpu_crash_dump, gpu_crash_dump_size, &decoder))) {
129 LOG_ERROR(Render_Vulkan, "Failed to create decoder");
130 return;
131 }
132 SCOPE_EXIT({ GFSDK_Aftermath_GpuCrashDump_DestroyDecoder(decoder); });
133
134 u32 json_size = 0;
135 if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GpuCrashDump_GenerateJSON(
136 decoder, GFSDK_Aftermath_GpuCrashDumpDecoderFlags_ALL_INFO,
137 GFSDK_Aftermath_GpuCrashDumpFormatterFlags_NONE, nullptr, nullptr, nullptr, nullptr,
138 this, &json_size))) {
139 LOG_ERROR(Render_Vulkan, "Failed to generate JSON");
140 return;
141 }
142 std::vector<char> json(json_size);
143 if (!GFSDK_Aftermath_SUCCEED(
144 GFSDK_Aftermath_GpuCrashDump_GetJSON(decoder, json_size, json.data()))) {
145 LOG_ERROR(Render_Vulkan, "Failed to query JSON");
146 return;
147 }
148
149 const std::string base_name = [this] {
150 const int id = dump_id++;
151 if (id == 0) {
152 return fmt::format("{}/crash.nv-gpudmp", dump_dir);
153 } else {
154 return fmt::format("{}/crash_{}.nv-gpudmp", dump_dir, id);
155 }
156 }();
157
158 std::string_view dump_view(static_cast<const char*>(gpu_crash_dump), gpu_crash_dump_size);
159 if (FileUtil::WriteStringToFile(false, base_name, dump_view) != gpu_crash_dump_size) {
160 LOG_ERROR(Render_Vulkan, "Failed to write dump file");
161 return;
162 }
163 const std::string_view json_view(json.data(), json.size());
164 if (FileUtil::WriteStringToFile(true, base_name + ".json", json_view) != json.size()) {
165 LOG_ERROR(Render_Vulkan, "Failed to write JSON");
166 return;
167 }
168}
169
170void NsightAftermathTracker::OnShaderDebugInfoCallback(const void* shader_debug_info,
171 u32 shader_debug_info_size) {
172 std::scoped_lock lock{mutex};
173
174 GFSDK_Aftermath_ShaderDebugInfoIdentifier identifier;
175 if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GetShaderDebugInfoIdentifier(
176 GFSDK_Aftermath_Version_API, shader_debug_info, shader_debug_info_size, &identifier))) {
177 LOG_ERROR(Render_Vulkan, "GFSDK_Aftermath_GetShaderDebugInfoIdentifier failed");
178 return;
179 }
180
181 const std::string path =
182 fmt::format("{}/shader_{:016x}{:016x}.nvdbg", dump_dir, identifier.id[0], identifier.id[1]);
183 FileUtil::IOFile file(path, "wb");
184 if (!file.IsOpen()) {
185 LOG_ERROR(Render_Vulkan, "Failed to create file {}", path);
186 return;
187 }
188 if (file.WriteBytes(static_cast<const u8*>(shader_debug_info), shader_debug_info_size) !=
189 shader_debug_info_size) {
190 LOG_ERROR(Render_Vulkan, "Failed to write file {}", path);
191 return;
192 }
193}
194
195void NsightAftermathTracker::OnCrashDumpDescriptionCallback(
196 PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription add_description) {
197 add_description(GFSDK_Aftermath_GpuCrashDumpDescriptionKey_ApplicationName, "yuzu");
198}
199
200void NsightAftermathTracker::GpuCrashDumpCallback(const void* gpu_crash_dump,
201 u32 gpu_crash_dump_size, void* user_data) {
202 static_cast<NsightAftermathTracker*>(user_data)->OnGpuCrashDumpCallback(gpu_crash_dump,
203 gpu_crash_dump_size);
204}
205
206void NsightAftermathTracker::ShaderDebugInfoCallback(const void* shader_debug_info,
207 u32 shader_debug_info_size, void* user_data) {
208 static_cast<NsightAftermathTracker*>(user_data)->OnShaderDebugInfoCallback(
209 shader_debug_info, shader_debug_info_size);
210}
211
212void NsightAftermathTracker::CrashDumpDescriptionCallback(
213 PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription add_description, void* user_data) {
214 static_cast<NsightAftermathTracker*>(user_data)->OnCrashDumpDescriptionCallback(
215 add_description);
216}
217
218} // namespace Vulkan
219
220#endif // HAS_NSIGHT_AFTERMATH
diff --git a/src/video_core/renderer_vulkan/nsight_aftermath_tracker.h b/src/video_core/renderer_vulkan/nsight_aftermath_tracker.h
new file mode 100644
index 000000000..afe7ae99e
--- /dev/null
+++ b/src/video_core/renderer_vulkan/nsight_aftermath_tracker.h
@@ -0,0 +1,87 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <mutex>
8#include <string>
9#include <vector>
10
11#define VK_NO_PROTOTYPES
12#include <vulkan/vulkan.h>
13
14#ifdef HAS_NSIGHT_AFTERMATH
15#include <GFSDK_Aftermath_Defines.h>
16#include <GFSDK_Aftermath_GpuCrashDump.h>
17#include <GFSDK_Aftermath_GpuCrashDumpDecoding.h>
18#endif
19
20#include "common/common_types.h"
21#include "common/dynamic_library.h"
22
23namespace Vulkan {
24
25class NsightAftermathTracker {
26public:
27 NsightAftermathTracker();
28 ~NsightAftermathTracker();
29
30 NsightAftermathTracker(const NsightAftermathTracker&) = delete;
31 NsightAftermathTracker& operator=(const NsightAftermathTracker&) = delete;
32
33 // Delete move semantics because Aftermath initialization uses a pointer to this.
34 NsightAftermathTracker(NsightAftermathTracker&&) = delete;
35 NsightAftermathTracker& operator=(NsightAftermathTracker&&) = delete;
36
37 bool Initialize();
38
39 void SaveShader(const std::vector<u32>& spirv) const;
40
41private:
42#ifdef HAS_NSIGHT_AFTERMATH
43 static void GpuCrashDumpCallback(const void* gpu_crash_dump, u32 gpu_crash_dump_size,
44 void* user_data);
45
46 static void ShaderDebugInfoCallback(const void* shader_debug_info, u32 shader_debug_info_size,
47 void* user_data);
48
49 static void CrashDumpDescriptionCallback(
50 PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription add_description, void* user_data);
51
52 void OnGpuCrashDumpCallback(const void* gpu_crash_dump, u32 gpu_crash_dump_size);
53
54 void OnShaderDebugInfoCallback(const void* shader_debug_info, u32 shader_debug_info_size);
55
56 void OnCrashDumpDescriptionCallback(
57 PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription add_description);
58
59 mutable std::mutex mutex;
60
61 std::string dump_dir;
62 int dump_id = 0;
63
64 bool initialized = false;
65
66 Common::DynamicLibrary dl;
67 PFN_GFSDK_Aftermath_DisableGpuCrashDumps GFSDK_Aftermath_DisableGpuCrashDumps;
68 PFN_GFSDK_Aftermath_EnableGpuCrashDumps GFSDK_Aftermath_EnableGpuCrashDumps;
69 PFN_GFSDK_Aftermath_GetShaderDebugInfoIdentifier GFSDK_Aftermath_GetShaderDebugInfoIdentifier;
70 PFN_GFSDK_Aftermath_GetShaderHashSpirv GFSDK_Aftermath_GetShaderHashSpirv;
71 PFN_GFSDK_Aftermath_GpuCrashDump_CreateDecoder GFSDK_Aftermath_GpuCrashDump_CreateDecoder;
72 PFN_GFSDK_Aftermath_GpuCrashDump_DestroyDecoder GFSDK_Aftermath_GpuCrashDump_DestroyDecoder;
73 PFN_GFSDK_Aftermath_GpuCrashDump_GenerateJSON GFSDK_Aftermath_GpuCrashDump_GenerateJSON;
74 PFN_GFSDK_Aftermath_GpuCrashDump_GetJSON GFSDK_Aftermath_GpuCrashDump_GetJSON;
75#endif
76};
77
78#ifndef HAS_NSIGHT_AFTERMATH
79inline NsightAftermathTracker::NsightAftermathTracker() = default;
80inline NsightAftermathTracker::~NsightAftermathTracker() = default;
81inline bool NsightAftermathTracker::Initialize() {
82 return false;
83}
84inline void NsightAftermathTracker::SaveShader(const std::vector<u32>&) const {}
85#endif
86
87} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
index 23beafa4f..52566bb79 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
@@ -105,6 +105,8 @@ vk::DescriptorUpdateTemplateKHR VKComputePipeline::CreateDescriptorUpdateTemplat
105} 105}
106 106
107vk::ShaderModule VKComputePipeline::CreateShaderModule(const std::vector<u32>& code) const { 107vk::ShaderModule VKComputePipeline::CreateShaderModule(const std::vector<u32>& code) const {
108 device.SaveShader(code);
109
108 VkShaderModuleCreateInfo ci; 110 VkShaderModuleCreateInfo ci;
109 ci.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; 111 ci.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
110 ci.pNext = nullptr; 112 ci.pNext = nullptr;
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp
index 52d29e49d..e90c76492 100644
--- a/src/video_core/renderer_vulkan/vk_device.cpp
+++ b/src/video_core/renderer_vulkan/vk_device.cpp
@@ -9,6 +9,7 @@
9#include <string_view> 9#include <string_view>
10#include <thread> 10#include <thread>
11#include <unordered_set> 11#include <unordered_set>
12#include <utility>
12#include <vector> 13#include <vector>
13 14
14#include "common/assert.h" 15#include "common/assert.h"
@@ -167,6 +168,7 @@ bool VKDevice::Create() {
167 VkPhysicalDeviceFeatures2 features2; 168 VkPhysicalDeviceFeatures2 features2;
168 features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; 169 features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
169 features2.pNext = nullptr; 170 features2.pNext = nullptr;
171 const void* first_next = &features2;
170 void** next = &features2.pNext; 172 void** next = &features2.pNext;
171 173
172 auto& features = features2.features; 174 auto& features = features2.features;
@@ -296,7 +298,19 @@ bool VKDevice::Create() {
296 LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted"); 298 LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted");
297 } 299 }
298 300
299 logical = vk::Device::Create(physical, queue_cis, extensions, features2, dld); 301 VkDeviceDiagnosticsConfigCreateInfoNV diagnostics_nv;
302 if (nv_device_diagnostics_config) {
303 nsight_aftermath_tracker.Initialize();
304
305 diagnostics_nv.sType = VK_STRUCTURE_TYPE_DEVICE_DIAGNOSTICS_CONFIG_CREATE_INFO_NV;
306 diagnostics_nv.pNext = &features2;
307 diagnostics_nv.flags = VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_SHADER_DEBUG_INFO_BIT_NV |
308 VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_RESOURCE_TRACKING_BIT_NV |
309 VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_AUTOMATIC_CHECKPOINTS_BIT_NV;
310 first_next = &diagnostics_nv;
311 }
312
313 logical = vk::Device::Create(physical, queue_cis, extensions, first_next, dld);
300 if (!logical) { 314 if (!logical) {
301 LOG_ERROR(Render_Vulkan, "Failed to create logical device"); 315 LOG_ERROR(Render_Vulkan, "Failed to create logical device");
302 return false; 316 return false;
@@ -344,17 +358,12 @@ VkFormat VKDevice::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFla
344void VKDevice::ReportLoss() const { 358void VKDevice::ReportLoss() const {
345 LOG_CRITICAL(Render_Vulkan, "Device loss occured!"); 359 LOG_CRITICAL(Render_Vulkan, "Device loss occured!");
346 360
347 // Wait some time to let the log flush 361 // Wait for the log to flush and for Nsight Aftermath to dump the results
348 std::this_thread::sleep_for(std::chrono::seconds{1}); 362 std::this_thread::sleep_for(std::chrono::seconds{3});
349 363}
350 if (!nv_device_diagnostic_checkpoints) {
351 return;
352 }
353 364
354 [[maybe_unused]] const std::vector data = graphics_queue.GetCheckpointDataNV(dld); 365void VKDevice::SaveShader(const std::vector<u32>& spirv) const {
355 // Catch here in debug builds (or with optimizations disabled) the last graphics pipeline to be 366 nsight_aftermath_tracker.SaveShader(spirv);
356 // executed. It can be done on a debugger by evaluating the expression:
357 // *(VKGraphicsPipeline*)data[0]
358} 367}
359 368
360bool VKDevice::IsOptimalAstcSupported(const VkPhysicalDeviceFeatures& features) const { 369bool VKDevice::IsOptimalAstcSupported(const VkPhysicalDeviceFeatures& features) const {
@@ -527,8 +536,8 @@ std::vector<const char*> VKDevice::LoadExtensions() {
527 Test(extension, has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, 536 Test(extension, has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME,
528 false); 537 false);
529 if (Settings::values.renderer_debug) { 538 if (Settings::values.renderer_debug) {
530 Test(extension, nv_device_diagnostic_checkpoints, 539 Test(extension, nv_device_diagnostics_config,
531 VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME, true); 540 VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, true);
532 } 541 }
533 } 542 }
534 543
diff --git a/src/video_core/renderer_vulkan/vk_device.h b/src/video_core/renderer_vulkan/vk_device.h
index bac58d385..c8640762d 100644
--- a/src/video_core/renderer_vulkan/vk_device.h
+++ b/src/video_core/renderer_vulkan/vk_device.h
@@ -10,6 +10,7 @@
10#include <vector> 10#include <vector>
11 11
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "video_core/renderer_vulkan/nsight_aftermath_tracker.h"
13#include "video_core/renderer_vulkan/wrapper.h" 14#include "video_core/renderer_vulkan/wrapper.h"
14 15
15namespace Vulkan { 16namespace Vulkan {
@@ -43,6 +44,9 @@ public:
43 /// Reports a device loss. 44 /// Reports a device loss.
44 void ReportLoss() const; 45 void ReportLoss() const;
45 46
47 /// Reports a shader to Nsight Aftermath.
48 void SaveShader(const std::vector<u32>& spirv) const;
49
46 /// Returns the dispatch loader with direct function pointers of the device. 50 /// Returns the dispatch loader with direct function pointers of the device.
47 const vk::DeviceDispatch& GetDispatchLoader() const { 51 const vk::DeviceDispatch& GetDispatchLoader() const {
48 return dld; 52 return dld;
@@ -168,11 +172,6 @@ public:
168 return ext_transform_feedback; 172 return ext_transform_feedback;
169 } 173 }
170 174
171 /// Returns true if the device supports VK_NV_device_diagnostic_checkpoints.
172 bool IsNvDeviceDiagnosticCheckpoints() const {
173 return nv_device_diagnostic_checkpoints;
174 }
175
176 /// Returns the vendor name reported from Vulkan. 175 /// Returns the vendor name reported from Vulkan.
177 std::string_view GetVendorName() const { 176 std::string_view GetVendorName() const {
178 return vendor_name; 177 return vendor_name;
@@ -228,7 +227,7 @@ private:
228 bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted. 227 bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted.
229 bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer. 228 bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer.
230 bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback. 229 bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback.
231 bool nv_device_diagnostic_checkpoints{}; ///< Support for VK_NV_device_diagnostic_checkpoints. 230 bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config.
232 231
233 // Telemetry parameters 232 // Telemetry parameters
234 std::string vendor_name; ///< Device's driver name. 233 std::string vendor_name; ///< Device's driver name.
@@ -236,6 +235,9 @@ private:
236 235
237 /// Format properties dictionary. 236 /// Format properties dictionary.
238 std::unordered_map<VkFormat, VkFormatProperties> format_properties; 237 std::unordered_map<VkFormat, VkFormatProperties> format_properties;
238
239 /// Nsight Aftermath GPU crash tracker
240 NsightAftermathTracker nsight_aftermath_tracker;
239}; 241};
240 242
241} // namespace Vulkan 243} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.cpp b/src/video_core/renderer_vulkan/vk_fence_manager.cpp
new file mode 100644
index 000000000..a02be5487
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_fence_manager.cpp
@@ -0,0 +1,101 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6#include <thread>
7
8#include "video_core/renderer_vulkan/vk_buffer_cache.h"
9#include "video_core/renderer_vulkan/vk_device.h"
10#include "video_core/renderer_vulkan/vk_fence_manager.h"
11#include "video_core/renderer_vulkan/vk_scheduler.h"
12#include "video_core/renderer_vulkan/vk_texture_cache.h"
13#include "video_core/renderer_vulkan/wrapper.h"
14
15namespace Vulkan {
16
17InnerFence::InnerFence(const VKDevice& device, VKScheduler& scheduler, u32 payload, bool is_stubbed)
18 : VideoCommon::FenceBase(payload, is_stubbed), device{device}, scheduler{scheduler} {}
19
20InnerFence::InnerFence(const VKDevice& device, VKScheduler& scheduler, GPUVAddr address,
21 u32 payload, bool is_stubbed)
22 : VideoCommon::FenceBase(address, payload, is_stubbed), device{device}, scheduler{scheduler} {}
23
24InnerFence::~InnerFence() = default;
25
26void InnerFence::Queue() {
27 if (is_stubbed) {
28 return;
29 }
30 ASSERT(!event);
31
32 event = device.GetLogical().CreateEvent();
33 ticks = scheduler.Ticks();
34
35 scheduler.RequestOutsideRenderPassOperationContext();
36 scheduler.Record([event = *event](vk::CommandBuffer cmdbuf) {
37 cmdbuf.SetEvent(event, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
38 });
39}
40
41bool InnerFence::IsSignaled() const {
42 if (is_stubbed) {
43 return true;
44 }
45 ASSERT(event);
46 return IsEventSignalled();
47}
48
49void InnerFence::Wait() {
50 if (is_stubbed) {
51 return;
52 }
53 ASSERT(event);
54
55 if (ticks >= scheduler.Ticks()) {
56 scheduler.Flush();
57 }
58 while (!IsEventSignalled()) {
59 std::this_thread::yield();
60 }
61}
62
63bool InnerFence::IsEventSignalled() const {
64 switch (const VkResult result = event.GetStatus()) {
65 case VK_EVENT_SET:
66 return true;
67 case VK_EVENT_RESET:
68 return false;
69 default:
70 throw vk::Exception(result);
71 }
72}
73
74VKFenceManager::VKFenceManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
75 const VKDevice& device, VKScheduler& scheduler,
76 VKTextureCache& texture_cache, VKBufferCache& buffer_cache,
77 VKQueryCache& query_cache)
78 : GenericFenceManager(system, rasterizer, texture_cache, buffer_cache, query_cache),
79 device{device}, scheduler{scheduler} {}
80
81Fence VKFenceManager::CreateFence(u32 value, bool is_stubbed) {
82 return std::make_shared<InnerFence>(device, scheduler, value, is_stubbed);
83}
84
85Fence VKFenceManager::CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) {
86 return std::make_shared<InnerFence>(device, scheduler, addr, value, is_stubbed);
87}
88
89void VKFenceManager::QueueFence(Fence& fence) {
90 fence->Queue();
91}
92
93bool VKFenceManager::IsFenceSignaled(Fence& fence) const {
94 return fence->IsSignaled();
95}
96
97void VKFenceManager::WaitFence(Fence& fence) {
98 fence->Wait();
99}
100
101} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.h b/src/video_core/renderer_vulkan/vk_fence_manager.h
new file mode 100644
index 000000000..04d07fe6a
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_fence_manager.h
@@ -0,0 +1,74 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8
9#include "video_core/fence_manager.h"
10#include "video_core/renderer_vulkan/wrapper.h"
11
12namespace Core {
13class System;
14}
15
16namespace VideoCore {
17class RasterizerInterface;
18}
19
20namespace Vulkan {
21
22class VKBufferCache;
23class VKDevice;
24class VKQueryCache;
25class VKScheduler;
26class VKTextureCache;
27
28class InnerFence : public VideoCommon::FenceBase {
29public:
30 explicit InnerFence(const VKDevice& device, VKScheduler& scheduler, u32 payload,
31 bool is_stubbed);
32 explicit InnerFence(const VKDevice& device, VKScheduler& scheduler, GPUVAddr address,
33 u32 payload, bool is_stubbed);
34 ~InnerFence();
35
36 void Queue();
37
38 bool IsSignaled() const;
39
40 void Wait();
41
42private:
43 bool IsEventSignalled() const;
44
45 const VKDevice& device;
46 VKScheduler& scheduler;
47 vk::Event event;
48 u64 ticks = 0;
49};
50using Fence = std::shared_ptr<InnerFence>;
51
52using GenericFenceManager =
53 VideoCommon::FenceManager<Fence, VKTextureCache, VKBufferCache, VKQueryCache>;
54
55class VKFenceManager final : public GenericFenceManager {
56public:
57 explicit VKFenceManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
58 const VKDevice& device, VKScheduler& scheduler,
59 VKTextureCache& texture_cache, VKBufferCache& buffer_cache,
60 VKQueryCache& query_cache);
61
62protected:
63 Fence CreateFence(u32 value, bool is_stubbed) override;
64 Fence CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) override;
65 void QueueFence(Fence& fence) override;
66 bool IsFenceSignaled(Fence& fence) const override;
67 void WaitFence(Fence& fence) override;
68
69private:
70 const VKDevice& device;
71 VKScheduler& scheduler;
72};
73
74} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 343999cf5..8332b42aa 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -148,6 +148,8 @@ std::vector<vk::ShaderModule> VKGraphicsPipeline::CreateShaderModules(
148 continue; 148 continue;
149 } 149 }
150 150
151 device.SaveShader(stage->code);
152
151 ci.codeSize = stage->code.size() * sizeof(u32); 153 ci.codeSize = stage->code.size() * sizeof(u32);
152 ci.pCode = stage->code.data(); 154 ci.pCode = stage->code.data();
153 modules.push_back(device.GetLogical().CreateShaderModule(ci)); 155 modules.push_back(device.GetLogical().CreateShaderModule(ci));
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 8fdc6400d..91b1b16a5 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -207,7 +207,7 @@ std::array<Shader, Maxwell::MaxShaderProgram> VKPipelineCache::GetShaders() {
207 const GPUVAddr program_addr{GetShaderAddress(system, program)}; 207 const GPUVAddr program_addr{GetShaderAddress(system, program)};
208 const std::optional cpu_addr = memory_manager.GpuToCpuAddress(program_addr); 208 const std::optional cpu_addr = memory_manager.GpuToCpuAddress(program_addr);
209 ASSERT(cpu_addr); 209 ASSERT(cpu_addr);
210 auto shader = cpu_addr ? TryGet(*cpu_addr) : nullptr; 210 auto shader = cpu_addr ? TryGet(*cpu_addr) : null_shader;
211 if (!shader) { 211 if (!shader) {
212 const auto host_ptr{memory_manager.GetPointer(program_addr)}; 212 const auto host_ptr{memory_manager.GetPointer(program_addr)};
213 213
@@ -218,7 +218,11 @@ std::array<Shader, Maxwell::MaxShaderProgram> VKPipelineCache::GetShaders() {
218 218
219 shader = std::make_shared<CachedShader>(system, stage, program_addr, *cpu_addr, 219 shader = std::make_shared<CachedShader>(system, stage, program_addr, *cpu_addr,
220 std::move(code), stage_offset); 220 std::move(code), stage_offset);
221 Register(shader); 221 if (cpu_addr) {
222 Register(shader);
223 } else {
224 null_shader = shader;
225 }
222 } 226 }
223 shaders[index] = std::move(shader); 227 shaders[index] = std::move(shader);
224 } 228 }
@@ -261,7 +265,7 @@ VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCach
261 const auto cpu_addr = memory_manager.GpuToCpuAddress(program_addr); 265 const auto cpu_addr = memory_manager.GpuToCpuAddress(program_addr);
262 ASSERT(cpu_addr); 266 ASSERT(cpu_addr);
263 267
264 auto shader = cpu_addr ? TryGet(*cpu_addr) : nullptr; 268 auto shader = cpu_addr ? TryGet(*cpu_addr) : null_kernel;
265 if (!shader) { 269 if (!shader) {
266 // No shader found - create a new one 270 // No shader found - create a new one
267 const auto host_ptr = memory_manager.GetPointer(program_addr); 271 const auto host_ptr = memory_manager.GetPointer(program_addr);
@@ -271,7 +275,11 @@ VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCach
271 shader = std::make_shared<CachedShader>(system, Tegra::Engines::ShaderType::Compute, 275 shader = std::make_shared<CachedShader>(system, Tegra::Engines::ShaderType::Compute,
272 program_addr, *cpu_addr, std::move(code), 276 program_addr, *cpu_addr, std::move(code),
273 kernel_main_offset); 277 kernel_main_offset);
274 Register(shader); 278 if (cpu_addr) {
279 Register(shader);
280 } else {
281 null_kernel = shader;
282 }
275 } 283 }
276 284
277 Specialization specialization; 285 Specialization specialization;
@@ -330,8 +338,10 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) {
330 338
331 Specialization specialization; 339 Specialization specialization;
332 if (fixed_state.rasterizer.Topology() == Maxwell::PrimitiveTopology::Points) { 340 if (fixed_state.rasterizer.Topology() == Maxwell::PrimitiveTopology::Points) {
333 ASSERT(fixed_state.rasterizer.point_size != 0); 341 float point_size;
334 std::memcpy(&specialization.point_size, &fixed_state.rasterizer.point_size, sizeof(u32)); 342 std::memcpy(&point_size, &fixed_state.rasterizer.point_size, sizeof(float));
343 specialization.point_size = point_size;
344 ASSERT(point_size != 0.0f);
335 } 345 }
336 for (std::size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) { 346 for (std::size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) {
337 specialization.attribute_types[i] = fixed_state.vertex_input.attributes[i].Type(); 347 specialization.attribute_types[i] = fixed_state.vertex_input.attributes[i].Type();
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
index 7ccdb7083..602a0a340 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -182,6 +182,9 @@ private:
182 VKUpdateDescriptorQueue& update_descriptor_queue; 182 VKUpdateDescriptorQueue& update_descriptor_queue;
183 VKRenderPassCache& renderpass_cache; 183 VKRenderPassCache& renderpass_cache;
184 184
185 Shader null_shader{};
186 Shader null_kernel{};
187
185 std::array<Shader, Maxwell::MaxShaderProgram> last_shaders; 188 std::array<Shader, Maxwell::MaxShaderProgram> last_shaders;
186 189
187 GraphicsPipelineCacheKey last_graphics_key; 190 GraphicsPipelineCacheKey last_graphics_key;
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp
index 0966c7ff7..813f7c162 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp
@@ -113,8 +113,19 @@ u64 HostCounter::BlockingQuery() const {
113 if (ticks >= cache.Scheduler().Ticks()) { 113 if (ticks >= cache.Scheduler().Ticks()) {
114 cache.Scheduler().Flush(); 114 cache.Scheduler().Flush();
115 } 115 }
116 return cache.Device().GetLogical().GetQueryResult<u64>( 116 u64 data;
117 query.first, query.second, VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT); 117 const VkResult result = cache.Device().GetLogical().GetQueryResults(
118 query.first, query.second, 1, sizeof(data), &data, sizeof(data),
119 VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
120 switch (result) {
121 case VK_SUCCESS:
122 return data;
123 case VK_ERROR_DEVICE_LOST:
124 cache.Device().ReportLoss();
125 [[fallthrough]];
126 default:
127 throw vk::Exception(result);
128 }
118} 129}
119 130
120} // namespace Vulkan 131} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 71007bbe8..68464e637 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -17,6 +17,7 @@
17#include "common/microprofile.h" 17#include "common/microprofile.h"
18#include "core/core.h" 18#include "core/core.h"
19#include "core/memory.h" 19#include "core/memory.h"
20#include "core/settings.h"
20#include "video_core/engines/kepler_compute.h" 21#include "video_core/engines/kepler_compute.h"
21#include "video_core/engines/maxwell_3d.h" 22#include "video_core/engines/maxwell_3d.h"
22#include "video_core/renderer_vulkan/fixed_pipeline_state.h" 23#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
@@ -299,7 +300,9 @@ RasterizerVulkan::RasterizerVulkan(Core::System& system, Core::Frontend::EmuWind
299 pipeline_cache(system, *this, device, scheduler, descriptor_pool, update_descriptor_queue, 300 pipeline_cache(system, *this, device, scheduler, descriptor_pool, update_descriptor_queue,
300 renderpass_cache), 301 renderpass_cache),
301 buffer_cache(*this, system, device, memory_manager, scheduler, staging_pool), 302 buffer_cache(*this, system, device, memory_manager, scheduler, staging_pool),
302 sampler_cache(device), query_cache(system, *this, device, scheduler) { 303 sampler_cache(device),
304 fence_manager(system, *this, device, scheduler, texture_cache, buffer_cache, query_cache),
305 query_cache(system, *this, device, scheduler) {
303 scheduler.SetQueryCache(query_cache); 306 scheduler.SetQueryCache(query_cache);
304} 307}
305 308
@@ -347,11 +350,6 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
347 350
348 buffer_bindings.Bind(scheduler); 351 buffer_bindings.Bind(scheduler);
349 352
350 if (device.IsNvDeviceDiagnosticCheckpoints()) {
351 scheduler.Record(
352 [&pipeline](vk::CommandBuffer cmdbuf) { cmdbuf.SetCheckpointNV(&pipeline); });
353 }
354
355 BeginTransformFeedback(); 353 BeginTransformFeedback();
356 354
357 const auto pipeline_layout = pipeline.GetLayout(); 355 const auto pipeline_layout = pipeline.GetLayout();
@@ -365,6 +363,8 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
365 }); 363 });
366 364
367 EndTransformFeedback(); 365 EndTransformFeedback();
366
367 system.GPU().TickWork();
368} 368}
369 369
370void RasterizerVulkan::Clear() { 370void RasterizerVulkan::Clear() {
@@ -478,11 +478,6 @@ void RasterizerVulkan::DispatchCompute(GPUVAddr code_addr) {
478 TransitionImages(image_views, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 478 TransitionImages(image_views, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
479 VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); 479 VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT);
480 480
481 if (device.IsNvDeviceDiagnosticCheckpoints()) {
482 scheduler.Record(
483 [&pipeline](vk::CommandBuffer cmdbuf) { cmdbuf.SetCheckpointNV(nullptr); });
484 }
485
486 scheduler.Record([grid_x = launch_desc.grid_dim_x, grid_y = launch_desc.grid_dim_y, 481 scheduler.Record([grid_x = launch_desc.grid_dim_x, grid_y = launch_desc.grid_dim_y,
487 grid_z = launch_desc.grid_dim_z, pipeline_handle = pipeline.GetHandle(), 482 grid_z = launch_desc.grid_dim_z, pipeline_handle = pipeline.GetHandle(),
488 layout = pipeline.GetLayout(), 483 layout = pipeline.GetLayout(),
@@ -514,6 +509,13 @@ void RasterizerVulkan::FlushRegion(VAddr addr, u64 size) {
514 query_cache.FlushRegion(addr, size); 509 query_cache.FlushRegion(addr, size);
515} 510}
516 511
512bool RasterizerVulkan::MustFlushRegion(VAddr addr, u64 size) {
513 if (!Settings::IsGPULevelHigh()) {
514 return buffer_cache.MustFlushRegion(addr, size);
515 }
516 return texture_cache.MustFlushRegion(addr, size) || buffer_cache.MustFlushRegion(addr, size);
517}
518
517void RasterizerVulkan::InvalidateRegion(VAddr addr, u64 size) { 519void RasterizerVulkan::InvalidateRegion(VAddr addr, u64 size) {
518 if (addr == 0 || size == 0) { 520 if (addr == 0 || size == 0) {
519 return; 521 return;
@@ -524,6 +526,47 @@ void RasterizerVulkan::InvalidateRegion(VAddr addr, u64 size) {
524 query_cache.InvalidateRegion(addr, size); 526 query_cache.InvalidateRegion(addr, size);
525} 527}
526 528
529void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) {
530 if (addr == 0 || size == 0) {
531 return;
532 }
533 texture_cache.OnCPUWrite(addr, size);
534 pipeline_cache.InvalidateRegion(addr, size);
535 buffer_cache.OnCPUWrite(addr, size);
536 query_cache.InvalidateRegion(addr, size);
537}
538
539void RasterizerVulkan::SyncGuestHost() {
540 texture_cache.SyncGuestHost();
541 buffer_cache.SyncGuestHost();
542}
543
544void RasterizerVulkan::SignalSemaphore(GPUVAddr addr, u32 value) {
545 auto& gpu{system.GPU()};
546 if (!gpu.IsAsync()) {
547 gpu.MemoryManager().Write<u32>(addr, value);
548 return;
549 }
550 fence_manager.SignalSemaphore(addr, value);
551}
552
553void RasterizerVulkan::SignalSyncPoint(u32 value) {
554 auto& gpu{system.GPU()};
555 if (!gpu.IsAsync()) {
556 gpu.IncrementSyncPoint(value);
557 return;
558 }
559 fence_manager.SignalSyncPoint(value);
560}
561
562void RasterizerVulkan::ReleaseFences() {
563 auto& gpu{system.GPU()};
564 if (!gpu.IsAsync()) {
565 return;
566 }
567 fence_manager.WaitPendingFences();
568}
569
527void RasterizerVulkan::FlushAndInvalidateRegion(VAddr addr, u64 size) { 570void RasterizerVulkan::FlushAndInvalidateRegion(VAddr addr, u64 size) {
528 FlushRegion(addr, size); 571 FlushRegion(addr, size);
529 InvalidateRegion(addr, size); 572 InvalidateRegion(addr, size);
@@ -834,8 +877,12 @@ void RasterizerVulkan::SetupVertexArrays(FixedPipelineState::VertexInput& vertex
834 const GPUVAddr start{vertex_array.StartAddress()}; 877 const GPUVAddr start{vertex_array.StartAddress()};
835 const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()}; 878 const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()};
836 879
837 ASSERT(end > start); 880 ASSERT(end >= start);
838 const std::size_t size{end - start + 1}; 881 const std::size_t size{end - start};
882 if (size == 0) {
883 buffer_bindings.AddVertexBinding(DefaultBuffer(), 0);
884 continue;
885 }
839 const auto [buffer, offset] = buffer_cache.UploadMemory(start, size); 886 const auto [buffer, offset] = buffer_cache.UploadMemory(start, size);
840 buffer_bindings.AddVertexBinding(buffer, offset); 887 buffer_bindings.AddVertexBinding(buffer, offset);
841 } 888 }
@@ -990,8 +1037,7 @@ void RasterizerVulkan::SetupConstBuffer(const ConstBufferEntry& entry,
990 const Tegra::Engines::ConstBufferInfo& buffer) { 1037 const Tegra::Engines::ConstBufferInfo& buffer) {
991 if (!buffer.enabled) { 1038 if (!buffer.enabled) {
992 // Set values to zero to unbind buffers 1039 // Set values to zero to unbind buffers
993 update_descriptor_queue.AddBuffer(buffer_cache.GetEmptyBuffer(sizeof(float)), 0, 1040 update_descriptor_queue.AddBuffer(DefaultBuffer(), 0, DEFAULT_BUFFER_SIZE);
994 sizeof(float));
995 return; 1041 return;
996 } 1042 }
997 1043
@@ -1014,7 +1060,9 @@ void RasterizerVulkan::SetupGlobalBuffer(const GlobalBufferEntry& entry, GPUVAdd
1014 if (size == 0) { 1060 if (size == 0) {
1015 // Sometimes global memory pointers don't have a proper size. Upload a dummy entry 1061 // Sometimes global memory pointers don't have a proper size. Upload a dummy entry
1016 // because Vulkan doesn't like empty buffers. 1062 // because Vulkan doesn't like empty buffers.
1017 constexpr std::size_t dummy_size = 4; 1063 // Note: Do *not* use DefaultBuffer() here, storage buffers can be written breaking the
1064 // default buffer.
1065 static constexpr std::size_t dummy_size = 4;
1018 const auto buffer = buffer_cache.GetEmptyBuffer(dummy_size); 1066 const auto buffer = buffer_cache.GetEmptyBuffer(dummy_size);
1019 update_descriptor_queue.AddBuffer(buffer, 0, dummy_size); 1067 update_descriptor_queue.AddBuffer(buffer, 0, dummy_size);
1020 return; 1068 return;
@@ -1179,7 +1227,7 @@ std::size_t RasterizerVulkan::CalculateVertexArraysSize() const {
1179 const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()}; 1227 const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()};
1180 DEBUG_ASSERT(end >= start); 1228 DEBUG_ASSERT(end >= start);
1181 1229
1182 size += (end - start + 1) * regs.vertex_array[index].enable; 1230 size += (end - start) * regs.vertex_array[index].enable;
1183 } 1231 }
1184 return size; 1232 return size;
1185} 1233}
@@ -1226,4 +1274,29 @@ RenderPassParams RasterizerVulkan::GetRenderPassParams(Texceptions texceptions)
1226 return renderpass_params; 1274 return renderpass_params;
1227} 1275}
1228 1276
1277VkBuffer RasterizerVulkan::DefaultBuffer() {
1278 if (default_buffer) {
1279 return *default_buffer;
1280 }
1281
1282 VkBufferCreateInfo ci;
1283 ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
1284 ci.pNext = nullptr;
1285 ci.flags = 0;
1286 ci.size = DEFAULT_BUFFER_SIZE;
1287 ci.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
1288 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
1289 ci.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
1290 ci.queueFamilyIndexCount = 0;
1291 ci.pQueueFamilyIndices = nullptr;
1292 default_buffer = device.GetLogical().CreateBuffer(ci);
1293 default_buffer_commit = memory_manager.Commit(default_buffer, false);
1294
1295 scheduler.RequestOutsideRenderPassOperationContext();
1296 scheduler.Record([buffer = *default_buffer](vk::CommandBuffer cmdbuf) {
1297 cmdbuf.FillBuffer(buffer, 0, DEFAULT_BUFFER_SIZE, 0);
1298 });
1299 return *default_buffer;
1300}
1301
1229} // namespace Vulkan 1302} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index d9108f862..d41a7929e 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -21,6 +21,7 @@
21#include "video_core/renderer_vulkan/vk_buffer_cache.h" 21#include "video_core/renderer_vulkan/vk_buffer_cache.h"
22#include "video_core/renderer_vulkan/vk_compute_pass.h" 22#include "video_core/renderer_vulkan/vk_compute_pass.h"
23#include "video_core/renderer_vulkan/vk_descriptor_pool.h" 23#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
24#include "video_core/renderer_vulkan/vk_fence_manager.h"
24#include "video_core/renderer_vulkan/vk_memory_manager.h" 25#include "video_core/renderer_vulkan/vk_memory_manager.h"
25#include "video_core/renderer_vulkan/vk_pipeline_cache.h" 26#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
26#include "video_core/renderer_vulkan/vk_query_cache.h" 27#include "video_core/renderer_vulkan/vk_query_cache.h"
@@ -118,7 +119,13 @@ public:
118 void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override; 119 void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override;
119 void FlushAll() override; 120 void FlushAll() override;
120 void FlushRegion(VAddr addr, u64 size) override; 121 void FlushRegion(VAddr addr, u64 size) override;
122 bool MustFlushRegion(VAddr addr, u64 size) override;
121 void InvalidateRegion(VAddr addr, u64 size) override; 123 void InvalidateRegion(VAddr addr, u64 size) override;
124 void OnCPUWrite(VAddr addr, u64 size) override;
125 void SyncGuestHost() override;
126 void SignalSemaphore(GPUVAddr addr, u32 value) override;
127 void SignalSyncPoint(u32 value) override;
128 void ReleaseFences() override;
122 void FlushAndInvalidateRegion(VAddr addr, u64 size) override; 129 void FlushAndInvalidateRegion(VAddr addr, u64 size) override;
123 void FlushCommands() override; 130 void FlushCommands() override;
124 void TickFrame() override; 131 void TickFrame() override;
@@ -148,6 +155,7 @@ private:
148 using Texceptions = std::bitset<Maxwell::NumRenderTargets + 1>; 155 using Texceptions = std::bitset<Maxwell::NumRenderTargets + 1>;
149 156
150 static constexpr std::size_t ZETA_TEXCEPTION_INDEX = 8; 157 static constexpr std::size_t ZETA_TEXCEPTION_INDEX = 8;
158 static constexpr VkDeviceSize DEFAULT_BUFFER_SIZE = 4 * sizeof(float);
151 159
152 void FlushWork(); 160 void FlushWork();
153 161
@@ -240,6 +248,8 @@ private:
240 248
241 RenderPassParams GetRenderPassParams(Texceptions texceptions) const; 249 RenderPassParams GetRenderPassParams(Texceptions texceptions) const;
242 250
251 VkBuffer DefaultBuffer();
252
243 Core::System& system; 253 Core::System& system;
244 Core::Frontend::EmuWindow& render_window; 254 Core::Frontend::EmuWindow& render_window;
245 VKScreenInfo& screen_info; 255 VKScreenInfo& screen_info;
@@ -261,8 +271,12 @@ private:
261 VKPipelineCache pipeline_cache; 271 VKPipelineCache pipeline_cache;
262 VKBufferCache buffer_cache; 272 VKBufferCache buffer_cache;
263 VKSamplerCache sampler_cache; 273 VKSamplerCache sampler_cache;
274 VKFenceManager fence_manager;
264 VKQueryCache query_cache; 275 VKQueryCache query_cache;
265 276
277 vk::Buffer default_buffer;
278 VKMemoryCommit default_buffer_commit;
279
266 std::array<View, Maxwell::NumRenderTargets> color_attachments; 280 std::array<View, Maxwell::NumRenderTargets> color_attachments;
267 View zeta_attachment; 281 View zeta_attachment;
268 282
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index 900f551b3..ae7ba3eb5 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -166,7 +166,15 @@ void VKScheduler::SubmitExecution(VkSemaphore semaphore) {
166 submit_info.pCommandBuffers = current_cmdbuf.address(); 166 submit_info.pCommandBuffers = current_cmdbuf.address();
167 submit_info.signalSemaphoreCount = semaphore ? 1 : 0; 167 submit_info.signalSemaphoreCount = semaphore ? 1 : 0;
168 submit_info.pSignalSemaphores = &semaphore; 168 submit_info.pSignalSemaphores = &semaphore;
169 device.GetGraphicsQueue().Submit(submit_info, *current_fence); 169 switch (const VkResult result = device.GetGraphicsQueue().Submit(submit_info, *current_fence)) {
170 case VK_SUCCESS:
171 break;
172 case VK_ERROR_DEVICE_LOST:
173 device.ReportLoss();
174 [[fallthrough]];
175 default:
176 vk::Check(result);
177 }
170} 178}
171 179
172void VKScheduler::AllocateNewContext() { 180void VKScheduler::AllocateNewContext() {
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
index abd836efe..45c180221 100644
--- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
+++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
@@ -78,7 +78,7 @@ VKBuffer& VKStagingBufferPool::CreateStagingBuffer(std::size_t size, bool host_v
78 ci.size = 1ULL << log2; 78 ci.size = 1ULL << log2;
79 ci.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | 79 ci.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
80 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | 80 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
81 VK_BUFFER_USAGE_INDEX_BUFFER_BIT; 81 VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
82 ci.sharingMode = VK_SHARING_MODE_EXCLUSIVE; 82 ci.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
83 ci.queueFamilyIndexCount = 0; 83 ci.queueFamilyIndexCount = 0;
84 ci.pQueueFamilyIndices = nullptr; 84 ci.pQueueFamilyIndices = nullptr;
diff --git a/src/video_core/renderer_vulkan/wrapper.cpp b/src/video_core/renderer_vulkan/wrapper.cpp
index 9b94dfff1..7f5bc1404 100644
--- a/src/video_core/renderer_vulkan/wrapper.cpp
+++ b/src/video_core/renderer_vulkan/wrapper.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
5#include <exception> 6#include <exception>
6#include <memory> 7#include <memory>
7#include <optional> 8#include <optional>
@@ -16,6 +17,23 @@ namespace Vulkan::vk {
16 17
17namespace { 18namespace {
18 19
20void SortPhysicalDevices(std::vector<VkPhysicalDevice>& devices, const InstanceDispatch& dld) {
21 std::stable_sort(devices.begin(), devices.end(), [&](auto lhs, auto rhs) {
22 // This will call Vulkan more than needed, but these calls are cheap.
23 const auto lhs_properties = vk::PhysicalDevice(lhs, dld).GetProperties();
24 const auto rhs_properties = vk::PhysicalDevice(rhs, dld).GetProperties();
25
26 // Prefer discrete GPUs, Nvidia over AMD, AMD over Intel, Intel over the rest.
27 const bool preferred =
28 (lhs_properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU &&
29 rhs_properties.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) ||
30 (lhs_properties.vendorID == 0x10DE && rhs_properties.vendorID != 0x10DE) ||
31 (lhs_properties.vendorID == 0x1002 && rhs_properties.vendorID != 0x1002) ||
32 (lhs_properties.vendorID == 0x8086 && rhs_properties.vendorID != 0x8086);
33 return !preferred;
34 });
35}
36
19template <typename T> 37template <typename T>
20bool Proc(T& result, const InstanceDispatch& dld, const char* proc_name, 38bool Proc(T& result, const InstanceDispatch& dld, const char* proc_name,
21 VkInstance instance = nullptr) noexcept { 39 VkInstance instance = nullptr) noexcept {
@@ -61,9 +79,9 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
61 X(vkCmdPipelineBarrier); 79 X(vkCmdPipelineBarrier);
62 X(vkCmdPushConstants); 80 X(vkCmdPushConstants);
63 X(vkCmdSetBlendConstants); 81 X(vkCmdSetBlendConstants);
64 X(vkCmdSetCheckpointNV);
65 X(vkCmdSetDepthBias); 82 X(vkCmdSetDepthBias);
66 X(vkCmdSetDepthBounds); 83 X(vkCmdSetDepthBounds);
84 X(vkCmdSetEvent);
67 X(vkCmdSetScissor); 85 X(vkCmdSetScissor);
68 X(vkCmdSetStencilCompareMask); 86 X(vkCmdSetStencilCompareMask);
69 X(vkCmdSetStencilReference); 87 X(vkCmdSetStencilReference);
@@ -76,6 +94,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
76 X(vkCreateDescriptorPool); 94 X(vkCreateDescriptorPool);
77 X(vkCreateDescriptorSetLayout); 95 X(vkCreateDescriptorSetLayout);
78 X(vkCreateDescriptorUpdateTemplateKHR); 96 X(vkCreateDescriptorUpdateTemplateKHR);
97 X(vkCreateEvent);
79 X(vkCreateFence); 98 X(vkCreateFence);
80 X(vkCreateFramebuffer); 99 X(vkCreateFramebuffer);
81 X(vkCreateGraphicsPipelines); 100 X(vkCreateGraphicsPipelines);
@@ -94,6 +113,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
94 X(vkDestroyDescriptorPool); 113 X(vkDestroyDescriptorPool);
95 X(vkDestroyDescriptorSetLayout); 114 X(vkDestroyDescriptorSetLayout);
96 X(vkDestroyDescriptorUpdateTemplateKHR); 115 X(vkDestroyDescriptorUpdateTemplateKHR);
116 X(vkDestroyEvent);
97 X(vkDestroyFence); 117 X(vkDestroyFence);
98 X(vkDestroyFramebuffer); 118 X(vkDestroyFramebuffer);
99 X(vkDestroyImage); 119 X(vkDestroyImage);
@@ -113,10 +133,10 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
113 X(vkFreeMemory); 133 X(vkFreeMemory);
114 X(vkGetBufferMemoryRequirements); 134 X(vkGetBufferMemoryRequirements);
115 X(vkGetDeviceQueue); 135 X(vkGetDeviceQueue);
136 X(vkGetEventStatus);
116 X(vkGetFenceStatus); 137 X(vkGetFenceStatus);
117 X(vkGetImageMemoryRequirements); 138 X(vkGetImageMemoryRequirements);
118 X(vkGetQueryPoolResults); 139 X(vkGetQueryPoolResults);
119 X(vkGetQueueCheckpointDataNV);
120 X(vkMapMemory); 140 X(vkMapMemory);
121 X(vkQueueSubmit); 141 X(vkQueueSubmit);
122 X(vkResetFences); 142 X(vkResetFences);
@@ -271,6 +291,10 @@ void Destroy(VkDevice device, VkDeviceMemory handle, const DeviceDispatch& dld)
271 dld.vkFreeMemory(device, handle, nullptr); 291 dld.vkFreeMemory(device, handle, nullptr);
272} 292}
273 293
294void Destroy(VkDevice device, VkEvent handle, const DeviceDispatch& dld) noexcept {
295 dld.vkDestroyEvent(device, handle, nullptr);
296}
297
274void Destroy(VkDevice device, VkFence handle, const DeviceDispatch& dld) noexcept { 298void Destroy(VkDevice device, VkFence handle, const DeviceDispatch& dld) noexcept {
275 dld.vkDestroyFence(device, handle, nullptr); 299 dld.vkDestroyFence(device, handle, nullptr);
276} 300}
@@ -383,7 +407,8 @@ std::optional<std::vector<VkPhysicalDevice>> Instance::EnumeratePhysicalDevices(
383 if (dld->vkEnumeratePhysicalDevices(handle, &num, physical_devices.data()) != VK_SUCCESS) { 407 if (dld->vkEnumeratePhysicalDevices(handle, &num, physical_devices.data()) != VK_SUCCESS) {
384 return std::nullopt; 408 return std::nullopt;
385 } 409 }
386 return physical_devices; 410 SortPhysicalDevices(physical_devices, *dld);
411 return std::make_optional(std::move(physical_devices));
387} 412}
388 413
389DebugCallback Instance::TryCreateDebugCallback( 414DebugCallback Instance::TryCreateDebugCallback(
@@ -409,17 +434,6 @@ DebugCallback Instance::TryCreateDebugCallback(
409 return DebugCallback(messenger, handle, *dld); 434 return DebugCallback(messenger, handle, *dld);
410} 435}
411 436
412std::vector<VkCheckpointDataNV> Queue::GetCheckpointDataNV(const DeviceDispatch& dld) const {
413 if (!dld.vkGetQueueCheckpointDataNV) {
414 return {};
415 }
416 u32 num;
417 dld.vkGetQueueCheckpointDataNV(queue, &num, nullptr);
418 std::vector<VkCheckpointDataNV> checkpoints(num);
419 dld.vkGetQueueCheckpointDataNV(queue, &num, checkpoints.data());
420 return checkpoints;
421}
422
423void Buffer::BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const { 437void Buffer::BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const {
424 Check(dld->vkBindBufferMemory(owner, handle, memory, offset)); 438 Check(dld->vkBindBufferMemory(owner, handle, memory, offset));
425} 439}
@@ -469,12 +483,11 @@ std::vector<VkImage> SwapchainKHR::GetImages() const {
469} 483}
470 484
471Device Device::Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci, 485Device Device::Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci,
472 Span<const char*> enabled_extensions, 486 Span<const char*> enabled_extensions, const void* next,
473 const VkPhysicalDeviceFeatures2& enabled_features,
474 DeviceDispatch& dld) noexcept { 487 DeviceDispatch& dld) noexcept {
475 VkDeviceCreateInfo ci; 488 VkDeviceCreateInfo ci;
476 ci.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; 489 ci.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
477 ci.pNext = &enabled_features; 490 ci.pNext = next;
478 ci.flags = 0; 491 ci.flags = 0;
479 ci.queueCreateInfoCount = queues_ci.size(); 492 ci.queueCreateInfoCount = queues_ci.size();
480 ci.pQueueCreateInfos = queues_ci.data(); 493 ci.pQueueCreateInfos = queues_ci.data();
@@ -613,6 +626,16 @@ ShaderModule Device::CreateShaderModule(const VkShaderModuleCreateInfo& ci) cons
613 return ShaderModule(object, handle, *dld); 626 return ShaderModule(object, handle, *dld);
614} 627}
615 628
629Event Device::CreateEvent() const {
630 VkEventCreateInfo ci;
631 ci.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
632 ci.pNext = nullptr;
633 ci.flags = 0;
634 VkEvent object;
635 Check(dld->vkCreateEvent(handle, &ci, nullptr, &object));
636 return Event(object, handle, *dld);
637}
638
616SwapchainKHR Device::CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const { 639SwapchainKHR Device::CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const {
617 VkSwapchainKHR object; 640 VkSwapchainKHR object;
618 Check(dld->vkCreateSwapchainKHR(handle, &ci, nullptr, &object)); 641 Check(dld->vkCreateSwapchainKHR(handle, &ci, nullptr, &object));
diff --git a/src/video_core/renderer_vulkan/wrapper.h b/src/video_core/renderer_vulkan/wrapper.h
index fb3657819..bda16a2cb 100644
--- a/src/video_core/renderer_vulkan/wrapper.h
+++ b/src/video_core/renderer_vulkan/wrapper.h
@@ -197,9 +197,9 @@ struct DeviceDispatch : public InstanceDispatch {
197 PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier; 197 PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier;
198 PFN_vkCmdPushConstants vkCmdPushConstants; 198 PFN_vkCmdPushConstants vkCmdPushConstants;
199 PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants; 199 PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants;
200 PFN_vkCmdSetCheckpointNV vkCmdSetCheckpointNV;
201 PFN_vkCmdSetDepthBias vkCmdSetDepthBias; 200 PFN_vkCmdSetDepthBias vkCmdSetDepthBias;
202 PFN_vkCmdSetDepthBounds vkCmdSetDepthBounds; 201 PFN_vkCmdSetDepthBounds vkCmdSetDepthBounds;
202 PFN_vkCmdSetEvent vkCmdSetEvent;
203 PFN_vkCmdSetScissor vkCmdSetScissor; 203 PFN_vkCmdSetScissor vkCmdSetScissor;
204 PFN_vkCmdSetStencilCompareMask vkCmdSetStencilCompareMask; 204 PFN_vkCmdSetStencilCompareMask vkCmdSetStencilCompareMask;
205 PFN_vkCmdSetStencilReference vkCmdSetStencilReference; 205 PFN_vkCmdSetStencilReference vkCmdSetStencilReference;
@@ -212,6 +212,7 @@ struct DeviceDispatch : public InstanceDispatch {
212 PFN_vkCreateDescriptorPool vkCreateDescriptorPool; 212 PFN_vkCreateDescriptorPool vkCreateDescriptorPool;
213 PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout; 213 PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout;
214 PFN_vkCreateDescriptorUpdateTemplateKHR vkCreateDescriptorUpdateTemplateKHR; 214 PFN_vkCreateDescriptorUpdateTemplateKHR vkCreateDescriptorUpdateTemplateKHR;
215 PFN_vkCreateEvent vkCreateEvent;
215 PFN_vkCreateFence vkCreateFence; 216 PFN_vkCreateFence vkCreateFence;
216 PFN_vkCreateFramebuffer vkCreateFramebuffer; 217 PFN_vkCreateFramebuffer vkCreateFramebuffer;
217 PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines; 218 PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines;
@@ -230,6 +231,7 @@ struct DeviceDispatch : public InstanceDispatch {
230 PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool; 231 PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool;
231 PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout; 232 PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout;
232 PFN_vkDestroyDescriptorUpdateTemplateKHR vkDestroyDescriptorUpdateTemplateKHR; 233 PFN_vkDestroyDescriptorUpdateTemplateKHR vkDestroyDescriptorUpdateTemplateKHR;
234 PFN_vkDestroyEvent vkDestroyEvent;
233 PFN_vkDestroyFence vkDestroyFence; 235 PFN_vkDestroyFence vkDestroyFence;
234 PFN_vkDestroyFramebuffer vkDestroyFramebuffer; 236 PFN_vkDestroyFramebuffer vkDestroyFramebuffer;
235 PFN_vkDestroyImage vkDestroyImage; 237 PFN_vkDestroyImage vkDestroyImage;
@@ -249,10 +251,10 @@ struct DeviceDispatch : public InstanceDispatch {
249 PFN_vkFreeMemory vkFreeMemory; 251 PFN_vkFreeMemory vkFreeMemory;
250 PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements; 252 PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
251 PFN_vkGetDeviceQueue vkGetDeviceQueue; 253 PFN_vkGetDeviceQueue vkGetDeviceQueue;
254 PFN_vkGetEventStatus vkGetEventStatus;
252 PFN_vkGetFenceStatus vkGetFenceStatus; 255 PFN_vkGetFenceStatus vkGetFenceStatus;
253 PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements; 256 PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
254 PFN_vkGetQueryPoolResults vkGetQueryPoolResults; 257 PFN_vkGetQueryPoolResults vkGetQueryPoolResults;
255 PFN_vkGetQueueCheckpointDataNV vkGetQueueCheckpointDataNV;
256 PFN_vkMapMemory vkMapMemory; 258 PFN_vkMapMemory vkMapMemory;
257 PFN_vkQueueSubmit vkQueueSubmit; 259 PFN_vkQueueSubmit vkQueueSubmit;
258 PFN_vkResetFences vkResetFences; 260 PFN_vkResetFences vkResetFences;
@@ -281,6 +283,7 @@ void Destroy(VkDevice, VkDescriptorPool, const DeviceDispatch&) noexcept;
281void Destroy(VkDevice, VkDescriptorSetLayout, const DeviceDispatch&) noexcept; 283void Destroy(VkDevice, VkDescriptorSetLayout, const DeviceDispatch&) noexcept;
282void Destroy(VkDevice, VkDescriptorUpdateTemplateKHR, const DeviceDispatch&) noexcept; 284void Destroy(VkDevice, VkDescriptorUpdateTemplateKHR, const DeviceDispatch&) noexcept;
283void Destroy(VkDevice, VkDeviceMemory, const DeviceDispatch&) noexcept; 285void Destroy(VkDevice, VkDeviceMemory, const DeviceDispatch&) noexcept;
286void Destroy(VkDevice, VkEvent, const DeviceDispatch&) noexcept;
284void Destroy(VkDevice, VkFence, const DeviceDispatch&) noexcept; 287void Destroy(VkDevice, VkFence, const DeviceDispatch&) noexcept;
285void Destroy(VkDevice, VkFramebuffer, const DeviceDispatch&) noexcept; 288void Destroy(VkDevice, VkFramebuffer, const DeviceDispatch&) noexcept;
286void Destroy(VkDevice, VkImage, const DeviceDispatch&) noexcept; 289void Destroy(VkDevice, VkImage, const DeviceDispatch&) noexcept;
@@ -567,12 +570,8 @@ public:
567 /// Construct a queue handle. 570 /// Construct a queue handle.
568 constexpr Queue(VkQueue queue, const DeviceDispatch& dld) noexcept : queue{queue}, dld{&dld} {} 571 constexpr Queue(VkQueue queue, const DeviceDispatch& dld) noexcept : queue{queue}, dld{&dld} {}
569 572
570 /// Returns the checkpoint data. 573 VkResult Submit(Span<VkSubmitInfo> submit_infos, VkFence fence) const noexcept {
571 /// @note Returns an empty vector when the function pointer is not present. 574 return dld->vkQueueSubmit(queue, submit_infos.size(), submit_infos.data(), fence);
572 std::vector<VkCheckpointDataNV> GetCheckpointDataNV(const DeviceDispatch& dld) const;
573
574 void Submit(Span<VkSubmitInfo> submit_infos, VkFence fence) const {
575 Check(dld->vkQueueSubmit(queue, submit_infos.size(), submit_infos.data(), fence));
576 } 575 }
577 576
578 VkResult Present(const VkPresentInfoKHR& present_info) const noexcept { 577 VkResult Present(const VkPresentInfoKHR& present_info) const noexcept {
@@ -654,13 +653,21 @@ public:
654 std::vector<VkImage> GetImages() const; 653 std::vector<VkImage> GetImages() const;
655}; 654};
656 655
656class Event : public Handle<VkEvent, VkDevice, DeviceDispatch> {
657 using Handle<VkEvent, VkDevice, DeviceDispatch>::Handle;
658
659public:
660 VkResult GetStatus() const noexcept {
661 return dld->vkGetEventStatus(owner, handle);
662 }
663};
664
657class Device : public Handle<VkDevice, NoOwner, DeviceDispatch> { 665class Device : public Handle<VkDevice, NoOwner, DeviceDispatch> {
658 using Handle<VkDevice, NoOwner, DeviceDispatch>::Handle; 666 using Handle<VkDevice, NoOwner, DeviceDispatch>::Handle;
659 667
660public: 668public:
661 static Device Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci, 669 static Device Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci,
662 Span<const char*> enabled_extensions, 670 Span<const char*> enabled_extensions, const void* next,
663 const VkPhysicalDeviceFeatures2& enabled_features,
664 DeviceDispatch& dld) noexcept; 671 DeviceDispatch& dld) noexcept;
665 672
666 Queue GetQueue(u32 family_index) const noexcept; 673 Queue GetQueue(u32 family_index) const noexcept;
@@ -702,6 +709,8 @@ public:
702 709
703 ShaderModule CreateShaderModule(const VkShaderModuleCreateInfo& ci) const; 710 ShaderModule CreateShaderModule(const VkShaderModuleCreateInfo& ci) const;
704 711
712 Event CreateEvent() const;
713
705 SwapchainKHR CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const; 714 SwapchainKHR CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const;
706 715
707 DeviceMemory TryAllocateMemory(const VkMemoryAllocateInfo& ai) const noexcept; 716 DeviceMemory TryAllocateMemory(const VkMemoryAllocateInfo& ai) const noexcept;
@@ -734,18 +743,11 @@ public:
734 dld->vkResetQueryPoolEXT(handle, query_pool, first, count); 743 dld->vkResetQueryPoolEXT(handle, query_pool, first, count);
735 } 744 }
736 745
737 void GetQueryResults(VkQueryPool query_pool, u32 first, u32 count, std::size_t data_size, 746 VkResult GetQueryResults(VkQueryPool query_pool, u32 first, u32 count, std::size_t data_size,
738 void* data, VkDeviceSize stride, VkQueryResultFlags flags) const { 747 void* data, VkDeviceSize stride, VkQueryResultFlags flags) const
739 Check(dld->vkGetQueryPoolResults(handle, query_pool, first, count, data_size, data, stride, 748 noexcept {
740 flags)); 749 return dld->vkGetQueryPoolResults(handle, query_pool, first, count, data_size, data, stride,
741 } 750 flags);
742
743 template <typename T>
744 T GetQueryResult(VkQueryPool query_pool, u32 first, VkQueryResultFlags flags) const {
745 static_assert(std::is_trivially_copyable_v<T>);
746 T value;
747 GetQueryResults(query_pool, first, 1, sizeof(T), &value, sizeof(T), flags);
748 return value;
749 } 751 }
750}; 752};
751 753
@@ -920,10 +922,6 @@ public:
920 dld->vkCmdPushConstants(handle, layout, flags, offset, size, values); 922 dld->vkCmdPushConstants(handle, layout, flags, offset, size, values);
921 } 923 }
922 924
923 void SetCheckpointNV(const void* checkpoint_marker) const noexcept {
924 dld->vkCmdSetCheckpointNV(handle, checkpoint_marker);
925 }
926
927 void SetViewport(u32 first, Span<VkViewport> viewports) const noexcept { 925 void SetViewport(u32 first, Span<VkViewport> viewports) const noexcept {
928 dld->vkCmdSetViewport(handle, first, viewports.size(), viewports.data()); 926 dld->vkCmdSetViewport(handle, first, viewports.size(), viewports.data());
929 } 927 }
@@ -956,6 +954,10 @@ public:
956 dld->vkCmdSetDepthBounds(handle, min_depth_bounds, max_depth_bounds); 954 dld->vkCmdSetDepthBounds(handle, min_depth_bounds, max_depth_bounds);
957 } 955 }
958 956
957 void SetEvent(VkEvent event, VkPipelineStageFlags stage_flags) const noexcept {
958 dld->vkCmdSetEvent(handle, event, stage_flags);
959 }
960
959 void BindTransformFeedbackBuffersEXT(u32 first, u32 count, const VkBuffer* buffers, 961 void BindTransformFeedbackBuffersEXT(u32 first, u32 count, const VkBuffer* buffers,
960 const VkDeviceSize* offsets, 962 const VkDeviceSize* offsets,
961 const VkDeviceSize* sizes) const noexcept { 963 const VkDeviceSize* sizes) const noexcept {
diff --git a/src/video_core/shader/control_flow.cpp b/src/video_core/shader/control_flow.cpp
index 6d313963a..e00a3fb70 100644
--- a/src/video_core/shader/control_flow.cpp
+++ b/src/video_core/shader/control_flow.cpp
@@ -587,8 +587,6 @@ bool TryQuery(CFGRebuildState& state) {
587 return true; 587 return true;
588} 588}
589 589
590} // Anonymous namespace
591
592void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch_info) { 590void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch_info) {
593 const auto get_expr = ([&](const Condition& cond) -> Expr { 591 const auto get_expr = ([&](const Condition& cond) -> Expr {
594 Expr result{}; 592 Expr result{};
@@ -655,6 +653,8 @@ void DecompileShader(CFGRebuildState& state) {
655 state.manager->Decompile(); 653 state.manager->Decompile();
656} 654}
657 655
656} // Anonymous namespace
657
658std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 start_address, 658std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 start_address,
659 const CompilerSettings& settings, 659 const CompilerSettings& settings,
660 Registry& registry) { 660 Registry& registry) {
diff --git a/src/video_core/shader/decode/arithmetic_half.cpp b/src/video_core/shader/decode/arithmetic_half.cpp
index ee7d9a29d..a276aee44 100644
--- a/src/video_core/shader/decode/arithmetic_half.cpp
+++ b/src/video_core/shader/decode/arithmetic_half.cpp
@@ -19,22 +19,46 @@ u32 ShaderIR::DecodeArithmeticHalf(NodeBlock& bb, u32 pc) {
19 const Instruction instr = {program_code[pc]}; 19 const Instruction instr = {program_code[pc]};
20 const auto opcode = OpCode::Decode(instr); 20 const auto opcode = OpCode::Decode(instr);
21 21
22 if (opcode->get().GetId() == OpCode::Id::HADD2_C || 22 bool negate_a = false;
23 opcode->get().GetId() == OpCode::Id::HADD2_R) { 23 bool negate_b = false;
24 bool absolute_a = false;
25 bool absolute_b = false;
26
27 switch (opcode->get().GetId()) {
28 case OpCode::Id::HADD2_R:
24 if (instr.alu_half.ftz == 0) { 29 if (instr.alu_half.ftz == 0) {
25 LOG_DEBUG(HW_GPU, "{} without FTZ is not implemented", opcode->get().GetName()); 30 LOG_DEBUG(HW_GPU, "{} without FTZ is not implemented", opcode->get().GetName());
26 } 31 }
32 negate_a = ((instr.value >> 43) & 1) != 0;
33 negate_b = ((instr.value >> 31) & 1) != 0;
34 absolute_a = ((instr.value >> 44) & 1) != 0;
35 absolute_b = ((instr.value >> 30) & 1) != 0;
36 break;
37 case OpCode::Id::HADD2_C:
38 if (instr.alu_half.ftz == 0) {
39 LOG_DEBUG(HW_GPU, "{} without FTZ is not implemented", opcode->get().GetName());
40 }
41 negate_a = ((instr.value >> 43) & 1) != 0;
42 negate_b = ((instr.value >> 56) & 1) != 0;
43 absolute_a = ((instr.value >> 44) & 1) != 0;
44 absolute_b = ((instr.value >> 54) & 1) != 0;
45 break;
46 case OpCode::Id::HMUL2_R:
47 negate_a = ((instr.value >> 43) & 1) != 0;
48 absolute_a = ((instr.value >> 44) & 1) != 0;
49 absolute_b = ((instr.value >> 30) & 1) != 0;
50 break;
51 case OpCode::Id::HMUL2_C:
52 negate_b = ((instr.value >> 31) & 1) != 0;
53 absolute_a = ((instr.value >> 44) & 1) != 0;
54 absolute_b = ((instr.value >> 54) & 1) != 0;
55 break;
27 } 56 }
28 57
29 const bool negate_a =
30 opcode->get().GetId() != OpCode::Id::HMUL2_R && instr.alu_half.negate_a != 0;
31 const bool negate_b =
32 opcode->get().GetId() != OpCode::Id::HMUL2_C && instr.alu_half.negate_b != 0;
33
34 Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.alu_half.type_a); 58 Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.alu_half.type_a);
35 op_a = GetOperandAbsNegHalf(op_a, instr.alu_half.abs_a, negate_a); 59 op_a = GetOperandAbsNegHalf(op_a, absolute_a, negate_a);
36 60
37 auto [type_b, op_b] = [&]() -> std::tuple<HalfType, Node> { 61 auto [type_b, op_b] = [this, instr, opcode]() -> std::pair<HalfType, Node> {
38 switch (opcode->get().GetId()) { 62 switch (opcode->get().GetId()) {
39 case OpCode::Id::HADD2_C: 63 case OpCode::Id::HADD2_C:
40 case OpCode::Id::HMUL2_C: 64 case OpCode::Id::HMUL2_C:
@@ -48,17 +72,16 @@ u32 ShaderIR::DecodeArithmeticHalf(NodeBlock& bb, u32 pc) {
48 } 72 }
49 }(); 73 }();
50 op_b = UnpackHalfFloat(op_b, type_b); 74 op_b = UnpackHalfFloat(op_b, type_b);
51 // redeclaration to avoid a bug in clang with reusing local bindings in lambdas 75 op_b = GetOperandAbsNegHalf(op_b, absolute_b, negate_b);
52 Node op_b_alt = GetOperandAbsNegHalf(op_b, instr.alu_half.abs_b, negate_b);
53 76
54 Node value = [&]() { 77 Node value = [this, opcode, op_a, op_b = op_b] {
55 switch (opcode->get().GetId()) { 78 switch (opcode->get().GetId()) {
56 case OpCode::Id::HADD2_C: 79 case OpCode::Id::HADD2_C:
57 case OpCode::Id::HADD2_R: 80 case OpCode::Id::HADD2_R:
58 return Operation(OperationCode::HAdd, PRECISE, op_a, op_b_alt); 81 return Operation(OperationCode::HAdd, PRECISE, op_a, op_b);
59 case OpCode::Id::HMUL2_C: 82 case OpCode::Id::HMUL2_C:
60 case OpCode::Id::HMUL2_R: 83 case OpCode::Id::HMUL2_R:
61 return Operation(OperationCode::HMul, PRECISE, op_a, op_b_alt); 84 return Operation(OperationCode::HMul, PRECISE, op_a, op_b);
62 default: 85 default:
63 UNIMPLEMENTED_MSG("Unhandled half float instruction: {}", opcode->get().GetName()); 86 UNIMPLEMENTED_MSG("Unhandled half float instruction: {}", opcode->get().GetName());
64 return Immediate(0); 87 return Immediate(0);
diff --git a/src/video_core/shader/decode/arithmetic_integer.cpp b/src/video_core/shader/decode/arithmetic_integer.cpp
index 0f4c3103a..9af8c606d 100644
--- a/src/video_core/shader/decode/arithmetic_integer.cpp
+++ b/src/video_core/shader/decode/arithmetic_integer.cpp
@@ -249,8 +249,8 @@ u32 ShaderIR::DecodeArithmeticInteger(NodeBlock& bb, u32 pc) {
249 } 249 }
250 case OpCode::Id::LEA_IMM: { 250 case OpCode::Id::LEA_IMM: {
251 const bool neg = instr.lea.imm.neg != 0; 251 const bool neg = instr.lea.imm.neg != 0;
252 return {Immediate(static_cast<u32>(instr.lea.imm.entry_a)), 252 return {GetOperandAbsNegInteger(GetRegister(instr.gpr8), false, neg, true),
253 GetOperandAbsNegInteger(GetRegister(instr.gpr8), false, neg, true), 253 Immediate(static_cast<u32>(instr.lea.imm.entry_a)),
254 Immediate(static_cast<u32>(instr.lea.imm.entry_b))}; 254 Immediate(static_cast<u32>(instr.lea.imm.entry_b))};
255 } 255 }
256 case OpCode::Id::LEA_RZ: { 256 case OpCode::Id::LEA_RZ: {
diff --git a/src/video_core/texture_cache/surface_base.h b/src/video_core/texture_cache/surface_base.h
index c5ab21f56..79e10ffbb 100644
--- a/src/video_core/texture_cache/surface_base.h
+++ b/src/video_core/texture_cache/surface_base.h
@@ -192,6 +192,22 @@ public:
192 index = index_; 192 index = index_;
193 } 193 }
194 194
195 void SetMemoryMarked(bool is_memory_marked_) {
196 is_memory_marked = is_memory_marked_;
197 }
198
199 bool IsMemoryMarked() const {
200 return is_memory_marked;
201 }
202
203 void SetSyncPending(bool is_sync_pending_) {
204 is_sync_pending = is_sync_pending_;
205 }
206
207 bool IsSyncPending() const {
208 return is_sync_pending;
209 }
210
195 void MarkAsPicked(bool is_picked_) { 211 void MarkAsPicked(bool is_picked_) {
196 is_picked = is_picked_; 212 is_picked = is_picked_;
197 } 213 }
@@ -303,6 +319,8 @@ private:
303 bool is_target{}; 319 bool is_target{};
304 bool is_registered{}; 320 bool is_registered{};
305 bool is_picked{}; 321 bool is_picked{};
322 bool is_memory_marked{};
323 bool is_sync_pending{};
306 u32 index{NO_RT}; 324 u32 index{NO_RT};
307 u64 modification_tick{}; 325 u64 modification_tick{};
308}; 326};
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 69ca08fd1..cf6bd005a 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -6,6 +6,7 @@
6 6
7#include <algorithm> 7#include <algorithm>
8#include <array> 8#include <array>
9#include <list>
9#include <memory> 10#include <memory>
10#include <mutex> 11#include <mutex>
11#include <set> 12#include <set>
@@ -62,6 +63,30 @@ public:
62 } 63 }
63 } 64 }
64 65
66 void OnCPUWrite(VAddr addr, std::size_t size) {
67 std::lock_guard lock{mutex};
68
69 for (const auto& surface : GetSurfacesInRegion(addr, size)) {
70 if (surface->IsMemoryMarked()) {
71 UnmarkMemory(surface);
72 surface->SetSyncPending(true);
73 marked_for_unregister.emplace_back(surface);
74 }
75 }
76 }
77
78 void SyncGuestHost() {
79 std::lock_guard lock{mutex};
80
81 for (const auto& surface : marked_for_unregister) {
82 if (surface->IsRegistered()) {
83 surface->SetSyncPending(false);
84 Unregister(surface);
85 }
86 }
87 marked_for_unregister.clear();
88 }
89
65 /** 90 /**
66 * Guarantees that rendertargets don't unregister themselves if the 91 * Guarantees that rendertargets don't unregister themselves if the
67 * collide. Protection is currently only done on 3D slices. 92 * collide. Protection is currently only done on 3D slices.
@@ -85,10 +110,20 @@ public:
85 return a->GetModificationTick() < b->GetModificationTick(); 110 return a->GetModificationTick() < b->GetModificationTick();
86 }); 111 });
87 for (const auto& surface : surfaces) { 112 for (const auto& surface : surfaces) {
113 mutex.unlock();
88 FlushSurface(surface); 114 FlushSurface(surface);
115 mutex.lock();
89 } 116 }
90 } 117 }
91 118
119 bool MustFlushRegion(VAddr addr, std::size_t size) {
120 std::lock_guard lock{mutex};
121
122 const auto surfaces = GetSurfacesInRegion(addr, size);
123 return std::any_of(surfaces.cbegin(), surfaces.cend(),
124 [](const TSurface& surface) { return surface->IsModified(); });
125 }
126
92 TView GetTextureSurface(const Tegra::Texture::TICEntry& tic, 127 TView GetTextureSurface(const Tegra::Texture::TICEntry& tic,
93 const VideoCommon::Shader::Sampler& entry) { 128 const VideoCommon::Shader::Sampler& entry) {
94 std::lock_guard lock{mutex}; 129 std::lock_guard lock{mutex};
@@ -206,8 +241,14 @@ public:
206 241
207 auto surface_view = GetSurface(gpu_addr, *cpu_addr, 242 auto surface_view = GetSurface(gpu_addr, *cpu_addr,
208 SurfaceParams::CreateForFramebuffer(system, index), true); 243 SurfaceParams::CreateForFramebuffer(system, index), true);
209 if (render_targets[index].target) 244 if (render_targets[index].target) {
210 render_targets[index].target->MarkAsRenderTarget(false, NO_RT); 245 auto& surface = render_targets[index].target;
246 surface->MarkAsRenderTarget(false, NO_RT);
247 const auto& cr_params = surface->GetSurfaceParams();
248 if (!cr_params.is_tiled && Settings::values.use_asynchronous_gpu_emulation) {
249 AsyncFlushSurface(surface);
250 }
251 }
211 render_targets[index].target = surface_view.first; 252 render_targets[index].target = surface_view.first;
212 render_targets[index].view = surface_view.second; 253 render_targets[index].view = surface_view.second;
213 if (render_targets[index].target) 254 if (render_targets[index].target)
@@ -284,6 +325,34 @@ public:
284 return ++ticks; 325 return ++ticks;
285 } 326 }
286 327
328 void CommitAsyncFlushes() {
329 committed_flushes.push_back(uncommitted_flushes);
330 uncommitted_flushes.reset();
331 }
332
333 bool HasUncommittedFlushes() const {
334 return uncommitted_flushes != nullptr;
335 }
336
337 bool ShouldWaitAsyncFlushes() const {
338 return !committed_flushes.empty() && committed_flushes.front() != nullptr;
339 }
340
341 void PopAsyncFlushes() {
342 if (committed_flushes.empty()) {
343 return;
344 }
345 auto& flush_list = committed_flushes.front();
346 if (!flush_list) {
347 committed_flushes.pop_front();
348 return;
349 }
350 for (TSurface& surface : *flush_list) {
351 FlushSurface(surface);
352 }
353 committed_flushes.pop_front();
354 }
355
287protected: 356protected:
288 explicit TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 357 explicit TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
289 bool is_astc_supported) 358 bool is_astc_supported)
@@ -345,9 +414,20 @@ protected:
345 surface->SetCpuAddr(*cpu_addr); 414 surface->SetCpuAddr(*cpu_addr);
346 RegisterInnerCache(surface); 415 RegisterInnerCache(surface);
347 surface->MarkAsRegistered(true); 416 surface->MarkAsRegistered(true);
417 surface->SetMemoryMarked(true);
348 rasterizer.UpdatePagesCachedCount(*cpu_addr, size, 1); 418 rasterizer.UpdatePagesCachedCount(*cpu_addr, size, 1);
349 } 419 }
350 420
421 void UnmarkMemory(TSurface surface) {
422 if (!surface->IsMemoryMarked()) {
423 return;
424 }
425 const std::size_t size = surface->GetSizeInBytes();
426 const VAddr cpu_addr = surface->GetCpuAddr();
427 rasterizer.UpdatePagesCachedCount(cpu_addr, size, -1);
428 surface->SetMemoryMarked(false);
429 }
430
351 void Unregister(TSurface surface) { 431 void Unregister(TSurface surface) {
352 if (guard_render_targets && surface->IsProtected()) { 432 if (guard_render_targets && surface->IsProtected()) {
353 return; 433 return;
@@ -355,9 +435,11 @@ protected:
355 if (!guard_render_targets && surface->IsRenderTarget()) { 435 if (!guard_render_targets && surface->IsRenderTarget()) {
356 ManageRenderTargetUnregister(surface); 436 ManageRenderTargetUnregister(surface);
357 } 437 }
358 const std::size_t size = surface->GetSizeInBytes(); 438 UnmarkMemory(surface);
359 const VAddr cpu_addr = surface->GetCpuAddr(); 439 if (surface->IsSyncPending()) {
360 rasterizer.UpdatePagesCachedCount(cpu_addr, size, -1); 440 marked_for_unregister.remove(surface);
441 surface->SetSyncPending(false);
442 }
361 UnregisterInnerCache(surface); 443 UnregisterInnerCache(surface);
362 surface->MarkAsRegistered(false); 444 surface->MarkAsRegistered(false);
363 ReserveSurface(surface->GetSurfaceParams(), surface); 445 ReserveSurface(surface->GetSurfaceParams(), surface);
@@ -417,7 +499,7 @@ private:
417 **/ 499 **/
418 RecycleStrategy PickStrategy(std::vector<TSurface>& overlaps, const SurfaceParams& params, 500 RecycleStrategy PickStrategy(std::vector<TSurface>& overlaps, const SurfaceParams& params,
419 const GPUVAddr gpu_addr, const MatchTopologyResult untopological) { 501 const GPUVAddr gpu_addr, const MatchTopologyResult untopological) {
420 if (Settings::values.use_accurate_gpu_emulation) { 502 if (Settings::IsGPULevelExtreme()) {
421 return RecycleStrategy::Flush; 503 return RecycleStrategy::Flush;
422 } 504 }
423 // 3D Textures decision 505 // 3D Textures decision
@@ -461,7 +543,7 @@ private:
461 } 543 }
462 switch (PickStrategy(overlaps, params, gpu_addr, untopological)) { 544 switch (PickStrategy(overlaps, params, gpu_addr, untopological)) {
463 case RecycleStrategy::Ignore: { 545 case RecycleStrategy::Ignore: {
464 return InitializeSurface(gpu_addr, params, Settings::values.use_accurate_gpu_emulation); 546 return InitializeSurface(gpu_addr, params, Settings::IsGPULevelExtreme());
465 } 547 }
466 case RecycleStrategy::Flush: { 548 case RecycleStrategy::Flush: {
467 std::sort(overlaps.begin(), overlaps.end(), 549 std::sort(overlaps.begin(), overlaps.end(),
@@ -509,7 +591,7 @@ private:
509 } 591 }
510 const auto& final_params = new_surface->GetSurfaceParams(); 592 const auto& final_params = new_surface->GetSurfaceParams();
511 if (cr_params.type != final_params.type) { 593 if (cr_params.type != final_params.type) {
512 if (Settings::values.use_accurate_gpu_emulation) { 594 if (Settings::IsGPULevelExtreme()) {
513 BufferCopy(current_surface, new_surface); 595 BufferCopy(current_surface, new_surface);
514 } 596 }
515 } else { 597 } else {
@@ -598,7 +680,7 @@ private:
598 if (passed_tests == 0) { 680 if (passed_tests == 0) {
599 return {}; 681 return {};
600 // In Accurate GPU all tests should pass, else we recycle 682 // In Accurate GPU all tests should pass, else we recycle
601 } else if (Settings::values.use_accurate_gpu_emulation && passed_tests != overlaps.size()) { 683 } else if (Settings::IsGPULevelExtreme() && passed_tests != overlaps.size()) {
602 return {}; 684 return {};
603 } 685 }
604 for (const auto& surface : overlaps) { 686 for (const auto& surface : overlaps) {
@@ -668,7 +750,7 @@ private:
668 for (const auto& surface : overlaps) { 750 for (const auto& surface : overlaps) {
669 if (!surface->MatchTarget(params.target)) { 751 if (!surface->MatchTarget(params.target)) {
670 if (overlaps.size() == 1 && surface->GetCpuAddr() == cpu_addr) { 752 if (overlaps.size() == 1 && surface->GetCpuAddr() == cpu_addr) {
671 if (Settings::values.use_accurate_gpu_emulation) { 753 if (Settings::IsGPULevelExtreme()) {
672 return std::nullopt; 754 return std::nullopt;
673 } 755 }
674 Unregister(surface); 756 Unregister(surface);
@@ -1106,6 +1188,13 @@ private:
1106 TView view; 1188 TView view;
1107 }; 1189 };
1108 1190
1191 void AsyncFlushSurface(TSurface& surface) {
1192 if (!uncommitted_flushes) {
1193 uncommitted_flushes = std::make_shared<std::list<TSurface>>();
1194 }
1195 uncommitted_flushes->push_back(surface);
1196 }
1197
1109 VideoCore::RasterizerInterface& rasterizer; 1198 VideoCore::RasterizerInterface& rasterizer;
1110 1199
1111 FormatLookupTable format_lookup_table; 1200 FormatLookupTable format_lookup_table;
@@ -1150,6 +1239,11 @@ private:
1150 std::unordered_map<u32, TSurface> invalid_cache; 1239 std::unordered_map<u32, TSurface> invalid_cache;
1151 std::vector<u8> invalid_memory; 1240 std::vector<u8> invalid_memory;
1152 1241
1242 std::list<TSurface> marked_for_unregister;
1243
1244 std::shared_ptr<std::list<TSurface>> uncommitted_flushes{};
1245 std::list<std::shared_ptr<std::list<TSurface>>> committed_flushes;
1246
1153 StagingCache staging_cache; 1247 StagingCache staging_cache;
1154 std::recursive_mutex mutex; 1248 std::recursive_mutex mutex;
1155}; 1249};
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 7df5f1452..fae8638ec 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -11,6 +11,7 @@
11#include "video_core/textures/texture.h" 11#include "video_core/textures/texture.h"
12 12
13namespace Tegra::Texture { 13namespace Tegra::Texture {
14namespace {
14 15
15/** 16/**
16 * This table represents the internal swizzle of a gob, 17 * This table represents the internal swizzle of a gob,
@@ -174,6 +175,8 @@ void SwizzledData(u8* const swizzled_data, u8* const unswizzled_data, const bool
174 } 175 }
175} 176}
176 177
178} // Anonymous namespace
179
177void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel, 180void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel,
178 u32 out_bytes_per_pixel, u8* const swizzled_data, u8* const unswizzled_data, 181 u32 out_bytes_per_pixel, u8* const swizzled_data, u8* const unswizzled_data,
179 bool unswizzle, u32 block_height, u32 block_depth, u32 width_spacing) { 182 bool unswizzle, u32 block_height, u32 block_depth, u32 width_spacing) {
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index e5eac3f3b..9f2d6d308 100644
--- a/src/video_core/textures/decoders.h
+++ b/src/video_core/textures/decoders.h
@@ -56,8 +56,7 @@ void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32
56 u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height, 56 u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height,
57 u32 offset_x, u32 offset_y); 57 u32 offset_x, u32 offset_y);
58 58
59void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32 dst_y, 59void SwizzleKepler(u32 width, u32 height, u32 dst_x, u32 dst_y, u32 block_height,
60 const u32 block_height, const std::size_t copy_size, const u8* source_data, 60 std::size_t copy_size, const u8* source_data, u8* swizzle_data);
61 u8* swizzle_data);
62 61
63} // namespace Tegra::Texture 62} // namespace Tegra::Texture
diff --git a/src/yuzu/applets/profile_select.cpp b/src/yuzu/applets/profile_select.cpp
index 6aff38735..4bc8ee726 100644
--- a/src/yuzu/applets/profile_select.cpp
+++ b/src/yuzu/applets/profile_select.cpp
@@ -17,6 +17,7 @@
17#include "yuzu/applets/profile_select.h" 17#include "yuzu/applets/profile_select.h"
18#include "yuzu/main.h" 18#include "yuzu/main.h"
19 19
20namespace {
20QString FormatUserEntryText(const QString& username, Common::UUID uuid) { 21QString FormatUserEntryText(const QString& username, Common::UUID uuid) {
21 return QtProfileSelectionDialog::tr( 22 return QtProfileSelectionDialog::tr(
22 "%1\n%2", "%1 is the profile username, %2 is the formatted UUID (e.g. " 23 "%1\n%2", "%1 is the profile username, %2 is the formatted UUID (e.g. "
@@ -41,6 +42,7 @@ QPixmap GetIcon(Common::UUID uuid) {
41 42
42 return icon.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); 43 return icon.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
43} 44}
45} // Anonymous namespace
44 46
45QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent) 47QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent)
46 : QDialog(parent), profile_manager(std::make_unique<Service::Account::ProfileManager>()) { 48 : QDialog(parent), profile_manager(std::make_unique<Service::Account::ProfileManager>()) {
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 7f6dfac84..a44eed047 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -12,7 +12,6 @@
12#include "input_common/main.h" 12#include "input_common/main.h"
13#include "input_common/udp/client.h" 13#include "input_common/udp/client.h"
14#include "yuzu/configuration/config.h" 14#include "yuzu/configuration/config.h"
15#include "yuzu/uisettings.h"
16 15
17Config::Config() { 16Config::Config() {
18 // TODO: Don't hardcode the path; let the frontend decide where to put the config files. 17 // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
@@ -212,12 +211,13 @@ const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default
212// This must be in alphabetical order according to action name as it must have the same order as 211// This must be in alphabetical order according to action name as it must have the same order as
213// UISetting::values.shortcuts, which is alphabetically ordered. 212// UISetting::values.shortcuts, which is alphabetically ordered.
214// clang-format off 213// clang-format off
215const std::array<UISettings::Shortcut, 15> default_hotkeys{{ 214const std::array<UISettings::Shortcut, 15> Config::default_hotkeys{{
216 {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::ApplicationShortcut}}, 215 {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::ApplicationShortcut}},
216 {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}},
217 {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}}, 217 {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}},
218 {QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}}, 218 {QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}},
219 {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}},
220 {QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), Qt::WindowShortcut}}, 219 {QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), Qt::WindowShortcut}},
220 {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}},
221 {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}}, 221 {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}},
222 {QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}}, 222 {QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}},
223 {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::ApplicationShortcut}}, 223 {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::ApplicationShortcut}},
@@ -227,7 +227,6 @@ const std::array<UISettings::Shortcut, 15> default_hotkeys{{
227 {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}}, 227 {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}},
228 {QStringLiteral("Toggle Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}}, 228 {QStringLiteral("Toggle Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}},
229 {QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}}, 229 {QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}},
230 {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}},
231}}; 230}};
232// clang-format on 231// clang-format on
233 232
@@ -639,11 +638,13 @@ void Config::ReadRendererValues() {
639 Settings::values.frame_limit = ReadSetting(QStringLiteral("frame_limit"), 100).toInt(); 638 Settings::values.frame_limit = ReadSetting(QStringLiteral("frame_limit"), 100).toInt();
640 Settings::values.use_disk_shader_cache = 639 Settings::values.use_disk_shader_cache =
641 ReadSetting(QStringLiteral("use_disk_shader_cache"), true).toBool(); 640 ReadSetting(QStringLiteral("use_disk_shader_cache"), true).toBool();
642 Settings::values.use_accurate_gpu_emulation = 641 const int gpu_accuracy_level = ReadSetting(QStringLiteral("gpu_accuracy"), 0).toInt();
643 ReadSetting(QStringLiteral("use_accurate_gpu_emulation"), false).toBool(); 642 Settings::values.gpu_accuracy = static_cast<Settings::GPUAccuracy>(gpu_accuracy_level);
644 Settings::values.use_asynchronous_gpu_emulation = 643 Settings::values.use_asynchronous_gpu_emulation =
645 ReadSetting(QStringLiteral("use_asynchronous_gpu_emulation"), false).toBool(); 644 ReadSetting(QStringLiteral("use_asynchronous_gpu_emulation"), false).toBool();
646 Settings::values.use_vsync = ReadSetting(QStringLiteral("use_vsync"), true).toBool(); 645 Settings::values.use_vsync = ReadSetting(QStringLiteral("use_vsync"), true).toBool();
646 Settings::values.use_fast_gpu_time =
647 ReadSetting(QStringLiteral("use_fast_gpu_time"), true).toBool();
647 Settings::values.force_30fps_mode = 648 Settings::values.force_30fps_mode =
648 ReadSetting(QStringLiteral("force_30fps_mode"), false).toBool(); 649 ReadSetting(QStringLiteral("force_30fps_mode"), false).toBool();
649 650
@@ -1080,11 +1081,12 @@ void Config::SaveRendererValues() {
1080 WriteSetting(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100); 1081 WriteSetting(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100);
1081 WriteSetting(QStringLiteral("use_disk_shader_cache"), Settings::values.use_disk_shader_cache, 1082 WriteSetting(QStringLiteral("use_disk_shader_cache"), Settings::values.use_disk_shader_cache,
1082 true); 1083 true);
1083 WriteSetting(QStringLiteral("use_accurate_gpu_emulation"), 1084 WriteSetting(QStringLiteral("gpu_accuracy"), static_cast<int>(Settings::values.gpu_accuracy),
1084 Settings::values.use_accurate_gpu_emulation, false); 1085 0);
1085 WriteSetting(QStringLiteral("use_asynchronous_gpu_emulation"), 1086 WriteSetting(QStringLiteral("use_asynchronous_gpu_emulation"),
1086 Settings::values.use_asynchronous_gpu_emulation, false); 1087 Settings::values.use_asynchronous_gpu_emulation, false);
1087 WriteSetting(QStringLiteral("use_vsync"), Settings::values.use_vsync, true); 1088 WriteSetting(QStringLiteral("use_vsync"), Settings::values.use_vsync, true);
1089 WriteSetting(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time, true);
1088 WriteSetting(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode, false); 1090 WriteSetting(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode, false);
1089 1091
1090 // Cast to double because Qt's written float values are not human-readable 1092 // Cast to double because Qt's written float values are not human-readable
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index ba6888004..5cd2a5feb 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -9,6 +9,7 @@
9#include <string> 9#include <string>
10#include <QVariant> 10#include <QVariant>
11#include "core/settings.h" 11#include "core/settings.h"
12#include "yuzu/uisettings.h"
12 13
13class QSettings; 14class QSettings;
14 15
@@ -26,6 +27,7 @@ public:
26 default_mouse_buttons; 27 default_mouse_buttons;
27 static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys; 28 static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys;
28 static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods; 29 static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods;
30 static const std::array<UISettings::Shortcut, 15> default_hotkeys;
29 31
30private: 32private:
31 void ReadValues(); 33 void ReadValues();
diff --git a/src/yuzu/configuration/configure_filesystem.cpp b/src/yuzu/configuration/configure_filesystem.cpp
index 29f540eb7..835ee821c 100644
--- a/src/yuzu/configuration/configure_filesystem.cpp
+++ b/src/yuzu/configuration/configure_filesystem.cpp
@@ -138,7 +138,7 @@ void ConfigureFilesystem::SetDirectory(DirectoryTarget target, QLineEdit* edit)
138 str = QFileDialog::getOpenFileName(this, caption, QFileInfo(edit->text()).dir().path(), 138 str = QFileDialog::getOpenFileName(this, caption, QFileInfo(edit->text()).dir().path(),
139 QStringLiteral("NX Gamecard;*.xci")); 139 QStringLiteral("NX Gamecard;*.xci"));
140 } else { 140 } else {
141 str = QFileDialog::getExistingDirectory(this, caption, edit->text()); 141 str = QFileDialog::getExistingDirectory(this, caption, edit->text()) + QDir::separator();
142 } 142 }
143 143
144 if (str.isEmpty()) 144 if (str.isEmpty())
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
index b9f429f84..5bb2ae555 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -19,9 +19,10 @@ ConfigureGraphicsAdvanced::~ConfigureGraphicsAdvanced() = default;
19 19
20void ConfigureGraphicsAdvanced::SetConfiguration() { 20void ConfigureGraphicsAdvanced::SetConfiguration() {
21 const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); 21 const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
22 ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); 22 ui->gpu_accuracy->setCurrentIndex(static_cast<int>(Settings::values.gpu_accuracy));
23 ui->use_vsync->setEnabled(runtime_lock); 23 ui->use_vsync->setEnabled(runtime_lock);
24 ui->use_vsync->setChecked(Settings::values.use_vsync); 24 ui->use_vsync->setChecked(Settings::values.use_vsync);
25 ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time);
25 ui->force_30fps_mode->setEnabled(runtime_lock); 26 ui->force_30fps_mode->setEnabled(runtime_lock);
26 ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode); 27 ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode);
27 ui->anisotropic_filtering_combobox->setEnabled(runtime_lock); 28 ui->anisotropic_filtering_combobox->setEnabled(runtime_lock);
@@ -29,8 +30,10 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
29} 30}
30 31
31void ConfigureGraphicsAdvanced::ApplyConfiguration() { 32void ConfigureGraphicsAdvanced::ApplyConfiguration() {
32 Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked(); 33 auto gpu_accuracy = static_cast<Settings::GPUAccuracy>(ui->gpu_accuracy->currentIndex());
34 Settings::values.gpu_accuracy = gpu_accuracy;
33 Settings::values.use_vsync = ui->use_vsync->isChecked(); 35 Settings::values.use_vsync = ui->use_vsync->isChecked();
36 Settings::values.use_fast_gpu_time = ui->use_fast_gpu_time->isChecked();
34 Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked(); 37 Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked();
35 Settings::values.max_anisotropy = ui->anisotropic_filtering_combobox->currentIndex(); 38 Settings::values.max_anisotropy = ui->anisotropic_filtering_combobox->currentIndex();
36} 39}
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui
index 42eec278e..770b80c50 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.ui
+++ b/src/yuzu/configuration/configure_graphics_advanced.ui
@@ -23,11 +23,34 @@
23 </property> 23 </property>
24 <layout class="QVBoxLayout" name="verticalLayout_3"> 24 <layout class="QVBoxLayout" name="verticalLayout_3">
25 <item> 25 <item>
26 <widget class="QCheckBox" name="use_accurate_gpu_emulation"> 26 <layout class="QHBoxLayout" name="horizontalLayout_2">
27 <property name="text"> 27 <item>
28 <string>Use accurate GPU emulation (slow)</string> 28 <widget class="QLabel" name="label_gpu_accuracy">
29 </property> 29 <property name="text">
30 </widget> 30 <string>Accuracy Level:</string>
31 </property>
32 </widget>
33 </item>
34 <item>
35 <widget class="QComboBox" name="gpu_accuracy">
36 <item>
37 <property name="text">
38 <string notr="true">Normal</string>
39 </property>
40 </item>
41 <item>
42 <property name="text">
43 <string notr="true">High</string>
44 </property>
45 </item>
46 <item>
47 <property name="text">
48 <string notr="true">Extreme(very slow)</string>
49 </property>
50 </item>
51 </widget>
52 </item>
53 </layout>
31 </item> 54 </item>
32 <item> 55 <item>
33 <widget class="QCheckBox" name="use_vsync"> 56 <widget class="QCheckBox" name="use_vsync">
@@ -47,6 +70,13 @@
47 </widget> 70 </widget>
48 </item> 71 </item>
49 <item> 72 <item>
73 <widget class="QCheckBox" name="use_fast_gpu_time">
74 <property name="text">
75 <string>Use Fast GPU Time</string>
76 </property>
77 </widget>
78 </item>
79 <item>
50 <layout class="QHBoxLayout" name="horizontalLayout_1"> 80 <layout class="QHBoxLayout" name="horizontalLayout_1">
51 <item> 81 <item>
52 <widget class="QLabel" name="af_label"> 82 <widget class="QLabel" name="af_label">
diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp
index fa9052136..6f7fd4414 100644
--- a/src/yuzu/configuration/configure_hotkeys.cpp
+++ b/src/yuzu/configuration/configure_hotkeys.cpp
@@ -2,10 +2,12 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <QMenu>
5#include <QMessageBox> 6#include <QMessageBox>
6#include <QStandardItemModel> 7#include <QStandardItemModel>
7#include "core/settings.h" 8#include "core/settings.h"
8#include "ui_configure_hotkeys.h" 9#include "ui_configure_hotkeys.h"
10#include "yuzu/configuration/config.h"
9#include "yuzu/configuration/configure_hotkeys.h" 11#include "yuzu/configuration/configure_hotkeys.h"
10#include "yuzu/hotkeys.h" 12#include "yuzu/hotkeys.h"
11#include "yuzu/util/sequence_dialog/sequence_dialog.h" 13#include "yuzu/util/sequence_dialog/sequence_dialog.h"
@@ -19,6 +21,9 @@ ConfigureHotkeys::ConfigureHotkeys(QWidget* parent)
19 model->setColumnCount(3); 21 model->setColumnCount(3);
20 22
21 connect(ui->hotkey_list, &QTreeView::doubleClicked, this, &ConfigureHotkeys::Configure); 23 connect(ui->hotkey_list, &QTreeView::doubleClicked, this, &ConfigureHotkeys::Configure);
24 connect(ui->hotkey_list, &QTreeView::customContextMenuRequested, this,
25 &ConfigureHotkeys::PopupContextMenu);
26 ui->hotkey_list->setContextMenuPolicy(Qt::CustomContextMenu);
22 ui->hotkey_list->setModel(model); 27 ui->hotkey_list->setModel(model);
23 28
24 // TODO(Kloen): Make context configurable as well (hiding the column for now) 29 // TODO(Kloen): Make context configurable as well (hiding the column for now)
@@ -27,6 +32,10 @@ ConfigureHotkeys::ConfigureHotkeys(QWidget* parent)
27 ui->hotkey_list->setColumnWidth(0, 200); 32 ui->hotkey_list->setColumnWidth(0, 200);
28 ui->hotkey_list->resizeColumnToContents(1); 33 ui->hotkey_list->resizeColumnToContents(1);
29 34
35 connect(ui->button_restore_defaults, &QPushButton::clicked, this,
36 &ConfigureHotkeys::RestoreDefaults);
37 connect(ui->button_clear_all, &QPushButton::clicked, this, &ConfigureHotkeys::ClearAll);
38
30 RetranslateUI(); 39 RetranslateUI();
31} 40}
32 41
@@ -71,7 +80,6 @@ void ConfigureHotkeys::Configure(QModelIndex index) {
71 } 80 }
72 81
73 index = index.sibling(index.row(), 1); 82 index = index.sibling(index.row(), 1);
74 auto* const model = ui->hotkey_list->model();
75 const auto previous_key = model->data(index); 83 const auto previous_key = model->data(index);
76 84
77 SequenceDialog hotkey_dialog{this}; 85 SequenceDialog hotkey_dialog{this};
@@ -81,31 +89,33 @@ void ConfigureHotkeys::Configure(QModelIndex index) {
81 if (return_code == QDialog::Rejected || key_sequence.isEmpty()) { 89 if (return_code == QDialog::Rejected || key_sequence.isEmpty()) {
82 return; 90 return;
83 } 91 }
92 const auto [key_sequence_used, used_action] = IsUsedKey(key_sequence);
84 93
85 if (IsUsedKey(key_sequence) && key_sequence != QKeySequence(previous_key.toString())) { 94 if (key_sequence_used && key_sequence != QKeySequence(previous_key.toString())) {
86 QMessageBox::warning(this, tr("Conflicting Key Sequence"), 95 QMessageBox::warning(
87 tr("The entered key sequence is already assigned to another hotkey.")); 96 this, tr("Conflicting Key Sequence"),
97 tr("The entered key sequence is already assigned to: %1").arg(used_action));
88 } else { 98 } else {
89 model->setData(index, key_sequence.toString(QKeySequence::NativeText)); 99 model->setData(index, key_sequence.toString(QKeySequence::NativeText));
90 } 100 }
91} 101}
92 102
93bool ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) const { 103std::pair<bool, QString> ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) const {
94 for (int r = 0; r < model->rowCount(); r++) { 104 for (int r = 0; r < model->rowCount(); ++r) {
95 const QStandardItem* const parent = model->item(r, 0); 105 const QStandardItem* const parent = model->item(r, 0);
96 106
97 for (int r2 = 0; r2 < parent->rowCount(); r2++) { 107 for (int r2 = 0; r2 < parent->rowCount(); ++r2) {
98 const QStandardItem* const key_seq_item = parent->child(r2, 1); 108 const QStandardItem* const key_seq_item = parent->child(r2, 1);
99 const auto key_seq_str = key_seq_item->text(); 109 const auto key_seq_str = key_seq_item->text();
100 const auto key_seq = QKeySequence::fromString(key_seq_str, QKeySequence::NativeText); 110 const auto key_seq = QKeySequence::fromString(key_seq_str, QKeySequence::NativeText);
101 111
102 if (key_sequence == key_seq) { 112 if (key_sequence == key_seq) {
103 return true; 113 return std::make_pair(true, parent->child(r2, 0)->text());
104 } 114 }
105 } 115 }
106 } 116 }
107 117
108 return false; 118 return std::make_pair(false, QString());
109} 119}
110 120
111void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) { 121void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) {
@@ -128,3 +138,55 @@ void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) {
128 138
129 registry.SaveHotkeys(); 139 registry.SaveHotkeys();
130} 140}
141
142void ConfigureHotkeys::RestoreDefaults() {
143 for (int r = 0; r < model->rowCount(); ++r) {
144 const QStandardItem* parent = model->item(r, 0);
145
146 for (int r2 = 0; r2 < parent->rowCount(); ++r2) {
147 model->item(r, 0)->child(r2, 1)->setText(Config::default_hotkeys[r2].shortcut.first);
148 }
149 }
150}
151
152void ConfigureHotkeys::ClearAll() {
153 for (int r = 0; r < model->rowCount(); ++r) {
154 const QStandardItem* parent = model->item(r, 0);
155
156 for (int r2 = 0; r2 < parent->rowCount(); ++r2) {
157 model->item(r, 0)->child(r2, 1)->setText(tr(""));
158 }
159 }
160}
161
162void ConfigureHotkeys::PopupContextMenu(const QPoint& menu_location) {
163 QModelIndex index = ui->hotkey_list->indexAt(menu_location);
164 if (!index.parent().isValid()) {
165 return;
166 }
167
168 const auto selected = index.sibling(index.row(), 1);
169 QMenu context_menu;
170
171 QAction* restore_default = context_menu.addAction(tr("Restore Default"));
172 QAction* clear = context_menu.addAction(tr("Clear"));
173
174 connect(restore_default, &QAction::triggered, [this, selected] {
175 const QKeySequence& default_key_sequence = QKeySequence::fromString(
176 Config::default_hotkeys[selected.row()].shortcut.first, QKeySequence::NativeText);
177 const auto [key_sequence_used, used_action] = IsUsedKey(default_key_sequence);
178
179 if (key_sequence_used &&
180 default_key_sequence != QKeySequence(model->data(selected).toString())) {
181
182 QMessageBox::warning(
183 this, tr("Conflicting Key Sequence"),
184 tr("The default key sequence is already assigned to: %1").arg(used_action));
185 } else {
186 model->setData(selected, default_key_sequence.toString(QKeySequence::NativeText));
187 }
188 });
189 connect(clear, &QAction::triggered, [this, selected] { model->setData(selected, tr("")); });
190
191 context_menu.exec(ui->hotkey_list->viewport()->mapToGlobal(menu_location));
192}
diff --git a/src/yuzu/configuration/configure_hotkeys.h b/src/yuzu/configuration/configure_hotkeys.h
index 8f8c6173b..a2ec3323e 100644
--- a/src/yuzu/configuration/configure_hotkeys.h
+++ b/src/yuzu/configuration/configure_hotkeys.h
@@ -35,7 +35,11 @@ private:
35 void RetranslateUI(); 35 void RetranslateUI();
36 36
37 void Configure(QModelIndex index); 37 void Configure(QModelIndex index);
38 bool IsUsedKey(QKeySequence key_sequence) const; 38 std::pair<bool, QString> IsUsedKey(QKeySequence key_sequence) const;
39
40 void RestoreDefaults();
41 void ClearAll();
42 void PopupContextMenu(const QPoint& menu_location);
39 43
40 std::unique_ptr<Ui::ConfigureHotkeys> ui; 44 std::unique_ptr<Ui::ConfigureHotkeys> ui;
41 45
diff --git a/src/yuzu/configuration/configure_hotkeys.ui b/src/yuzu/configuration/configure_hotkeys.ui
index 0d0b70f38..6d9f861e3 100644
--- a/src/yuzu/configuration/configure_hotkeys.ui
+++ b/src/yuzu/configuration/configure_hotkeys.ui
@@ -6,8 +6,8 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>363</width> 9 <width>439</width>
10 <height>388</height> 10 <height>510</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -15,7 +15,7 @@
15 </property> 15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout"> 16 <layout class="QVBoxLayout" name="verticalLayout">
17 <item> 17 <item>
18 <layout class="QVBoxLayout" name="verticalLayout_2"> 18 <layout class="QHBoxLayout" name="horizontalLayout">
19 <item> 19 <item>
20 <widget class="QLabel" name="label_2"> 20 <widget class="QLabel" name="label_2">
21 <property name="text"> 21 <property name="text">
@@ -24,6 +24,37 @@
24 </widget> 24 </widget>
25 </item> 25 </item>
26 <item> 26 <item>
27 <spacer name="horizontalSpacer">
28 <property name="orientation">
29 <enum>Qt::Horizontal</enum>
30 </property>
31 <property name="sizeHint" stdset="0">
32 <size>
33 <width>40</width>
34 <height>20</height>
35 </size>
36 </property>
37 </spacer>
38 </item>
39 <item>
40 <widget class="QPushButton" name="button_clear_all">
41 <property name="text">
42 <string>Clear All</string>
43 </property>
44 </widget>
45 </item>
46 <item>
47 <widget class="QPushButton" name="button_restore_defaults">
48 <property name="text">
49 <string>Restore Defaults</string>
50 </property>
51 </widget>
52 </item>
53 </layout>
54 </item>
55 <item>
56 <layout class="QVBoxLayout" name="verticalLayout_2">
57 <item>
27 <widget class="QTreeView" name="hotkey_list"> 58 <widget class="QTreeView" name="hotkey_list">
28 <property name="editTriggers"> 59 <property name="editTriggers">
29 <set>QAbstractItemView::NoEditTriggers</set> 60 <set>QAbstractItemView::NoEditTriggers</set>
@@ -39,4 +70,4 @@
39 </widget> 70 </widget>
40 <resources/> 71 <resources/>
41 <connections/> 72 <connections/>
42</ui> \ No newline at end of file 73</ui>
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 15ac30f12..e4eb5594b 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -56,7 +56,6 @@ static void SetAnalogButton(const Common::ParamPackage& input_param,
56 if (analog_param.Get("engine", "") != "analog_from_button") { 56 if (analog_param.Get("engine", "") != "analog_from_button") {
57 analog_param = { 57 analog_param = {
58 {"engine", "analog_from_button"}, 58 {"engine", "analog_from_button"},
59 {"modifier_scale", "0.5"},
60 }; 59 };
61 } 60 }
62 analog_param.Set(button_name, input_param.Serialize()); 61 analog_param.Set(button_name, input_param.Serialize());
@@ -236,8 +235,10 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
236 widget->setVisible(false); 235 widget->setVisible(false);
237 236
238 analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog}; 237 analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog};
239 analog_map_deadzone = {ui->sliderLStickDeadzone, ui->sliderRStickDeadzone}; 238 analog_map_deadzone_and_modifier_slider = {ui->sliderLStickDeadzoneAndModifier,
240 analog_map_deadzone_label = {ui->labelLStickDeadzone, ui->labelRStickDeadzone}; 239 ui->sliderRStickDeadzoneAndModifier};
240 analog_map_deadzone_and_modifier_slider_label = {ui->labelLStickDeadzoneAndModifier,
241 ui->labelRStickDeadzoneAndModifier};
241 242
242 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { 243 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
243 auto* const button = button_map[button_id]; 244 auto* const button = button_map[button_id];
@@ -328,10 +329,18 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
328 InputCommon::Polling::DeviceType::Analog); 329 InputCommon::Polling::DeviceType::Analog);
329 } 330 }
330 }); 331 });
331 connect(analog_map_deadzone[analog_id], &QSlider::valueChanged, [=] { 332
332 const float deadzone = analog_map_deadzone[analog_id]->value() / 100.0f; 333 connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, [=] {
333 analog_map_deadzone_label[analog_id]->setText(tr("Deadzone: %1").arg(deadzone)); 334 const float slider_value = analog_map_deadzone_and_modifier_slider[analog_id]->value();
334 analogs_param[analog_id].Set("deadzone", deadzone); 335 if (analogs_param[analog_id].Get("engine", "") == "sdl") {
336 analog_map_deadzone_and_modifier_slider_label[analog_id]->setText(
337 tr("Deadzone: %1%").arg(slider_value));
338 analogs_param[analog_id].Set("deadzone", slider_value / 100.0f);
339 } else {
340 analog_map_deadzone_and_modifier_slider_label[analog_id]->setText(
341 tr("Modifier Scale: %1%").arg(slider_value));
342 analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f);
343 }
335 }); 344 });
336 } 345 }
337 346
@@ -517,20 +526,31 @@ void ConfigureInputPlayer::UpdateButtonLabels() {
517 analog_map_stick[analog_id]->setText(tr("Set Analog Stick")); 526 analog_map_stick[analog_id]->setText(tr("Set Analog Stick"));
518 527
519 auto& param = analogs_param[analog_id]; 528 auto& param = analogs_param[analog_id];
520 auto* const analog_deadzone_slider = analog_map_deadzone[analog_id]; 529 auto* const analog_stick_slider = analog_map_deadzone_and_modifier_slider[analog_id];
521 auto* const analog_deadzone_label = analog_map_deadzone_label[analog_id]; 530 auto* const analog_stick_slider_label =
522 531 analog_map_deadzone_and_modifier_slider_label[analog_id];
523 if (param.Has("engine") && param.Get("engine", "") == "sdl") { 532
524 if (!param.Has("deadzone")) { 533 if (param.Has("engine")) {
525 param.Set("deadzone", 0.1f); 534 if (param.Get("engine", "") == "sdl") {
535 if (!param.Has("deadzone")) {
536 param.Set("deadzone", 0.1f);
537 }
538
539 analog_stick_slider->setValue(static_cast<int>(param.Get("deadzone", 0.1f) * 100));
540 if (analog_stick_slider->value() == 0) {
541 analog_stick_slider_label->setText(tr("Deadzone: 0%"));
542 }
543 } else {
544 if (!param.Has("modifier_scale")) {
545 param.Set("modifier_scale", 0.5f);
546 }
547
548 analog_stick_slider->setValue(
549 static_cast<int>(param.Get("modifier_scale", 0.5f) * 100));
550 if (analog_stick_slider->value() == 0) {
551 analog_stick_slider_label->setText(tr("Modifier Scale: 0%"));
552 }
526 } 553 }
527
528 analog_deadzone_slider->setValue(static_cast<int>(param.Get("deadzone", 0.1f) * 100));
529 analog_deadzone_slider->setVisible(true);
530 analog_deadzone_label->setVisible(true);
531 } else {
532 analog_deadzone_slider->setVisible(false);
533 analog_deadzone_label->setVisible(false);
534 } 554 }
535 } 555 }
536} 556}
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index 045704e47..95afa5375 100644
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -97,8 +97,10 @@ private:
97 /// Analog inputs are also represented each with a single button, used to configure with an 97 /// Analog inputs are also represented each with a single button, used to configure with an
98 /// actual analog stick 98 /// actual analog stick
99 std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick; 99 std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick;
100 std::array<QSlider*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone; 100 std::array<QSlider*, Settings::NativeAnalog::NumAnalogs>
101 std::array<QLabel*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone_label; 101 analog_map_deadzone_and_modifier_slider;
102 std::array<QLabel*, Settings::NativeAnalog::NumAnalogs>
103 analog_map_deadzone_and_modifier_slider_label;
102 104
103 static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons; 105 static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
104 106
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui
index 4b37746a1..f27a77180 100644
--- a/src/yuzu/configuration/configure_input_player.ui
+++ b/src/yuzu/configuration/configure_input_player.ui
@@ -171,11 +171,11 @@
171 </layout> 171 </layout>
172 </item> 172 </item>
173 <item row="4" column="0" colspan="2"> 173 <item row="4" column="0" colspan="2">
174 <layout class="QVBoxLayout" name="sliderRStickDeadzoneVerticalLayout"> 174 <layout class="QVBoxLayout" name="sliderRStickDeadzoneAndModifierVerticalLayout">
175 <item> 175 <item>
176 <layout class="QHBoxLayout" name="sliderRStickDeadzoneHorizontalLayout"> 176 <layout class="QHBoxLayout" name="sliderRStickDeadzoneAndModifierHorizontalLayout">
177 <item> 177 <item>
178 <widget class="QLabel" name="labelRStickDeadzone"> 178 <widget class="QLabel" name="labelRStickDeadzoneAndModifier">
179 <property name="text"> 179 <property name="text">
180 <string>Deadzone: 0</string> 180 <string>Deadzone: 0</string>
181 </property> 181 </property>
@@ -187,7 +187,7 @@
187 </layout> 187 </layout>
188 </item> 188 </item>
189 <item> 189 <item>
190 <widget class="QSlider" name="sliderRStickDeadzone"> 190 <widget class="QSlider" name="sliderRStickDeadzoneAndModifier">
191 <property name="orientation"> 191 <property name="orientation">
192 <enum>Qt::Horizontal</enum> 192 <enum>Qt::Horizontal</enum>
193 </property> 193 </property>
@@ -784,14 +784,14 @@
784 </layout> 784 </layout>
785 </item> 785 </item>
786 <item row="5" column="1" colspan="2"> 786 <item row="5" column="1" colspan="2">
787 <layout class="QVBoxLayout" name="sliderLStickDeadzoneVerticalLayout"> 787 <layout class="QVBoxLayout" name="sliderLStickDeadzoneAndModifierVerticalLayout">
788 <property name="sizeConstraint"> 788 <property name="sizeConstraint">
789 <enum>QLayout::SetDefaultConstraint</enum> 789 <enum>QLayout::SetDefaultConstraint</enum>
790 </property> 790 </property>
791 <item> 791 <item>
792 <layout class="QHBoxLayout" name="sliderLStickDeadzoneHorizontalLayout"> 792 <layout class="QHBoxLayout" name="sliderLStickDeadzoneAndModifierHorizontalLayout">
793 <item> 793 <item>
794 <widget class="QLabel" name="labelLStickDeadzone"> 794 <widget class="QLabel" name="labelLStickDeadzoneAndModifier">
795 <property name="text"> 795 <property name="text">
796 <string>Deadzone: 0</string> 796 <string>Deadzone: 0</string>
797 </property> 797 </property>
@@ -803,7 +803,7 @@
803 </layout> 803 </layout>
804 </item> 804 </item>
805 <item> 805 <item>
806 <widget class="QSlider" name="sliderLStickDeadzone"> 806 <widget class="QSlider" name="sliderLStickDeadzoneAndModifier">
807 <property name="orientation"> 807 <property name="orientation">
808 <enum>Qt::Horizontal</enum> 808 <enum>Qt::Horizontal</enum>
809 </property> 809 </property>
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h
index 3e6d5a7cd..0cd0054c8 100644
--- a/src/yuzu/game_list_p.h
+++ b/src/yuzu/game_list_p.h
@@ -126,13 +126,6 @@ public:
126 126
127 return GameListItem::data(role); 127 return GameListItem::data(role);
128 } 128 }
129
130 /**
131 * Override to prevent automatic sorting.
132 */
133 bool operator<(const QStandardItem& other) const override {
134 return false;
135 }
136}; 129};
137 130
138class GameListItemCompat : public GameListItem { 131class GameListItemCompat : public GameListItem {
@@ -279,6 +272,13 @@ public:
279 return static_cast<int>(dir_type); 272 return static_cast<int>(dir_type);
280 } 273 }
281 274
275 /**
276 * Override to prevent automatic sorting between folders and the addDir button.
277 */
278 bool operator<(const QStandardItem& other) const override {
279 return false;
280 }
281
282private: 282private:
283 GameListItemType dir_type; 283 GameListItemType dir_type;
284}; 284};
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 80341747f..8476a5a16 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -388,12 +388,14 @@ void Config::ReadValues() {
388 static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100)); 388 static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100));
389 Settings::values.use_disk_shader_cache = 389 Settings::values.use_disk_shader_cache =
390 sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false); 390 sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false);
391 Settings::values.use_accurate_gpu_emulation = 391 const int gpu_accuracy_level = sdl2_config->GetInteger("Renderer", "gpu_accuracy", 0);
392 sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false); 392 Settings::values.gpu_accuracy = static_cast<Settings::GPUAccuracy>(gpu_accuracy_level);
393 Settings::values.use_asynchronous_gpu_emulation = 393 Settings::values.use_asynchronous_gpu_emulation =
394 sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false); 394 sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false);
395 Settings::values.use_vsync = 395 Settings::values.use_vsync =
396 static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync", 1)); 396 static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync", 1));
397 Settings::values.use_fast_gpu_time =
398 sdl2_config->GetBoolean("Renderer", "use_fast_gpu_time", true);
397 399
398 Settings::values.bg_red = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0)); 400 Settings::values.bg_red = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0));
399 Settings::values.bg_green = 401 Settings::values.bg_green =
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 171d16fa0..60b1a62fa 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -146,9 +146,9 @@ frame_limit =
146# 0 (default): Off, 1 : On 146# 0 (default): Off, 1 : On
147use_disk_shader_cache = 147use_disk_shader_cache =
148 148
149# Whether to use accurate GPU emulation 149# Which gpu accuracy level to use
150# 0 (default): Off (fast), 1 : On (slow) 150# 0 (Normal), 1 (High), 2 (Extreme)
151use_accurate_gpu_emulation = 151gpu_accuracy =
152 152
153# Whether to use asynchronous GPU emulation 153# Whether to use asynchronous GPU emulation
154# 0 : Off (slow), 1 (default): On (fast) 154# 0 : Off (slow), 1 (default): On (fast)
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index 19584360c..e5e684206 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -181,9 +181,10 @@ void EmuWindow_SDL2::PollEvents() {
181 const u32 current_time = SDL_GetTicks(); 181 const u32 current_time = SDL_GetTicks();
182 if (current_time > last_time + 2000) { 182 if (current_time > last_time + 2000) {
183 const auto results = Core::System::GetInstance().GetAndResetPerfStats(); 183 const auto results = Core::System::GetInstance().GetAndResetPerfStats();
184 const auto title = fmt::format( 184 const auto title =
185 "yuzu {} | {}-{} | FPS: {:.0f} ({:.0%})", Common::g_build_fullname, 185 fmt::format("yuzu {} | {}-{} | FPS: {:.0f} ({:.0f}%)", Common::g_build_fullname,
186 Common::g_scm_branch, Common::g_scm_desc, results.game_fps, results.emulation_speed); 186 Common::g_scm_branch, Common::g_scm_desc, results.game_fps,
187 results.emulation_speed * 100.0);
187 SDL_SetWindowTitle(render_window, title.c_str()); 188 SDL_SetWindowTitle(render_window, title.c_str());
188 last_time = current_time; 189 last_time = current_time;
189 } 190 }
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
index f2990910e..cb8e68a39 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
@@ -29,6 +29,7 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen)
29 SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); 29 SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
30 30
31 SDL_SysWMinfo wm; 31 SDL_SysWMinfo wm;
32 SDL_VERSION(&wm.version);
32 if (SDL_GetWindowWMInfo(render_window, &wm) == SDL_FALSE) { 33 if (SDL_GetWindowWMInfo(render_window, &wm) == SDL_FALSE) {
33 LOG_CRITICAL(Frontend, "Failed to get information from the window manager"); 34 LOG_CRITICAL(Frontend, "Failed to get information from the window manager");
34 std::exit(EXIT_FAILURE); 35 std::exit(EXIT_FAILURE);
@@ -70,7 +71,7 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen)
70EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() = default; 71EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() = default;
71 72
72std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const { 73std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const {
73 return nullptr; 74 return std::make_unique<DummyContext>();
74} 75}
75 76
76void EmuWindow_SDL2_VK::Present() { 77void EmuWindow_SDL2_VK::Present() {
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
index b8021ebea..77a6ca72b 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
@@ -22,3 +22,5 @@ public:
22 22
23 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; 23 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
24}; 24};
25
26class DummyContext : public Core::Frontend::GraphicsContext {};
diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp
index ee2591c8f..3be58b15d 100644
--- a/src/yuzu_tester/config.cpp
+++ b/src/yuzu_tester/config.cpp
@@ -126,10 +126,12 @@ void Config::ReadValues() {
126 Settings::values.frame_limit = 100; 126 Settings::values.frame_limit = 100;
127 Settings::values.use_disk_shader_cache = 127 Settings::values.use_disk_shader_cache =
128 sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false); 128 sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false);
129 Settings::values.use_accurate_gpu_emulation = 129 const int gpu_accuracy_level = sdl2_config->GetInteger("Renderer", "gpu_accuracy", 0);
130 sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false); 130 Settings::values.gpu_accuracy = static_cast<Settings::GPUAccuracy>(gpu_accuracy_level);
131 Settings::values.use_asynchronous_gpu_emulation = 131 Settings::values.use_asynchronous_gpu_emulation =
132 sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false); 132 sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false);
133 Settings::values.use_fast_gpu_time =
134 sdl2_config->GetBoolean("Renderer", "use_fast_gpu_time", true);
133 135
134 Settings::values.bg_red = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0)); 136 Settings::values.bg_red = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0));
135 Settings::values.bg_green = 137 Settings::values.bg_green =