Compare commits
42 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
60928cf8cd | ||
|
|
72aa418b0b | ||
|
|
cd6cf0422d | ||
|
|
daecbd3a7f | ||
|
|
ee76b546d4 | ||
|
|
be238fb26a | ||
|
|
4d9c9e567e | ||
|
|
c8a67a725d | ||
|
|
84eb3e7d02 | ||
|
|
097de2febc | ||
|
|
b7a938e817 | ||
|
|
f90d980837 | ||
|
|
8e3371a5c5 | ||
|
|
d20f91da11 | ||
|
|
5082712b4e | ||
|
|
ba5210675a | ||
|
|
fc34749778 | ||
|
|
5cf93c1346 | ||
|
|
2726d705f8 | ||
|
|
281437c811 | ||
|
|
a41c6dafea | ||
|
|
095bc88428 | ||
|
|
9173f07a51 | ||
|
|
3dc38d185b | ||
|
|
ea6fa044f3 | ||
|
|
40cd0bb97b | ||
|
|
fe1f06c856 | ||
|
|
c3e1ffc44b | ||
|
|
b44fbf6cdd | ||
|
|
d2e009f355 | ||
|
|
a69813948f | ||
|
|
c45af76ea0 | ||
|
|
bc5ed1aa1b | ||
|
|
a237fb5f75 | ||
|
|
c76163b611 | ||
|
|
dc61b7045b | ||
|
|
d8a783a368 | ||
|
|
6dd6dc046c | ||
|
|
2348eb41f3 | ||
|
|
894cc9d876 | ||
|
|
ffb79afd29 | ||
|
|
2b3d66fe69 |
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"};
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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{};
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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{};
|
||||
|
||||
@@ -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"},
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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{};
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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{};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -79,39 +79,39 @@
|
||||
<string>&View</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="menu_Reset_Window_Size">
|
||||
<property name="title">
|
||||
<string>&Reset Window Size</string>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>&Reset Window Size</string>
|
||||
</property>
|
||||
</widget>
|
||||
<action name="action_Reset_Window_Size_720">
|
||||
<property name="text">
|
||||
<string>Reset Window Size to &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 &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 &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>&Debugging</string>
|
||||
</property>
|
||||
</widget>
|
||||
<action name="action_Reset_Window_Size_720">
|
||||
<property name="text">
|
||||
<string>Reset Window Size to &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 &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 &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>&Tools</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuTAS">
|
||||
<property name="title">
|
||||
<string>&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 &TAS...</string>
|
||||
<string>&Configure TAS...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Configure_Current_Game">
|
||||
@@ -320,6 +330,30 @@
|
||||
<string>Configure C&urrent Game...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_TAS_Start">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Start</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_TAS_Reset">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Reset</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_TAS_Record">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>R&ecord</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="yuzu.qrc"/>
|
||||
|
||||
Reference in New Issue
Block a user