Compare commits

...

42 Commits

Author SHA1 Message Date
liushuyu
60928cf8cd video_core/codec: address comments 2021-11-24 18:06:38 -07:00
liushuyu
72aa418b0b video_core/codecs: fix multiple decoding issues on Linux ...
* when someone installed Intel video drivers on an AMD system, the
  decoder will select the Intel VA-API decoding driver and yuzu will
  crash due to incorrect driver selection; the fix will check if the
  currently about-to-use driver is loaded in the kernel
* when using NVIDIA driver on Linux with a ffmpeg that does not have
  CUDA capability enabled, the decoder will crash; the fix simply
  making the decoder prefers the VDPAU driver over CUDA on Linux
2021-11-24 17:23:57 -07:00
bunnei
cd6cf0422d Merge pull request #7404 from Kewlan/per-game-framerate-cap
configure_general: Allow framerate cap to be used in custom game configs
2021-11-24 02:36:30 -08:00
bunnei
daecbd3a7f Merge pull request #7394 from Morph1984/svc-SetMemoryPermission
kernel: svc: Implement SetProcessMemoryPermission
2021-11-22 14:06:09 -08:00
Mai M
ee76b546d4 Merge pull request #7406 from heinermann/tas_menu
Added TAS controls to the menu under Tools
2021-11-21 23:17:07 -05:00
Mai M
be238fb26a Merge pull request #7407 from jam1garner/ic-ivau-instruction
Add InstructionCacheOperationRaised handler for 'ic ivau' instruction
2021-11-21 23:16:36 -05:00
jam1garner
4d9c9e567e arm: dynarmic: Cleanup icache op handling 2021-11-21 22:44:13 -05:00
jam1garner
c8a67a725d arm: dynarmic: Implement icache op handling for 'ic iallu' instruction 2021-11-21 21:18:56 -05:00
jam1garner
84eb3e7d02 arm: dynarmic: Implement icache op handling for 'ic ivau' instruction 2021-11-21 21:10:14 -05:00
Adam Heinermann
097de2febc const fixes 2021-11-21 18:07:37 -08:00
Adam Heinermann
b7a938e817 Apply clang format 2021-11-21 18:02:08 -08:00
Adam Heinermann
f90d980837 Added TAS controls to the menu under Tools 2021-11-21 17:28:47 -08:00
Kewlan
8e3371a5c5 configure_general: Allow framerate cap to be used in custom game configs 2021-11-21 16:57:00 +01:00
bunnei
d20f91da11 Merge pull request #7395 from Morph1984/resolve-comments
general: Resolve comments in PR #7368
2021-11-21 02:42:24 -08:00
bunnei
5082712b4e Merge pull request #7389 from ameerj/screenshot-1x
Fix screenshot dimensions when at 1x scale
2021-11-21 02:31:32 -08:00
bunnei
ba5210675a Merge pull request #7359 from heinermann/kthread_crash
Fix crash on exit due to static scoped dummy threads
2021-11-20 23:59:58 -08:00
bunnei
fc34749778 Merge pull request #7393 from Morph1984/pm-ams-get-pid
service: pm: Implement AtmosphereGetProcessId
2021-11-20 22:08:25 -08:00
Morph
5cf93c1346 kernel: svc: Move all IsValid functions to an anonymous namespace 2021-11-20 22:49:13 -05:00
Morph
2726d705f8 kernel: svc: Implement SetProcessMemoryPermission
- Used by Skyline modding framework
2021-11-20 22:18:56 -05:00
Morph
281437c811 kernel: KPageTable: Rename SetCodeMemoryPermission to SetProcessMemoryPermission 2021-11-20 22:18:56 -05:00
Morph
a41c6dafea vk_texture_cache: Mark VkBufferUsageFlags as static constexpr 2021-11-20 21:49:37 -05:00
Morph
095bc88428 vk_blit_image: Consolidate CreatePipelineTargetEx functions 2021-11-20 21:18:37 -05:00
Morph
9173f07a51 service: pm: Implement AtmosphereGetProcessId
- Used by Skyline modding framework
2021-11-20 20:56:29 -05:00
Morph
3dc38d185b service: pm: Add all relevant result codes 2021-11-20 20:56:28 -05:00
bunnei
ea6fa044f3 Merge pull request #7368 from FernandoS27/vulkan-conv
Fix ART Blit detection regression and add D24S8 <-> RGBA8 conv to Vulkan
2021-11-20 16:51:13 -08:00
Morph
40cd0bb97b service: pm: Rename title id to program id 2021-11-20 19:39:26 -05:00
ameerj
fe1f06c856 Fix screenshot dimensions when at 1x scale
This was regressed by ART.
Prior to ART, the screenshots were saved at the title's framebuffer resolution. A misunderstanding of the existing logic led to screenshot dimensions becoming dependent on the host render window size.

This changes the behavior to match how it was prior to ART at 1x, with screenshots now always being the title's framebuffer dimensions scaled by the resolution scaling factor.
2021-11-20 17:50:24 -05:00
bunnei
c3e1ffc44b Merge pull request #7294 from vonchenplus/fix_image_update_error_when_width_too_small
Fix image update/download error when width too small
2021-11-19 15:56:27 -08:00
bunnei
b44fbf6cdd Merge pull request #7369 from Morph1984/amd-fsr-statusbar
main: Shorten AMD FSR status bar text
2021-11-19 01:20:27 -08:00
bunnei
d2e009f355 Merge pull request #7273 from fpdotmonkey/submodule-checkout-command-on-new-line
Move the cmake submodule checkout command to a new line
2021-11-19 01:19:50 -08:00
bunnei
a69813948f Merge pull request #7342 from goldenx86/patch-3
Replace keys error pop up
2021-11-19 01:17:18 -08:00
bunnei
c45af76ea0 Merge pull request #7357 from Morph1984/s8_uint
video_core: Implement S8_UINT format
2021-11-19 01:16:49 -08:00
Morph
bc5ed1aa1b main: Fix default AA name
By default, no AA is applied, not FXAA
2021-11-18 23:22:32 -05:00
Morph
a237fb5f75 configure_graphics_ui: AMD's -> AMD
AMD officially markets FSR as AMD FidelityFX™️ Super Resolution
2021-11-18 23:17:16 -05:00
Morph
c76163b611 main: Shorten AMD FSR status bar text
AMD'S FIDELITYFX SR -> FSR
2021-11-18 23:17:16 -05:00
Morph
dc61b7045b renderer_vulkan: Implement S8_UINT stencil format
It should be noted that on Windows, only nvidia gpus support this format natively as of this commit.
2021-11-18 00:05:51 -05:00
Adam Heinermann
d8a783a368 Fix crash on exit due to static scoped dummy threads 2021-11-17 15:29:25 -08:00
Morph
6dd6dc046c renderer_opengl: Implement S8_UINT stencil format 2021-11-17 15:05:07 -05:00
Morph
2348eb41f3 video_core: Add S8_UINT stencil format 2021-11-17 15:04:38 -05:00
Feng Chen
894cc9d876 Fix image update/download error when width too small 2021-11-17 12:21:17 +08:00
Matías Locatti
ffb79afd29 Replace keys error pop up
Fight me.
2021-11-16 03:53:28 -03:00
Fletcher Porter
2b3d66fe69 Move the cmake submodule checkout command to a new line
Presently, if you forget to initialize the git submodules before
running cmake, there'll be a helpful message that reminds you to do so.
However, on narrow terminals (e.g. 80 wide) there's a word wrap that
includes a new line in the middle of the git command, precluding easy
copy-paste.  This moves the entire git command to its own line to avoid
such tragedies.

Before:

```
CMake Error at CMakeLists.txt:59 (message):
  Git submodule externals/inih/inih not found.  Please run: git submodule
  update --init --recursive
```

After:

```
CMake Error at CMakeLists.txt:59 (message):
  Git submodule externals/inih/inih not found.  Please run:

  git submodule update --init --recursive
```
2021-11-02 00:01:39 -07:00
37 changed files with 545 additions and 196 deletions

