Compare commits
52 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa471274d9 | ||
|
|
1f4dfb3998 | ||
|
|
c9454c8422 | ||
|
|
6088898b02 | ||
|
|
a788c861bd | ||
|
|
ee2206a1b7 | ||
|
|
065f83c6c3 | ||
|
|
ee3f576495 | ||
|
|
b42595fa6b | ||
|
|
0faf7b17a1 | ||
|
|
382722b9c4 | ||
|
|
0d8ef2d3b9 | ||
|
|
ea80e2bc57 | ||
|
|
83a2fb3c3a | ||
|
|
e2f2155dab | ||
|
|
c0b2b7020d | ||
|
|
66b73fd399 | ||
|
|
9cb4b7be40 | ||
|
|
598954436f | ||
|
|
b569641098 | ||
|
|
6300ccbc3c | ||
|
|
dffa1a872a | ||
|
|
c9305959d3 | ||
|
|
6951741a94 | ||
|
|
0371650bd7 | ||
|
|
dae2449880 | ||
|
|
0032cf3818 | ||
|
|
75d23a3679 | ||
|
|
58ad8dfac6 | ||
|
|
4667ed8e22 | ||
|
|
676172e20d | ||
|
|
d316d248ab | ||
|
|
b758c861b0 | ||
|
|
fec4eb9776 | ||
|
|
ca51f99840 | ||
|
|
13aa664f3f | ||
|
|
ad53b233c5 | ||
|
|
970d9e57c8 | ||
|
|
8676832064 | ||
|
|
fc436bb09b | ||
|
|
cd91e98dab | ||
|
|
7c458311d3 | ||
|
|
b0aa8ad736 | ||
|
|
be6466d5c0 | ||
|
|
b117ca5fce | ||
|
|
9ebc27234d | ||
|
|
bbb396d7f1 | ||
|
|
9ff72ca9f2 | ||
|
|
d708d03d20 | ||
|
|
5f2d9f282a | ||
|
|
f2a2f818b6 | ||
|
|
c6a0ab9792 |
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -43,3 +43,6 @@
|
||||
[submodule "externals/zstd"]
|
||||
path = externals/zstd
|
||||
url = https://github.com/facebook/zstd
|
||||
[submodule "sirit"]
|
||||
path = externals/sirit
|
||||
url = https://github.com/ReinUsesLisp/sirit
|
||||
|
||||
5
externals/CMakeLists.txt
vendored
5
externals/CMakeLists.txt
vendored
@@ -72,6 +72,11 @@ if (USE_DISCORD_PRESENCE)
|
||||
target_include_directories(discord-rpc INTERFACE ./discord-rpc/include)
|
||||
endif()
|
||||
|
||||
# Sirit
|
||||
if (ENABLE_VULKAN)
|
||||
add_subdirectory(sirit)
|
||||
endif()
|
||||
|
||||
if (ENABLE_WEB_SERVICE)
|
||||
# LibreSSL
|
||||
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
|
||||
|
||||
1
externals/sirit
vendored
Submodule
1
externals/sirit
vendored
Submodule
Submodule externals/sirit added at f7c4b07a7e
@@ -20,7 +20,7 @@ struct ScopeExitHelper {
|
||||
|
||||
template <typename Func>
|
||||
ScopeExitHelper<Func> ScopeExit(Func&& func) {
|
||||
return ScopeExitHelper<Func>(std::move(func));
|
||||
return ScopeExitHelper<Func>(std::forward<Func>(func));
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
|
||||
@@ -21,11 +21,6 @@
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <cstdlib>
|
||||
#elif defined(__linux__)
|
||||
#include <byteswap.h>
|
||||
#elif defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || \
|
||||
defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
#include <sys/endian.h>
|
||||
#endif
|
||||
#include <cstring>
|
||||
#include "common/common_types.h"
|
||||
@@ -62,86 +57,49 @@
|
||||
namespace Common {
|
||||
|
||||
#ifdef _MSC_VER
|
||||
inline u16 swap16(u16 _data) {
|
||||
return _byteswap_ushort(_data);
|
||||
[[nodiscard]] inline u16 swap16(u16 data) noexcept {
|
||||
return _byteswap_ushort(data);
|
||||
}
|
||||
inline u32 swap32(u32 _data) {
|
||||
return _byteswap_ulong(_data);
|
||||
[[nodiscard]] inline u32 swap32(u32 data) noexcept {
|
||||
return _byteswap_ulong(data);
|
||||
}
|
||||
inline u64 swap64(u64 _data) {
|
||||
return _byteswap_uint64(_data);
|
||||
[[nodiscard]] inline u64 swap64(u64 data) noexcept {
|
||||
return _byteswap_uint64(data);
|
||||
}
|
||||
#elif defined(ARCHITECTURE_ARM) && (__ARM_ARCH >= 6)
|
||||
inline u16 swap16(u16 _data) {
|
||||
u32 data = _data;
|
||||
__asm__("rev16 %0, %1\n" : "=l"(data) : "l"(data));
|
||||
return (u16)data;
|
||||
}
|
||||
inline u32 swap32(u32 _data) {
|
||||
__asm__("rev %0, %1\n" : "=l"(_data) : "l"(_data));
|
||||
return _data;
|
||||
}
|
||||
inline u64 swap64(u64 _data) {
|
||||
return ((u64)swap32(_data) << 32) | swap32(_data >> 32);
|
||||
}
|
||||
#elif __linux__
|
||||
inline u16 swap16(u16 _data) {
|
||||
return bswap_16(_data);
|
||||
}
|
||||
inline u32 swap32(u32 _data) {
|
||||
return bswap_32(_data);
|
||||
}
|
||||
inline u64 swap64(u64 _data) {
|
||||
return bswap_64(_data);
|
||||
}
|
||||
#elif __APPLE__
|
||||
inline __attribute__((always_inline)) u16 swap16(u16 _data) {
|
||||
return (_data >> 8) | (_data << 8);
|
||||
}
|
||||
inline __attribute__((always_inline)) u32 swap32(u32 _data) {
|
||||
return __builtin_bswap32(_data);
|
||||
}
|
||||
inline __attribute__((always_inline)) u64 swap64(u64 _data) {
|
||||
return __builtin_bswap64(_data);
|
||||
}
|
||||
#elif defined(__Bitrig__) || defined(__OpenBSD__)
|
||||
#elif defined(__clang__) || defined(__GNUC__)
|
||||
#if defined(__Bitrig__) || defined(__OpenBSD__)
|
||||
// redefine swap16, swap32, swap64 as inline functions
|
||||
#undef swap16
|
||||
#undef swap32
|
||||
#undef swap64
|
||||
inline u16 swap16(u16 _data) {
|
||||
return __swap16(_data);
|
||||
#endif
|
||||
[[nodiscard]] inline u16 swap16(u16 data) noexcept {
|
||||
return __builtin_bswap16(data);
|
||||
}
|
||||
inline u32 swap32(u32 _data) {
|
||||
return __swap32(_data);
|
||||
[[nodiscard]] inline u32 swap32(u32 data) noexcept {
|
||||
return __builtin_bswap32(data);
|
||||
}
|
||||
inline u64 swap64(u64 _data) {
|
||||
return __swap64(_data);
|
||||
}
|
||||
#elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__)
|
||||
inline u16 swap16(u16 _data) {
|
||||
return bswap16(_data);
|
||||
}
|
||||
inline u32 swap32(u32 _data) {
|
||||
return bswap32(_data);
|
||||
}
|
||||
inline u64 swap64(u64 _data) {
|
||||
return bswap64(_data);
|
||||
[[nodiscard]] inline u64 swap64(u64 data) noexcept {
|
||||
return __builtin_bswap64(data);
|
||||
}
|
||||
#else
|
||||
// Slow generic implementation.
|
||||
inline u16 swap16(u16 data) {
|
||||
// Generic implementation.
|
||||
[[nodiscard]] inline u16 swap16(u16 data) noexcept {
|
||||
return (data >> 8) | (data << 8);
|
||||
}
|
||||
inline u32 swap32(u32 data) {
|
||||
return (swap16(data) << 16) | swap16(data >> 16);
|
||||
[[nodiscard]] inline u32 swap32(u32 data) noexcept {
|
||||
return ((data & 0xFF000000U) >> 24) | ((data & 0x00FF0000U) >> 8) |
|
||||
((data & 0x0000FF00U) << 8) | ((data & 0x000000FFU) << 24);
|
||||
}
|
||||
inline u64 swap64(u64 data) {
|
||||
return ((u64)swap32(data) << 32) | swap32(data >> 32);
|
||||
[[nodiscard]] inline u64 swap64(u64 data) noexcept {
|
||||
return ((data & 0xFF00000000000000ULL) >> 56) | ((data & 0x00FF000000000000ULL) >> 40) |
|
||||
((data & 0x0000FF0000000000ULL) >> 24) | ((data & 0x000000FF00000000ULL) >> 8) |
|
||||
((data & 0x00000000FF000000ULL) << 8) | ((data & 0x0000000000FF0000ULL) << 24) |
|
||||
((data & 0x000000000000FF00ULL) << 40) | ((data & 0x00000000000000FFULL) << 56);
|
||||
}
|
||||
#endif
|
||||
|
||||
inline float swapf(float f) {
|
||||
[[nodiscard]] inline float swapf(float f) noexcept {
|
||||
static_assert(sizeof(u32) == sizeof(float), "float must be the same size as uint32_t.");
|
||||
|
||||
u32 value;
|
||||
@@ -153,7 +111,7 @@ inline float swapf(float f) {
|
||||
return f;
|
||||
}
|
||||
|
||||
inline double swapd(double f) {
|
||||
[[nodiscard]] inline double swapd(double f) noexcept {
|
||||
static_assert(sizeof(u64) == sizeof(double), "double must be the same size as uint64_t.");
|
||||
|
||||
u64 value;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "core/core_timing.h"
|
||||
#include "core/core_timing_util.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
#include "core/hle/kernel/vm_manager.h"
|
||||
@@ -99,7 +100,7 @@ public:
|
||||
}
|
||||
|
||||
void CallSVC(u32 swi) override {
|
||||
Kernel::CallSVC(swi);
|
||||
Kernel::CallSVC(parent.system, swi);
|
||||
}
|
||||
|
||||
void AddTicks(u64 ticks) override {
|
||||
@@ -112,14 +113,14 @@ public:
|
||||
// Always execute at least one tick.
|
||||
amortized_ticks = std::max<u64>(amortized_ticks, 1);
|
||||
|
||||
parent.core_timing.AddTicks(amortized_ticks);
|
||||
parent.system.CoreTiming().AddTicks(amortized_ticks);
|
||||
num_interpreted_instructions = 0;
|
||||
}
|
||||
u64 GetTicksRemaining() override {
|
||||
return std::max(parent.core_timing.GetDowncount(), 0);
|
||||
return std::max(parent.system.CoreTiming().GetDowncount(), 0);
|
||||
}
|
||||
u64 GetCNTPCT() override {
|
||||
return Timing::CpuCyclesToClockCycles(parent.core_timing.GetTicks());
|
||||
return Timing::CpuCyclesToClockCycles(parent.system.CoreTiming().GetTicks());
|
||||
}
|
||||
|
||||
ARM_Dynarmic& parent;
|
||||
@@ -129,7 +130,7 @@ public:
|
||||
};
|
||||
|
||||
std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
|
||||
auto* current_process = Core::CurrentProcess();
|
||||
auto* current_process = system.Kernel().CurrentProcess();
|
||||
auto** const page_table = current_process->VMManager().page_table.pointers.data();
|
||||
|
||||
Dynarmic::A64::UserConfig config;
|
||||
@@ -171,10 +172,10 @@ void ARM_Dynarmic::Step() {
|
||||
cb->InterpreterFallback(jit->GetPC(), 1);
|
||||
}
|
||||
|
||||
ARM_Dynarmic::ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor,
|
||||
ARM_Dynarmic::ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor,
|
||||
std::size_t core_index)
|
||||
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{core_timing},
|
||||
core_index{core_index}, core_timing{core_timing},
|
||||
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{system},
|
||||
core_index{core_index}, system{system},
|
||||
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {
|
||||
ThreadContext ctx{};
|
||||
inner_unicorn.SaveContext(ctx);
|
||||
|
||||
@@ -12,19 +12,15 @@
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
#include "core/arm/unicorn/arm_unicorn.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
class CoreTiming;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
|
||||
class ARM_Dynarmic_Callbacks;
|
||||
class DynarmicExclusiveMonitor;
|
||||
class System;
|
||||
|
||||
class ARM_Dynarmic final : public ARM_Interface {
|
||||
public:
|
||||
ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor,
|
||||
std::size_t core_index);
|
||||
ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index);
|
||||
~ARM_Dynarmic() override;
|
||||
|
||||
void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
|
||||
@@ -63,7 +59,7 @@ private:
|
||||
ARM_Unicorn inner_unicorn;
|
||||
|
||||
std::size_t core_index;
|
||||
Timing::CoreTiming& core_timing;
|
||||
System& system;
|
||||
DynarmicExclusiveMonitor& exclusive_monitor;
|
||||
};
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
@@ -49,20 +48,6 @@ static void CodeHook(uc_engine* uc, uint64_t address, uint32_t size, void* user_
|
||||
}
|
||||
}
|
||||
|
||||
static void InterruptHook(uc_engine* uc, u32 intNo, void* user_data) {
|
||||
u32 esr{};
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr));
|
||||
|
||||
auto ec = esr >> 26;
|
||||
auto iss = esr & 0xFFFFFF;
|
||||
|
||||
switch (ec) {
|
||||
case 0x15: // SVC
|
||||
Kernel::CallSVC(iss);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int size, u64 value,
|
||||
void* user_data) {
|
||||
ARM_Interface::ThreadContext ctx{};
|
||||
@@ -72,7 +57,7 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si
|
||||
return {};
|
||||
}
|
||||
|
||||
ARM_Unicorn::ARM_Unicorn(Timing::CoreTiming& core_timing) : core_timing{core_timing} {
|
||||
ARM_Unicorn::ARM_Unicorn(System& system) : system{system} {
|
||||
CHECKED(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc));
|
||||
|
||||
auto fpv = 3 << 20;
|
||||
@@ -177,7 +162,7 @@ void ARM_Unicorn::Run() {
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
ExecuteInstructions(std::max(4000000, 0));
|
||||
} else {
|
||||
ExecuteInstructions(std::max(core_timing.GetDowncount(), 0));
|
||||
ExecuteInstructions(std::max(system.CoreTiming().GetDowncount(), 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +175,7 @@ MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64));
|
||||
void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
|
||||
MICROPROFILE_SCOPE(ARM_Jit_Unicorn);
|
||||
CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
|
||||
core_timing.AddTicks(num_instructions);
|
||||
system.CoreTiming().AddTicks(num_instructions);
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) {
|
||||
uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
|
||||
@@ -273,4 +258,20 @@ void ARM_Unicorn::RecordBreak(GDBStub::BreakpointAddress bkpt) {
|
||||
last_bkpt_hit = true;
|
||||
}
|
||||
|
||||
void ARM_Unicorn::InterruptHook(uc_engine* uc, u32 int_no, void* user_data) {
|
||||
u32 esr{};
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr));
|
||||
|
||||
const auto ec = esr >> 26;
|
||||
const auto iss = esr & 0xFFFFFF;
|
||||
|
||||
auto* const arm_instance = static_cast<ARM_Unicorn*>(user_data);
|
||||
|
||||
switch (ec) {
|
||||
case 0x15: // SVC
|
||||
Kernel::CallSVC(arm_instance->system, iss);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -9,15 +9,13 @@
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
class CoreTiming;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
|
||||
class System;
|
||||
|
||||
class ARM_Unicorn final : public ARM_Interface {
|
||||
public:
|
||||
explicit ARM_Unicorn(Timing::CoreTiming& core_timing);
|
||||
explicit ARM_Unicorn(System& system);
|
||||
~ARM_Unicorn() override;
|
||||
|
||||
void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
|
||||
@@ -47,8 +45,10 @@ public:
|
||||
void RecordBreak(GDBStub::BreakpointAddress bkpt);
|
||||
|
||||
private:
|
||||
static void InterruptHook(uc_engine* uc, u32 int_no, void* user_data);
|
||||
|
||||
uc_engine* uc{};
|
||||
Timing::CoreTiming& core_timing;
|
||||
System& system;
|
||||
GDBStub::BreakpointAddress last_bkpt{};
|
||||
bool last_bkpt_hit = false;
|
||||
};
|
||||
|
||||
@@ -55,13 +55,13 @@ Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_ba
|
||||
: cpu_barrier{cpu_barrier}, core_timing{system.CoreTiming()}, core_index{core_index} {
|
||||
if (Settings::values.use_cpu_jit) {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
arm_interface = std::make_unique<ARM_Dynarmic>(core_timing, exclusive_monitor, core_index);
|
||||
arm_interface = std::make_unique<ARM_Dynarmic>(system, exclusive_monitor, core_index);
|
||||
#else
|
||||
arm_interface = std::make_unique<ARM_Unicorn>();
|
||||
arm_interface = std::make_unique<ARM_Unicorn>(system);
|
||||
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
|
||||
#endif
|
||||
} else {
|
||||
arm_interface = std::make_unique<ARM_Unicorn>(core_timing);
|
||||
arm_interface = std::make_unique<ARM_Unicorn>(system);
|
||||
}
|
||||
|
||||
scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface);
|
||||
|
||||
@@ -12,6 +12,23 @@
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
/**
|
||||
* Represents a graphics context that can be used for background computation or drawing. If the
|
||||
* graphics backend doesn't require the context, then the implementation of these methods can be
|
||||
* stubs
|
||||
*/
|
||||
class GraphicsContext {
|
||||
public:
|
||||
/// Makes the graphics context current for the caller thread
|
||||
virtual void MakeCurrent() = 0;
|
||||
|
||||
/// Releases (dunno if this is the "right" word) the context from the caller thread
|
||||
virtual void DoneCurrent() = 0;
|
||||
|
||||
/// Swap buffers to display the next frame
|
||||
virtual void SwapBuffers() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Abstraction class used to provide an interface between emulation code and the frontend
|
||||
* (e.g. SDL, QGLWidget, GLFW, etc...).
|
||||
@@ -30,7 +47,7 @@ namespace Core::Frontend {
|
||||
* - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please
|
||||
* re-read the upper points again and think about it if you don't see this.
|
||||
*/
|
||||
class EmuWindow {
|
||||
class EmuWindow : public GraphicsContext {
|
||||
public:
|
||||
/// Data structure to store emuwindow configuration
|
||||
struct WindowConfig {
|
||||
@@ -40,17 +57,21 @@ public:
|
||||
std::pair<unsigned, unsigned> min_client_area_size;
|
||||
};
|
||||
|
||||
/// Swap buffers to display the next frame
|
||||
virtual void SwapBuffers() = 0;
|
||||
|
||||
/// Polls window events
|
||||
virtual void PollEvents() = 0;
|
||||
|
||||
/// Makes the graphics context current for the caller thread
|
||||
virtual void MakeCurrent() = 0;
|
||||
|
||||
/// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
|
||||
virtual void DoneCurrent() = 0;
|
||||
/**
|
||||
* Returns a GraphicsContext that the frontend provides that is shared with the emu window. This
|
||||
* context can be used from other threads for background graphics computation. If the frontend
|
||||
* is using a graphics backend that doesn't need anything specific to run on a different thread,
|
||||
* then it can use a stubbed implemenation for GraphicsContext.
|
||||
*
|
||||
* If the return value is null, then the core should assume that the frontend cannot provide a
|
||||
* Shared Context
|
||||
*/
|
||||
virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal that a touch pressed event has occurred (e.g. mouse click pressed)
|
||||
|
||||
@@ -25,7 +25,7 @@ public:
|
||||
return name;
|
||||
}
|
||||
|
||||
static const HandleType HANDLE_TYPE = HandleType::ClientPort;
|
||||
static constexpr HandleType HANDLE_TYPE = HandleType::ClientPort;
|
||||
HandleType GetHandleType() const override {
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
return name;
|
||||
}
|
||||
|
||||
static const HandleType HANDLE_TYPE = HandleType::ClientSession;
|
||||
static constexpr HandleType HANDLE_TYPE = HandleType::ClientSession;
|
||||
HandleType GetHandleType() const override {
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ public:
|
||||
return name;
|
||||
}
|
||||
|
||||
static const HandleType HANDLE_TYPE = HandleType::Process;
|
||||
static constexpr HandleType HANDLE_TYPE = HandleType::Process;
|
||||
HandleType GetHandleType() const override {
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
return reset_type;
|
||||
}
|
||||
|
||||
static const HandleType HANDLE_TYPE = HandleType::ReadableEvent;
|
||||
static constexpr HandleType HANDLE_TYPE = HandleType::ReadableEvent;
|
||||
HandleType GetHandleType() const override {
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ public:
|
||||
return GetTypeName();
|
||||
}
|
||||
|
||||
static const HandleType HANDLE_TYPE = HandleType::ResourceLimit;
|
||||
static constexpr HandleType HANDLE_TYPE = HandleType::ResourceLimit;
|
||||
HandleType GetHandleType() const override {
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
return name;
|
||||
}
|
||||
|
||||
static const HandleType HANDLE_TYPE = HandleType::ServerPort;
|
||||
static constexpr HandleType HANDLE_TYPE = HandleType::ServerPort;
|
||||
HandleType GetHandleType() const override {
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
return name;
|
||||
}
|
||||
|
||||
static const HandleType HANDLE_TYPE = HandleType::ServerSession;
|
||||
static constexpr HandleType HANDLE_TYPE = HandleType::ServerSession;
|
||||
HandleType GetHandleType() const override {
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ public:
|
||||
return name;
|
||||
}
|
||||
|
||||
static const HandleType HANDLE_TYPE = HandleType::SharedMemory;
|
||||
static constexpr HandleType HANDLE_TYPE = HandleType::SharedMemory;
|
||||
HandleType GetHandleType() const override {
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,8 +6,12 @@
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
void CallSVC(u32 immediate);
|
||||
void CallSVC(Core::System& system, u32 immediate);
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -11,278 +11,312 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
static inline u64 Param(int n) {
|
||||
return Core::CurrentArmInterface().GetReg(n);
|
||||
static inline u64 Param(const Core::System& system, int n) {
|
||||
return system.CurrentArmInterface().GetReg(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* HLE a function return from the current ARM userland process
|
||||
* @param res Result to return
|
||||
* @param system System context
|
||||
* @param result Result to return
|
||||
*/
|
||||
static inline void FuncReturn(u64 res) {
|
||||
Core::CurrentArmInterface().SetReg(0, res);
|
||||
static inline void FuncReturn(Core::System& system, u64 result) {
|
||||
system.CurrentArmInterface().SetReg(0, result);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Function wrappers that return type ResultCode
|
||||
|
||||
template <ResultCode func(u64)>
|
||||
void SvcWrap() {
|
||||
FuncReturn(func(Param(0)).raw);
|
||||
template <ResultCode func(Core::System&, u64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
FuncReturn(system, func(system, Param(system, 0)).raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(u32)>
|
||||
void SvcWrap() {
|
||||
FuncReturn(func(static_cast<u32>(Param(0))).raw);
|
||||
template <ResultCode func(Core::System&, u32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(u32, u32)>
|
||||
void SvcWrap() {
|
||||
FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1))).raw);
|
||||
template <ResultCode func(Core::System&, u32, u32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
FuncReturn(
|
||||
system,
|
||||
func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(u32*)>
|
||||
void SvcWrap() {
|
||||
template <ResultCode func(Core::System&, u32*)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
u32 param = 0;
|
||||
const u32 retval = func(¶m).raw;
|
||||
Core::CurrentArmInterface().SetReg(1, param);
|
||||
FuncReturn(retval);
|
||||
const u32 retval = func(system, ¶m).raw;
|
||||
system.CurrentArmInterface().SetReg(1, param);
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
template <ResultCode func(u32*, u32)>
|
||||
void SvcWrap() {
|
||||
template <ResultCode func(Core::System&, u32*, u32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
u32 param_1 = 0;
|
||||
u32 retval = func(¶m_1, static_cast<u32>(Param(1))).raw;
|
||||
Core::CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(retval);
|
||||
const u32 retval = func(system, ¶m_1, static_cast<u32>(Param(system, 1))).raw;
|
||||
system.CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
template <ResultCode func(u32*, u32*)>
|
||||
void SvcWrap() {
|
||||
template <ResultCode func(Core::System&, u32*, u32*)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
u32 param_1 = 0;
|
||||
u32 param_2 = 0;
|
||||
const u32 retval = func(¶m_1, ¶m_2).raw;
|
||||
const u32 retval = func(system, ¶m_1, ¶m_2).raw;
|
||||
|
||||
auto& arm_interface = Core::CurrentArmInterface();
|
||||
auto& arm_interface = system.CurrentArmInterface();
|
||||
arm_interface.SetReg(1, param_1);
|
||||
arm_interface.SetReg(2, param_2);
|
||||
|
||||
FuncReturn(retval);
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
template <ResultCode func(u32*, u64)>
|
||||
void SvcWrap() {
|
||||
template <ResultCode func(Core::System&, u32*, u64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
u32 param_1 = 0;
|
||||
const u32 retval = func(¶m_1, Param(1)).raw;
|
||||
Core::CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(retval);
|
||||
const u32 retval = func(system, ¶m_1, Param(system, 1)).raw;
|
||||
system.CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
template <ResultCode func(u32*, u64, u32)>
|
||||
void SvcWrap() {
|
||||
template <ResultCode func(Core::System&, u32*, u64, u32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
u32 param_1 = 0;
|
||||
const u32 retval = func(¶m_1, Param(1), static_cast<u32>(Param(2))).raw;
|
||||
Core::CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(retval);
|
||||
const u32 retval =
|
||||
func(system, ¶m_1, Param(system, 1), static_cast<u32>(Param(system, 2))).raw;
|
||||
|
||||
system.CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
template <ResultCode func(u64*, u32)>
|
||||
void SvcWrap() {
|
||||
template <ResultCode func(Core::System&, u64*, u32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
u64 param_1 = 0;
|
||||
const u32 retval = func(¶m_1, static_cast<u32>(Param(1))).raw;
|
||||
Core::CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(retval);
|
||||
const u32 retval = func(system, ¶m_1, static_cast<u32>(Param(system, 1))).raw;
|
||||
|
||||
system.CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
template <ResultCode func(u64, s32)>
|
||||
void SvcWrap() {
|
||||
FuncReturn(func(Param(0), static_cast<s32>(Param(1))).raw);
|
||||
template <ResultCode func(Core::System&, u64, s32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
FuncReturn(system, func(system, Param(system, 0), static_cast<s32>(Param(system, 1))).raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(u64, u32)>
|
||||
void SvcWrap() {
|
||||
FuncReturn(func(Param(0), static_cast<u32>(Param(1))).raw);
|
||||
template <ResultCode func(Core::System&, u64, u32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1))).raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(u64*, u64)>
|
||||
void SvcWrap() {
|
||||
template <ResultCode func(Core::System&, u64*, u64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
u64 param_1 = 0;
|
||||
u32 retval = func(¶m_1, Param(1)).raw;
|
||||
Core::CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(retval);
|
||||
const u32 retval = func(system, ¶m_1, Param(system, 1)).raw;
|
||||
|
||||
system.CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
template <ResultCode func(u64*, u32, u32)>
|
||||
void SvcWrap() {
|
||||
template <ResultCode func(Core::System&, u64*, u32, u32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
u64 param_1 = 0;
|
||||
u32 retval = func(¶m_1, static_cast<u32>(Param(1)), static_cast<u32>(Param(2))).raw;
|
||||
Core::CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(retval);
|
||||
const u32 retval = func(system, ¶m_1, static_cast<u32>(Param(system, 1)),
|
||||
static_cast<u32>(Param(system, 2)))
|
||||
.raw;
|
||||
|
||||
system.CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
template <ResultCode func(u32, u64)>
|
||||
void SvcWrap() {
|
||||
FuncReturn(func(static_cast<u32>(Param(0)), Param(1)).raw);
|
||||
template <ResultCode func(Core::System&, u32, u64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1)).raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(u32, u32, u64)>
|
||||
void SvcWrap() {
|
||||
FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1)), Param(2)).raw);
|
||||
template <ResultCode func(Core::System&, u32, u32, u64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)),
|
||||
static_cast<u32>(Param(system, 1)), Param(system, 2))
|
||||
.raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(u32, u32*, u64*)>
|
||||
void SvcWrap() {
|
||||
template <ResultCode func(Core::System&, u32, u32*, u64*)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
u32 param_1 = 0;
|
||||
u64 param_2 = 0;
|
||||
ResultCode retval = func(static_cast<u32>(Param(2)), ¶m_1, ¶m_2);
|
||||
Core::CurrentArmInterface().SetReg(1, param_1);
|
||||
Core::CurrentArmInterface().SetReg(2, param_2);
|
||||
FuncReturn(retval.raw);
|
||||
const ResultCode retval = func(system, static_cast<u32>(Param(system, 2)), ¶m_1, ¶m_2);
|
||||
|
||||
system.CurrentArmInterface().SetReg(1, param_1);
|
||||
system.CurrentArmInterface().SetReg(2, param_2);
|
||||
FuncReturn(system, retval.raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(u64, u64, u32, u32)>
|
||||
void SvcWrap() {
|
||||
template <ResultCode func(Core::System&, u64, u64, u32, u32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
|
||||
static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3)))
|
||||
.raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u64, u64, u32, u64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
|
||||
static_cast<u32>(Param(system, 2)), Param(system, 3))
|
||||
.raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u32, u64, u32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
|
||||
static_cast<u32>(Param(system, 2)))
|
||||
.raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u64, u64, u64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
FuncReturn(system, func(system, Param(system, 0), Param(system, 1), Param(system, 2)).raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u64, u64, u32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
FuncReturn(
|
||||
func(Param(0), Param(1), static_cast<u32>(Param(2)), static_cast<u32>(Param(3))).raw);
|
||||
system,
|
||||
func(system, Param(system, 0), Param(system, 1), static_cast<u32>(Param(system, 2))).raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(u64, u64, u32, u64)>
|
||||
void SvcWrap() {
|
||||
FuncReturn(func(Param(0), Param(1), static_cast<u32>(Param(2)), Param(3)).raw);
|
||||
template <ResultCode func(Core::System&, u32, u64, u64, u32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
|
||||
Param(system, 2), static_cast<u32>(Param(system, 3)))
|
||||
.raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(u32, u64, u32)>
|
||||
void SvcWrap() {
|
||||
FuncReturn(func(static_cast<u32>(Param(0)), Param(1), static_cast<u32>(Param(2))).raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(u64, u64, u64)>
|
||||
void SvcWrap() {
|
||||
FuncReturn(func(Param(0), Param(1), Param(2)).raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(u64, u64, u32)>
|
||||
void SvcWrap() {
|
||||
FuncReturn(func(Param(0), Param(1), static_cast<u32>(Param(2))).raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(u32, u64, u64, u32)>
|
||||
void SvcWrap() {
|
||||
template <ResultCode func(Core::System&, u32, u64, u64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
FuncReturn(
|
||||
func(static_cast<u32>(Param(0)), Param(1), Param(2), static_cast<u32>(Param(3))).raw);
|
||||
system,
|
||||
func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)).raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(u32, u64, u64)>
|
||||
void SvcWrap() {
|
||||
FuncReturn(func(static_cast<u32>(Param(0)), Param(1), Param(2)).raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(u32*, u64, u64, s64)>
|
||||
void SvcWrap() {
|
||||
template <ResultCode func(Core::System&, u32*, u64, u64, s64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
u32 param_1 = 0;
|
||||
ResultCode retval =
|
||||
func(¶m_1, Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3)));
|
||||
Core::CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(retval.raw);
|
||||
const u32 retval = func(system, ¶m_1, Param(system, 1), static_cast<u32>(Param(system, 2)),
|
||||
static_cast<s64>(Param(system, 3)))
|
||||
.raw;
|
||||
|
||||
system.CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
template <ResultCode func(u64, u64, u32, s64)>
|
||||
void SvcWrap() {
|
||||
FuncReturn(
|
||||
func(Param(0), Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3))).raw);
|
||||
template <ResultCode func(Core::System&, u64, u64, u32, s64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
|
||||
static_cast<u32>(Param(system, 2)), static_cast<s64>(Param(system, 3)))
|
||||
.raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(u64*, u64, u64, u64)>
|
||||
void SvcWrap() {
|
||||
template <ResultCode func(Core::System&, u64*, u64, u64, u64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
u64 param_1 = 0;
|
||||
u32 retval = func(¶m_1, Param(1), Param(2), Param(3)).raw;
|
||||
Core::CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(retval);
|
||||
const u32 retval =
|
||||
func(system, ¶m_1, Param(system, 1), Param(system, 2), Param(system, 3)).raw;
|
||||
|
||||
system.CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
template <ResultCode func(u32*, u64, u64, u64, u32, s32)>
|
||||
void SvcWrap() {
|
||||
template <ResultCode func(Core::System&, u32*, u64, u64, u64, u32, s32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
u32 param_1 = 0;
|
||||
u32 retval = func(¶m_1, Param(1), Param(2), Param(3), static_cast<u32>(Param(4)),
|
||||
static_cast<s32>(Param(5)))
|
||||
.raw;
|
||||
Core::CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(retval);
|
||||
const u32 retval = func(system, ¶m_1, Param(system, 1), Param(system, 2), Param(system, 3),
|
||||
static_cast<u32>(Param(system, 4)), static_cast<s32>(Param(system, 5)))
|
||||
.raw;
|
||||
|
||||
system.CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
template <ResultCode func(u32*, u64, u64, u32)>
|
||||
void SvcWrap() {
|
||||
template <ResultCode func(Core::System&, u32*, u64, u64, u32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
u32 param_1 = 0;
|
||||
u32 retval = func(¶m_1, Param(1), Param(2), static_cast<u32>(Param(3))).raw;
|
||||
Core::CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(retval);
|
||||
const u32 retval = func(system, ¶m_1, Param(system, 1), Param(system, 2),
|
||||
static_cast<u32>(Param(system, 3)))
|
||||
.raw;
|
||||
|
||||
system.CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
template <ResultCode func(Handle*, u64, u32, u32)>
|
||||
void SvcWrap() {
|
||||
template <ResultCode func(Core::System&, Handle*, u64, u32, u32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
u32 param_1 = 0;
|
||||
u32 retval =
|
||||
func(¶m_1, Param(1), static_cast<u32>(Param(2)), static_cast<u32>(Param(3))).raw;
|
||||
Core::CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(retval);
|
||||
const u32 retval = func(system, ¶m_1, Param(system, 1), static_cast<u32>(Param(system, 2)),
|
||||
static_cast<u32>(Param(system, 3)))
|
||||
.raw;
|
||||
|
||||
system.CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
template <ResultCode func(u64, u32, s32, s64)>
|
||||
void SvcWrap() {
|
||||
FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)),
|
||||
static_cast<s64>(Param(3)))
|
||||
.raw);
|
||||
template <ResultCode func(Core::System&, u64, u32, s32, s64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)),
|
||||
static_cast<s32>(Param(system, 2)), static_cast<s64>(Param(system, 3)))
|
||||
.raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(u64, u32, s32, s32)>
|
||||
void SvcWrap() {
|
||||
FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)),
|
||||
static_cast<s32>(Param(3)))
|
||||
.raw);
|
||||
template <ResultCode func(Core::System&, u64, u32, s32, s32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)),
|
||||
static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3)))
|
||||
.raw);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Function wrappers that return type u32
|
||||
|
||||
template <u32 func()>
|
||||
void SvcWrap() {
|
||||
FuncReturn(func());
|
||||
template <u32 func(Core::System&)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
FuncReturn(system, func(system));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Function wrappers that return type u64
|
||||
|
||||
template <u64 func()>
|
||||
void SvcWrap() {
|
||||
FuncReturn(func());
|
||||
template <u64 func(Core::System&)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
FuncReturn(system, func(system));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// Function wrappers that return type void
|
||||
|
||||
template <void func()>
|
||||
void SvcWrap() {
|
||||
func();
|
||||
template <void func(Core::System&)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
func(system);
|
||||
}
|
||||
|
||||
template <void func(s64)>
|
||||
void SvcWrap() {
|
||||
func(static_cast<s64>(Param(0)));
|
||||
template <void func(Core::System&, s64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
func(system, static_cast<s64>(Param(system, 0)));
|
||||
}
|
||||
|
||||
template <void func(u64, u64 len)>
|
||||
void SvcWrap() {
|
||||
func(Param(0), Param(1));
|
||||
template <void func(Core::System&, u64, u64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
func(system, Param(system, 0), Param(system, 1));
|
||||
}
|
||||
|
||||
template <void func(u64, u64, u64)>
|
||||
void SvcWrap() {
|
||||
func(Param(0), Param(1), Param(2));
|
||||
template <void func(Core::System&, u64, u64, u64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
func(system, Param(system, 0), Param(system, 1), Param(system, 2));
|
||||
}
|
||||
|
||||
template <void func(u32, u64, u64)>
|
||||
void SvcWrap() {
|
||||
func(static_cast<u32>(Param(0)), Param(1), Param(2));
|
||||
template <void func(Core::System&, u32, u64, u64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2));
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -106,7 +106,7 @@ public:
|
||||
return "Thread";
|
||||
}
|
||||
|
||||
static const HandleType HANDLE_TYPE = HandleType::Thread;
|
||||
static constexpr HandleType HANDLE_TYPE = HandleType::Thread;
|
||||
HandleType GetHandleType() const override {
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
return name;
|
||||
}
|
||||
|
||||
static const HandleType HANDLE_TYPE = HandleType::WritableEvent;
|
||||
static constexpr HandleType HANDLE_TYPE = HandleType::WritableEvent;
|
||||
HandleType GetHandleType() const override {
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
@@ -115,11 +115,12 @@ private:
|
||||
|
||||
void Read(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u64 unk = rp.Pop<u64>();
|
||||
const u64 option = rp.Pop<u64>();
|
||||
const s64 offset = rp.Pop<s64>();
|
||||
const s64 length = rp.Pop<s64>();
|
||||
|
||||
LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length);
|
||||
LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset,
|
||||
length);
|
||||
|
||||
// Error checking
|
||||
if (length < 0) {
|
||||
@@ -148,11 +149,12 @@ private:
|
||||
|
||||
void Write(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u64 unk = rp.Pop<u64>();
|
||||
const u64 option = rp.Pop<u64>();
|
||||
const s64 offset = rp.Pop<s64>();
|
||||
const s64 length = rp.Pop<s64>();
|
||||
|
||||
LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length);
|
||||
LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset,
|
||||
length);
|
||||
|
||||
// Error checking
|
||||
if (length < 0) {
|
||||
@@ -250,10 +252,7 @@ private:
|
||||
u64 next_entry_index = 0;
|
||||
|
||||
void Read(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u64 unk = rp.Pop<u64>();
|
||||
|
||||
LOG_DEBUG(Service_FS, "called, unk=0x{:X}", unk);
|
||||
LOG_DEBUG(Service_FS, "called.");
|
||||
|
||||
// Calculate how many entries we can fit in the output buffer
|
||||
const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry);
|
||||
|
||||
@@ -93,12 +93,18 @@ public:
|
||||
}
|
||||
|
||||
void LoadNrr(Kernel::HLERequestContext& ctx) {
|
||||
struct Parameters {
|
||||
u64_le process_id;
|
||||
u64_le nrr_address;
|
||||
u64_le nrr_size;
|
||||
};
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
rp.Skip(2, false);
|
||||
const VAddr nrr_addr{rp.Pop<VAddr>()};
|
||||
const u64 nrr_size{rp.Pop<u64>()};
|
||||
LOG_DEBUG(Service_LDR, "called with nrr_addr={:016X}, nrr_size={:016X}", nrr_addr,
|
||||
nrr_size);
|
||||
const auto [process_id, nrr_address, nrr_size] = rp.PopRaw<Parameters>();
|
||||
|
||||
LOG_DEBUG(Service_LDR,
|
||||
"called with process_id={:016X}, nrr_address={:016X}, nrr_size={:016X}",
|
||||
process_id, nrr_address, nrr_size);
|
||||
|
||||
if (!initialized) {
|
||||
LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
|
||||
@@ -116,24 +122,26 @@ public:
|
||||
}
|
||||
|
||||
// NRR Address does not fall on 0x1000 byte boundary
|
||||
if (!Common::Is4KBAligned(nrr_addr)) {
|
||||
LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr);
|
||||
if (!Common::Is4KBAligned(nrr_address)) {
|
||||
LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!",
|
||||
nrr_address);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_INVALID_ALIGNMENT);
|
||||
return;
|
||||
}
|
||||
|
||||
// NRR Size is zero or causes overflow
|
||||
if (nrr_addr + nrr_size <= nrr_addr || nrr_size == 0 || !Common::Is4KBAligned(nrr_size)) {
|
||||
if (nrr_address + nrr_size <= nrr_address || nrr_size == 0 ||
|
||||
!Common::Is4KBAligned(nrr_size)) {
|
||||
LOG_ERROR(Service_LDR, "NRR Size is invalid! (nrr_address={:016X}, nrr_size={:016X})",
|
||||
nrr_addr, nrr_size);
|
||||
nrr_address, nrr_size);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_INVALID_SIZE);
|
||||
return;
|
||||
}
|
||||
// Read NRR data from memory
|
||||
std::vector<u8> nrr_data(nrr_size);
|
||||
Memory::ReadBlock(nrr_addr, nrr_data.data(), nrr_size);
|
||||
Memory::ReadBlock(nrr_address, nrr_data.data(), nrr_size);
|
||||
NRRHeader header;
|
||||
std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader));
|
||||
|
||||
@@ -174,7 +182,7 @@ public:
|
||||
hashes.emplace_back(hash);
|
||||
}
|
||||
|
||||
nrr.insert_or_assign(nrr_addr, std::move(hashes));
|
||||
nrr.insert_or_assign(nrr_address, std::move(hashes));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -188,23 +196,30 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
rp.Skip(2, false);
|
||||
const auto nrr_addr{rp.Pop<VAddr>()};
|
||||
LOG_DEBUG(Service_LDR, "called with nrr_addr={:016X}", nrr_addr);
|
||||
struct Parameters {
|
||||
u64_le process_id;
|
||||
u64_le nrr_address;
|
||||
};
|
||||
|
||||
if (!Common::Is4KBAligned(nrr_addr)) {
|
||||
LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr);
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto [process_id, nrr_address] = rp.PopRaw<Parameters>();
|
||||
|
||||
LOG_DEBUG(Service_LDR, "called with process_id={:016X}, nrr_addr={:016X}", process_id,
|
||||
nrr_address);
|
||||
|
||||
if (!Common::Is4KBAligned(nrr_address)) {
|
||||
LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!",
|
||||
nrr_address);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_INVALID_ALIGNMENT);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto iter = nrr.find(nrr_addr);
|
||||
const auto iter = nrr.find(nrr_address);
|
||||
if (iter == nrr.end()) {
|
||||
LOG_ERROR(Service_LDR,
|
||||
"Attempting to unload NRR which has not been loaded! (addr={:016X})",
|
||||
nrr_addr);
|
||||
nrr_address);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_INVALID_NRR_ADDRESS);
|
||||
return;
|
||||
@@ -216,16 +231,22 @@ public:
|
||||
}
|
||||
|
||||
void LoadNro(Kernel::HLERequestContext& ctx) {
|
||||
struct Parameters {
|
||||
u64_le process_id;
|
||||
u64_le image_address;
|
||||
u64_le image_size;
|
||||
u64_le bss_address;
|
||||
u64_le bss_size;
|
||||
};
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
rp.Skip(2, false);
|
||||
const VAddr nro_addr{rp.Pop<VAddr>()};
|
||||
const u64 nro_size{rp.Pop<u64>()};
|
||||
const VAddr bss_addr{rp.Pop<VAddr>()};
|
||||
const u64 bss_size{rp.Pop<u64>()};
|
||||
LOG_DEBUG(
|
||||
Service_LDR,
|
||||
"called with nro_addr={:016X}, nro_size={:016X}, bss_addr={:016X}, bss_size={:016X}",
|
||||
nro_addr, nro_size, bss_addr, bss_size);
|
||||
const auto [process_id, nro_address, nro_size, bss_address, bss_size] =
|
||||
rp.PopRaw<Parameters>();
|
||||
|
||||
LOG_DEBUG(Service_LDR,
|
||||
"called with pid={:016X}, nro_addr={:016X}, nro_size={:016X}, bss_addr={:016X}, "
|
||||
"bss_size={:016X}",
|
||||
process_id, nro_address, nro_size, bss_address, bss_size);
|
||||
|
||||
if (!initialized) {
|
||||
LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
|
||||
@@ -243,8 +264,9 @@ public:
|
||||
}
|
||||
|
||||
// NRO Address does not fall on 0x1000 byte boundary
|
||||
if (!Common::Is4KBAligned(nro_addr)) {
|
||||
LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!", nro_addr);
|
||||
if (!Common::Is4KBAligned(nro_address)) {
|
||||
LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!",
|
||||
nro_address);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_INVALID_ALIGNMENT);
|
||||
return;
|
||||
@@ -252,15 +274,15 @@ public:
|
||||
|
||||
// NRO Size or BSS Size is zero or causes overflow
|
||||
const auto nro_size_valid =
|
||||
nro_size != 0 && nro_addr + nro_size > nro_addr && Common::Is4KBAligned(nro_size);
|
||||
const auto bss_size_valid =
|
||||
nro_size + bss_size >= nro_size && (bss_size == 0 || bss_addr + bss_size > bss_addr);
|
||||
nro_size != 0 && nro_address + nro_size > nro_address && Common::Is4KBAligned(nro_size);
|
||||
const auto bss_size_valid = nro_size + bss_size >= nro_size &&
|
||||
(bss_size == 0 || bss_address + bss_size > bss_address);
|
||||
|
||||
if (!nro_size_valid || !bss_size_valid) {
|
||||
LOG_ERROR(Service_LDR,
|
||||
"NRO Size or BSS Size is invalid! (nro_address={:016X}, nro_size={:016X}, "
|
||||
"bss_address={:016X}, bss_size={:016X})",
|
||||
nro_addr, nro_size, bss_addr, bss_size);
|
||||
nro_address, nro_size, bss_address, bss_size);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_INVALID_SIZE);
|
||||
return;
|
||||
@@ -268,7 +290,7 @@ public:
|
||||
|
||||
// Read NRO data from memory
|
||||
std::vector<u8> nro_data(nro_size);
|
||||
Memory::ReadBlock(nro_addr, nro_data.data(), nro_size);
|
||||
Memory::ReadBlock(nro_address, nro_data.data(), nro_size);
|
||||
|
||||
SHA256Hash hash{};
|
||||
mbedtls_sha256(nro_data.data(), nro_data.size(), hash.data(), 0);
|
||||
@@ -318,17 +340,18 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(vm_manager
|
||||
.MirrorMemory(*map_address, nro_addr, nro_size, Kernel::MemoryState::ModuleCode)
|
||||
.IsSuccess());
|
||||
ASSERT(vm_manager.UnmapRange(nro_addr, nro_size).IsSuccess());
|
||||
ASSERT(
|
||||
vm_manager
|
||||
.MirrorMemory(*map_address, nro_address, nro_size, Kernel::MemoryState::ModuleCode)
|
||||
.IsSuccess());
|
||||
ASSERT(vm_manager.UnmapRange(nro_address, nro_size).IsSuccess());
|
||||
|
||||
if (bss_size > 0) {
|
||||
ASSERT(vm_manager
|
||||
.MirrorMemory(*map_address + nro_size, bss_addr, bss_size,
|
||||
.MirrorMemory(*map_address + nro_size, bss_address, bss_size,
|
||||
Kernel::MemoryState::ModuleCode)
|
||||
.IsSuccess());
|
||||
ASSERT(vm_manager.UnmapRange(bss_addr, bss_size).IsSuccess());
|
||||
ASSERT(vm_manager.UnmapRange(bss_address, bss_size).IsSuccess());
|
||||
}
|
||||
|
||||
vm_manager.ReprotectRange(*map_address, header.text_size,
|
||||
@@ -348,13 +371,6 @@ public:
|
||||
}
|
||||
|
||||
void UnloadNro(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
rp.Skip(2, false);
|
||||
const VAddr mapped_addr{rp.PopRaw<VAddr>()};
|
||||
const VAddr heap_addr{rp.PopRaw<VAddr>()};
|
||||
LOG_DEBUG(Service_LDR, "called with mapped_addr={:016X}, heap_addr={:016X}", mapped_addr,
|
||||
heap_addr);
|
||||
|
||||
if (!initialized) {
|
||||
LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -362,22 +378,30 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Common::Is4KBAligned(mapped_addr) || !Common::Is4KBAligned(heap_addr)) {
|
||||
LOG_ERROR(Service_LDR,
|
||||
"NRO/BSS Address has invalid alignment (actual nro_addr={:016X}, "
|
||||
"bss_addr={:016X})!",
|
||||
mapped_addr, heap_addr);
|
||||
struct Parameters {
|
||||
u64_le process_id;
|
||||
u64_le nro_address;
|
||||
};
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto [process_id, nro_address] = rp.PopRaw<Parameters>();
|
||||
LOG_DEBUG(Service_LDR, "called with process_id={:016X}, nro_address=0x{:016X}", process_id,
|
||||
nro_address);
|
||||
|
||||
if (!Common::Is4KBAligned(nro_address)) {
|
||||
LOG_ERROR(Service_LDR, "NRO address has invalid alignment (nro_address=0x{:016X})",
|
||||
nro_address);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_INVALID_ALIGNMENT);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto iter = nro.find(mapped_addr);
|
||||
const auto iter = nro.find(nro_address);
|
||||
if (iter == nro.end()) {
|
||||
LOG_ERROR(Service_LDR,
|
||||
"The NRO attempting to unmap was not mapped or has an invalid address "
|
||||
"(actual {:016X})!",
|
||||
mapped_addr);
|
||||
"The NRO attempting to be unmapped was not mapped or has an invalid address "
|
||||
"(nro_address=0x{:016X})!",
|
||||
nro_address);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_INVALID_NRO_ADDRESS);
|
||||
return;
|
||||
@@ -386,10 +410,7 @@ public:
|
||||
auto& vm_manager = Core::CurrentProcess()->VMManager();
|
||||
const auto& nro_size = iter->second.size;
|
||||
|
||||
ASSERT(vm_manager
|
||||
.MirrorMemory(heap_addr, mapped_addr, nro_size, Kernel::MemoryState::ModuleCode)
|
||||
.IsSuccess());
|
||||
ASSERT(vm_manager.UnmapRange(mapped_addr, nro_size).IsSuccess());
|
||||
ASSERT(vm_manager.UnmapRange(nro_address, nro_size).IsSuccess());
|
||||
|
||||
Core::System::GetInstance().InvalidateCpuInstructionCaches();
|
||||
|
||||
@@ -459,11 +480,10 @@ private:
|
||||
std::map<VAddr, NROInfo> nro;
|
||||
std::map<VAddr, std::vector<SHA256Hash>> nrr;
|
||||
|
||||
bool IsValidNROHash(const SHA256Hash& hash) {
|
||||
return std::any_of(
|
||||
nrr.begin(), nrr.end(), [&hash](const std::pair<VAddr, std::vector<SHA256Hash>>& p) {
|
||||
return std::find(p.second.begin(), p.second.end(), hash) != p.second.end();
|
||||
});
|
||||
bool IsValidNROHash(const SHA256Hash& hash) const {
|
||||
return std::any_of(nrr.begin(), nrr.end(), [&hash](const auto& p) {
|
||||
return std::find(p.second.begin(), p.second.end(), hash) != p.second.end();
|
||||
});
|
||||
}
|
||||
|
||||
static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) {
|
||||
|
||||
@@ -21,12 +21,13 @@
|
||||
#include "core/hle/service/vi/display/vi_display.h"
|
||||
#include "core/hle/service/vi/layer/vi_layer.h"
|
||||
#include "core/perf_stats.h"
|
||||
#include "core/settings.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
|
||||
constexpr std::size_t SCREEN_REFRESH_RATE = 60;
|
||||
constexpr s64 frame_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE);
|
||||
constexpr s64 frame_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60);
|
||||
constexpr s64 frame_ticks_30fps = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 30);
|
||||
|
||||
NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_timing} {
|
||||
displays.emplace_back(0, "Default");
|
||||
@@ -36,13 +37,15 @@ NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_t
|
||||
displays.emplace_back(4, "Null");
|
||||
|
||||
// Schedule the screen composition events
|
||||
composition_event =
|
||||
core_timing.RegisterEvent("ScreenComposition", [this](u64 userdata, s64 cycles_late) {
|
||||
const auto ticks = Settings::values.force_30fps_mode ? frame_ticks_30fps : frame_ticks;
|
||||
|
||||
composition_event = core_timing.RegisterEvent(
|
||||
"ScreenComposition", [this, ticks](u64 userdata, s64 cycles_late) {
|
||||
Compose();
|
||||
this->core_timing.ScheduleEvent(frame_ticks - cycles_late, composition_event);
|
||||
this->core_timing.ScheduleEvent(ticks - cycles_late, composition_event);
|
||||
});
|
||||
|
||||
core_timing.ScheduleEvent(frame_ticks, composition_event);
|
||||
core_timing.ScheduleEvent(ticks, composition_event);
|
||||
}
|
||||
|
||||
NVFlinger::~NVFlinger() {
|
||||
@@ -62,6 +65,7 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
|
||||
const auto itr =
|
||||
std::find_if(displays.begin(), displays.end(),
|
||||
[&](const VI::Display& display) { return display.GetName() == name; });
|
||||
|
||||
if (itr == displays.end()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -393,6 +393,7 @@ struct Values {
|
||||
bool use_disk_shader_cache;
|
||||
bool use_accurate_gpu_emulation;
|
||||
bool use_asynchronous_gpu_emulation;
|
||||
bool force_30fps_mode;
|
||||
|
||||
float bg_red;
|
||||
float bg_green;
|
||||
|
||||
@@ -106,6 +106,8 @@ add_library(video_core STATIC
|
||||
textures/decoders.cpp
|
||||
textures/decoders.h
|
||||
textures/texture.h
|
||||
texture_cache.cpp
|
||||
texture_cache.h
|
||||
video_core.cpp
|
||||
video_core.h
|
||||
)
|
||||
@@ -127,12 +129,14 @@ if (ENABLE_VULKAN)
|
||||
renderer_vulkan/vk_sampler_cache.h
|
||||
renderer_vulkan/vk_scheduler.cpp
|
||||
renderer_vulkan/vk_scheduler.h
|
||||
renderer_vulkan/vk_shader_decompiler.cpp
|
||||
renderer_vulkan/vk_shader_decompiler.h
|
||||
renderer_vulkan/vk_stream_buffer.cpp
|
||||
renderer_vulkan/vk_stream_buffer.h
|
||||
renderer_vulkan/vk_swapchain.cpp
|
||||
renderer_vulkan/vk_swapchain.h)
|
||||
|
||||
target_include_directories(video_core PRIVATE ../../externals/Vulkan-Headers/include)
|
||||
target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include)
|
||||
target_compile_definitions(video_core PRIVATE HAS_VULKAN)
|
||||
endif()
|
||||
|
||||
@@ -140,3 +144,6 @@ create_target_directory_groups(video_core)
|
||||
|
||||
target_link_libraries(video_core PUBLIC common core)
|
||||
target_link_libraries(video_core PRIVATE glad)
|
||||
if (ENABLE_VULKAN)
|
||||
target_link_libraries(video_core PRIVATE sirit)
|
||||
endif()
|
||||
|
||||
@@ -917,21 +917,34 @@ union Instruction {
|
||||
} iset;
|
||||
|
||||
union {
|
||||
BitField<8, 2, Register::Size> dest_size;
|
||||
BitField<10, 2, Register::Size> src_size;
|
||||
BitField<12, 1, u64> is_output_signed;
|
||||
BitField<13, 1, u64> is_input_signed;
|
||||
BitField<41, 2, u64> selector;
|
||||
BitField<41, 2, u64> selector; // i2i and i2f only
|
||||
BitField<45, 1, u64> negate_a;
|
||||
BitField<49, 1, u64> abs_a;
|
||||
BitField<10, 2, Register::Size> src_size;
|
||||
BitField<13, 1, u64> is_input_signed;
|
||||
BitField<8, 2, Register::Size> dst_size;
|
||||
BitField<12, 1, u64> is_output_signed;
|
||||
|
||||
union {
|
||||
BitField<39, 2, u64> tab5cb8_2;
|
||||
} i2f;
|
||||
|
||||
union {
|
||||
BitField<39, 2, F2iRoundingOp> rounding;
|
||||
} f2i;
|
||||
|
||||
union {
|
||||
BitField<39, 4, F2fRoundingOp> rounding;
|
||||
BitField<8, 2, Register::Size> src_size;
|
||||
BitField<10, 2, Register::Size> dst_size;
|
||||
BitField<39, 4, u64> rounding;
|
||||
// H0, H1 extract for F16 missing
|
||||
BitField<41, 1, u64> selector; // Guessed as some games set it, TODO: reverse this value
|
||||
F2fRoundingOp GetRoundingMode() const {
|
||||
constexpr u64 rounding_mask = 0x0B;
|
||||
return static_cast<F2fRoundingOp>(rounding.Value() & rounding_mask);
|
||||
}
|
||||
} f2f;
|
||||
|
||||
} conversion;
|
||||
|
||||
union {
|
||||
@@ -1678,7 +1691,7 @@ private:
|
||||
INST("0011100-00101---", Id::SHR_IMM, Type::Shift, "SHR_IMM"),
|
||||
INST("0100110011100---", Id::I2I_C, Type::Conversion, "I2I_C"),
|
||||
INST("0101110011100---", Id::I2I_R, Type::Conversion, "I2I_R"),
|
||||
INST("01110001-1000---", Id::I2I_IMM, Type::Conversion, "I2I_IMM"),
|
||||
INST("0011101-11100---", Id::I2I_IMM, Type::Conversion, "I2I_IMM"),
|
||||
INST("0100110010111---", Id::I2F_C, Type::Conversion, "I2F_C"),
|
||||
INST("0101110010111---", Id::I2F_R, Type::Conversion, "I2F_R"),
|
||||
INST("0011100-10111---", Id::I2F_IMM, Type::Conversion, "I2F_IMM"),
|
||||
|
||||
@@ -112,11 +112,26 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
|
||||
params.pixel_format = PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(),
|
||||
params.srgb_conversion);
|
||||
|
||||
if (params.pixel_format == PixelFormat::R16U && config.tsc.depth_compare_enabled) {
|
||||
if (config.tsc.depth_compare_enabled) {
|
||||
// Some titles create a 'R16U' (normalized 16-bit) texture with depth_compare enabled,
|
||||
// then attempt to sample from it via a shadow sampler. Convert format to Z16 (which also
|
||||
// causes GetFormatType to properly return 'Depth' below).
|
||||
params.pixel_format = PixelFormat::Z16;
|
||||
if (GetFormatType(params.pixel_format) == SurfaceType::ColorTexture) {
|
||||
switch (params.pixel_format) {
|
||||
case PixelFormat::R16S:
|
||||
case PixelFormat::R16U:
|
||||
case PixelFormat::R16F:
|
||||
params.pixel_format = PixelFormat::Z16;
|
||||
break;
|
||||
case PixelFormat::R32F:
|
||||
params.pixel_format = PixelFormat::Z32F;
|
||||
break;
|
||||
default:
|
||||
LOG_WARNING(HW_GPU, "Color texture format being used with depth compare: {}",
|
||||
static_cast<u32>(params.pixel_format));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value());
|
||||
@@ -266,6 +281,10 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
|
||||
params.component_type = ComponentTypeFromRenderTarget(config.format);
|
||||
params.type = GetFormatType(params.pixel_format);
|
||||
params.width = config.width;
|
||||
if (!params.is_tiled) {
|
||||
const u32 bpp = params.GetFormatBpp() / 8;
|
||||
params.pitch = config.width * bpp;
|
||||
}
|
||||
params.height = config.height;
|
||||
params.unaligned_height = config.height;
|
||||
params.target = SurfaceTarget::Texture2D;
|
||||
@@ -1175,10 +1194,16 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
|
||||
return new_surface;
|
||||
}
|
||||
|
||||
const bool old_compressed =
|
||||
GetFormatTuple(old_params.pixel_format, old_params.component_type).compressed;
|
||||
const bool new_compressed =
|
||||
GetFormatTuple(new_params.pixel_format, new_params.component_type).compressed;
|
||||
const bool compatible_formats =
|
||||
GetFormatBpp(old_params.pixel_format) == GetFormatBpp(new_params.pixel_format) &&
|
||||
!(old_compressed || new_compressed);
|
||||
// For compatible surfaces, we can just do fast glCopyImageSubData based copy
|
||||
if (old_params.target == new_params.target && old_params.type == new_params.type &&
|
||||
old_params.depth == new_params.depth && old_params.depth == 1 &&
|
||||
GetFormatBpp(old_params.pixel_format) == GetFormatBpp(new_params.pixel_format)) {
|
||||
if (old_params.target == new_params.target && old_params.depth == new_params.depth &&
|
||||
old_params.depth == 1 && compatible_formats) {
|
||||
FastCopySurface(old_surface, new_surface);
|
||||
return new_surface;
|
||||
}
|
||||
@@ -1193,7 +1218,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
|
||||
case SurfaceTarget::TextureCubemap:
|
||||
case SurfaceTarget::Texture2DArray:
|
||||
case SurfaceTarget::TextureCubeArray:
|
||||
if (old_params.pixel_format == new_params.pixel_format)
|
||||
if (compatible_formats)
|
||||
FastLayeredCopySurface(old_surface, new_surface);
|
||||
else {
|
||||
AccurateCopySurface(old_surface, new_surface);
|
||||
|
||||
1379
src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
Normal file
1379
src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
Normal file
File diff suppressed because it is too large
Load Diff
80
src/video_core/renderer_vulkan/vk_shader_decompiler.h
Normal file
80
src/video_core/renderer_vulkan/vk_shader_decompiler.h
Normal file
@@ -0,0 +1,80 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <sirit/sirit.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
class ShaderIR;
|
||||
}
|
||||
|
||||
namespace Vulkan::VKShader {
|
||||
|
||||
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
||||
|
||||
using SamplerEntry = VideoCommon::Shader::Sampler;
|
||||
|
||||
constexpr u32 DESCRIPTOR_SET = 0;
|
||||
|
||||
class ConstBufferEntry : public VideoCommon::Shader::ConstBuffer {
|
||||
public:
|
||||
explicit constexpr ConstBufferEntry(const VideoCommon::Shader::ConstBuffer& entry, u32 index)
|
||||
: VideoCommon::Shader::ConstBuffer{entry}, index{index} {}
|
||||
|
||||
constexpr u32 GetIndex() const {
|
||||
return index;
|
||||
}
|
||||
|
||||
private:
|
||||
u32 index{};
|
||||
};
|
||||
|
||||
class GlobalBufferEntry {
|
||||
public:
|
||||
explicit GlobalBufferEntry(u32 cbuf_index, u32 cbuf_offset)
|
||||
: cbuf_index{cbuf_index}, cbuf_offset{cbuf_offset} {}
|
||||
|
||||
u32 GetCbufIndex() const {
|
||||
return cbuf_index;
|
||||
}
|
||||
|
||||
u32 GetCbufOffset() const {
|
||||
return cbuf_offset;
|
||||
}
|
||||
|
||||
private:
|
||||
u32 cbuf_index{};
|
||||
u32 cbuf_offset{};
|
||||
};
|
||||
|
||||
struct ShaderEntries {
|
||||
u32 const_buffers_base_binding{};
|
||||
u32 global_buffers_base_binding{};
|
||||
u32 samplers_base_binding{};
|
||||
std::vector<ConstBufferEntry> const_buffers;
|
||||
std::vector<GlobalBufferEntry> global_buffers;
|
||||
std::vector<SamplerEntry> samplers;
|
||||
std::set<u32> attributes;
|
||||
std::array<bool, Maxwell::NumClipDistances> clip_distances{};
|
||||
std::size_t shader_length{};
|
||||
Sirit::Id entry_function{};
|
||||
std::vector<Sirit::Id> interfaces;
|
||||
};
|
||||
|
||||
using DecompilerResult = std::pair<std::unique_ptr<Sirit::Module>, ShaderEntries>;
|
||||
|
||||
DecompilerResult Decompile(const VideoCommon::Shader::ShaderIR& ir, Maxwell::ShaderStage stage);
|
||||
|
||||
} // namespace Vulkan::VKShader
|
||||
@@ -18,13 +18,29 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) {
|
||||
const auto opcode = OpCode::Decode(instr);
|
||||
|
||||
switch (opcode->get().GetId()) {
|
||||
case OpCode::Id::I2I_R: {
|
||||
case OpCode::Id::I2I_R:
|
||||
case OpCode::Id::I2I_C:
|
||||
case OpCode::Id::I2I_IMM: {
|
||||
UNIMPLEMENTED_IF(instr.conversion.selector);
|
||||
UNIMPLEMENTED_IF(instr.conversion.dst_size != Register::Size::Word);
|
||||
UNIMPLEMENTED_IF(instr.alu.saturate_d);
|
||||
|
||||
const bool input_signed = instr.conversion.is_input_signed;
|
||||
const bool output_signed = instr.conversion.is_output_signed;
|
||||
|
||||
Node value = GetRegister(instr.gpr20);
|
||||
Node value = [&]() {
|
||||
switch (opcode->get().GetId()) {
|
||||
case OpCode::Id::I2I_R:
|
||||
return GetRegister(instr.gpr20);
|
||||
case OpCode::Id::I2I_C:
|
||||
return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset());
|
||||
case OpCode::Id::I2I_IMM:
|
||||
return Immediate(instr.alu.GetSignedImm20_20());
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return Immediate(0);
|
||||
}
|
||||
}();
|
||||
value = ConvertIntegerSize(value, instr.conversion.src_size, input_signed);
|
||||
|
||||
value = GetOperandAbsNegInteger(value, instr.conversion.abs_a, instr.conversion.negate_a,
|
||||
@@ -38,17 +54,24 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) {
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::I2F_R:
|
||||
case OpCode::Id::I2F_C: {
|
||||
UNIMPLEMENTED_IF(instr.conversion.dest_size != Register::Size::Word);
|
||||
case OpCode::Id::I2F_C:
|
||||
case OpCode::Id::I2F_IMM: {
|
||||
UNIMPLEMENTED_IF(instr.conversion.dst_size != Register::Size::Word);
|
||||
UNIMPLEMENTED_IF(instr.conversion.selector);
|
||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
||||
"Condition codes generation in I2F is not implemented");
|
||||
|
||||
Node value = [&]() {
|
||||
if (instr.is_b_gpr) {
|
||||
switch (opcode->get().GetId()) {
|
||||
case OpCode::Id::I2F_R:
|
||||
return GetRegister(instr.gpr20);
|
||||
} else {
|
||||
case OpCode::Id::I2F_C:
|
||||
return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset());
|
||||
case OpCode::Id::I2F_IMM:
|
||||
return Immediate(instr.alu.GetSignedImm20_20());
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return Immediate(0);
|
||||
}
|
||||
}();
|
||||
const bool input_signed = instr.conversion.is_input_signed;
|
||||
@@ -62,24 +85,31 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) {
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::F2F_R:
|
||||
case OpCode::Id::F2F_C: {
|
||||
UNIMPLEMENTED_IF(instr.conversion.dest_size != Register::Size::Word);
|
||||
UNIMPLEMENTED_IF(instr.conversion.src_size != Register::Size::Word);
|
||||
case OpCode::Id::F2F_C:
|
||||
case OpCode::Id::F2F_IMM: {
|
||||
UNIMPLEMENTED_IF(instr.conversion.f2f.dst_size != Register::Size::Word);
|
||||
UNIMPLEMENTED_IF(instr.conversion.f2f.src_size != Register::Size::Word);
|
||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
||||
"Condition codes generation in F2F is not implemented");
|
||||
|
||||
Node value = [&]() {
|
||||
if (instr.is_b_gpr) {
|
||||
switch (opcode->get().GetId()) {
|
||||
case OpCode::Id::F2F_R:
|
||||
return GetRegister(instr.gpr20);
|
||||
} else {
|
||||
case OpCode::Id::F2F_C:
|
||||
return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset());
|
||||
case OpCode::Id::F2F_IMM:
|
||||
return GetImmediate19(instr);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return Immediate(0);
|
||||
}
|
||||
}();
|
||||
|
||||
value = GetOperandAbsNegFloat(value, instr.conversion.abs_a, instr.conversion.negate_a);
|
||||
|
||||
value = [&]() {
|
||||
switch (instr.conversion.f2f.rounding) {
|
||||
switch (instr.conversion.f2f.GetRoundingMode()) {
|
||||
case Tegra::Shader::F2fRoundingOp::None:
|
||||
return value;
|
||||
case Tegra::Shader::F2fRoundingOp::Round:
|
||||
@@ -102,15 +132,22 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) {
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::F2I_R:
|
||||
case OpCode::Id::F2I_C: {
|
||||
case OpCode::Id::F2I_C:
|
||||
case OpCode::Id::F2I_IMM: {
|
||||
UNIMPLEMENTED_IF(instr.conversion.src_size != Register::Size::Word);
|
||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
||||
"Condition codes generation in F2I is not implemented");
|
||||
Node value = [&]() {
|
||||
if (instr.is_b_gpr) {
|
||||
switch (opcode->get().GetId()) {
|
||||
case OpCode::Id::F2I_R:
|
||||
return GetRegister(instr.gpr20);
|
||||
} else {
|
||||
case OpCode::Id::F2I_C:
|
||||
return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset());
|
||||
case OpCode::Id::F2I_IMM:
|
||||
return GetImmediate19(instr);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return Immediate(0);
|
||||
}
|
||||
}();
|
||||
|
||||
@@ -134,7 +171,7 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) {
|
||||
}();
|
||||
const bool is_signed = instr.conversion.is_output_signed;
|
||||
value = SignedOperation(OperationCode::ICastFloat, is_signed, PRECISE, value);
|
||||
value = ConvertIntegerSize(value, instr.conversion.dest_size, is_signed);
|
||||
value = ConvertIntegerSize(value, instr.conversion.dst_size, is_signed);
|
||||
|
||||
SetRegister(bb, instr.gpr0, value);
|
||||
break;
|
||||
|
||||
@@ -294,6 +294,8 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
|
||||
return PixelFormat::Z16;
|
||||
case Tegra::Texture::TextureFormat::Z24S8:
|
||||
return PixelFormat::Z24S8;
|
||||
case Tegra::Texture::TextureFormat::ZF32_X24S8:
|
||||
return PixelFormat::Z32FS8;
|
||||
case Tegra::Texture::TextureFormat::DXT1:
|
||||
return is_srgb ? PixelFormat::DXT1_SRGB : PixelFormat::DXT1;
|
||||
case Tegra::Texture::TextureFormat::DXT23:
|
||||
|
||||
386
src/video_core/texture_cache.cpp
Normal file
386
src/video_core/texture_cache.cpp
Normal file
@@ -0,0 +1,386 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/cityhash.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/core.h"
|
||||
#include "video_core/surface.h"
|
||||
#include "video_core/texture_cache.h"
|
||||
#include "video_core/textures/decoders.h"
|
||||
#include "video_core/textures/texture.h"
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
using VideoCore::Surface::SurfaceTarget;
|
||||
|
||||
using VideoCore::Surface::ComponentTypeFromDepthFormat;
|
||||
using VideoCore::Surface::ComponentTypeFromRenderTarget;
|
||||
using VideoCore::Surface::ComponentTypeFromTexture;
|
||||
using VideoCore::Surface::PixelFormatFromDepthFormat;
|
||||
using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
|
||||
using VideoCore::Surface::PixelFormatFromTextureFormat;
|
||||
using VideoCore::Surface::SurfaceTargetFromTextureType;
|
||||
|
||||
constexpr u32 GetMipmapSize(bool uncompressed, u32 mip_size, u32 tile) {
|
||||
return uncompressed ? mip_size : std::max(1U, (mip_size + tile - 1) / tile);
|
||||
}
|
||||
|
||||
SurfaceParams SurfaceParams::CreateForTexture(Core::System& system,
|
||||
const Tegra::Texture::FullTextureInfo& config) {
|
||||
SurfaceParams params;
|
||||
params.is_tiled = config.tic.IsTiled();
|
||||
params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0,
|
||||
params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0,
|
||||
params.block_depth = params.is_tiled ? config.tic.BlockDepth() : 0,
|
||||
params.tile_width_spacing = params.is_tiled ? (1 << config.tic.tile_width_spacing.Value()) : 1;
|
||||
params.pixel_format =
|
||||
PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(), false);
|
||||
params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value());
|
||||
params.type = GetFormatType(params.pixel_format);
|
||||
params.target = SurfaceTargetFromTextureType(config.tic.texture_type);
|
||||
params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format));
|
||||
params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format));
|
||||
params.depth = config.tic.Depth();
|
||||
if (params.target == SurfaceTarget::TextureCubemap ||
|
||||
params.target == SurfaceTarget::TextureCubeArray) {
|
||||
params.depth *= 6;
|
||||
}
|
||||
params.pitch = params.is_tiled ? 0 : config.tic.Pitch();
|
||||
params.unaligned_height = config.tic.Height();
|
||||
params.num_levels = config.tic.max_mip_level + 1;
|
||||
|
||||
params.CalculateCachedValues();
|
||||
return params;
|
||||
}
|
||||
|
||||
SurfaceParams SurfaceParams::CreateForDepthBuffer(
|
||||
Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format,
|
||||
u32 block_width, u32 block_height, u32 block_depth,
|
||||
Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) {
|
||||
SurfaceParams params;
|
||||
params.is_tiled = type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
|
||||
params.block_width = 1 << std::min(block_width, 5U);
|
||||
params.block_height = 1 << std::min(block_height, 5U);
|
||||
params.block_depth = 1 << std::min(block_depth, 5U);
|
||||
params.tile_width_spacing = 1;
|
||||
params.pixel_format = PixelFormatFromDepthFormat(format);
|
||||
params.component_type = ComponentTypeFromDepthFormat(format);
|
||||
params.type = GetFormatType(params.pixel_format);
|
||||
params.width = zeta_width;
|
||||
params.height = zeta_height;
|
||||
params.unaligned_height = zeta_height;
|
||||
params.target = SurfaceTarget::Texture2D;
|
||||
params.depth = 1;
|
||||
params.num_levels = 1;
|
||||
|
||||
params.CalculateCachedValues();
|
||||
return params;
|
||||
}
|
||||
|
||||
SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::size_t index) {
|
||||
const auto& config{system.GPU().Maxwell3D().regs.rt[index]};
|
||||
SurfaceParams params;
|
||||
params.is_tiled =
|
||||
config.memory_layout.type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
|
||||
params.block_width = 1 << config.memory_layout.block_width;
|
||||
params.block_height = 1 << config.memory_layout.block_height;
|
||||
params.block_depth = 1 << config.memory_layout.block_depth;
|
||||
params.tile_width_spacing = 1;
|
||||
params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
|
||||
params.component_type = ComponentTypeFromRenderTarget(config.format);
|
||||
params.type = GetFormatType(params.pixel_format);
|
||||
if (params.is_tiled) {
|
||||
params.width = config.width;
|
||||
} else {
|
||||
const u32 bpp = GetFormatBpp(params.pixel_format) / CHAR_BIT;
|
||||
params.pitch = config.width;
|
||||
params.width = params.pitch / bpp;
|
||||
}
|
||||
params.height = config.height;
|
||||
params.depth = 1;
|
||||
params.unaligned_height = config.height;
|
||||
params.target = SurfaceTarget::Texture2D;
|
||||
params.num_levels = 1;
|
||||
|
||||
params.CalculateCachedValues();
|
||||
return params;
|
||||
}
|
||||
|
||||
SurfaceParams SurfaceParams::CreateForFermiCopySurface(
|
||||
const Tegra::Engines::Fermi2D::Regs::Surface& config) {
|
||||
SurfaceParams params{};
|
||||
params.is_tiled = !config.linear;
|
||||
params.block_width = params.is_tiled ? std::min(config.BlockWidth(), 32U) : 0,
|
||||
params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0,
|
||||
params.block_depth = params.is_tiled ? std::min(config.BlockDepth(), 32U) : 0,
|
||||
params.tile_width_spacing = 1;
|
||||
params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
|
||||
params.component_type = ComponentTypeFromRenderTarget(config.format);
|
||||
params.type = GetFormatType(params.pixel_format);
|
||||
params.width = config.width;
|
||||
params.height = config.height;
|
||||
params.unaligned_height = config.height;
|
||||
// TODO(Rodrigo): Try to guess the surface target from depth and layer parameters
|
||||
params.target = SurfaceTarget::Texture2D;
|
||||
params.depth = 1;
|
||||
params.num_levels = 1;
|
||||
|
||||
params.CalculateCachedValues();
|
||||
return params;
|
||||
}
|
||||
|
||||
u32 SurfaceParams::GetMipWidth(u32 level) const {
|
||||
return std::max(1U, width >> level);
|
||||
}
|
||||
|
||||
u32 SurfaceParams::GetMipHeight(u32 level) const {
|
||||
return std::max(1U, height >> level);
|
||||
}
|
||||
|
||||
u32 SurfaceParams::GetMipDepth(u32 level) const {
|
||||
return IsLayered() ? depth : std::max(1U, depth >> level);
|
||||
}
|
||||
|
||||
bool SurfaceParams::IsLayered() const {
|
||||
switch (target) {
|
||||
case SurfaceTarget::Texture1DArray:
|
||||
case SurfaceTarget::Texture2DArray:
|
||||
case SurfaceTarget::TextureCubeArray:
|
||||
case SurfaceTarget::TextureCubemap:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
u32 SurfaceParams::GetMipBlockHeight(u32 level) const {
|
||||
// Auto block resizing algorithm from:
|
||||
// https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nv50/nv50_miptree.c
|
||||
if (level == 0) {
|
||||
return block_height;
|
||||
}
|
||||
const u32 height{GetMipHeight(level)};
|
||||
const u32 default_block_height{GetDefaultBlockHeight(pixel_format)};
|
||||
const u32 blocks_in_y{(height + default_block_height - 1) / default_block_height};
|
||||
u32 block_height = 16;
|
||||
while (block_height > 1 && blocks_in_y <= block_height * 4) {
|
||||
block_height >>= 1;
|
||||
}
|
||||
return block_height;
|
||||
}
|
||||
|
||||
u32 SurfaceParams::GetMipBlockDepth(u32 level) const {
|
||||
if (level == 0)
|
||||
return block_depth;
|
||||
if (target != SurfaceTarget::Texture3D)
|
||||
return 1;
|
||||
|
||||
const u32 depth{GetMipDepth(level)};
|
||||
u32 block_depth = 32;
|
||||
while (block_depth > 1 && depth * 2 <= block_depth) {
|
||||
block_depth >>= 1;
|
||||
}
|
||||
if (block_depth == 32 && GetMipBlockHeight(level) >= 4) {
|
||||
return 16;
|
||||
}
|
||||
return block_depth;
|
||||
}
|
||||
|
||||
std::size_t SurfaceParams::GetGuestMipmapLevelOffset(u32 level) const {
|
||||
std::size_t offset = 0;
|
||||
for (u32 i = 0; i < level; i++) {
|
||||
offset += GetInnerMipmapMemorySize(i, false, IsLayered(), false);
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
std::size_t SurfaceParams::GetHostMipmapLevelOffset(u32 level) const {
|
||||
std::size_t offset = 0;
|
||||
for (u32 i = 0; i < level; i++) {
|
||||
offset += GetInnerMipmapMemorySize(i, true, false, false);
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
std::size_t SurfaceParams::GetGuestLayerSize() const {
|
||||
return GetInnerMemorySize(false, true, false);
|
||||
}
|
||||
|
||||
std::size_t SurfaceParams::GetHostLayerSize(u32 level) const {
|
||||
return GetInnerMipmapMemorySize(level, true, IsLayered(), false);
|
||||
}
|
||||
|
||||
bool SurfaceParams::IsFamiliar(const SurfaceParams& view_params) const {
|
||||
if (std::tie(is_tiled, tile_width_spacing, pixel_format, component_type, type) !=
|
||||
std::tie(view_params.is_tiled, view_params.tile_width_spacing, view_params.pixel_format,
|
||||
view_params.component_type, view_params.type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const SurfaceTarget view_target{view_params.target};
|
||||
if (view_target == target) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (target) {
|
||||
case SurfaceTarget::Texture1D:
|
||||
case SurfaceTarget::Texture2D:
|
||||
case SurfaceTarget::Texture3D:
|
||||
return false;
|
||||
case SurfaceTarget::Texture1DArray:
|
||||
return view_target == SurfaceTarget::Texture1D;
|
||||
case SurfaceTarget::Texture2DArray:
|
||||
return view_target == SurfaceTarget::Texture2D;
|
||||
case SurfaceTarget::TextureCubemap:
|
||||
return view_target == SurfaceTarget::Texture2D ||
|
||||
view_target == SurfaceTarget::Texture2DArray;
|
||||
case SurfaceTarget::TextureCubeArray:
|
||||
return view_target == SurfaceTarget::Texture2D ||
|
||||
view_target == SurfaceTarget::Texture2DArray ||
|
||||
view_target == SurfaceTarget::TextureCubemap;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented texture family={}", static_cast<u32>(target));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool SurfaceParams::IsPixelFormatZeta() const {
|
||||
return pixel_format >= VideoCore::Surface::PixelFormat::MaxColorFormat &&
|
||||
pixel_format < VideoCore::Surface::PixelFormat::MaxDepthStencilFormat;
|
||||
}
|
||||
|
||||
void SurfaceParams::CalculateCachedValues() {
|
||||
guest_size_in_bytes = GetInnerMemorySize(false, false, false);
|
||||
|
||||
// ASTC is uncompressed in software, in emulated as RGBA8
|
||||
if (IsPixelFormatASTC(pixel_format)) {
|
||||
host_size_in_bytes = width * height * depth * 4;
|
||||
} else {
|
||||
host_size_in_bytes = GetInnerMemorySize(true, false, false);
|
||||
}
|
||||
|
||||
switch (target) {
|
||||
case SurfaceTarget::Texture1D:
|
||||
case SurfaceTarget::Texture2D:
|
||||
case SurfaceTarget::Texture3D:
|
||||
num_layers = 1;
|
||||
break;
|
||||
case SurfaceTarget::Texture1DArray:
|
||||
case SurfaceTarget::Texture2DArray:
|
||||
case SurfaceTarget::TextureCubemap:
|
||||
case SurfaceTarget::TextureCubeArray:
|
||||
num_layers = depth;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t SurfaceParams::GetInnerMipmapMemorySize(u32 level, bool as_host_size, bool layer_only,
|
||||
bool uncompressed) const {
|
||||
const bool tiled{as_host_size ? false : is_tiled};
|
||||
const u32 tile_x{GetDefaultBlockWidth(pixel_format)};
|
||||
const u32 tile_y{GetDefaultBlockHeight(pixel_format)};
|
||||
const u32 width{GetMipmapSize(uncompressed, GetMipWidth(level), tile_x)};
|
||||
const u32 height{GetMipmapSize(uncompressed, GetMipHeight(level), tile_y)};
|
||||
const u32 depth{layer_only ? 1U : GetMipDepth(level)};
|
||||
return Tegra::Texture::CalculateSize(tiled, GetBytesPerPixel(pixel_format), width, height,
|
||||
depth, GetMipBlockHeight(level), GetMipBlockDepth(level));
|
||||
}
|
||||
|
||||
std::size_t SurfaceParams::GetInnerMemorySize(bool as_host_size, bool layer_only,
|
||||
bool uncompressed) const {
|
||||
std::size_t size = 0;
|
||||
for (u32 level = 0; level < num_levels; ++level) {
|
||||
size += GetInnerMipmapMemorySize(level, as_host_size, layer_only, uncompressed);
|
||||
}
|
||||
if (!as_host_size && is_tiled) {
|
||||
size = Common::AlignUp(size, Tegra::Texture::GetGOBSize() * block_height * block_depth);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
std::map<u64, std::pair<u32, u32>> SurfaceParams::CreateViewOffsetMap() const {
|
||||
std::map<u64, std::pair<u32, u32>> view_offset_map;
|
||||
switch (target) {
|
||||
case SurfaceTarget::Texture1D:
|
||||
case SurfaceTarget::Texture2D:
|
||||
case SurfaceTarget::Texture3D: {
|
||||
constexpr u32 layer = 0;
|
||||
for (u32 level = 0; level < num_levels; ++level) {
|
||||
const std::size_t offset{GetGuestMipmapLevelOffset(level)};
|
||||
view_offset_map.insert({offset, {layer, level}});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SurfaceTarget::Texture1DArray:
|
||||
case SurfaceTarget::Texture2DArray:
|
||||
case SurfaceTarget::TextureCubemap:
|
||||
case SurfaceTarget::TextureCubeArray: {
|
||||
const std::size_t layer_size{GetGuestLayerSize()};
|
||||
for (u32 level = 0; level < num_levels; ++level) {
|
||||
const std::size_t level_offset{GetGuestMipmapLevelOffset(level)};
|
||||
for (u32 layer = 0; layer < num_layers; ++layer) {
|
||||
const auto layer_offset{static_cast<std::size_t>(layer_size * layer)};
|
||||
const std::size_t offset{level_offset + layer_offset};
|
||||
view_offset_map.insert({offset, {layer, level}});
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented surface target {}", static_cast<u32>(target));
|
||||
}
|
||||
return view_offset_map;
|
||||
}
|
||||
|
||||
bool SurfaceParams::IsViewValid(const SurfaceParams& view_params, u32 layer, u32 level) const {
|
||||
return IsDimensionValid(view_params, level) && IsDepthValid(view_params, level) &&
|
||||
IsInBounds(view_params, layer, level);
|
||||
}
|
||||
|
||||
bool SurfaceParams::IsDimensionValid(const SurfaceParams& view_params, u32 level) const {
|
||||
return view_params.width == GetMipWidth(level) && view_params.height == GetMipHeight(level);
|
||||
}
|
||||
|
||||
bool SurfaceParams::IsDepthValid(const SurfaceParams& view_params, u32 level) const {
|
||||
if (view_params.target != SurfaceTarget::Texture3D) {
|
||||
return true;
|
||||
}
|
||||
return view_params.depth == GetMipDepth(level);
|
||||
}
|
||||
|
||||
bool SurfaceParams::IsInBounds(const SurfaceParams& view_params, u32 layer, u32 level) const {
|
||||
return layer + view_params.num_layers <= num_layers &&
|
||||
level + view_params.num_levels <= num_levels;
|
||||
}
|
||||
|
||||
std::size_t HasheableSurfaceParams::Hash() const {
|
||||
return static_cast<std::size_t>(
|
||||
Common::CityHash64(reinterpret_cast<const char*>(this), sizeof(*this)));
|
||||
}
|
||||
|
||||
bool HasheableSurfaceParams::operator==(const HasheableSurfaceParams& rhs) const {
|
||||
return std::tie(is_tiled, block_width, block_height, block_depth, tile_width_spacing, width,
|
||||
height, depth, pitch, unaligned_height, num_levels, pixel_format,
|
||||
component_type, type, target) ==
|
||||
std::tie(rhs.is_tiled, rhs.block_width, rhs.block_height, rhs.block_depth,
|
||||
rhs.tile_width_spacing, rhs.width, rhs.height, rhs.depth, rhs.pitch,
|
||||
rhs.unaligned_height, rhs.num_levels, rhs.pixel_format, rhs.component_type,
|
||||
rhs.type, rhs.target);
|
||||
}
|
||||
|
||||
std::size_t ViewKey::Hash() const {
|
||||
return static_cast<std::size_t>(
|
||||
Common::CityHash64(reinterpret_cast<const char*>(this), sizeof(*this)));
|
||||
}
|
||||
|
||||
bool ViewKey::operator==(const ViewKey& rhs) const {
|
||||
return std::tie(base_layer, num_layers, base_level, num_levels) ==
|
||||
std::tie(rhs.base_layer, rhs.num_layers, rhs.base_level, rhs.num_levels);
|
||||
}
|
||||
|
||||
} // namespace VideoCommon
|
||||
586
src/video_core/texture_cache.h
Normal file
586
src/video_core/texture_cache.h
Normal file
@@ -0,0 +1,586 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <boost/icl/interval_map.hpp>
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
#include "video_core/surface.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Tegra::Texture {
|
||||
struct FullTextureInfo;
|
||||
}
|
||||
|
||||
namespace VideoCore {
|
||||
class RasterizerInterface;
|
||||
}
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
class HasheableSurfaceParams {
|
||||
public:
|
||||
std::size_t Hash() const;
|
||||
|
||||
bool operator==(const HasheableSurfaceParams& rhs) const;
|
||||
|
||||
protected:
|
||||
// Avoid creation outside of a managed environment.
|
||||
HasheableSurfaceParams() = default;
|
||||
|
||||
bool is_tiled;
|
||||
u32 block_width;
|
||||
u32 block_height;
|
||||
u32 block_depth;
|
||||
u32 tile_width_spacing;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 depth;
|
||||
u32 pitch;
|
||||
u32 unaligned_height;
|
||||
u32 num_levels;
|
||||
VideoCore::Surface::PixelFormat pixel_format;
|
||||
VideoCore::Surface::ComponentType component_type;
|
||||
VideoCore::Surface::SurfaceType type;
|
||||
VideoCore::Surface::SurfaceTarget target;
|
||||
};
|
||||
|
||||
class SurfaceParams final : public HasheableSurfaceParams {
|
||||
public:
|
||||
/// Creates SurfaceCachedParams from a texture configuration.
|
||||
static SurfaceParams CreateForTexture(Core::System& system,
|
||||
const Tegra::Texture::FullTextureInfo& config);
|
||||
|
||||
/// Creates SurfaceCachedParams for a depth buffer configuration.
|
||||
static SurfaceParams CreateForDepthBuffer(
|
||||
Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format,
|
||||
u32 block_width, u32 block_height, u32 block_depth,
|
||||
Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type);
|
||||
|
||||
/// Creates SurfaceCachedParams from a framebuffer configuration.
|
||||
static SurfaceParams CreateForFramebuffer(Core::System& system, std::size_t index);
|
||||
|
||||
/// Creates SurfaceCachedParams from a Fermi2D surface configuration.
|
||||
static SurfaceParams CreateForFermiCopySurface(
|
||||
const Tegra::Engines::Fermi2D::Regs::Surface& config);
|
||||
|
||||
bool IsTiled() const {
|
||||
return is_tiled;
|
||||
}
|
||||
|
||||
u32 GetBlockWidth() const {
|
||||
return block_width;
|
||||
}
|
||||
|
||||
u32 GetTileWidthSpacing() const {
|
||||
return tile_width_spacing;
|
||||
}
|
||||
|
||||
u32 GetWidth() const {
|
||||
return width;
|
||||
}
|
||||
|
||||
u32 GetHeight() const {
|
||||
return height;
|
||||
}
|
||||
|
||||
u32 GetDepth() const {
|
||||
return depth;
|
||||
}
|
||||
|
||||
u32 GetPitch() const {
|
||||
return pitch;
|
||||
}
|
||||
|
||||
u32 GetNumLevels() const {
|
||||
return num_levels;
|
||||
}
|
||||
|
||||
VideoCore::Surface::PixelFormat GetPixelFormat() const {
|
||||
return pixel_format;
|
||||
}
|
||||
|
||||
VideoCore::Surface::ComponentType GetComponentType() const {
|
||||
return component_type;
|
||||
}
|
||||
|
||||
VideoCore::Surface::SurfaceTarget GetTarget() const {
|
||||
return target;
|
||||
}
|
||||
|
||||
VideoCore::Surface::SurfaceType GetType() const {
|
||||
return type;
|
||||
}
|
||||
|
||||
std::size_t GetGuestSizeInBytes() const {
|
||||
return guest_size_in_bytes;
|
||||
}
|
||||
|
||||
std::size_t GetHostSizeInBytes() const {
|
||||
return host_size_in_bytes;
|
||||
}
|
||||
|
||||
u32 GetNumLayers() const {
|
||||
return num_layers;
|
||||
}
|
||||
|
||||
/// Returns the width of a given mipmap level.
|
||||
u32 GetMipWidth(u32 level) const;
|
||||
|
||||
/// Returns the height of a given mipmap level.
|
||||
u32 GetMipHeight(u32 level) const;
|
||||
|
||||
/// Returns the depth of a given mipmap level.
|
||||
u32 GetMipDepth(u32 level) const;
|
||||
|
||||
/// Returns true if these parameters are from a layered surface.
|
||||
bool IsLayered() const;
|
||||
|
||||
/// Returns the block height of a given mipmap level.
|
||||
u32 GetMipBlockHeight(u32 level) const;
|
||||
|
||||
/// Returns the block depth of a given mipmap level.
|
||||
u32 GetMipBlockDepth(u32 level) const;
|
||||
|
||||
/// Returns the offset in bytes in guest memory of a given mipmap level.
|
||||
std::size_t GetGuestMipmapLevelOffset(u32 level) const;
|
||||
|
||||
/// Returns the offset in bytes in host memory (linear) of a given mipmap level.
|
||||
std::size_t GetHostMipmapLevelOffset(u32 level) const;
|
||||
|
||||
/// Returns the size of a layer in bytes in guest memory.
|
||||
std::size_t GetGuestLayerSize() const;
|
||||
|
||||
/// Returns the size of a layer in bytes in host memory for a given mipmap level.
|
||||
std::size_t GetHostLayerSize(u32 level) const;
|
||||
|
||||
/// Returns true if another surface can be familiar with this. This is a loosely defined term
|
||||
/// that reflects the possibility of these two surface parameters potentially being part of a
|
||||
/// bigger superset.
|
||||
bool IsFamiliar(const SurfaceParams& view_params) const;
|
||||
|
||||
/// Returns true if the pixel format is a depth and/or stencil format.
|
||||
bool IsPixelFormatZeta() const;
|
||||
|
||||
/// Creates a map that redirects an address difference to a layer and mipmap level.
|
||||
std::map<u64, std::pair<u32, u32>> CreateViewOffsetMap() const;
|
||||
|
||||
/// Returns true if the passed surface view parameters is equal or a valid subset of this.
|
||||
bool IsViewValid(const SurfaceParams& view_params, u32 layer, u32 level) const;
|
||||
|
||||
private:
|
||||
/// Calculates values that can be deduced from HasheableSurfaceParams.
|
||||
void CalculateCachedValues();
|
||||
|
||||
/// Returns the size of a given mipmap level.
|
||||
std::size_t GetInnerMipmapMemorySize(u32 level, bool as_host_size, bool layer_only,
|
||||
bool uncompressed) const;
|
||||
|
||||
/// Returns the size of all mipmap levels and aligns as needed.
|
||||
std::size_t GetInnerMemorySize(bool as_host_size, bool layer_only, bool uncompressed) const;
|
||||
|
||||
/// Returns true if the passed view width and height match the size of this params in a given
|
||||
/// mipmap level.
|
||||
bool IsDimensionValid(const SurfaceParams& view_params, u32 level) const;
|
||||
|
||||
/// Returns true if the passed view depth match the size of this params in a given mipmap level.
|
||||
bool IsDepthValid(const SurfaceParams& view_params, u32 level) const;
|
||||
|
||||
/// Returns true if the passed view layers and mipmap levels are in bounds.
|
||||
bool IsInBounds(const SurfaceParams& view_params, u32 layer, u32 level) const;
|
||||
|
||||
std::size_t guest_size_in_bytes;
|
||||
std::size_t host_size_in_bytes;
|
||||
u32 num_layers;
|
||||
};
|
||||
|
||||
struct ViewKey {
|
||||
std::size_t Hash() const;
|
||||
|
||||
bool operator==(const ViewKey& rhs) const;
|
||||
|
||||
u32 base_layer{};
|
||||
u32 num_layers{};
|
||||
u32 base_level{};
|
||||
u32 num_levels{};
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
||||
namespace std {
|
||||
|
||||
template <>
|
||||
struct hash<VideoCommon::SurfaceParams> {
|
||||
std::size_t operator()(const VideoCommon::SurfaceParams& k) const noexcept {
|
||||
return k.Hash();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<VideoCommon::ViewKey> {
|
||||
std::size_t operator()(const VideoCommon::ViewKey& k) const noexcept {
|
||||
return k.Hash();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
template <typename TView, typename TExecutionContext>
|
||||
class SurfaceBase {
|
||||
static_assert(std::is_trivially_copyable_v<TExecutionContext>);
|
||||
|
||||
public:
|
||||
virtual void LoadBuffer() = 0;
|
||||
|
||||
virtual TExecutionContext FlushBuffer(TExecutionContext exctx) = 0;
|
||||
|
||||
virtual TExecutionContext UploadTexture(TExecutionContext exctx) = 0;
|
||||
|
||||
TView* TryGetView(VAddr view_addr, const SurfaceParams& view_params) {
|
||||
if (view_addr < cpu_addr || !params.IsFamiliar(view_params)) {
|
||||
// It can't be a view if it's in a prior address.
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto relative_offset{static_cast<u64>(view_addr - cpu_addr)};
|
||||
const auto it{view_offset_map.find(relative_offset)};
|
||||
if (it == view_offset_map.end()) {
|
||||
// Couldn't find an aligned view.
|
||||
return {};
|
||||
}
|
||||
const auto [layer, level] = it->second;
|
||||
|
||||
if (!params.IsViewValid(view_params, layer, level)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return GetView(layer, view_params.GetNumLayers(), level, view_params.GetNumLevels());
|
||||
}
|
||||
|
||||
VAddr GetCpuAddr() const {
|
||||
ASSERT(is_registered);
|
||||
return cpu_addr;
|
||||
}
|
||||
|
||||
u8* GetHostPtr() const {
|
||||
ASSERT(is_registered);
|
||||
return host_ptr;
|
||||
}
|
||||
|
||||
CacheAddr GetCacheAddr() const {
|
||||
ASSERT(is_registered);
|
||||
return cache_addr;
|
||||
}
|
||||
|
||||
std::size_t GetSizeInBytes() const {
|
||||
return params.GetGuestSizeInBytes();
|
||||
}
|
||||
|
||||
void MarkAsModified(bool is_modified_) {
|
||||
is_modified = is_modified_;
|
||||
}
|
||||
|
||||
const SurfaceParams& GetSurfaceParams() const {
|
||||
return params;
|
||||
}
|
||||
|
||||
TView* GetView(VAddr view_addr, const SurfaceParams& view_params) {
|
||||
TView* view{TryGetView(view_addr, view_params)};
|
||||
ASSERT(view != nullptr);
|
||||
return view;
|
||||
}
|
||||
|
||||
void Register(VAddr cpu_addr_, u8* host_ptr_) {
|
||||
ASSERT(!is_registered);
|
||||
is_registered = true;
|
||||
cpu_addr = cpu_addr_;
|
||||
host_ptr = host_ptr_;
|
||||
cache_addr = ToCacheAddr(host_ptr_);
|
||||
}
|
||||
|
||||
void Register(VAddr cpu_addr_) {
|
||||
Register(cpu_addr_, Memory::GetPointer(cpu_addr_));
|
||||
}
|
||||
|
||||
void Unregister() {
|
||||
ASSERT(is_registered);
|
||||
is_registered = false;
|
||||
}
|
||||
|
||||
bool IsRegistered() const {
|
||||
return is_registered;
|
||||
}
|
||||
|
||||
protected:
|
||||
explicit SurfaceBase(const SurfaceParams& params)
|
||||
: params{params}, view_offset_map{params.CreateViewOffsetMap()} {}
|
||||
|
||||
~SurfaceBase() = default;
|
||||
|
||||
virtual std::unique_ptr<TView> CreateView(const ViewKey& view_key) = 0;
|
||||
|
||||
bool IsModified() const {
|
||||
return is_modified;
|
||||
}
|
||||
|
||||
const SurfaceParams params;
|
||||
|
||||
private:
|
||||
TView* GetView(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels) {
|
||||
const ViewKey key{base_layer, num_layers, base_level, num_levels};
|
||||
const auto [entry, is_cache_miss] = views.try_emplace(key);
|
||||
auto& view{entry->second};
|
||||
if (is_cache_miss) {
|
||||
view = CreateView(key);
|
||||
}
|
||||
return view.get();
|
||||
}
|
||||
|
||||
const std::map<u64, std::pair<u32, u32>> view_offset_map;
|
||||
|
||||
VAddr cpu_addr{};
|
||||
u8* host_ptr{};
|
||||
CacheAddr cache_addr{};
|
||||
bool is_modified{};
|
||||
bool is_registered{};
|
||||
std::unordered_map<ViewKey, std::unique_ptr<TView>> views;
|
||||
};
|
||||
|
||||
template <typename TSurface, typename TView, typename TExecutionContext>
|
||||
class TextureCache {
|
||||
static_assert(std::is_trivially_copyable_v<TExecutionContext>);
|
||||
using ResultType = std::tuple<TView*, TExecutionContext>;
|
||||
using IntervalMap = boost::icl::interval_map<CacheAddr, std::set<TSurface*>>;
|
||||
using IntervalType = typename IntervalMap::interval_type;
|
||||
|
||||
public:
|
||||
void InvalidateRegion(CacheAddr addr, std::size_t size) {
|
||||
for (TSurface* surface : GetSurfacesInRegion(addr, size)) {
|
||||
if (!surface->IsRegistered()) {
|
||||
// Skip duplicates
|
||||
continue;
|
||||
}
|
||||
Unregister(surface);
|
||||
}
|
||||
}
|
||||
|
||||
ResultType GetTextureSurface(TExecutionContext exctx,
|
||||
const Tegra::Texture::FullTextureInfo& config) {
|
||||
auto& memory_manager{system.GPU().MemoryManager()};
|
||||
const auto cpu_addr{memory_manager.GpuToCpuAddress(config.tic.Address())};
|
||||
if (!cpu_addr) {
|
||||
return {{}, exctx};
|
||||
}
|
||||
const auto params{SurfaceParams::CreateForTexture(system, config)};
|
||||
return GetSurfaceView(exctx, *cpu_addr, params, true);
|
||||
}
|
||||
|
||||
ResultType GetDepthBufferSurface(TExecutionContext exctx, bool preserve_contents) {
|
||||
const auto& regs{system.GPU().Maxwell3D().regs};
|
||||
if (!regs.zeta.Address() || !regs.zeta_enable) {
|
||||
return {{}, exctx};
|
||||
}
|
||||
|
||||
auto& memory_manager{system.GPU().MemoryManager()};
|
||||
const auto cpu_addr{memory_manager.GpuToCpuAddress(regs.zeta.Address())};
|
||||
if (!cpu_addr) {
|
||||
return {{}, exctx};
|
||||
}
|
||||
|
||||
const auto depth_params{SurfaceParams::CreateForDepthBuffer(
|
||||
system, regs.zeta_width, regs.zeta_height, regs.zeta.format,
|
||||
regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height,
|
||||
regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)};
|
||||
return GetSurfaceView(exctx, *cpu_addr, depth_params, preserve_contents);
|
||||
}
|
||||
|
||||
ResultType GetColorBufferSurface(TExecutionContext exctx, std::size_t index,
|
||||
bool preserve_contents) {
|
||||
ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets);
|
||||
|
||||
const auto& regs{system.GPU().Maxwell3D().regs};
|
||||
if (index >= regs.rt_control.count || regs.rt[index].Address() == 0 ||
|
||||
regs.rt[index].format == Tegra::RenderTargetFormat::NONE) {
|
||||
return {{}, exctx};
|
||||
}
|
||||
|
||||
auto& memory_manager{system.GPU().MemoryManager()};
|
||||
const auto& config{system.GPU().Maxwell3D().regs.rt[index]};
|
||||
const auto cpu_addr{memory_manager.GpuToCpuAddress(
|
||||
config.Address() + config.base_layer * config.layer_stride * sizeof(u32))};
|
||||
if (!cpu_addr) {
|
||||
return {{}, exctx};
|
||||
}
|
||||
|
||||
return GetSurfaceView(exctx, *cpu_addr, SurfaceParams::CreateForFramebuffer(system, index),
|
||||
preserve_contents);
|
||||
}
|
||||
|
||||
ResultType GetFermiSurface(TExecutionContext exctx,
|
||||
const Tegra::Engines::Fermi2D::Regs::Surface& config) {
|
||||
const auto cpu_addr{system.GPU().MemoryManager().GpuToCpuAddress(config.Address())};
|
||||
ASSERT(cpu_addr);
|
||||
return GetSurfaceView(exctx, *cpu_addr, SurfaceParams::CreateForFermiCopySurface(config),
|
||||
true);
|
||||
}
|
||||
|
||||
TSurface* TryFindFramebufferSurface(const u8* host_ptr) const {
|
||||
const auto it{registered_surfaces.find(ToCacheAddr(host_ptr))};
|
||||
return it != registered_surfaces.end() ? *it->second.begin() : nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer)
|
||||
: system{system}, rasterizer{rasterizer} {}
|
||||
|
||||
~TextureCache() = default;
|
||||
|
||||
virtual ResultType TryFastGetSurfaceView(TExecutionContext exctx, VAddr cpu_addr, u8* host_ptr,
|
||||
const SurfaceParams& params, bool preserve_contents,
|
||||
const std::vector<TSurface*>& overlaps) = 0;
|
||||
|
||||
virtual std::unique_ptr<TSurface> CreateSurface(const SurfaceParams& params) = 0;
|
||||
|
||||
void Register(TSurface* surface, VAddr cpu_addr, u8* host_ptr) {
|
||||
surface->Register(cpu_addr, host_ptr);
|
||||
registered_surfaces.add({GetSurfaceInterval(surface), {surface}});
|
||||
rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), 1);
|
||||
}
|
||||
|
||||
void Unregister(TSurface* surface) {
|
||||
registered_surfaces.subtract({GetSurfaceInterval(surface), {surface}});
|
||||
rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), -1);
|
||||
surface->Unregister();
|
||||
}
|
||||
|
||||
TSurface* GetUncachedSurface(const SurfaceParams& params) {
|
||||
if (TSurface* surface = TryGetReservedSurface(params); surface)
|
||||
return surface;
|
||||
// No reserved surface available, create a new one and reserve it
|
||||
auto new_surface{CreateSurface(params)};
|
||||
TSurface* surface{new_surface.get()};
|
||||
ReserveSurface(params, std::move(new_surface));
|
||||
return surface;
|
||||
}
|
||||
|
||||
Core::System& system;
|
||||
|
||||
private:
|
||||
ResultType GetSurfaceView(TExecutionContext exctx, VAddr cpu_addr, const SurfaceParams& params,
|
||||
bool preserve_contents) {
|
||||
const auto host_ptr{Memory::GetPointer(cpu_addr)};
|
||||
const auto cache_addr{ToCacheAddr(host_ptr)};
|
||||
const auto overlaps{GetSurfacesInRegion(cache_addr, params.GetGuestSizeInBytes())};
|
||||
if (overlaps.empty()) {
|
||||
return LoadSurfaceView(exctx, cpu_addr, host_ptr, params, preserve_contents);
|
||||
}
|
||||
|
||||
if (overlaps.size() == 1) {
|
||||
if (TView* view = overlaps[0]->TryGetView(cpu_addr, params); view)
|
||||
return {view, exctx};
|
||||
}
|
||||
|
||||
TView* fast_view;
|
||||
std::tie(fast_view, exctx) =
|
||||
TryFastGetSurfaceView(exctx, cpu_addr, host_ptr, params, preserve_contents, overlaps);
|
||||
|
||||
for (TSurface* surface : overlaps) {
|
||||
if (!fast_view) {
|
||||
// Flush even when we don't care about the contents, to preserve memory not written
|
||||
// by the new surface.
|
||||
exctx = surface->FlushBuffer(exctx);
|
||||
}
|
||||
Unregister(surface);
|
||||
}
|
||||
|
||||
if (fast_view) {
|
||||
return {fast_view, exctx};
|
||||
}
|
||||
|
||||
return LoadSurfaceView(exctx, cpu_addr, host_ptr, params, preserve_contents);
|
||||
}
|
||||
|
||||
ResultType LoadSurfaceView(TExecutionContext exctx, VAddr cpu_addr, u8* host_ptr,
|
||||
const SurfaceParams& params, bool preserve_contents) {
|
||||
TSurface* new_surface{GetUncachedSurface(params)};
|
||||
Register(new_surface, cpu_addr, host_ptr);
|
||||
if (preserve_contents) {
|
||||
exctx = LoadSurface(exctx, new_surface);
|
||||
}
|
||||
return {new_surface->GetView(cpu_addr, params), exctx};
|
||||
}
|
||||
|
||||
TExecutionContext LoadSurface(TExecutionContext exctx, TSurface* surface) {
|
||||
surface->LoadBuffer();
|
||||
exctx = surface->UploadTexture(exctx);
|
||||
surface->MarkAsModified(false);
|
||||
return exctx;
|
||||
}
|
||||
|
||||
std::vector<TSurface*> GetSurfacesInRegion(CacheAddr cache_addr, std::size_t size) const {
|
||||
if (size == 0) {
|
||||
return {};
|
||||
}
|
||||
const IntervalType interval{cache_addr, cache_addr + size};
|
||||
|
||||
std::vector<TSurface*> surfaces;
|
||||
for (auto& pair : boost::make_iterator_range(registered_surfaces.equal_range(interval))) {
|
||||
surfaces.push_back(*pair.second.begin());
|
||||
}
|
||||
return surfaces;
|
||||
}
|
||||
|
||||
void ReserveSurface(const SurfaceParams& params, std::unique_ptr<TSurface> surface) {
|
||||
surface_reserve[params].push_back(std::move(surface));
|
||||
}
|
||||
|
||||
TSurface* TryGetReservedSurface(const SurfaceParams& params) {
|
||||
auto search{surface_reserve.find(params)};
|
||||
if (search == surface_reserve.end()) {
|
||||
return {};
|
||||
}
|
||||
for (auto& surface : search->second) {
|
||||
if (!surface->IsRegistered()) {
|
||||
return surface.get();
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
IntervalType GetSurfaceInterval(TSurface* surface) const {
|
||||
return IntervalType::right_open(surface->GetCacheAddr(),
|
||||
surface->GetCacheAddr() + surface->GetSizeInBytes());
|
||||
}
|
||||
|
||||
VideoCore::RasterizerInterface& rasterizer;
|
||||
|
||||
IntervalMap registered_surfaces;
|
||||
|
||||
/// The surface reserve is a "backup" cache, this is where we put unique surfaces that have
|
||||
/// previously been used. This is to prevent surfaces from being constantly created and
|
||||
/// destroyed when used with different surface parameters.
|
||||
std::unordered_map<SurfaceParams, std::list<std::unique_ptr<TSurface>>> surface_reserve;
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
||||
@@ -1,6 +1,13 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QApplication>
|
||||
#include <QHBoxLayout>
|
||||
#include <QKeyEvent>
|
||||
#include <QOffscreenSurface>
|
||||
#include <QOpenGLWindow>
|
||||
#include <QPainter>
|
||||
#include <QScreen>
|
||||
#include <QWindow>
|
||||
#include <fmt/format.h>
|
||||
@@ -82,13 +89,36 @@ void EmuThread::run() {
|
||||
render_window->moveContext();
|
||||
}
|
||||
|
||||
class GGLContext : public Core::Frontend::GraphicsContext {
|
||||
public:
|
||||
explicit GGLContext(QOpenGLContext* shared_context) : surface() {
|
||||
context = std::make_unique<QOpenGLContext>(shared_context);
|
||||
surface.setFormat(shared_context->format());
|
||||
surface.create();
|
||||
}
|
||||
|
||||
void MakeCurrent() override {
|
||||
context->makeCurrent(&surface);
|
||||
}
|
||||
|
||||
void DoneCurrent() override {
|
||||
context->doneCurrent();
|
||||
}
|
||||
|
||||
void SwapBuffers() override {}
|
||||
|
||||
private:
|
||||
std::unique_ptr<QOpenGLContext> context;
|
||||
QOffscreenSurface surface;
|
||||
};
|
||||
|
||||
// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL
|
||||
// context.
|
||||
// The corresponding functionality is handled in EmuThread instead
|
||||
class GGLWidgetInternal : public QGLWidget {
|
||||
class GGLWidgetInternal : public QOpenGLWindow {
|
||||
public:
|
||||
GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent)
|
||||
: QGLWidget(fmt, parent), parent(parent) {}
|
||||
GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context)
|
||||
: QOpenGLWindow(shared_context), parent(parent) {}
|
||||
|
||||
void paintEvent(QPaintEvent* ev) override {
|
||||
if (do_painting) {
|
||||
@@ -101,9 +131,51 @@ public:
|
||||
parent->OnFramebufferSizeChanged();
|
||||
}
|
||||
|
||||
void keyPressEvent(QKeyEvent* event) override {
|
||||
InputCommon::GetKeyboard()->PressKey(event->key());
|
||||
}
|
||||
|
||||
void keyReleaseEvent(QKeyEvent* event) override {
|
||||
InputCommon::GetKeyboard()->ReleaseKey(event->key());
|
||||
}
|
||||
|
||||
void mousePressEvent(QMouseEvent* event) override {
|
||||
if (event->source() == Qt::MouseEventSynthesizedBySystem)
|
||||
return; // touch input is handled in TouchBeginEvent
|
||||
|
||||
const auto pos{event->pos()};
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
const auto [x, y] = parent->ScaleTouch(pos);
|
||||
parent->TouchPressed(x, y);
|
||||
} else if (event->button() == Qt::RightButton) {
|
||||
InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
|
||||
}
|
||||
}
|
||||
|
||||
void mouseMoveEvent(QMouseEvent* event) override {
|
||||
if (event->source() == Qt::MouseEventSynthesizedBySystem)
|
||||
return; // touch input is handled in TouchUpdateEvent
|
||||
|
||||
const auto pos{event->pos()};
|
||||
const auto [x, y] = parent->ScaleTouch(pos);
|
||||
parent->TouchMoved(x, y);
|
||||
InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
|
||||
}
|
||||
|
||||
void mouseReleaseEvent(QMouseEvent* event) override {
|
||||
if (event->source() == Qt::MouseEventSynthesizedBySystem)
|
||||
return; // touch input is handled in TouchEndEvent
|
||||
|
||||
if (event->button() == Qt::LeftButton)
|
||||
parent->TouchReleased();
|
||||
else if (event->button() == Qt::RightButton)
|
||||
InputCommon::GetMotionEmu()->EndTilt();
|
||||
}
|
||||
|
||||
void DisablePainting() {
|
||||
do_painting = false;
|
||||
}
|
||||
|
||||
void EnablePainting() {
|
||||
do_painting = true;
|
||||
}
|
||||
@@ -114,7 +186,7 @@ private:
|
||||
};
|
||||
|
||||
GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
|
||||
: QWidget(parent), child(nullptr), emu_thread(emu_thread) {
|
||||
: QWidget(parent), child(nullptr), context(nullptr), emu_thread(emu_thread) {
|
||||
|
||||
setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
|
||||
.arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc));
|
||||
@@ -137,19 +209,19 @@ void GRenderWindow::moveContext() {
|
||||
auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr)
|
||||
? emu_thread
|
||||
: qApp->thread();
|
||||
child->context()->moveToThread(thread);
|
||||
context->moveToThread(thread);
|
||||
}
|
||||
|
||||
void GRenderWindow::SwapBuffers() {
|
||||
// In our multi-threaded QGLWidget use case we shouldn't need to call `makeCurrent`,
|
||||
// In our multi-threaded QWidget use case we shouldn't need to call `makeCurrent`,
|
||||
// since we never call `doneCurrent` in this thread.
|
||||
// However:
|
||||
// - The Qt debug runtime prints a bogus warning on the console if `makeCurrent` wasn't called
|
||||
// since the last time `swapBuffers` was executed;
|
||||
// - On macOS, if `makeCurrent` isn't called explicitely, resizing the buffer breaks.
|
||||
child->makeCurrent();
|
||||
context->makeCurrent(child);
|
||||
|
||||
child->swapBuffers();
|
||||
context->swapBuffers(child);
|
||||
if (!first_frame) {
|
||||
emit FirstFrameDisplayed();
|
||||
first_frame = true;
|
||||
@@ -157,11 +229,11 @@ void GRenderWindow::SwapBuffers() {
|
||||
}
|
||||
|
||||
void GRenderWindow::MakeCurrent() {
|
||||
child->makeCurrent();
|
||||
context->makeCurrent(child);
|
||||
}
|
||||
|
||||
void GRenderWindow::DoneCurrent() {
|
||||
child->doneCurrent();
|
||||
context->doneCurrent();
|
||||
}
|
||||
|
||||
void GRenderWindow::PollEvents() {}
|
||||
@@ -174,14 +246,26 @@ void GRenderWindow::PollEvents() {}
|
||||
void GRenderWindow::OnFramebufferSizeChanged() {
|
||||
// Screen changes potentially incur a change in screen DPI, hence we should update the
|
||||
// framebuffer size
|
||||
qreal pixelRatio = windowPixelRatio();
|
||||
qreal pixelRatio = GetWindowPixelRatio();
|
||||
unsigned width = child->QPaintDevice::width() * pixelRatio;
|
||||
unsigned height = child->QPaintDevice::height() * pixelRatio;
|
||||
UpdateCurrentFramebufferLayout(width, height);
|
||||
}
|
||||
|
||||
void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) {
|
||||
if (child) {
|
||||
child->keyPressEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void GRenderWindow::ForwardKeyReleaseEvent(QKeyEvent* event) {
|
||||
if (child) {
|
||||
child->keyReleaseEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void GRenderWindow::BackupGeometry() {
|
||||
geometry = ((QGLWidget*)this)->saveGeometry();
|
||||
geometry = ((QWidget*)this)->saveGeometry();
|
||||
}
|
||||
|
||||
void GRenderWindow::RestoreGeometry() {
|
||||
@@ -199,18 +283,18 @@ QByteArray GRenderWindow::saveGeometry() {
|
||||
// If we are a top-level widget, store the current geometry
|
||||
// otherwise, store the last backup
|
||||
if (parent() == nullptr)
|
||||
return ((QGLWidget*)this)->saveGeometry();
|
||||
return ((QWidget*)this)->saveGeometry();
|
||||
else
|
||||
return geometry;
|
||||
}
|
||||
|
||||
qreal GRenderWindow::windowPixelRatio() const {
|
||||
qreal GRenderWindow::GetWindowPixelRatio() const {
|
||||
// windowHandle() might not be accessible until the window is displayed to screen.
|
||||
return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f;
|
||||
}
|
||||
|
||||
std::pair<unsigned, unsigned> GRenderWindow::ScaleTouch(const QPointF pos) const {
|
||||
const qreal pixel_ratio = windowPixelRatio();
|
||||
const qreal pixel_ratio = GetWindowPixelRatio();
|
||||
return {static_cast<unsigned>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})),
|
||||
static_cast<unsigned>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))};
|
||||
}
|
||||
@@ -220,47 +304,6 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
|
||||
QWidget::closeEvent(event);
|
||||
}
|
||||
|
||||
void GRenderWindow::keyPressEvent(QKeyEvent* event) {
|
||||
InputCommon::GetKeyboard()->PressKey(event->key());
|
||||
}
|
||||
|
||||
void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
|
||||
InputCommon::GetKeyboard()->ReleaseKey(event->key());
|
||||
}
|
||||
|
||||
void GRenderWindow::mousePressEvent(QMouseEvent* event) {
|
||||
if (event->source() == Qt::MouseEventSynthesizedBySystem)
|
||||
return; // touch input is handled in TouchBeginEvent
|
||||
|
||||
auto pos = event->pos();
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
const auto [x, y] = ScaleTouch(pos);
|
||||
this->TouchPressed(x, y);
|
||||
} else if (event->button() == Qt::RightButton) {
|
||||
InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
|
||||
}
|
||||
}
|
||||
|
||||
void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
|
||||
if (event->source() == Qt::MouseEventSynthesizedBySystem)
|
||||
return; // touch input is handled in TouchUpdateEvent
|
||||
|
||||
auto pos = event->pos();
|
||||
const auto [x, y] = ScaleTouch(pos);
|
||||
this->TouchMoved(x, y);
|
||||
InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
|
||||
}
|
||||
|
||||
void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
|
||||
if (event->source() == Qt::MouseEventSynthesizedBySystem)
|
||||
return; // touch input is handled in TouchEndEvent
|
||||
|
||||
if (event->button() == Qt::LeftButton)
|
||||
this->TouchReleased();
|
||||
else if (event->button() == Qt::RightButton)
|
||||
InputCommon::GetMotionEmu()->EndTilt();
|
||||
}
|
||||
|
||||
void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) {
|
||||
// TouchBegin always has exactly one touch point, so take the .first()
|
||||
const auto [x, y] = ScaleTouch(event->touchPoints().first().pos());
|
||||
@@ -313,35 +356,60 @@ void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) {
|
||||
NotifyClientAreaSizeChanged(std::make_pair(width, height));
|
||||
}
|
||||
|
||||
void GRenderWindow::InitRenderTarget() {
|
||||
if (child) {
|
||||
delete child;
|
||||
}
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
|
||||
return std::make_unique<GGLContext>(shared_context.get());
|
||||
}
|
||||
|
||||
if (layout()) {
|
||||
delete layout();
|
||||
}
|
||||
void GRenderWindow::InitRenderTarget() {
|
||||
shared_context.reset();
|
||||
context.reset();
|
||||
|
||||
delete child;
|
||||
child = nullptr;
|
||||
|
||||
delete container;
|
||||
container = nullptr;
|
||||
|
||||
delete layout();
|
||||
|
||||
first_frame = false;
|
||||
|
||||
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
|
||||
// WA_DontShowOnScreen, WA_DeleteOnClose
|
||||
QGLFormat fmt;
|
||||
QSurfaceFormat fmt;
|
||||
fmt.setVersion(4, 3);
|
||||
fmt.setProfile(QGLFormat::CoreProfile);
|
||||
fmt.setProfile(QSurfaceFormat::CoreProfile);
|
||||
// TODO: expose a setting for buffer value (ie default/single/double/triple)
|
||||
fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
|
||||
shared_context = std::make_unique<QOpenGLContext>();
|
||||
shared_context->setFormat(fmt);
|
||||
shared_context->create();
|
||||
context = std::make_unique<QOpenGLContext>();
|
||||
context->setShareContext(shared_context.get());
|
||||
context->setFormat(fmt);
|
||||
context->create();
|
||||
fmt.setSwapInterval(false);
|
||||
|
||||
// Requests a forward-compatible context, which is required to get a 3.2+ context on OS X
|
||||
fmt.setOption(QGL::NoDeprecatedFunctions);
|
||||
child = new GGLWidgetInternal(this, shared_context.get());
|
||||
container = QWidget::createWindowContainer(child, this);
|
||||
|
||||
child = new GGLWidgetInternal(fmt, this);
|
||||
QBoxLayout* layout = new QHBoxLayout(this);
|
||||
|
||||
resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
|
||||
layout->addWidget(child);
|
||||
layout->addWidget(container);
|
||||
layout->setMargin(0);
|
||||
setLayout(layout);
|
||||
|
||||
// Reset minimum size to avoid unwanted resizes when this function is called for a second time.
|
||||
setMinimumSize(1, 1);
|
||||
|
||||
// Show causes the window to actually be created and the OpenGL context as well, but we don't
|
||||
// want the widget to be shown yet, so immediately hide it.
|
||||
show();
|
||||
hide();
|
||||
|
||||
resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
|
||||
child->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
|
||||
container->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
|
||||
|
||||
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
|
||||
|
||||
OnFramebufferSizeChanged();
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <QGLWidget>
|
||||
#include <QImage>
|
||||
#include <QThread>
|
||||
#include <QWidget>
|
||||
#include "common/thread.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
@@ -21,6 +21,8 @@ class QTouchEvent;
|
||||
class GGLWidgetInternal;
|
||||
class GMainWindow;
|
||||
class GRenderWindow;
|
||||
class QSurface;
|
||||
class QOpenGLContext;
|
||||
|
||||
namespace VideoCore {
|
||||
enum class LoadCallbackStage;
|
||||
@@ -121,25 +123,21 @@ public:
|
||||
void MakeCurrent() override;
|
||||
void DoneCurrent() override;
|
||||
void PollEvents() override;
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
|
||||
|
||||
void ForwardKeyPressEvent(QKeyEvent* event);
|
||||
void ForwardKeyReleaseEvent(QKeyEvent* event);
|
||||
|
||||
void BackupGeometry();
|
||||
void RestoreGeometry();
|
||||
void restoreGeometry(const QByteArray& geometry); // overridden
|
||||
QByteArray saveGeometry(); // overridden
|
||||
|
||||
qreal windowPixelRatio() const;
|
||||
qreal GetWindowPixelRatio() const;
|
||||
std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const;
|
||||
|
||||
void closeEvent(QCloseEvent* event) override;
|
||||
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
void keyReleaseEvent(QKeyEvent* event) override;
|
||||
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
void mouseMoveEvent(QMouseEvent* event) override;
|
||||
void mouseReleaseEvent(QMouseEvent* event) override;
|
||||
|
||||
bool event(QEvent* event) override;
|
||||
|
||||
void focusOutEvent(QFocusEvent* event) override;
|
||||
|
||||
void OnClientAreaResized(unsigned width, unsigned height);
|
||||
@@ -161,7 +159,6 @@ signals:
|
||||
void FirstFrameDisplayed();
|
||||
|
||||
private:
|
||||
std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const;
|
||||
void TouchBeginEvent(const QTouchEvent* event);
|
||||
void TouchUpdateEvent(const QTouchEvent* event);
|
||||
void TouchEndEvent();
|
||||
@@ -169,11 +166,17 @@ private:
|
||||
void OnMinimalClientAreaChangeRequest(
|
||||
const std::pair<unsigned, unsigned>& minimal_size) override;
|
||||
|
||||
GGLWidgetInternal* child;
|
||||
QWidget* container = nullptr;
|
||||
GGLWidgetInternal* child = nullptr;
|
||||
|
||||
QByteArray geometry;
|
||||
|
||||
EmuThread* emu_thread;
|
||||
// Context that backs the GGLWidgetInternal (and will be used by core to render)
|
||||
std::unique_ptr<QOpenGLContext> context;
|
||||
// Context that will be shared between all newly created contexts. This should never be made
|
||||
// current
|
||||
std::unique_ptr<QOpenGLContext> shared_context;
|
||||
|
||||
/// Temporary storage of the screenshot taken
|
||||
QImage screenshot_image;
|
||||
|
||||
@@ -394,6 +394,7 @@ void Config::ReadValues() {
|
||||
ReadSetting("use_accurate_gpu_emulation", false).toBool();
|
||||
Settings::values.use_asynchronous_gpu_emulation =
|
||||
ReadSetting("use_asynchronous_gpu_emulation", false).toBool();
|
||||
Settings::values.force_30fps_mode = ReadSetting("force_30fps_mode", false).toBool();
|
||||
|
||||
Settings::values.bg_red = ReadSetting("bg_red", 0.0).toFloat();
|
||||
Settings::values.bg_green = ReadSetting("bg_green", 0.0).toFloat();
|
||||
@@ -523,8 +524,8 @@ void Config::ReadValues() {
|
||||
qt_config->beginGroup("Paths");
|
||||
UISettings::values.roms_path = ReadSetting("romsPath").toString();
|
||||
UISettings::values.symbols_path = ReadSetting("symbolsPath").toString();
|
||||
UISettings::values.gamedir = ReadSetting("gameListRootDir", ".").toString();
|
||||
UISettings::values.gamedir_deepscan = ReadSetting("gameListDeepScan", false).toBool();
|
||||
UISettings::values.game_directory_path = ReadSetting("gameListRootDir", ".").toString();
|
||||
UISettings::values.game_directory_deepscan = ReadSetting("gameListDeepScan", false).toBool();
|
||||
UISettings::values.recent_files = ReadSetting("recentFiles").toStringList();
|
||||
qt_config->endGroup();
|
||||
|
||||
@@ -664,6 +665,7 @@ void Config::SaveValues() {
|
||||
WriteSetting("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation, false);
|
||||
WriteSetting("use_asynchronous_gpu_emulation", Settings::values.use_asynchronous_gpu_emulation,
|
||||
false);
|
||||
WriteSetting("force_30fps_mode", Settings::values.force_30fps_mode, false);
|
||||
|
||||
// Cast to double because Qt's written float values are not human-readable
|
||||
WriteSetting("bg_red", (double)Settings::values.bg_red, 0.0);
|
||||
@@ -768,8 +770,8 @@ void Config::SaveValues() {
|
||||
WriteSetting("romsPath", UISettings::values.roms_path);
|
||||
WriteSetting("symbolsPath", UISettings::values.symbols_path);
|
||||
WriteSetting("screenshotPath", UISettings::values.screenshot_path);
|
||||
WriteSetting("gameListRootDir", UISettings::values.gamedir, ".");
|
||||
WriteSetting("gameListDeepScan", UISettings::values.gamedir_deepscan, false);
|
||||
WriteSetting("gameListRootDir", UISettings::values.game_directory_path, ".");
|
||||
WriteSetting("gameListDeepScan", UISettings::values.game_directory_deepscan, false);
|
||||
WriteSetting("recentFiles", UISettings::values.recent_files);
|
||||
qt_config->endGroup();
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
|
||||
ConfigureGeneral::~ConfigureGeneral() = default;
|
||||
|
||||
void ConfigureGeneral::setConfiguration() {
|
||||
ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan);
|
||||
ui->toggle_deepscan->setChecked(UISettings::values.game_directory_deepscan);
|
||||
ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
|
||||
ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot);
|
||||
ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
|
||||
@@ -36,7 +36,7 @@ void ConfigureGeneral::setConfiguration() {
|
||||
}
|
||||
|
||||
void ConfigureGeneral::applyConfiguration() {
|
||||
UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked();
|
||||
UISettings::values.game_directory_deepscan = ui->toggle_deepscan->isChecked();
|
||||
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
|
||||
UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked();
|
||||
UISettings::values.theme =
|
||||
|
||||
@@ -77,6 +77,8 @@ void ConfigureGraphics::setConfiguration() {
|
||||
ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation);
|
||||
ui->use_asynchronous_gpu_emulation->setEnabled(!Core::System::GetInstance().IsPoweredOn());
|
||||
ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation);
|
||||
ui->force_30fps_mode->setEnabled(!Core::System::GetInstance().IsPoweredOn());
|
||||
ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode);
|
||||
UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green,
|
||||
Settings::values.bg_blue));
|
||||
}
|
||||
@@ -90,6 +92,7 @@ void ConfigureGraphics::applyConfiguration() {
|
||||
Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked();
|
||||
Settings::values.use_asynchronous_gpu_emulation =
|
||||
ui->use_asynchronous_gpu_emulation->isChecked();
|
||||
Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked();
|
||||
Settings::values.bg_red = static_cast<float>(bg_color.redF());
|
||||
Settings::values.bg_green = static_cast<float>(bg_color.greenF());
|
||||
Settings::values.bg_blue = static_cast<float>(bg_color.blueF());
|
||||
|
||||
@@ -70,6 +70,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="force_30fps_mode">
|
||||
<property name="text">
|
||||
<string>Force 30 FPS mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
|
||||
@@ -467,9 +467,10 @@ void GameList::LoadInterfaceLayout() {
|
||||
const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci", "nsp"};
|
||||
|
||||
void GameList::RefreshGameDirectory() {
|
||||
if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) {
|
||||
if (!UISettings::values.game_directory_path.isEmpty() && current_worker != nullptr) {
|
||||
LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list.");
|
||||
search_field->clear();
|
||||
PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
|
||||
PopulateAsync(UISettings::values.game_directory_path,
|
||||
UISettings::values.game_directory_deepscan);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +213,8 @@ GMainWindow::GMainWindow()
|
||||
OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning);
|
||||
|
||||
game_list->LoadCompatibilityList();
|
||||
game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
|
||||
game_list->PopulateAsync(UISettings::values.game_directory_path,
|
||||
UISettings::values.game_directory_deepscan);
|
||||
|
||||
// Show one-time "callout" messages to the user
|
||||
ShowTelemetryCallout();
|
||||
@@ -1278,8 +1279,8 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) {
|
||||
|
||||
const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
|
||||
if (reload) {
|
||||
game_list->PopulateAsync(UISettings::values.gamedir,
|
||||
UISettings::values.gamedir_deepscan);
|
||||
game_list->PopulateAsync(UISettings::values.game_directory_path,
|
||||
UISettings::values.game_directory_deepscan);
|
||||
}
|
||||
|
||||
config->Save();
|
||||
@@ -1367,7 +1368,8 @@ void GMainWindow::OnMenuInstallToNAND() {
|
||||
const auto success = [this]() {
|
||||
QMessageBox::information(this, tr("Successfully Installed"),
|
||||
tr("The file was successfully installed."));
|
||||
game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
|
||||
game_list->PopulateAsync(UISettings::values.game_directory_path,
|
||||
UISettings::values.game_directory_deepscan);
|
||||
};
|
||||
|
||||
const auto failed = [this]() {
|
||||
@@ -1494,8 +1496,8 @@ void GMainWindow::OnMenuInstallToNAND() {
|
||||
void GMainWindow::OnMenuSelectGameListRoot() {
|
||||
QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory"));
|
||||
if (!dir_path.isEmpty()) {
|
||||
UISettings::values.gamedir = dir_path;
|
||||
game_list->PopulateAsync(dir_path, UISettings::values.gamedir_deepscan);
|
||||
UISettings::values.game_directory_path = dir_path;
|
||||
game_list->PopulateAsync(dir_path, UISettings::values.game_directory_deepscan);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1517,7 +1519,8 @@ void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target)
|
||||
: FileUtil::UserPath::NANDDir,
|
||||
dir_path.toStdString());
|
||||
Service::FileSystem::CreateFactories(*vfs);
|
||||
game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
|
||||
game_list->PopulateAsync(UISettings::values.game_directory_path,
|
||||
UISettings::values.game_directory_deepscan);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1669,8 +1672,8 @@ void GMainWindow::OnConfigure() {
|
||||
|
||||
const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
|
||||
if (reload) {
|
||||
game_list->PopulateAsync(UISettings::values.gamedir,
|
||||
UISettings::values.gamedir_deepscan);
|
||||
game_list->PopulateAsync(UISettings::values.game_directory_path,
|
||||
UISettings::values.game_directory_deepscan);
|
||||
}
|
||||
|
||||
config->Save();
|
||||
@@ -1920,7 +1923,8 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
|
||||
Service::FileSystem::CreateFactories(*vfs);
|
||||
|
||||
if (behavior == ReinitializeKeyBehavior::Warning) {
|
||||
game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
|
||||
game_list->PopulateAsync(UISettings::values.game_directory_path,
|
||||
UISettings::values.game_directory_deepscan);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2027,6 +2031,18 @@ void GMainWindow::dragMoveEvent(QDragMoveEvent* event) {
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
|
||||
void GMainWindow::keyPressEvent(QKeyEvent* event) {
|
||||
if (render_window) {
|
||||
render_window->ForwardKeyPressEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::keyReleaseEvent(QKeyEvent* event) {
|
||||
if (render_window) {
|
||||
render_window->ForwardKeyReleaseEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
bool GMainWindow::ConfirmChangeGame() {
|
||||
if (emu_thread == nullptr)
|
||||
return true;
|
||||
@@ -2094,7 +2110,8 @@ int main(int argc, char* argv[]) {
|
||||
QCoreApplication::setOrganizationName("yuzu team");
|
||||
QCoreApplication::setApplicationName("yuzu");
|
||||
|
||||
QApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
|
||||
// Enables the core to make the qt created contexts current on std::threads
|
||||
QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
|
||||
QApplication app(argc, argv);
|
||||
|
||||
// Qt changes the locale and causes issues in float conversion using std::to_string() when
|
||||
|
||||
@@ -252,4 +252,8 @@ protected:
|
||||
void dropEvent(QDropEvent* event) override;
|
||||
void dragEnterEvent(QDragEnterEvent* event) override;
|
||||
void dragMoveEvent(QDragMoveEvent* event) override;
|
||||
|
||||
// Overrides used to forward signals to the render window when the focus moves out.
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
void keyReleaseEvent(QKeyEvent* event) override;
|
||||
};
|
||||
|
||||
@@ -55,8 +55,8 @@ struct Values {
|
||||
QString roms_path;
|
||||
QString symbols_path;
|
||||
QString screenshot_path;
|
||||
QString gamedir;
|
||||
bool gamedir_deepscan;
|
||||
QString game_directory_path;
|
||||
bool game_directory_deepscan;
|
||||
QStringList recent_files;
|
||||
|
||||
QString theme;
|
||||
|
||||
@@ -19,6 +19,37 @@
|
||||
#include "input_common/sdl/sdl.h"
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
|
||||
|
||||
class SDLGLContext : public Core::Frontend::GraphicsContext {
|
||||
public:
|
||||
explicit SDLGLContext() {
|
||||
// create a hidden window to make the shared context against
|
||||
window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, // x position
|
||||
SDL_WINDOWPOS_UNDEFINED, // y position
|
||||
Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height,
|
||||
SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN);
|
||||
context = SDL_GL_CreateContext(window);
|
||||
}
|
||||
|
||||
~SDLGLContext() {
|
||||
SDL_GL_DeleteContext(context);
|
||||
SDL_DestroyWindow(window);
|
||||
}
|
||||
|
||||
void MakeCurrent() override {
|
||||
SDL_GL_MakeCurrent(window, context);
|
||||
}
|
||||
|
||||
void DoneCurrent() override {
|
||||
SDL_GL_MakeCurrent(window, nullptr);
|
||||
}
|
||||
|
||||
void SwapBuffers() override {}
|
||||
|
||||
private:
|
||||
SDL_Window* window;
|
||||
SDL_GLContext context;
|
||||
};
|
||||
|
||||
void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
|
||||
TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
|
||||
InputCommon::GetMotionEmu()->Tilt(x, y);
|
||||
@@ -153,6 +184,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
|
||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
|
||||
SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
|
||||
|
||||
std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname,
|
||||
Common::g_scm_branch, Common::g_scm_desc);
|
||||
@@ -171,7 +203,6 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
|
||||
if (fullscreen) {
|
||||
Fullscreen();
|
||||
}
|
||||
|
||||
gl_context = SDL_GL_CreateContext(render_window);
|
||||
|
||||
if (gl_context == nullptr) {
|
||||
@@ -278,3 +309,7 @@ void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(
|
||||
|
||||
SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second);
|
||||
}
|
||||
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2::CreateSharedContext() const {
|
||||
return std::make_unique<SDLGLContext>();
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ public:
|
||||
/// Releases the GL context from the caller thread
|
||||
void DoneCurrent() override;
|
||||
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
|
||||
|
||||
/// Whether the window is still open, and a close request hasn't yet been sent
|
||||
bool IsOpen() const;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user