View File

@@ -57,7 +57,7 @@ function(check_submodules_present)
string(REGEX REPLACE "path *= *" "" module ${module})
if (NOT EXISTS "${PROJECT_SOURCE_DIR}/${module}/.git")
message(FATAL_ERROR "Git submodule ${module} not found. "
"Please run: git submodule update --init --recursive")
"Please run: \ngit submodule update --init --recursive")
endif()
endforeach()
endfunction()

View File

@@ -183,6 +183,7 @@ void RestoreGlobalState(bool is_powered_on) {
values.max_anisotropy.SetGlobal(true);
values.use_speed_limit.SetGlobal(true);
values.speed_limit.SetGlobal(true);
values.fps_cap.SetGlobal(true);
values.use_disk_shader_cache.SetGlobal(true);
values.gpu_accuracy.SetGlobal(true);
values.use_asynchronous_gpu_emulation.SetGlobal(true);

View File

@@ -525,7 +525,7 @@ struct Values {
Setting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"};
Setting<bool> accelerate_astc{true, "accelerate_astc"};
Setting<bool> use_vsync{true, "use_vsync"};
BasicRangedSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"};
RangedSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"};
BasicSetting<bool> disable_fps_limit{false, "disable_fps_limit"};
RangedSetting<ShaderBackend> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL,
ShaderBackend::SPIRV, "shader_backend"};

View File

@@ -86,6 +86,26 @@ public:
num_instructions, MemoryReadCode(pc));
}
void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op,
VAddr value) override {
switch (op) {
case Dynarmic::A64::InstructionCacheOperation::InvalidateByVAToPoU: {
static constexpr u64 ICACHE_LINE_SIZE = 64;
const u64 cache_line_start = value & ~(ICACHE_LINE_SIZE - 1);
parent.InvalidateCacheRange(cache_line_start, ICACHE_LINE_SIZE);
break;
}
case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoU:
parent.ClearInstructionCache();
break;
case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoUInnerSharable:
default:
LOG_DEBUG(Core_ARM, "Unprocesseed instruction cache operation: {}", op);
break;
}
}
void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override {
switch (exception) {
case Dynarmic::A64::Exception::WaitForInterrupt:

View File

@@ -25,7 +25,12 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) {
ASSERT(height > 0);
// The drawing code needs at least somewhat valid values for both screens
// so just calculate them both even if the other isn't showing.
FramebufferLayout res{width, height, false, {}};
FramebufferLayout res{
.width = width,
.height = height,
.screen = {},
.is_srgb = false,
};
const float window_aspect_ratio = static_cast<float>(height) / static_cast<float>(width);
const float emulation_aspect_ratio = EmulationAspectRatio(

View File

@@ -35,17 +35,8 @@ enum class AspectRatio {
struct FramebufferLayout {
u32 width{ScreenUndocked::Width};
u32 height{ScreenUndocked::Height};
bool is_srgb{};
Common::Rectangle<u32> screen;
/**
* Returns the ration of pixel size of the screen, compared to the native size of the undocked
* Switch screen.
*/
float GetScalingRatio() const {
return static_cast<float>(screen.GetWidth()) / ScreenUndocked::Width;
}
bool is_srgb{};
};
/**

View File

@@ -685,8 +685,8 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list,
return ResultSuccess;
}
ResultCode KPageTable::SetCodeMemoryPermission(VAddr addr, std::size_t size,
KMemoryPermission perm) {
ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
KMemoryPermission perm) {
std::lock_guard lock{page_table_lock};

View File

@@ -41,7 +41,7 @@ public:
ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state,
KMemoryPermission perm);
ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state);
ResultCode SetCodeMemoryPermission(VAddr addr, std::size_t size, KMemoryPermission perm);
ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size, KMemoryPermission perm);
KMemoryInfo QueryInfo(VAddr addr);
ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm);
ResultCode ResetTransferMemory(VAddr addr, std::size_t size);

View File

@@ -528,7 +528,7 @@ void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) {
std::lock_guard lock{HLE::g_hle_lock};
const auto ReprotectSegment = [&](const CodeSet::Segment& segment,
KMemoryPermission permission) {
page_table->SetCodeMemoryPermission(segment.addr + base_addr, segment.size, permission);
page_table->SetProcessMemoryPermission(segment.addr + base_addr, segment.size, permission);
};
kernel.System().Memory().WriteBlock(*this, base_addr, code_set.memory.data(),

View File

@@ -300,15 +300,16 @@ struct KernelCore::Impl {
// Gets the dummy KThread for the caller, allocating a new one if this is the first time
KThread* GetHostDummyThread() {
auto make_thread = [this]() {
std::unique_ptr<KThread> thread = std::make_unique<KThread>(system.Kernel());
std::lock_guard lk(dummy_thread_lock);
auto& thread = dummy_threads.emplace_back(std::make_unique<KThread>(system.Kernel()));
KAutoObject::Create(thread.get());
ASSERT(KThread::InitializeDummyThread(thread.get()).IsSuccess());
thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId()));
return thread;
return thread.get();
};
thread_local auto thread = make_thread();
return thread.get();
thread_local KThread* saved_thread = make_thread();
return saved_thread;
}
/// Registers a CPU core thread by allocating a host thread ID for it
@@ -695,6 +696,12 @@ struct KernelCore::Impl {
return port;
}
std::mutex server_ports_lock;
std::mutex server_sessions_lock;
std::mutex registered_objects_lock;
std::mutex registered_in_use_objects_lock;
std::mutex dummy_thread_lock;
std::atomic<u32> next_object_id{0};
std::atomic<u64> next_kernel_process_id{KProcess::InitialKIPIDMin};
std::atomic<u64> next_user_process_id{KProcess::ProcessIDMin};
@@ -725,10 +732,6 @@ struct KernelCore::Impl {
std::unordered_set<KServerSession*> server_sessions;
std::unordered_set<KAutoObject*> registered_objects;
std::unordered_set<KAutoObject*> registered_in_use_objects;
std::mutex server_ports_lock;
std::mutex server_sessions_lock;
std::mutex registered_objects_lock;
std::mutex registered_in_use_objects_lock;
std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
std::vector<Kernel::PhysicalCore> cores;
@@ -753,6 +756,9 @@ struct KernelCore::Impl {
std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};
std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
// Specifically tracked to be automatically destroyed with kernel
std::vector<std::unique_ptr<KThread>> dummy_threads;
bool is_multicore{};
bool is_phantom_mode_for_singlecore{};
u32 single_core_thread_id{};

View File

@@ -1169,6 +1169,8 @@ static u32 GetCurrentProcessorNumber32(Core::System& system) {
return GetCurrentProcessorNumber(system);
}
namespace {
constexpr bool IsValidSharedMemoryPermission(Svc::MemoryPermission perm) {
switch (perm) {
case Svc::MemoryPermission::Read:
@@ -1179,10 +1181,24 @@ constexpr bool IsValidSharedMemoryPermission(Svc::MemoryPermission perm) {
}
}
constexpr bool IsValidRemoteSharedMemoryPermission(Svc::MemoryPermission perm) {
[[maybe_unused]] constexpr bool IsValidRemoteSharedMemoryPermission(Svc::MemoryPermission perm) {
return IsValidSharedMemoryPermission(perm) || perm == Svc::MemoryPermission::DontCare;
}
constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) {
switch (perm) {
case Svc::MemoryPermission::None:
case Svc::MemoryPermission::Read:
case Svc::MemoryPermission::ReadWrite:
case Svc::MemoryPermission::ReadExecute:
return true;
default:
return false;
}
}
} // Anonymous namespace
static ResultCode MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address,
u64 size, Svc::MemoryPermission map_perm) {
LOG_TRACE(Kernel_SVC,
@@ -1262,6 +1278,34 @@ static ResultCode UnmapSharedMemory32(Core::System& system, Handle shmem_handle,
return UnmapSharedMemory(system, shmem_handle, address, size);
}
static ResultCode SetProcessMemoryPermission(Core::System& system, Handle process_handle,
VAddr address, u64 size, Svc::MemoryPermission perm) {
LOG_TRACE(Kernel_SVC,
"called, process_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
process_handle, address, size, perm);
// Validate the address/size.
R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
R_UNLESS(size > 0, ResultInvalidSize);
R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
// Validate the memory permission.
R_UNLESS(IsValidProcessMemoryPermission(perm), ResultInvalidNewMemoryPermission);
// Get the process from its handle.
KScopedAutoObject process =
system.CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle);
R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
// Validate that the address is in range.
auto& page_table = process->PageTable();
R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
// Set the memory permission.
return page_table.SetProcessMemoryPermission(address, size, ConvertToKMemoryPermission(perm));
}
static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address,
VAddr page_info_address, Handle process_handle,
VAddr address) {
@@ -1459,10 +1503,14 @@ static void ExitProcess32(Core::System& system) {
ExitProcess(system);
}
static constexpr bool IsValidVirtualCoreId(int32_t core_id) {
namespace {
constexpr bool IsValidVirtualCoreId(int32_t core_id) {
return (0 <= core_id && core_id < static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES));
}
} // Anonymous namespace
/// Creates a new thread
static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg,
VAddr stack_bottom, u32 priority, s32 core_id) {
@@ -1846,7 +1894,9 @@ static ResultCode ResetSignal32(Core::System& system, Handle handle) {
return ResetSignal(system, handle);
}
static constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) {
namespace {
constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) {
switch (perm) {
case MemoryPermission::None:
case MemoryPermission::Read:
@@ -1857,6 +1907,8 @@ static constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) {
}
}
} // Anonymous namespace
/// Creates a TransferMemory object
static ResultCode CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size,
MemoryPermission map_perm) {
@@ -2588,7 +2640,7 @@ static const FunctionDef SVC_Table_64[] = {
{0x70, nullptr, "CreatePort"},
{0x71, nullptr, "ManageNamedPort"},
{0x72, nullptr, "ConnectToPort"},
{0x73, nullptr, "SetProcessMemoryPermission"},
{0x73, SvcWrap64<SetProcessMemoryPermission>, "SetProcessMemoryPermission"},
{0x74, nullptr, "MapProcessMemory"},
{0x75, nullptr, "UnmapProcessMemory"},
{0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"},

View File

@@ -396,12 +396,12 @@ public:
CopyCode(nro_addr + nro_header.segment_headers[DATA_INDEX].memory_offset, data_start,
nro_header.segment_headers[DATA_INDEX].memory_size);
CASCADE_CODE(process->PageTable().SetCodeMemoryPermission(
CASCADE_CODE(process->PageTable().SetProcessMemoryPermission(
text_start, ro_start - text_start, Kernel::KMemoryPermission::ReadAndExecute));
CASCADE_CODE(process->PageTable().SetCodeMemoryPermission(ro_start, data_start - ro_start,
Kernel::KMemoryPermission::Read));
CASCADE_CODE(process->PageTable().SetProcessMemoryPermission(
ro_start, data_start - ro_start, Kernel::KMemoryPermission::Read));
return process->PageTable().SetCodeMemoryPermission(
return process->PageTable().SetProcessMemoryPermission(
data_start, bss_end_addr - data_start, Kernel::KMemoryPermission::ReadAndWrite);
}

View File

@@ -13,7 +13,12 @@ namespace Service::PM {
namespace {
constexpr ResultCode ERROR_PROCESS_NOT_FOUND{ErrorModule::PM, 1};
constexpr ResultCode ResultProcessNotFound{ErrorModule::PM, 1};
[[maybe_unused]] constexpr ResultCode ResultAlreadyStarted{ErrorModule::PM, 2};
[[maybe_unused]] constexpr ResultCode ResultNotTerminated{ErrorModule::PM, 3};
[[maybe_unused]] constexpr ResultCode ResultDebugHookInUse{ErrorModule::PM, 4};
[[maybe_unused]] constexpr ResultCode ResultApplicationRunning{ErrorModule::PM, 5};
[[maybe_unused]] constexpr ResultCode ResultInvalidSize{ErrorModule::PM, 6};
constexpr u64 NO_PROCESS_FOUND_PID{0};
@@ -95,18 +100,18 @@ public:
private:
void GetProcessId(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto title_id = rp.PopRaw<u64>();
const auto program_id = rp.PopRaw<u64>();
LOG_DEBUG(Service_PM, "called, title_id={:016X}", title_id);
LOG_DEBUG(Service_PM, "called, program_id={:016X}", program_id);
const auto process =
SearchProcessList(kernel.GetProcessList(), [title_id](const auto& proc) {
return proc->GetProgramID() == title_id;
SearchProcessList(kernel.GetProcessList(), [program_id](const auto& proc) {
return proc->GetProgramID() == program_id;
});
if (!process.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_PROCESS_NOT_FOUND);
rb.Push(ResultProcessNotFound);
return;
}
@@ -128,13 +133,16 @@ public:
explicit Info(Core::System& system_, const std::vector<Kernel::KProcess*>& process_list_)
: ServiceFramework{system_, "pm:info"}, process_list{process_list_} {
static const FunctionInfo functions[] = {
{0, &Info::GetTitleId, "GetTitleId"},
{0, &Info::GetProgramId, "GetProgramId"},
{65000, &Info::AtmosphereGetProcessId, "AtmosphereGetProcessId"},
{65001, nullptr, "AtmosphereHasLaunchedProgram"},
{65002, nullptr, "AtmosphereGetProcessInfo"},
};
RegisterHandlers(functions);
}
private:
void GetTitleId(Kernel::HLERequestContext& ctx) {
void GetProgramId(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto process_id = rp.PopRaw<u64>();
@@ -146,7 +154,7 @@ private:
if (!process.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_PROCESS_NOT_FOUND);
rb.Push(ResultProcessNotFound);
return;
}
@@ -155,6 +163,27 @@ private:
rb.Push((*process)->GetProgramID());
}
void AtmosphereGetProcessId(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto program_id = rp.PopRaw<u64>();
LOG_DEBUG(Service_PM, "called, program_id={:016X}", program_id);
const auto process = SearchProcessList(process_list, [program_id](const auto& proc) {
return proc->GetProgramID() == program_id;
});
if (!process.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultProcessNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push((*process)->GetProcessID());
}
const std::vector<Kernel::KProcess*>& process_list;
};

View File

@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cstdio>
#include <fstream>
#include <vector>
#include "common/assert.h"
@@ -59,6 +61,34 @@ Codec::~Codec() {
av_buffer_unref(&av_gpu_decoder);
}
#ifdef LIBVA_FOUND
// List all the currently loaded Linux modules
static std::vector<std::string> ListLinuxKernelModules() {
using FILEPtr = std::unique_ptr<FILE, decltype(&std::fclose)>;
auto module_listing = FILEPtr{fopen("/proc/modules", "rt"), std::fclose};
std::vector<std::string> modules{};
if (!module_listing) {
LOG_WARNING(Service_NVDRV, "Could not open /proc/modules to collect available modules");
return modules;
}
char* buffer = nullptr;
size_t buf_len = 0;
while (getline(&buffer, &buf_len, module_listing.get()) != -1) {
// format for the module listing file (sysfs)
// <name> <module_size> <depended_by_count> <depended_by_names> <status> <load_address>
auto line = std::string(buffer);
// we are only interested in module names
auto name_pos = line.find_first_of(" ");
if (name_pos == std::string::npos) {
continue;
}
modules.push_back(line.erase(name_pos));
}
free(buffer);
return modules;
}
#endif
bool Codec::CreateGpuAvDevice() {
#if defined(LIBVA_FOUND)
static constexpr std::array<const char*, 3> VAAPI_DRIVERS = {
@@ -67,8 +97,16 @@ bool Codec::CreateGpuAvDevice() {
"amdgpu",
};
AVDictionary* hwdevice_options = nullptr;
const auto loaded_modules = ListLinuxKernelModules();
av_dict_set(&hwdevice_options, "connection_type", "drm", 0);
for (const auto& driver : VAAPI_DRIVERS) {
// first check if the target driver is loaded in the kernel
bool found = std::any_of(loaded_modules.begin(), loaded_modules.end(),
[&driver](const auto& module) { return module == driver; });
if (!found) {
LOG_DEBUG(Service_NVDRV, "Kernel driver {} is not loaded, trying the next one", driver);
continue;
}
av_dict_set(&hwdevice_options, "kernel_driver", driver, 0);
const int hwdevice_error = av_hwdevice_ctx_create(&av_gpu_decoder, AV_HWDEVICE_TYPE_VAAPI,
nullptr, hwdevice_options, 0);
@@ -85,11 +123,12 @@ bool Codec::CreateGpuAvDevice() {
#endif
static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX;
static constexpr std::array GPU_DECODER_TYPES{
#ifdef linux
AV_HWDEVICE_TYPE_VDPAU,
#endif
AV_HWDEVICE_TYPE_CUDA,
#ifdef _WIN32
AV_HWDEVICE_TYPE_D3D11VA,
#else
AV_HWDEVICE_TYPE_VDPAU,
#endif
};
for (const auto& type : GPU_DECODER_TYPES) {

View File

@@ -83,6 +83,7 @@ enum class DepthFormat : u32 {
S8_UINT_Z24_UNORM = 0x14,
D24X8_UNORM = 0x15,
D24S8_UNORM = 0x16,
S8_UINT = 0x17,
D24C8_UNORM = 0x18,
D32_FLOAT_S8X24_UINT = 0x19,
};

View File

@@ -149,6 +149,8 @@ GLenum AttachmentType(PixelFormat format) {
switch (const SurfaceType type = VideoCore::Surface::GetFormatType(format); type) {
case SurfaceType::Depth:
return GL_DEPTH_ATTACHMENT;
case SurfaceType::Stencil:
return GL_STENCIL_ATTACHMENT;
case SurfaceType::DepthStencil:
return GL_DEPTH_STENCIL_ATTACHMENT;
default:
@@ -318,13 +320,12 @@ void AttachTexture(GLuint fbo, GLenum attachment, const ImageView* image_view) {
}
}
OGLTexture MakeImage(const VideoCommon::ImageInfo& info, GLenum gl_internal_format) {
OGLTexture MakeImage(const VideoCommon::ImageInfo& info, GLenum gl_internal_format,
GLsizei gl_num_levels) {
const GLenum target = ImageTarget(info);
const GLsizei width = info.size.width;
const GLsizei height = info.size.height;
const GLsizei depth = info.size.depth;
const int max_host_mip_levels = std::bit_width(info.size.width);
const GLsizei num_levels = std::min(info.resources.levels, max_host_mip_levels);
const GLsizei num_layers = info.resources.layers;
const GLsizei num_samples = info.num_samples;
@@ -336,10 +337,10 @@ OGLTexture MakeImage(const VideoCommon::ImageInfo& info, GLenum gl_internal_form
}
switch (target) {
case GL_TEXTURE_1D_ARRAY:
glTextureStorage2D(handle, num_levels, gl_internal_format, width, num_layers);
glTextureStorage2D(handle, gl_num_levels, gl_internal_format, width, num_layers);
break;
case GL_TEXTURE_2D_ARRAY:
glTextureStorage3D(handle, num_levels, gl_internal_format, width, height, num_layers);
glTextureStorage3D(handle, gl_num_levels, gl_internal_format, width, height, num_layers);
break;
case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: {
// TODO: Where should 'fixedsamplelocations' come from?
@@ -349,10 +350,10 @@ OGLTexture MakeImage(const VideoCommon::ImageInfo& info, GLenum gl_internal_form
break;
}
case GL_TEXTURE_RECTANGLE:
glTextureStorage2D(handle, num_levels, gl_internal_format, width, height);
glTextureStorage2D(handle, gl_num_levels, gl_internal_format, width, height);
break;
case GL_TEXTURE_3D:
glTextureStorage3D(handle, num_levels, gl_internal_format, width, height, depth);
glTextureStorage3D(handle, gl_num_levels, gl_internal_format, width, height, depth);
break;
case GL_TEXTURE_BUFFER:
UNREACHABLE();
@@ -694,7 +695,9 @@ Image::Image(TextureCacheRuntime& runtime_, const VideoCommon::ImageInfo& info_,
gl_format = tuple.format;
gl_type = tuple.type;
}
texture = MakeImage(info, gl_internal_format);
const int max_host_mip_levels = std::bit_width(info.size.width);
gl_num_levels = std::min(info.resources.levels, max_host_mip_levels);
texture = MakeImage(info, gl_internal_format, gl_num_levels);
current_texture = texture.handle;
if (runtime->device.HasDebuggingToolAttached()) {
const std::string name = VideoCommon::Name(*this);
@@ -722,6 +725,9 @@ void Image::UploadMemory(const ImageBufferMap& map,
u32 current_image_height = std::numeric_limits<u32>::max();
for (const VideoCommon::BufferImageCopy& copy : copies) {
if (copy.image_subresource.base_level >= gl_num_levels) {
continue;
}
if (current_row_length != copy.buffer_row_length) {
current_row_length = copy.buffer_row_length;
glPixelStorei(GL_UNPACK_ROW_LENGTH, current_row_length);
@@ -751,6 +757,9 @@ void Image::DownloadMemory(ImageBufferMap& map,
u32 current_image_height = std::numeric_limits<u32>::max();
for (const VideoCommon::BufferImageCopy& copy : copies) {
if (copy.image_subresource.base_level >= gl_num_levels) {
continue;
}
if (current_row_length != copy.buffer_row_length) {
current_row_length = copy.buffer_row_length;
glPixelStorei(GL_PACK_ROW_LENGTH, current_row_length);
@@ -790,7 +799,7 @@ GLuint Image::StorageHandle() noexcept {
}
store_view.Create();
glTextureView(store_view.handle, ImageTarget(info), current_texture, GL_RGBA8, 0,
info.resources.levels, 0, info.resources.layers);
gl_num_levels, 0, info.resources.layers);
return store_view.handle;
default:
return current_texture;
@@ -905,6 +914,8 @@ void Image::Scale(bool up_scale) {
return GL_COLOR_ATTACHMENT0;
case SurfaceType::Depth:
return GL_DEPTH_ATTACHMENT;
case SurfaceType::Stencil:
return GL_STENCIL_ATTACHMENT;
case SurfaceType::DepthStencil:
return GL_DEPTH_STENCIL_ATTACHMENT;
default:
@@ -918,8 +929,10 @@ void Image::Scale(bool up_scale) {
return GL_COLOR_BUFFER_BIT;
case SurfaceType::Depth:
return GL_DEPTH_BUFFER_BIT;
case SurfaceType::Stencil:
return GL_STENCIL_BUFFER_BIT;
case SurfaceType::DepthStencil:
return GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT;
return GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
default:
UNREACHABLE();
return GL_COLOR_BUFFER_BIT;
@@ -931,8 +944,10 @@ void Image::Scale(bool up_scale) {
return 0;
case SurfaceType::Depth:
return 1;
case SurfaceType::DepthStencil:
case SurfaceType::Stencil:
return 2;
case SurfaceType::DepthStencil:
return 3;
default:
UNREACHABLE();
return 0;
@@ -954,7 +969,7 @@ void Image::Scale(bool up_scale) {
auto dst_info = info;
dst_info.size.width = scaled_width;
dst_info.size.height = scaled_height;
upscaled_backup = MakeImage(dst_info, gl_internal_format);
upscaled_backup = MakeImage(dst_info, gl_internal_format, gl_num_levels);
}
const u32 src_width = up_scale ? original_width : scaled_width;
const u32 src_height = up_scale ? original_height : scaled_height;
@@ -1262,10 +1277,20 @@ Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM
}
if (const ImageView* const image_view = depth_buffer; image_view) {
if (GetFormatType(image_view->format) == SurfaceType::DepthStencil) {
buffer_bits |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
} else {
switch (GetFormatType(image_view->format)) {
case SurfaceType::Depth:
buffer_bits |= GL_DEPTH_BUFFER_BIT;
break;
case SurfaceType::Stencil:
buffer_bits |= GL_STENCIL_BUFFER_BIT;
break;
case SurfaceType::DepthStencil:
buffer_bits |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
break;
default:
UNREACHABLE();
buffer_bits |= GL_DEPTH_BUFFER_BIT;
break;
}
const GLenum attachment = AttachmentType(image_view->format);
AttachTexture(handle, attachment, image_view);

View File

@@ -168,8 +168,8 @@ private:
std::array<GLuint, Shader::NUM_TEXTURE_TYPES> null_image_views{};
std::array<OGLFramebuffer, 3> rescale_draw_fbos;
std::array<OGLFramebuffer, 3> rescale_read_fbos;
std::array<OGLFramebuffer, 4> rescale_draw_fbos;
std::array<OGLFramebuffer, 4> rescale_read_fbos;
const Settings::ResolutionScalingInfo& resolution;
};
@@ -225,6 +225,7 @@ private:
GLenum gl_internal_format = GL_NONE;
GLenum gl_format = GL_NONE;
GLenum gl_type = GL_NONE;
GLsizei gl_num_levels{};
TextureCacheRuntime* runtime{};
GLuint current_texture{};
};

View File

@@ -108,6 +108,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB
{GL_RGB9_E5, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV}, // E5B9G9R9_FLOAT
{GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT}, // D32_FLOAT
{GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // D16_UNORM
{GL_STENCIL_INDEX8, GL_STENCIL, GL_UNSIGNED_BYTE}, // S8_UINT
{GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // D24_UNORM_S8_UINT
{GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // S8_UINT_D24_UNORM
{GL_DEPTH32F_STENCIL8, GL_DEPTH_STENCIL,

View File

@@ -749,8 +749,9 @@ void BlitImageHelper::ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRend
});
}
void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
vk::ShaderModule& module, bool single_texture) {
void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
vk::ShaderModule& module, bool is_target_depth,
bool single_texture) {
if (pipeline) {
return;
}
@@ -767,7 +768,7 @@ void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRen
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pDepthStencilState = nullptr,
.pDepthStencilState = is_target_depth ? &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO : nullptr,
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO,
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.layout = single_texture ? *one_texture_pipeline_layout : *two_textures_pipeline_layout,
@@ -778,33 +779,14 @@ void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRen
});
}
void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
vk::ShaderModule& module, bool single_texture) {
ConvertPipelineEx(pipeline, renderpass, module, false, single_texture);
}
void BlitImageHelper::ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
vk::ShaderModule& module, bool single_texture) {
if (pipeline) {
return;
}
const std::array stages = MakeStages(*full_screen_vert, *module);
pipeline = device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(),
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pInputAssemblyState = &PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.pTessellationState = nullptr,
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO,
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.layout = single_texture ? *one_texture_pipeline_layout : *two_textures_pipeline_layout,
.renderPass = renderpass,
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
});
ConvertPipelineEx(pipeline, renderpass, module, true, single_texture);
}
} // namespace Vulkan

View File

@@ -89,6 +89,9 @@ private:
void ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass);
void ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
vk::ShaderModule& module, bool is_target_depth, bool single_texture);
void ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
vk::ShaderModule& module, bool single_texture);

View File

@@ -208,6 +208,9 @@ struct FormatTuple {
{VK_FORMAT_D32_SFLOAT, Attachable}, // D32_FLOAT
{VK_FORMAT_D16_UNORM, Attachable}, // D16_UNORM
// Stencil formats
{VK_FORMAT_S8_UINT, Attachable}, // S8_UINT
// DepthStencil formats
{VK_FORMAT_D24_UNORM_S8_UINT, Attachable}, // D24_UNORM_S8_UINT
{VK_FORMAT_D24_UNORM_S8_UINT, Attachable}, // S8_UINT_D24_UNORM (emulated)

View File

@@ -103,6 +103,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
break;
case VideoCore::Surface::SurfaceType::Depth:
case VideoCore::Surface::SurfaceType::Stencil:
case VideoCore::Surface::SurfaceType::DepthStencil:
usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
break;
@@ -174,6 +175,8 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
return VK_IMAGE_ASPECT_COLOR_BIT;
case VideoCore::Surface::SurfaceType::Depth:
return VK_IMAGE_ASPECT_DEPTH_BIT;
case VideoCore::Surface::SurfaceType::Stencil:
return VK_IMAGE_ASPECT_STENCIL_BIT;
case VideoCore::Surface::SurfaceType::DepthStencil:
return VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
default:
@@ -196,6 +199,8 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
case PixelFormat::D16_UNORM:
case PixelFormat::D32_FLOAT:
return VK_IMAGE_ASPECT_DEPTH_BIT;
case PixelFormat::S8_UINT:
return VK_IMAGE_ASPECT_STENCIL_BIT;
default:
return VK_IMAGE_ASPECT_COLOR_BIT;
}
@@ -782,9 +787,9 @@ VkBuffer TextureCacheRuntime::GetTemporaryBuffer(size_t needed_size) {
return *buffers[level];
}
const auto new_size = Common::NextPow2(needed_size);
VkBufferUsageFlags flags = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT |
VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
static constexpr VkBufferUsageFlags flags =
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
buffers[level] = device.GetLogical().CreateBuffer({
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,

View File

@@ -82,6 +82,8 @@ PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) {
return PixelFormat::D32_FLOAT;
case Tegra::DepthFormat::D16_UNORM:
return PixelFormat::D16_UNORM;
case Tegra::DepthFormat::S8_UINT:
return PixelFormat::S8_UINT;
case Tegra::DepthFormat::D32_FLOAT_S8X24_UINT:
return PixelFormat::D32_FLOAT_S8_UINT;
default:
@@ -213,6 +215,11 @@ SurfaceType GetFormatType(PixelFormat pixel_format) {
return SurfaceType::Depth;
}
if (static_cast<std::size_t>(pixel_format) <
static_cast<std::size_t>(PixelFormat::MaxStencilFormat)) {
return SurfaceType::Stencil;
}
if (static_cast<std::size_t>(pixel_format) <
static_cast<std::size_t>(PixelFormat::MaxDepthStencilFormat)) {
return SurfaceType::DepthStencil;

View File

@@ -110,8 +110,12 @@ enum class PixelFormat {
MaxDepthFormat,
// Stencil formats
S8_UINT = MaxDepthFormat,
MaxStencilFormat,
// DepthStencil formats
D24_UNORM_S8_UINT = MaxDepthFormat,
D24_UNORM_S8_UINT = MaxStencilFormat,
S8_UINT_D24_UNORM,
D32_FLOAT_S8_UINT,
@@ -125,8 +129,9 @@ constexpr std::size_t MaxPixelFormat = static_cast<std::size_t>(PixelFormat::Max
enum class SurfaceType {
ColorTexture = 0,
Depth = 1,
DepthStencil = 2,
Invalid = 3,
Stencil = 2,
DepthStencil = 3,
Invalid = 4,
};
enum class SurfaceTarget {
@@ -229,6 +234,7 @@ constexpr std::array<u32, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
1, // E5B9G9R9_FLOAT
1, // D32_FLOAT
1, // D16_UNORM
1, // S8_UINT
1, // D24_UNORM_S8_UINT
1, // S8_UINT_D24_UNORM
1, // D32_FLOAT_S8_UINT
@@ -328,6 +334,7 @@ constexpr std::array<u32, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
1, // E5B9G9R9_FLOAT
1, // D32_FLOAT
1, // D16_UNORM
1, // S8_UINT
1, // D24_UNORM_S8_UINT
1, // S8_UINT_D24_UNORM
1, // D32_FLOAT_S8_UINT
@@ -427,6 +434,7 @@ constexpr std::array<u32, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
32, // E5B9G9R9_FLOAT
32, // D32_FLOAT
16, // D16_UNORM
8, // S8_UINT
32, // D24_UNORM_S8_UINT
32, // S8_UINT_D24_UNORM
64, // D32_FLOAT_S8_UINT

View File

@@ -194,6 +194,8 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str
return "D32_FLOAT";
case PixelFormat::D16_UNORM:
return "D16_UNORM";
case PixelFormat::S8_UINT:
return "S8_UINT";
case PixelFormat::D24_UNORM_S8_UINT:
return "D24_UNORM_S8_UINT";
case PixelFormat::S8_UINT_D24_UNORM:

View File

@@ -55,10 +55,4 @@ std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Cor
}
}
float GetResolutionScaleFactor(const RendererBase& renderer) {
return Settings::values.resolution_info.active
? Settings::values.resolution_info.up_factor
: renderer.GetRenderWindow().GetFramebufferLayout().GetScalingRatio();
}
} // namespace VideoCore

View File

@@ -25,6 +25,4 @@ class RendererBase;
/// Creates an emulated GPU instance using the given system context.
std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system);
float GetResolutionScaleFactor(const RendererBase& renderer);
} // namespace VideoCore

View File

@@ -21,6 +21,13 @@
namespace Vulkan {
namespace {
namespace Alternatives {
constexpr std::array STENCIL8_UINT{
VK_FORMAT_D16_UNORM_S8_UINT,
VK_FORMAT_D24_UNORM_S8_UINT,
VK_FORMAT_D32_SFLOAT_S8_UINT,
VK_FORMAT_UNDEFINED,
};
constexpr std::array DEPTH24_UNORM_STENCIL8_UINT{
VK_FORMAT_D32_SFLOAT_S8_UINT,
VK_FORMAT_D16_UNORM_S8_UINT,
@@ -74,6 +81,8 @@ void SetNext(void**& next, T& data) {
constexpr const VkFormat* GetFormatAlternatives(VkFormat format) {
switch (format) {
case VK_FORMAT_S8_UINT:
return Alternatives::STENCIL8_UINT.data();
case VK_FORMAT_D24_UNORM_S8_UINT:
return Alternatives::DEPTH24_UNORM_STENCIL8_UINT.data();
case VK_FORMAT_D16_UNORM_S8_UINT:
@@ -145,6 +154,7 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica
VK_FORMAT_R4G4B4A4_UNORM_PACK16,
VK_FORMAT_D32_SFLOAT,
VK_FORMAT_D16_UNORM,
VK_FORMAT_S8_UINT,
VK_FORMAT_D16_UNORM_S8_UINT,
VK_FORMAT_D24_UNORM_S8_UINT,
VK_FORMAT_D32_SFLOAT_S8_UINT,

View File

@@ -303,6 +303,7 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram,
Qt::QueuedConnection);
connect(this, &GRenderWindow::ExitSignal, parent, &GMainWindow::OnExit, Qt::QueuedConnection);
connect(this, &GRenderWindow::TasPlaybackStateChanged, parent, &GMainWindow::OnTasStateChanged);
}
void GRenderWindow::ExecuteProgram(std::size_t program_index) {
@@ -319,10 +320,18 @@ GRenderWindow::~GRenderWindow() {
void GRenderWindow::OnFrameDisplayed() {
input_subsystem->GetTas()->UpdateThread();
const TasInput::TasState new_tas_state = std::get<0>(input_subsystem->GetTas()->GetStatus());
if (!first_frame) {
last_tas_state = new_tas_state;
first_frame = true;
emit FirstFrameDisplayed();
}
if (new_tas_state != last_tas_state) {
last_tas_state = new_tas_state;
emit TasPlaybackStateChanged();
}
}
bool GRenderWindow::IsShown() const {
@@ -630,7 +639,7 @@ void GRenderWindow::ReleaseRenderTarget() {
void GRenderWindow::CaptureScreenshot(const QString& screenshot_path) {
auto& renderer = system.Renderer();
const f32 res_scale = VideoCore::GetResolutionScaleFactor(renderer);
const f32 res_scale = Settings::values.resolution_info.up_factor;
const Layout::FramebufferLayout layout{Layout::FrameLayoutFromResolutionScale(res_scale)};
screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32);

View File

@@ -41,6 +41,10 @@ enum class LoadCallbackStage;
class RendererBase;
} // namespace VideoCore
namespace TasInput {
enum class TasState;
}
class EmuThread final : public QThread {
Q_OBJECT
@@ -203,6 +207,7 @@ signals:
void ExecuteProgramSignal(std::size_t program_index);
void ExitSignal();
void MouseActivity();
void TasPlaybackStateChanged();
private:
void TouchBeginEvent(const QTouchEvent* event);
@@ -236,6 +241,7 @@ private:
QWidget* child_widget = nullptr;
bool first_frame = false;
TasInput::TasState last_tas_state;
std::array<std::size_t, 16> touch_ids{};

View File

@@ -830,6 +830,7 @@ void Config::ReadRendererValues() {
ReadGlobalSetting(Settings::values.max_anisotropy);
ReadGlobalSetting(Settings::values.use_speed_limit);
ReadGlobalSetting(Settings::values.speed_limit);
ReadGlobalSetting(Settings::values.fps_cap);
ReadGlobalSetting(Settings::values.use_disk_shader_cache);
ReadGlobalSetting(Settings::values.gpu_accuracy);
ReadGlobalSetting(Settings::values.use_asynchronous_gpu_emulation);
@@ -844,7 +845,6 @@ void Config::ReadRendererValues() {
ReadGlobalSetting(Settings::values.bg_blue);
if (global) {
ReadBasicSetting(Settings::values.fps_cap);
ReadBasicSetting(Settings::values.renderer_debug);
ReadBasicSetting(Settings::values.renderer_shader_feedback);
ReadBasicSetting(Settings::values.enable_nsight_aftermath);
@@ -1382,6 +1382,7 @@ void Config::SaveRendererValues() {
WriteGlobalSetting(Settings::values.max_anisotropy);
WriteGlobalSetting(Settings::values.use_speed_limit);
WriteGlobalSetting(Settings::values.speed_limit);
WriteGlobalSetting(Settings::values.fps_cap);
WriteGlobalSetting(Settings::values.use_disk_shader_cache);
WriteSetting(QString::fromStdString(Settings::values.gpu_accuracy.GetLabel()),
static_cast<u32>(Settings::values.gpu_accuracy.GetValue(global)),
@@ -1405,7 +1406,6 @@ void Config::SaveRendererValues() {
WriteGlobalSetting(Settings::values.bg_blue);
if (global) {
WriteBasicSetting(Settings::values.fps_cap);
WriteBasicSetting(Settings::values.renderer_debug);
WriteBasicSetting(Settings::values.renderer_shader_feedback);
WriteBasicSetting(Settings::values.enable_nsight_aftermath);

View File

@@ -30,6 +30,9 @@ ConfigureGeneral::ConfigureGeneral(const Core::System& system_, QWidget* parent)
connect(ui->button_reset_defaults, &QPushButton::clicked, this,
&ConfigureGeneral::ResetDefaults);
ui->fps_cap_label->setVisible(Settings::IsConfiguringGlobal());
ui->fps_cap_combobox->setVisible(!Settings::IsConfiguringGlobal());
}
ConfigureGeneral::~ConfigureGeneral() = default;
@@ -57,6 +60,11 @@ void ConfigureGeneral::SetConfiguration() {
} else {
ui->speed_limit->setEnabled(Settings::values.use_speed_limit.GetValue() &&
use_speed_limit != ConfigurationShared::CheckState::Global);
ui->fps_cap_combobox->setCurrentIndex(Settings::values.fps_cap.UsingGlobal() ? 0 : 1);
ui->fps_cap->setEnabled(!Settings::values.fps_cap.UsingGlobal());
ConfigurationShared::SetHighlight(ui->fps_cap_layout,
!Settings::values.fps_cap.UsingGlobal());
}
}
@@ -106,6 +114,13 @@ void ConfigureGeneral::ApplyConfiguration() {
Qt::Checked);
Settings::values.speed_limit.SetValue(ui->speed_limit->value());
}
if (ui->fps_cap_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
Settings::values.fps_cap.SetGlobal(true);
} else {
Settings::values.fps_cap.SetGlobal(false);
Settings::values.fps_cap.SetValue(ui->fps_cap->value());
}
}
}
@@ -148,4 +163,9 @@ void ConfigureGeneral::SetupPerGameUI() {
ui->speed_limit->setEnabled(ui->toggle_speed_limit->isChecked() &&
(use_speed_limit != ConfigurationShared::CheckState::Global));
});
connect(ui->fps_cap_combobox, qOverload<int>(&QComboBox::activated), this, [this](int index) {
ui->fps_cap->setEnabled(index == 1);
ConfigurationShared::SetHighlight(ui->fps_cap_layout, index == 1);
});
}

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>329</width>
<height>407</height>
<width>744</width>
<height>568</height>
</rect>
</property>
<property name="windowTitle">
@@ -28,34 +28,85 @@
<item>
<layout class="QVBoxLayout" name="GeneralVerticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="fps_cap_label">
<widget class="QWidget" name="fps_cap_layout" native="true">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,1">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QComboBox" name="fps_cap_combobox">
<property name="currentText">
<string>Use global framerate cap</string>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<item>
<property name="text">
<string>Framerate Cap</string>
<string>Use global framerate cap</string>
</property>
<property name="toolTip">
<string>Requires the use of the FPS Limiter Toggle hotkey to take effect.</string>
</item>
<item>
<property name="text">
<string>Set framerate cap:</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QSpinBox" name="fps_cap">
<property name="suffix">
<string>x</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>1000</number>
</property>
<property name="value">
<number>500</number>
</property>
</item>
<item>
<widget class="QLabel" name="fps_cap_label">
<property name="toolTip">
<string>Requires the use of the FPS Limiter Toggle hotkey to take effect.</string>
</property>
<property name="text">
<string>Framerate Cap</string>
</property>
</widget>
</item>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QSpinBox" name="fps_cap">
<property name="suffix">
<string>x</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>1000</number>
</property>
<property name="value">
<number>500</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">

View File

@@ -429,7 +429,7 @@
</item>
<item>
<property name="text">
<string>AMD's FidelityFX™ Super Resolution [Vulkan Only]</string>
<string>AMD FidelityFX™ Super Resolution [Vulkan Only]</string>
</property>
</item>
</widget>

View File

@@ -965,6 +965,9 @@ void GMainWindow::InitializeHotkeys() {
const QString toggle_status_bar = QStringLiteral("Toggle Status Bar");
const QString fullscreen = QStringLiteral("Fullscreen");
const QString capture_screenshot = QStringLiteral("Capture Screenshot");
const QString tas_start_stop = QStringLiteral("TAS Start/Stop");
const QString tas_record = QStringLiteral("TAS Record");
const QString tas_reset = QStringLiteral("TAS Reset");
ui->action_Load_File->setShortcut(hotkey_registry.GetKeySequence(main_window, load_file));
ui->action_Load_File->setShortcutContext(
@@ -1005,6 +1008,18 @@ void GMainWindow::InitializeHotkeys() {
ui->action_Fullscreen->setShortcutContext(
hotkey_registry.GetShortcutContext(main_window, fullscreen));
ui->action_TAS_Start->setShortcut(hotkey_registry.GetKeySequence(main_window, tas_start_stop));
ui->action_TAS_Start->setShortcutContext(
hotkey_registry.GetShortcutContext(main_window, tas_start_stop));
ui->action_TAS_Record->setShortcut(hotkey_registry.GetKeySequence(main_window, tas_record));
ui->action_TAS_Record->setShortcutContext(
hotkey_registry.GetShortcutContext(main_window, tas_record));
ui->action_TAS_Reset->setShortcut(hotkey_registry.GetKeySequence(main_window, tas_reset));
ui->action_TAS_Reset->setShortcutContext(
hotkey_registry.GetShortcutContext(main_window, tas_reset));
connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Load File"), this),
&QShortcut::activated, this, &GMainWindow::OnMenuLoadFile);
connect(
@@ -1095,28 +1110,6 @@ void GMainWindow::InitializeHotkeys() {
render_window->setAttribute(Qt::WA_Hover, true);
}
});
connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("TAS Start/Stop"), this),
&QShortcut::activated, this, [&] {
if (!emulation_running) {
return;
}
input_subsystem->GetTas()->StartStop();
});
connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("TAS Reset"), this),
&QShortcut::activated, this, [&] { input_subsystem->GetTas()->Reset(); });
connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("TAS Record"), this),
&QShortcut::activated, this, [&] {
if (!emulation_running) {
return;
}
bool is_recording = input_subsystem->GetTas()->Record();
if (!is_recording) {
const auto res = QMessageBox::question(this, tr("TAS Recording"),
tr("Overwrite file of player 1?"),
QMessageBox::Yes | QMessageBox::No);
input_subsystem->GetTas()->SaveRecording(res == QMessageBox::Yes);
}
});
}
void GMainWindow::SetDefaultUIGeometry() {
@@ -1236,11 +1229,11 @@ void GMainWindow::ConnectMenuEvents() {
connect(ui->action_Restart, &QAction::triggered, this,
[this] { BootGame(QString(game_path)); });
connect(ui->action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure);
connect(ui->action_Configure_Tas, &QAction::triggered, this, &GMainWindow::OnConfigureTas);
connect(ui->action_Configure_Current_Game, &QAction::triggered, this,
&GMainWindow::OnConfigurePerGame);
// View
connect(ui->action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
connect(ui->action_Single_Window_Mode, &QAction::triggered, this,
&GMainWindow::ToggleWindowMode);
connect(ui->action_Display_Dock_Widget_Headers, &QAction::triggered, this,
@@ -1258,17 +1251,20 @@ void GMainWindow::ConnectMenuEvents() {
ui->menu_Reset_Window_Size->addAction(ui->action_Reset_Window_Size_900);
ui->menu_Reset_Window_Size->addAction(ui->action_Reset_Window_Size_1080);
// Fullscreen
connect(ui->action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
// Movie
// Tools
connect(ui->action_Rederive, &QAction::triggered, this,
std::bind(&GMainWindow::OnReinitializeKeys, this, ReinitializeKeyBehavior::Warning));
connect(ui->action_Capture_Screenshot, &QAction::triggered, this,
&GMainWindow::OnCaptureScreenshot);
// TAS
connect(ui->action_TAS_Start, &QAction::triggered, this, &GMainWindow::OnTasStartStop);
connect(ui->action_TAS_Record, &QAction::triggered, this, &GMainWindow::OnTasRecord);
connect(ui->action_TAS_Reset, &QAction::triggered, this, &GMainWindow::OnTasReset);
connect(ui->action_Configure_Tas, &QAction::triggered, this, &GMainWindow::OnConfigureTas);
// Help
connect(ui->action_Open_yuzu_Folder, &QAction::triggered, this, &GMainWindow::OnOpenYuzuFolder);
connect(ui->action_Rederive, &QAction::triggered, this,
std::bind(&GMainWindow::OnReinitializeKeys, this, ReinitializeKeyBehavior::Warning));
connect(ui->action_About, &QAction::triggered, this, &GMainWindow::OnAbout);
}
@@ -1582,6 +1578,7 @@ void GMainWindow::ShutdownGame() {
game_list->SetFilterFocus();
tas_label->clear();
input_subsystem->GetTas()->Stop();
OnTasStateChanged();
render_window->removeEventFilter(render_window);
render_window->setAttribute(Qt::WA_Hover, false);
@@ -2509,6 +2506,7 @@ void GMainWindow::OnStartGame() {
ui->action_Restart->setEnabled(true);
ui->action_Configure_Current_Game->setEnabled(true);
ui->action_Report_Compatibility->setEnabled(true);
OnTasStateChanged();
discord_rpc->Update();
ui->action_Load_Amiibo->setEnabled(true);
@@ -2821,6 +2819,32 @@ void GMainWindow::OnConfigureTas() {
}
}
void GMainWindow::OnTasStartStop() {
if (!emulation_running) {
return;
}
input_subsystem->GetTas()->StartStop();
OnTasStateChanged();
}
void GMainWindow::OnTasRecord() {
if (!emulation_running) {
return;
}
const bool is_recording = input_subsystem->GetTas()->Record();
if (!is_recording) {
const auto res =
QMessageBox::question(this, tr("TAS Recording"), tr("Overwrite file of player 1?"),
QMessageBox::Yes | QMessageBox::No);
input_subsystem->GetTas()->SaveRecording(res == QMessageBox::Yes);
}
OnTasStateChanged();
}
void GMainWindow::OnTasReset() {
input_subsystem->GetTas()->Reset();
}
void GMainWindow::OnConfigurePerGame() {
const u64 title_id = system->GetCurrentProcessProgramID();
OpenPerGameConfiguration(title_id, game_path.toStdString());
@@ -3014,6 +3038,23 @@ QString GMainWindow::GetTasStateDescription() const {
}
}
void GMainWindow::OnTasStateChanged() {
bool is_running = false;
bool is_recording = false;
if (emulation_running) {
const TasInput::TasState tas_status = std::get<0>(input_subsystem->GetTas()->GetStatus());
is_running = tas_status == TasInput::TasState::Running;
is_recording = tas_status == TasInput::TasState::Recording;
}
ui->action_TAS_Start->setText(is_running ? tr("&Stop Running") : tr("&Start"));
ui->action_TAS_Record->setText(is_recording ? tr("Stop R&ecording") : tr("R&ecord"));
ui->action_TAS_Start->setEnabled(emulation_running);
ui->action_TAS_Record->setEnabled(emulation_running);
ui->action_TAS_Reset->setEnabled(emulation_running);
}
void GMainWindow::UpdateStatusBar() {
if (emu_thread == nullptr) {
status_bar_update_timer.stop();
@@ -3106,7 +3147,7 @@ void GMainWindow::UpdateFilterText() {
filter_status_button->setText(tr("SCALEFORCE"));
break;
case Settings::ScalingFilter::Fsr:
filter_status_button->setText(tr("AMD'S FIDELITYFX SR"));
filter_status_button->setText(tr("FSR"));
break;
default:
filter_status_button->setText(tr("BILINEAR"));
@@ -3117,15 +3158,15 @@ void GMainWindow::UpdateFilterText() {
void GMainWindow::UpdateAAText() {
const auto aa_mode = Settings::values.anti_aliasing.GetValue();
switch (aa_mode) {
case Settings::AntiAliasing::Fxaa:
aa_status_button->setText(tr("FXAA"));
break;
case Settings::AntiAliasing::None:
aa_status_button->setText(tr("NO AA"));
break;
default:
case Settings::AntiAliasing::Fxaa:
aa_status_button->setText(tr("FXAA"));
break;
default:
aa_status_button->setText(tr("NO AA"));
break;
}
}
@@ -3300,9 +3341,9 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
if (!errors.isEmpty()) {
QMessageBox::warning(
this, tr("Derivation Components Missing"),
tr("Components are missing that may hinder key derivation from completing. "
tr("Encryption keys are missing. "
"<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu "
"quickstart guide</a> to get all your keys and "
"quickstart guide</a> to get all your keys, firmware and "
"games.<br><br><small>(%1)</small>")
.arg(errors));
}

View File

@@ -177,6 +177,7 @@ public slots:
void WebBrowserOpenWebPage(const std::string& main_url, const std::string& additional_args,
bool is_local);
void OnAppFocusStateChanged(Qt::ApplicationState state);
void OnTasStateChanged();
private:
void RegisterMetaTypes();
@@ -268,6 +269,9 @@ private slots:
void OnMenuRecentFile();
void OnConfigure();
void OnConfigureTas();
void OnTasStartStop();
void OnTasRecord();
void OnTasReset();
void OnConfigurePerGame();
void OnLoadAmiibo();
void OnOpenYuzuFolder();
@@ -313,6 +317,7 @@ private:
void OpenURL(const QUrl& url);
void LoadTranslation();
void OpenPerGameConfiguration(u64 title_id, const std::string& file_name);
QString GetTasStateDescription() const;
std::unique_ptr<Ui::MainWindow> ui;

View File

@@ -79,39 +79,39 @@
<string>&amp;View</string>
</property>
<widget class="QMenu" name="menu_Reset_Window_Size">
<property name="title">
<string>&amp;Reset Window Size</string>
</property>
<property name="title">
<string>&amp;Reset Window Size</string>
</property>
</widget>
<action name="action_Reset_Window_Size_720">
<property name="text">
<string>Reset Window Size to &amp;720p</string>
</property>
<property name="iconText">
<string>Reset Window Size to 720p</string>
</property>
</action>
<action name="action_Reset_Window_Size_900">
<property name="text">
<string>Reset Window Size to &amp;900p</string>
</property>
<property name="iconText">
<string>Reset Window Size to 900p</string>
</property>
</action>
<action name="action_Reset_Window_Size_1080">
<property name="text">
<string>Reset Window Size to &amp;1080p</string>
</property>
<property name="iconText">
<string>Reset Window Size to 1080p</string>
</property>
</action>
<widget class="QMenu" name="menu_View_Debugging">
<property name="title">
<string>&amp;Debugging</string>
</property>
</widget>
<action name="action_Reset_Window_Size_720">
<property name="text">
<string>Reset Window Size to &amp;720p</string>
</property>
<property name="iconText">
<string>Reset Window Size to 720p</string>
</property>
</action>
<action name="action_Reset_Window_Size_900">
<property name="text">
<string>Reset Window Size to &amp;900p</string>
</property>
<property name="iconText">
<string>Reset Window Size to 900p</string>
</property>
</action>
<action name="action_Reset_Window_Size_1080">
<property name="text">
<string>Reset Window Size to &amp;1080p</string>
</property>
<property name="iconText">
<string>Reset Window Size to 1080p</string>
</property>
</action>
<addaction name="action_Fullscreen"/>
<addaction name="action_Single_Window_Mode"/>
<addaction name="action_Display_Dock_Widget_Headers"/>
@@ -125,10 +125,20 @@
<property name="title">
<string>&amp;Tools</string>
</property>
<widget class="QMenu" name="menuTAS">
<property name="title">
<string>&amp;TAS</string>
</property>
<addaction name="action_TAS_Start"/>
<addaction name="action_TAS_Record"/>
<addaction name="action_TAS_Reset"/>
<addaction name="separator"/>
<addaction name="action_Configure_Tas"/>
</widget>
<addaction name="action_Rederive"/>
<addaction name="separator"/>
<addaction name="action_Capture_Screenshot"/>
<addaction name="action_Configure_Tas"/>
<addaction name="menuTAS"/>
</widget>
<widget class="QMenu" name="menu_Help">
<property name="title">
@@ -309,7 +319,7 @@
</action>
<action name="action_Configure_Tas">
<property name="text">
<string>Configure &amp;TAS...</string>
<string>&amp;Configure TAS...</string>
</property>
</action>
<action name="action_Configure_Current_Game">
@@ -320,6 +330,30 @@
<string>Configure C&amp;urrent Game...</string>
</property>
</action>
<action name="action_TAS_Start">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>&amp;Start</string>
</property>
</action>
<action name="action_TAS_Reset">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>&amp;Reset</string>
</property>
</action>
<action name="action_TAS_Record">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>R&amp;ecord</string>
</property>
</action>
</widget>
<resources>
<include location="yuzu.qrc"/>