Compare commits
1 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cfbae58b2b |
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -13,6 +13,9 @@
|
||||
[submodule "dynarmic"]
|
||||
path = externals/dynarmic
|
||||
url = https://github.com/MerryMage/dynarmic.git
|
||||
[submodule "xbyak"]
|
||||
path = externals/xbyak
|
||||
url = https://github.com/herumi/xbyak.git
|
||||
[submodule "fmt"]
|
||||
path = externals/fmt
|
||||
url = https://github.com/fmtlib/fmt.git
|
||||
|
||||
9
externals/CMakeLists.txt
vendored
9
externals/CMakeLists.txt
vendored
@@ -9,6 +9,7 @@ target_include_directories(catch-single-include INTERFACE catch/single_include)
|
||||
|
||||
# Dynarmic
|
||||
if (ARCHITECTURE_x86_64)
|
||||
add_library(xbyak INTERFACE)
|
||||
set(DYNARMIC_TESTS OFF)
|
||||
set(DYNARMIC_NO_BUNDLED_FMT ON)
|
||||
add_subdirectory(dynarmic)
|
||||
@@ -52,6 +53,14 @@ target_include_directories(unicorn-headers INTERFACE ./unicorn/include)
|
||||
# SoundTouch
|
||||
add_subdirectory(soundtouch)
|
||||
|
||||
# Xbyak
|
||||
if (ARCHITECTURE_x86_64)
|
||||
# Defined before "dynarmic" above
|
||||
# add_library(xbyak INTERFACE)
|
||||
target_include_directories(xbyak INTERFACE ./xbyak/xbyak)
|
||||
target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES)
|
||||
endif()
|
||||
|
||||
# Opus
|
||||
add_subdirectory(opus)
|
||||
target_include_directories(opus INTERFACE ./opus/include)
|
||||
|
||||
1
externals/xbyak
vendored
Submodule
1
externals/xbyak
vendored
Submodule
Submodule externals/xbyak added at 1de435ed04
@@ -44,6 +44,7 @@ add_library(common STATIC
|
||||
detached_tasks.cpp
|
||||
detached_tasks.h
|
||||
bit_field.h
|
||||
bit_set.h
|
||||
cityhash.cpp
|
||||
cityhash.h
|
||||
color.h
|
||||
@@ -94,9 +95,14 @@ if(ARCHITECTURE_x86_64)
|
||||
PRIVATE
|
||||
x64/cpu_detect.cpp
|
||||
x64/cpu_detect.h
|
||||
x64/xbyak_abi.h
|
||||
x64/xbyak_util.h
|
||||
)
|
||||
endif()
|
||||
|
||||
create_target_directory_groups(common)
|
||||
|
||||
target_link_libraries(common PUBLIC Boost::boost fmt microprofile)
|
||||
if (ARCHITECTURE_x86_64)
|
||||
target_link_libraries(common PRIVATE xbyak)
|
||||
endif()
|
||||
|
||||
@@ -52,8 +52,5 @@ __declspec(noinline, noreturn)
|
||||
#define DEBUG_ASSERT_MSG(_a_, _desc_, ...)
|
||||
#endif
|
||||
|
||||
#define UNIMPLEMENTED() ASSERT_MSG(false, "Unimplemented code!")
|
||||
#define UNIMPLEMENTED() LOG_CRITICAL(Debug, "Unimplemented code!")
|
||||
#define UNIMPLEMENTED_MSG(...) ASSERT_MSG(false, __VA_ARGS__)
|
||||
|
||||
#define UNIMPLEMENTED_IF(cond) ASSERT_MSG(!(cond), "Unimplemented code!")
|
||||
#define UNIMPLEMENTED_IF_MSG(cond, ...) ASSERT_MSG(!(cond), __VA_ARGS__)
|
||||
|
||||
244
src/common/bit_set.h
Normal file
244
src/common/bit_set.h
Normal file
@@ -0,0 +1,244 @@
|
||||
// This file is under the public domain.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#ifdef _WIN32
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
#include <initializer_list>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
#include "common/common_types.h"
|
||||
|
||||
// namespace avoids conflict with OS X Carbon; don't use BitSet<T> directly
|
||||
namespace Common {
|
||||
|
||||
// Helper functions:
|
||||
|
||||
#ifdef _MSC_VER
|
||||
template <typename T>
|
||||
static inline int CountSetBits(T v) {
|
||||
// from https://graphics.stanford.edu/~seander/bithacks.html
|
||||
// GCC has this built in, but MSVC's intrinsic will only emit the actual
|
||||
// POPCNT instruction, which we're not depending on
|
||||
v = v - ((v >> 1) & (T) ~(T)0 / 3);
|
||||
v = (v & (T) ~(T)0 / 15 * 3) + ((v >> 2) & (T) ~(T)0 / 15 * 3);
|
||||
v = (v + (v >> 4)) & (T) ~(T)0 / 255 * 15;
|
||||
return (T)(v * ((T) ~(T)0 / 255)) >> (sizeof(T) - 1) * 8;
|
||||
}
|
||||
static inline int LeastSignificantSetBit(u8 val) {
|
||||
unsigned long index;
|
||||
_BitScanForward(&index, val);
|
||||
return (int)index;
|
||||
}
|
||||
static inline int LeastSignificantSetBit(u16 val) {
|
||||
unsigned long index;
|
||||
_BitScanForward(&index, val);
|
||||
return (int)index;
|
||||
}
|
||||
static inline int LeastSignificantSetBit(u32 val) {
|
||||
unsigned long index;
|
||||
_BitScanForward(&index, val);
|
||||
return (int)index;
|
||||
}
|
||||
static inline int LeastSignificantSetBit(u64 val) {
|
||||
unsigned long index;
|
||||
_BitScanForward64(&index, val);
|
||||
return (int)index;
|
||||
}
|
||||
#else
|
||||
static inline int CountSetBits(u8 val) {
|
||||
return __builtin_popcount(val);
|
||||
}
|
||||
static inline int CountSetBits(u16 val) {
|
||||
return __builtin_popcount(val);
|
||||
}
|
||||
static inline int CountSetBits(u32 val) {
|
||||
return __builtin_popcount(val);
|
||||
}
|
||||
static inline int CountSetBits(u64 val) {
|
||||
return __builtin_popcountll(val);
|
||||
}
|
||||
static inline int LeastSignificantSetBit(u8 val) {
|
||||
return __builtin_ctz(val);
|
||||
}
|
||||
static inline int LeastSignificantSetBit(u16 val) {
|
||||
return __builtin_ctz(val);
|
||||
}
|
||||
static inline int LeastSignificantSetBit(u32 val) {
|
||||
return __builtin_ctz(val);
|
||||
}
|
||||
static inline int LeastSignificantSetBit(u64 val) {
|
||||
return __builtin_ctzll(val);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Similar to std::bitset, this is a class which encapsulates a bitset, i.e.
|
||||
// using the set bits of an integer to represent a set of integers. Like that
|
||||
// class, it acts like an array of bools:
|
||||
// BitSet32 bs;
|
||||
// bs[1] = true;
|
||||
// but also like the underlying integer ([0] = least significant bit):
|
||||
// BitSet32 bs2 = ...;
|
||||
// bs = (bs ^ bs2) & BitSet32(0xffff);
|
||||
// The following additional functionality is provided:
|
||||
// - Construction using an initializer list.
|
||||
// BitSet bs { 1, 2, 4, 8 };
|
||||
// - Efficiently iterating through the set bits:
|
||||
// for (int i : bs)
|
||||
// [i is the *index* of a set bit]
|
||||
// (This uses the appropriate CPU instruction to find the next set bit in one
|
||||
// operation.)
|
||||
// - Counting set bits using .Count() - see comment on that method.
|
||||
|
||||
// TODO: use constexpr when MSVC gets out of the Dark Ages
|
||||
|
||||
template <typename IntTy>
|
||||
class BitSet {
|
||||
static_assert(!std::is_signed_v<IntTy>, "BitSet should not be used with signed types");
|
||||
|
||||
public:
|
||||
// A reference to a particular bit, returned from operator[].
|
||||
class Ref {
|
||||
public:
|
||||
Ref(Ref&& other) : m_bs(other.m_bs), m_mask(other.m_mask) {}
|
||||
Ref(BitSet* bs, IntTy mask) : m_bs(bs), m_mask(mask) {}
|
||||
operator bool() const {
|
||||
return (m_bs->m_val & m_mask) != 0;
|
||||
}
|
||||
bool operator=(bool set) {
|
||||
m_bs->m_val = (m_bs->m_val & ~m_mask) | (set ? m_mask : 0);
|
||||
return set;
|
||||
}
|
||||
|
||||
private:
|
||||
BitSet* m_bs;
|
||||
IntTy m_mask;
|
||||
};
|
||||
|
||||
// A STL-like iterator is required to be able to use range-based for loops.
|
||||
class Iterator {
|
||||
public:
|
||||
Iterator(const Iterator& other) : m_val(other.m_val), m_bit(other.m_bit) {}
|
||||
Iterator(IntTy val) : m_val(val), m_bit(0) {}
|
||||
Iterator& operator=(Iterator other) {
|
||||
new (this) Iterator(other);
|
||||
return *this;
|
||||
}
|
||||
int operator*() {
|
||||
return m_bit + ComputeLsb();
|
||||
}
|
||||
Iterator& operator++() {
|
||||
int lsb = ComputeLsb();
|
||||
m_val >>= lsb + 1;
|
||||
m_bit += lsb + 1;
|
||||
m_has_lsb = false;
|
||||
return *this;
|
||||
}
|
||||
Iterator operator++(int _) {
|
||||
Iterator other(*this);
|
||||
++*this;
|
||||
return other;
|
||||
}
|
||||
bool operator==(Iterator other) const {
|
||||
return m_val == other.m_val;
|
||||
}
|
||||
bool operator!=(Iterator other) const {
|
||||
return m_val != other.m_val;
|
||||
}
|
||||
|
||||
private:
|
||||
int ComputeLsb() {
|
||||
if (!m_has_lsb) {
|
||||
m_lsb = LeastSignificantSetBit(m_val);
|
||||
m_has_lsb = true;
|
||||
}
|
||||
return m_lsb;
|
||||
}
|
||||
IntTy m_val;
|
||||
int m_bit;
|
||||
int m_lsb = -1;
|
||||
bool m_has_lsb = false;
|
||||
};
|
||||
|
||||
BitSet() : m_val(0) {}
|
||||
explicit BitSet(IntTy val) : m_val(val) {}
|
||||
BitSet(std::initializer_list<int> init) {
|
||||
m_val = 0;
|
||||
for (int bit : init)
|
||||
m_val |= (IntTy)1 << bit;
|
||||
}
|
||||
|
||||
static BitSet AllTrue(std::size_t count) {
|
||||
return BitSet(count == sizeof(IntTy) * 8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1));
|
||||
}
|
||||
|
||||
Ref operator[](std::size_t bit) {
|
||||
return Ref(this, (IntTy)1 << bit);
|
||||
}
|
||||
const Ref operator[](std::size_t bit) const {
|
||||
return (*const_cast<BitSet*>(this))[bit];
|
||||
}
|
||||
bool operator==(BitSet other) const {
|
||||
return m_val == other.m_val;
|
||||
}
|
||||
bool operator!=(BitSet other) const {
|
||||
return m_val != other.m_val;
|
||||
}
|
||||
bool operator<(BitSet other) const {
|
||||
return m_val < other.m_val;
|
||||
}
|
||||
bool operator>(BitSet other) const {
|
||||
return m_val > other.m_val;
|
||||
}
|
||||
BitSet operator|(BitSet other) const {
|
||||
return BitSet(m_val | other.m_val);
|
||||
}
|
||||
BitSet operator&(BitSet other) const {
|
||||
return BitSet(m_val & other.m_val);
|
||||
}
|
||||
BitSet operator^(BitSet other) const {
|
||||
return BitSet(m_val ^ other.m_val);
|
||||
}
|
||||
BitSet operator~() const {
|
||||
return BitSet(~m_val);
|
||||
}
|
||||
BitSet& operator|=(BitSet other) {
|
||||
return *this = *this | other;
|
||||
}
|
||||
BitSet& operator&=(BitSet other) {
|
||||
return *this = *this & other;
|
||||
}
|
||||
BitSet& operator^=(BitSet other) {
|
||||
return *this = *this ^ other;
|
||||
}
|
||||
operator u32() = delete;
|
||||
operator bool() {
|
||||
return m_val != 0;
|
||||
}
|
||||
|
||||
// Warning: Even though on modern CPUs this is a single fast instruction,
|
||||
// Dolphin's official builds do not currently assume POPCNT support on x86,
|
||||
// so slower explicit bit twiddling is generated. Still should generally
|
||||
// be faster than a loop.
|
||||
unsigned int Count() const {
|
||||
return CountSetBits(m_val);
|
||||
}
|
||||
|
||||
Iterator begin() const {
|
||||
return Iterator(m_val);
|
||||
}
|
||||
Iterator end() const {
|
||||
return Iterator(0);
|
||||
}
|
||||
|
||||
IntTy m_val;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
typedef Common::BitSet<u8> BitSet8;
|
||||
typedef Common::BitSet<u16> BitSet16;
|
||||
typedef Common::BitSet<u32> BitSet32;
|
||||
typedef Common::BitSet<u64> BitSet64;
|
||||
@@ -4,12 +4,18 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <type_traits>
|
||||
|
||||
namespace MathUtil {
|
||||
|
||||
constexpr float PI = 3.14159265f;
|
||||
static constexpr float PI = 3.14159265f;
|
||||
|
||||
inline bool IntervalsIntersect(unsigned start0, unsigned length0, unsigned start1,
|
||||
unsigned length1) {
|
||||
return (std::max(start0, start1) < std::min(start0 + length0, start1 + length1));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
struct Rectangle {
|
||||
@@ -18,16 +24,16 @@ struct Rectangle {
|
||||
T right{};
|
||||
T bottom{};
|
||||
|
||||
constexpr Rectangle() = default;
|
||||
Rectangle() = default;
|
||||
|
||||
constexpr Rectangle(T left, T top, T right, T bottom)
|
||||
Rectangle(T left, T top, T right, T bottom)
|
||||
: left(left), top(top), right(right), bottom(bottom) {}
|
||||
|
||||
T GetWidth() const {
|
||||
return std::abs(static_cast<std::make_signed_t<T>>(right - left));
|
||||
return std::abs(static_cast<typename std::make_signed<T>::type>(right - left));
|
||||
}
|
||||
T GetHeight() const {
|
||||
return std::abs(static_cast<std::make_signed_t<T>>(bottom - top));
|
||||
return std::abs(static_cast<typename std::make_signed<T>::type>(bottom - top));
|
||||
}
|
||||
Rectangle<T> TranslateX(const T x) const {
|
||||
return Rectangle{left + x, top, right + x, bottom};
|
||||
|
||||
@@ -25,6 +25,23 @@
|
||||
|
||||
namespace Common {
|
||||
|
||||
int CurrentThreadId() {
|
||||
#ifdef _MSC_VER
|
||||
return GetCurrentThreadId();
|
||||
#elif defined __APPLE__
|
||||
return mach_thread_self();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Supporting functions
|
||||
void SleepCurrentThread(int ms) {
|
||||
Sleep(ms);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) {
|
||||
@@ -45,7 +62,7 @@ void SwitchCurrentThread() {
|
||||
|
||||
// This is implemented much nicer in upcoming msvc++, see:
|
||||
// http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.100).aspx
|
||||
void SetCurrentThreadName(const char* name) {
|
||||
void SetCurrentThreadName(const char* szThreadName) {
|
||||
static const DWORD MS_VC_EXCEPTION = 0x406D1388;
|
||||
|
||||
#pragma pack(push, 8)
|
||||
@@ -58,7 +75,7 @@ void SetCurrentThreadName(const char* name) {
|
||||
#pragma pack(pop)
|
||||
|
||||
info.dwType = 0x1000;
|
||||
info.szName = name;
|
||||
info.szName = szThreadName;
|
||||
info.dwThreadID = -1; // dwThreadID;
|
||||
info.dwFlags = 0;
|
||||
|
||||
@@ -90,6 +107,10 @@ void SetCurrentThreadAffinity(u32 mask) {
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
void SleepCurrentThread(int ms) {
|
||||
usleep(1000 * ms);
|
||||
}
|
||||
|
||||
void SwitchCurrentThread() {
|
||||
usleep(1000 * 1);
|
||||
}
|
||||
@@ -97,15 +118,15 @@ void SwitchCurrentThread() {
|
||||
|
||||
// MinGW with the POSIX threading model does not support pthread_setname_np
|
||||
#if !defined(_WIN32) || defined(_MSC_VER)
|
||||
void SetCurrentThreadName(const char* name) {
|
||||
void SetCurrentThreadName(const char* szThreadName) {
|
||||
#ifdef __APPLE__
|
||||
pthread_setname_np(name);
|
||||
pthread_setname_np(szThreadName);
|
||||
#elif defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
pthread_set_name_np(pthread_self(), name);
|
||||
pthread_set_name_np(pthread_self(), szThreadName);
|
||||
#elif defined(__NetBSD__)
|
||||
pthread_setname_np(pthread_self(), "%s", (void*)name);
|
||||
pthread_setname_np(pthread_self(), "%s", (void*)szThreadName);
|
||||
#else
|
||||
pthread_setname_np(pthread_self(), name);
|
||||
pthread_setname_np(pthread_self(), szThreadName);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -13,8 +13,15 @@
|
||||
|
||||
namespace Common {
|
||||
|
||||
int CurrentThreadId();
|
||||
|
||||
void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask);
|
||||
void SetCurrentThreadAffinity(u32 mask);
|
||||
|
||||
class Event {
|
||||
public:
|
||||
Event() : is_set(false) {}
|
||||
|
||||
void Set() {
|
||||
std::lock_guard<std::mutex> lk(mutex);
|
||||
if (!is_set) {
|
||||
@@ -46,14 +53,14 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
bool is_set = false;
|
||||
bool is_set;
|
||||
std::condition_variable condvar;
|
||||
std::mutex mutex;
|
||||
};
|
||||
|
||||
class Barrier {
|
||||
public:
|
||||
explicit Barrier(std::size_t count_) : count(count_) {}
|
||||
explicit Barrier(std::size_t count_) : count(count_), waiting(0), generation(0) {}
|
||||
|
||||
/// Blocks until all "count" threads have called Sync()
|
||||
void Sync() {
|
||||
@@ -73,13 +80,12 @@ public:
|
||||
private:
|
||||
std::condition_variable condvar;
|
||||
std::mutex mutex;
|
||||
std::size_t count;
|
||||
std::size_t waiting = 0;
|
||||
std::size_t generation = 0; // Incremented once each time the barrier is used
|
||||
const std::size_t count;
|
||||
std::size_t waiting;
|
||||
std::size_t generation; // Incremented once each time the barrier is used
|
||||
};
|
||||
|
||||
void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask);
|
||||
void SetCurrentThreadAffinity(u32 mask);
|
||||
void SleepCurrentThread(int ms);
|
||||
void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms
|
||||
void SetCurrentThreadName(const char* name);
|
||||
|
||||
|
||||
222
src/common/x64/xbyak_abi.h
Normal file
222
src/common/x64/xbyak_abi.h
Normal file
@@ -0,0 +1,222 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <initializer_list>
|
||||
#include <xbyak.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/bit_set.h"
|
||||
|
||||
namespace Common::X64 {
|
||||
|
||||
inline int RegToIndex(const Xbyak::Reg& reg) {
|
||||
using Kind = Xbyak::Reg::Kind;
|
||||
ASSERT_MSG((reg.getKind() & (Kind::REG | Kind::XMM)) != 0,
|
||||
"RegSet only support GPRs and XMM registers.");
|
||||
ASSERT_MSG(reg.getIdx() < 16, "RegSet only supports XXM0-15.");
|
||||
return reg.getIdx() + (reg.getKind() == Kind::REG ? 0 : 16);
|
||||
}
|
||||
|
||||
inline Xbyak::Reg64 IndexToReg64(int reg_index) {
|
||||
ASSERT(reg_index < 16);
|
||||
return Xbyak::Reg64(reg_index);
|
||||
}
|
||||
|
||||
inline Xbyak::Xmm IndexToXmm(int reg_index) {
|
||||
ASSERT(reg_index >= 16 && reg_index < 32);
|
||||
return Xbyak::Xmm(reg_index - 16);
|
||||
}
|
||||
|
||||
inline Xbyak::Reg IndexToReg(int reg_index) {
|
||||
if (reg_index < 16) {
|
||||
return IndexToReg64(reg_index);
|
||||
} else {
|
||||
return IndexToXmm(reg_index);
|
||||
}
|
||||
}
|
||||
|
||||
inline BitSet32 BuildRegSet(std::initializer_list<Xbyak::Reg> regs) {
|
||||
BitSet32 bits;
|
||||
for (const Xbyak::Reg& reg : regs) {
|
||||
bits[RegToIndex(reg)] = true;
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
const BitSet32 ABI_ALL_GPRS(0x0000FFFF);
|
||||
const BitSet32 ABI_ALL_XMMS(0xFFFF0000);
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
// Microsoft x64 ABI
|
||||
const Xbyak::Reg ABI_RETURN = Xbyak::util::rax;
|
||||
const Xbyak::Reg ABI_PARAM1 = Xbyak::util::rcx;
|
||||
const Xbyak::Reg ABI_PARAM2 = Xbyak::util::rdx;
|
||||
const Xbyak::Reg ABI_PARAM3 = Xbyak::util::r8;
|
||||
const Xbyak::Reg ABI_PARAM4 = Xbyak::util::r9;
|
||||
|
||||
const BitSet32 ABI_ALL_CALLER_SAVED = BuildRegSet({
|
||||
// GPRs
|
||||
Xbyak::util::rcx,
|
||||
Xbyak::util::rdx,
|
||||
Xbyak::util::r8,
|
||||
Xbyak::util::r9,
|
||||
Xbyak::util::r10,
|
||||
Xbyak::util::r11,
|
||||
// XMMs
|
||||
Xbyak::util::xmm0,
|
||||
Xbyak::util::xmm1,
|
||||
Xbyak::util::xmm2,
|
||||
Xbyak::util::xmm3,
|
||||
Xbyak::util::xmm4,
|
||||
Xbyak::util::xmm5,
|
||||
});
|
||||
|
||||
const BitSet32 ABI_ALL_CALLEE_SAVED = BuildRegSet({
|
||||
// GPRs
|
||||
Xbyak::util::rbx,
|
||||
Xbyak::util::rsi,
|
||||
Xbyak::util::rdi,
|
||||
Xbyak::util::rbp,
|
||||
Xbyak::util::r12,
|
||||
Xbyak::util::r13,
|
||||
Xbyak::util::r14,
|
||||
Xbyak::util::r15,
|
||||
// XMMs
|
||||
Xbyak::util::xmm6,
|
||||
Xbyak::util::xmm7,
|
||||
Xbyak::util::xmm8,
|
||||
Xbyak::util::xmm9,
|
||||
Xbyak::util::xmm10,
|
||||
Xbyak::util::xmm11,
|
||||
Xbyak::util::xmm12,
|
||||
Xbyak::util::xmm13,
|
||||
Xbyak::util::xmm14,
|
||||
Xbyak::util::xmm15,
|
||||
});
|
||||
|
||||
constexpr std::size_t ABI_SHADOW_SPACE = 0x20;
|
||||
|
||||
#else
|
||||
|
||||
// System V x86-64 ABI
|
||||
const Xbyak::Reg ABI_RETURN = Xbyak::util::rax;
|
||||
const Xbyak::Reg ABI_PARAM1 = Xbyak::util::rdi;
|
||||
const Xbyak::Reg ABI_PARAM2 = Xbyak::util::rsi;
|
||||
const Xbyak::Reg ABI_PARAM3 = Xbyak::util::rdx;
|
||||
const Xbyak::Reg ABI_PARAM4 = Xbyak::util::rcx;
|
||||
|
||||
const BitSet32 ABI_ALL_CALLER_SAVED = BuildRegSet({
|
||||
// GPRs
|
||||
Xbyak::util::rcx,
|
||||
Xbyak::util::rdx,
|
||||
Xbyak::util::rdi,
|
||||
Xbyak::util::rsi,
|
||||
Xbyak::util::r8,
|
||||
Xbyak::util::r9,
|
||||
Xbyak::util::r10,
|
||||
Xbyak::util::r11,
|
||||
// XMMs
|
||||
Xbyak::util::xmm0,
|
||||
Xbyak::util::xmm1,
|
||||
Xbyak::util::xmm2,
|
||||
Xbyak::util::xmm3,
|
||||
Xbyak::util::xmm4,
|
||||
Xbyak::util::xmm5,
|
||||
Xbyak::util::xmm6,
|
||||
Xbyak::util::xmm7,
|
||||
Xbyak::util::xmm8,
|
||||
Xbyak::util::xmm9,
|
||||
Xbyak::util::xmm10,
|
||||
Xbyak::util::xmm11,
|
||||
Xbyak::util::xmm12,
|
||||
Xbyak::util::xmm13,
|
||||
Xbyak::util::xmm14,
|
||||
Xbyak::util::xmm15,
|
||||
});
|
||||
|
||||
const BitSet32 ABI_ALL_CALLEE_SAVED = BuildRegSet({
|
||||
// GPRs
|
||||
Xbyak::util::rbx,
|
||||
Xbyak::util::rbp,
|
||||
Xbyak::util::r12,
|
||||
Xbyak::util::r13,
|
||||
Xbyak::util::r14,
|
||||
Xbyak::util::r15,
|
||||
});
|
||||
|
||||
constexpr std::size_t ABI_SHADOW_SPACE = 0;
|
||||
|
||||
#endif
|
||||
|
||||
inline void ABI_CalculateFrameSize(BitSet32 regs, std::size_t rsp_alignment,
|
||||
std::size_t needed_frame_size, s32* out_subtraction,
|
||||
s32* out_xmm_offset) {
|
||||
int count = (regs & ABI_ALL_GPRS).Count();
|
||||
rsp_alignment -= count * 8;
|
||||
std::size_t subtraction = 0;
|
||||
int xmm_count = (regs & ABI_ALL_XMMS).Count();
|
||||
if (xmm_count) {
|
||||
// If we have any XMMs to save, we must align the stack here.
|
||||
subtraction = rsp_alignment & 0xF;
|
||||
}
|
||||
subtraction += 0x10 * xmm_count;
|
||||
std::size_t xmm_base_subtraction = subtraction;
|
||||
subtraction += needed_frame_size;
|
||||
subtraction += ABI_SHADOW_SPACE;
|
||||
// Final alignment.
|
||||
rsp_alignment -= subtraction;
|
||||
subtraction += rsp_alignment & 0xF;
|
||||
|
||||
*out_subtraction = (s32)subtraction;
|
||||
*out_xmm_offset = (s32)(subtraction - xmm_base_subtraction);
|
||||
}
|
||||
|
||||
inline std::size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs,
|
||||
std::size_t rsp_alignment,
|
||||
std::size_t needed_frame_size = 0) {
|
||||
s32 subtraction, xmm_offset;
|
||||
ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset);
|
||||
|
||||
for (int reg_index : (regs & ABI_ALL_GPRS)) {
|
||||
code.push(IndexToReg64(reg_index));
|
||||
}
|
||||
|
||||
if (subtraction != 0) {
|
||||
code.sub(code.rsp, subtraction);
|
||||
}
|
||||
|
||||
for (int reg_index : (regs & ABI_ALL_XMMS)) {
|
||||
code.movaps(code.xword[code.rsp + xmm_offset], IndexToXmm(reg_index));
|
||||
xmm_offset += 0x10;
|
||||
}
|
||||
|
||||
return ABI_SHADOW_SPACE;
|
||||
}
|
||||
|
||||
inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs,
|
||||
std::size_t rsp_alignment,
|
||||
std::size_t needed_frame_size = 0) {
|
||||
s32 subtraction, xmm_offset;
|
||||
ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset);
|
||||
|
||||
for (int reg_index : (regs & ABI_ALL_XMMS)) {
|
||||
code.movaps(IndexToXmm(reg_index), code.xword[code.rsp + xmm_offset]);
|
||||
xmm_offset += 0x10;
|
||||
}
|
||||
|
||||
if (subtraction != 0) {
|
||||
code.add(code.rsp, subtraction);
|
||||
}
|
||||
|
||||
// GPRs need to be popped in reverse order
|
||||
for (int reg_index = 15; reg_index >= 0; reg_index--) {
|
||||
if (regs[reg_index]) {
|
||||
code.pop(IndexToReg64(reg_index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Common::X64
|
||||
47
src/common/x64/xbyak_util.h
Normal file
47
src/common/x64/xbyak_util.h
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <xbyak.h>
|
||||
#include "common/x64/xbyak_abi.h"
|
||||
|
||||
namespace Common::X64 {
|
||||
|
||||
// Constants for use with cmpps/cmpss
|
||||
enum {
|
||||
CMP_EQ = 0,
|
||||
CMP_LT = 1,
|
||||
CMP_LE = 2,
|
||||
CMP_UNORD = 3,
|
||||
CMP_NEQ = 4,
|
||||
CMP_NLT = 5,
|
||||
CMP_NLE = 6,
|
||||
CMP_ORD = 7,
|
||||
};
|
||||
|
||||
inline bool IsWithin2G(uintptr_t ref, uintptr_t target) {
|
||||
u64 distance = target - (ref + 5);
|
||||
return !(distance >= 0x8000'0000ULL && distance <= ~0x8000'0000ULL);
|
||||
}
|
||||
|
||||
inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) {
|
||||
return IsWithin2G(reinterpret_cast<uintptr_t>(code.getCurr()), target);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) {
|
||||
static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer.");
|
||||
std::size_t addr = reinterpret_cast<std::size_t>(f);
|
||||
if (IsWithin2G(code, addr)) {
|
||||
code.call(f);
|
||||
} else {
|
||||
// ABI_RETURN is a safe temp register to use before a call
|
||||
code.mov(ABI_RETURN, addr);
|
||||
code.call(ABI_RETURN);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Common::X64
|
||||
@@ -12,8 +12,6 @@ add_library(core STATIC
|
||||
core_timing.h
|
||||
core_timing_util.cpp
|
||||
core_timing_util.h
|
||||
cpu_core_manager.cpp
|
||||
cpu_core_manager.h
|
||||
crypto/aes_util.cpp
|
||||
crypto/aes_util.h
|
||||
crypto/encryption_layer.cpp
|
||||
@@ -158,8 +156,6 @@ add_library(core STATIC
|
||||
hle/service/am/applets/applets.h
|
||||
hle/service/am/applets/software_keyboard.cpp
|
||||
hle/service/am/applets/software_keyboard.h
|
||||
hle/service/am/applets/stub_applet.cpp
|
||||
hle/service/am/applets/stub_applet.h
|
||||
hle/service/am/idle.cpp
|
||||
hle/service/am/idle.h
|
||||
hle/service/am/omm.cpp
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "core/core.h"
|
||||
#include "core/core_cpu.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/cpu_core_manager.h"
|
||||
#include "core/file_sys/mode.h"
|
||||
#include "core/file_sys/vfs_concat.h"
|
||||
#include "core/file_sys/vfs_real.h"
|
||||
@@ -29,6 +28,7 @@
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/perf_stats.h"
|
||||
#include "core/settings.h"
|
||||
#include "core/telemetry_session.h"
|
||||
#include "frontend/applets/software_keyboard.h"
|
||||
#include "video_core/debug_utils/debug_utils.h"
|
||||
@@ -71,22 +71,64 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
||||
|
||||
return vfs->OpenFile(path, FileSys::Mode::Read);
|
||||
}
|
||||
|
||||
/// Runs a CPU core while the system is powered on
|
||||
void RunCpuCore(Cpu& cpu_state) {
|
||||
while (Core::System::GetInstance().IsPoweredOn()) {
|
||||
cpu_state.RunLoop(true);
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
struct System::Impl {
|
||||
Cpu& CurrentCpuCore() {
|
||||
return cpu_core_manager.GetCurrentCore();
|
||||
if (Settings::values.use_multi_core) {
|
||||
const auto& search = thread_to_cpu.find(std::this_thread::get_id());
|
||||
ASSERT(search != thread_to_cpu.end());
|
||||
ASSERT(search->second);
|
||||
return *search->second;
|
||||
}
|
||||
|
||||
// Otherwise, use single-threaded mode active_core variable
|
||||
return *cpu_cores[active_core];
|
||||
}
|
||||
|
||||
ResultStatus RunLoop(bool tight_loop) {
|
||||
status = ResultStatus::Success;
|
||||
|
||||
cpu_core_manager.RunLoop(tight_loop);
|
||||
// Update thread_to_cpu in case Core 0 is run from a different host thread
|
||||
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();
|
||||
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
GDBStub::HandlePacket();
|
||||
|
||||
// If the loop is halted and we want to step, use a tiny (1) number of instructions to
|
||||
// execute. Otherwise, get out of the loop function.
|
||||
if (GDBStub::GetCpuHaltFlag()) {
|
||||
if (GDBStub::GetCpuStepFlag()) {
|
||||
tight_loop = false;
|
||||
} else {
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
|
||||
cpu_cores[active_core]->RunLoop(tight_loop);
|
||||
if (Settings::values.use_multi_core) {
|
||||
// Cores 1-3 are run on other threads in this mode
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
GDBStub::SetCpuStepFlag(false);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
|
||||
ResultStatus Init(Frontend::EmuWindow& emu_window) {
|
||||
LOG_DEBUG(HW_Memory, "initialized OK");
|
||||
|
||||
CoreTiming::Init();
|
||||
@@ -103,6 +145,12 @@ struct System::Impl {
|
||||
auto main_process = Kernel::Process::Create(kernel, "main");
|
||||
kernel.MakeCurrentProcess(main_process.get());
|
||||
|
||||
cpu_barrier = std::make_unique<CpuBarrier>();
|
||||
cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
|
||||
for (std::size_t index = 0; index < cpu_cores.size(); ++index) {
|
||||
cpu_cores[index] = std::make_unique<Cpu>(*cpu_exclusive_monitor, *cpu_barrier, index);
|
||||
}
|
||||
|
||||
telemetry_session = std::make_unique<Core::TelemetrySession>();
|
||||
service_manager = std::make_shared<Service::SM::ServiceManager>();
|
||||
|
||||
@@ -116,8 +164,17 @@ struct System::Impl {
|
||||
|
||||
gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer());
|
||||
|
||||
cpu_core_manager.Initialize(system);
|
||||
is_powered_on = true;
|
||||
// Create threads for CPU cores 1-3, and build thread_to_cpu map
|
||||
// CPU core 0 is run on the main thread
|
||||
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();
|
||||
if (Settings::values.use_multi_core) {
|
||||
for (std::size_t index = 0; index < cpu_core_threads.size(); ++index) {
|
||||
cpu_core_threads[index] =
|
||||
std::make_unique<std::thread>(RunCpuCore, std::ref(*cpu_cores[index + 1]));
|
||||
thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1].get();
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DEBUG(Core, "Initialized OK");
|
||||
|
||||
// Reset counters and set time origin to current frame
|
||||
@@ -127,8 +184,7 @@ struct System::Impl {
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
|
||||
const std::string& filepath) {
|
||||
ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
|
||||
app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
|
||||
|
||||
if (!app_loader) {
|
||||
@@ -145,7 +201,7 @@ struct System::Impl {
|
||||
return ResultStatus::ErrorSystemMode;
|
||||
}
|
||||
|
||||
ResultStatus init_result{Init(system, emu_window)};
|
||||
ResultStatus init_result{Init(emu_window)};
|
||||
if (init_result != ResultStatus::Success) {
|
||||
LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
|
||||
static_cast<int>(init_result));
|
||||
@@ -175,8 +231,6 @@ struct System::Impl {
|
||||
Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
|
||||
perf_results.frametime * 1000.0);
|
||||
|
||||
is_powered_on = false;
|
||||
|
||||
// Shutdown emulation session
|
||||
renderer.reset();
|
||||
GDBStub::Shutdown();
|
||||
@@ -186,7 +240,19 @@ struct System::Impl {
|
||||
gpu_core.reset();
|
||||
|
||||
// Close all CPU/threading state
|
||||
cpu_core_manager.Shutdown();
|
||||
cpu_barrier->NotifyEnd();
|
||||
if (Settings::values.use_multi_core) {
|
||||
for (auto& thread : cpu_core_threads) {
|
||||
thread->join();
|
||||
thread.reset();
|
||||
}
|
||||
}
|
||||
thread_to_cpu.clear();
|
||||
for (auto& cpu_core : cpu_cores) {
|
||||
cpu_core.reset();
|
||||
}
|
||||
cpu_exclusive_monitor.reset();
|
||||
cpu_barrier.reset();
|
||||
|
||||
// Shutdown kernel and core timing
|
||||
kernel.Shutdown();
|
||||
@@ -223,8 +289,11 @@ struct System::Impl {
|
||||
std::unique_ptr<VideoCore::RendererBase> renderer;
|
||||
std::unique_ptr<Tegra::GPU> gpu_core;
|
||||
std::shared_ptr<Tegra::DebugContext> debug_context;
|
||||
CpuCoreManager cpu_core_manager;
|
||||
bool is_powered_on = false;
|
||||
std::unique_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
|
||||
std::unique_ptr<CpuBarrier> cpu_barrier;
|
||||
std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
|
||||
std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;
|
||||
std::size_t active_core{}; ///< Active core, only used in single thread mode
|
||||
|
||||
/// Frontend applets
|
||||
std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
|
||||
@@ -238,6 +307,9 @@ struct System::Impl {
|
||||
ResultStatus status = ResultStatus::Success;
|
||||
std::string status_details = "";
|
||||
|
||||
/// Map of guest threads to CPU cores
|
||||
std::map<std::thread::id, Cpu*> thread_to_cpu;
|
||||
|
||||
Core::PerfStats perf_stats;
|
||||
Core::FrameLimiter frame_limiter;
|
||||
};
|
||||
@@ -262,15 +334,17 @@ System::ResultStatus System::SingleStep() {
|
||||
}
|
||||
|
||||
void System::InvalidateCpuInstructionCaches() {
|
||||
impl->cpu_core_manager.InvalidateAllInstructionCaches();
|
||||
for (auto& cpu : impl->cpu_cores) {
|
||||
cpu->ArmInterface().ClearInstructionCache();
|
||||
}
|
||||
}
|
||||
|
||||
System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
|
||||
return impl->Load(*this, emu_window, filepath);
|
||||
return impl->Load(emu_window, filepath);
|
||||
}
|
||||
|
||||
bool System::IsPoweredOn() const {
|
||||
return impl->is_powered_on;
|
||||
return impl->cpu_barrier && impl->cpu_barrier->IsAlive();
|
||||
}
|
||||
|
||||
void System::PrepareReschedule() {
|
||||
@@ -334,20 +408,21 @@ const ARM_Interface& System::ArmInterface(std::size_t core_index) const {
|
||||
}
|
||||
|
||||
Cpu& System::CpuCore(std::size_t core_index) {
|
||||
return impl->cpu_core_manager.GetCore(core_index);
|
||||
ASSERT(core_index < NUM_CPU_CORES);
|
||||
return *impl->cpu_cores[core_index];
|
||||
}
|
||||
|
||||
const Cpu& System::CpuCore(std::size_t core_index) const {
|
||||
ASSERT(core_index < NUM_CPU_CORES);
|
||||
return impl->cpu_core_manager.GetCore(core_index);
|
||||
return *impl->cpu_cores[core_index];
|
||||
}
|
||||
|
||||
ExclusiveMonitor& System::Monitor() {
|
||||
return impl->cpu_core_manager.GetExclusiveMonitor();
|
||||
return *impl->cpu_exclusive_monitor;
|
||||
}
|
||||
|
||||
const ExclusiveMonitor& System::Monitor() const {
|
||||
return impl->cpu_core_manager.GetExclusiveMonitor();
|
||||
return *impl->cpu_exclusive_monitor;
|
||||
}
|
||||
|
||||
Tegra::GPU& System::GPU() {
|
||||
@@ -431,7 +506,7 @@ const Core::Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() cons
|
||||
}
|
||||
|
||||
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
|
||||
return impl->Init(*this, emu_window);
|
||||
return impl->Init(emu_window);
|
||||
}
|
||||
|
||||
void System::Shutdown() {
|
||||
|
||||
@@ -1,142 +0,0 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_cpu.h"
|
||||
#include "core/cpu_core_manager.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Core {
|
||||
namespace {
|
||||
void RunCpuCore(const System& system, Cpu& cpu_state) {
|
||||
while (system.IsPoweredOn()) {
|
||||
cpu_state.RunLoop(true);
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
CpuCoreManager::CpuCoreManager() = default;
|
||||
CpuCoreManager::~CpuCoreManager() = default;
|
||||
|
||||
void CpuCoreManager::Initialize(System& system) {
|
||||
barrier = std::make_unique<CpuBarrier>();
|
||||
exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size());
|
||||
|
||||
for (std::size_t index = 0; index < cores.size(); ++index) {
|
||||
cores[index] = std::make_unique<Cpu>(*exclusive_monitor, *barrier, index);
|
||||
}
|
||||
|
||||
// Create threads for CPU cores 1-3, and build thread_to_cpu map
|
||||
// CPU core 0 is run on the main thread
|
||||
thread_to_cpu[std::this_thread::get_id()] = cores[0].get();
|
||||
if (!Settings::values.use_multi_core) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < core_threads.size(); ++index) {
|
||||
core_threads[index] = std::make_unique<std::thread>(RunCpuCore, std::cref(system),
|
||||
std::ref(*cores[index + 1]));
|
||||
thread_to_cpu[core_threads[index]->get_id()] = cores[index + 1].get();
|
||||
}
|
||||
}
|
||||
|
||||
void CpuCoreManager::Shutdown() {
|
||||
barrier->NotifyEnd();
|
||||
if (Settings::values.use_multi_core) {
|
||||
for (auto& thread : core_threads) {
|
||||
thread->join();
|
||||
thread.reset();
|
||||
}
|
||||
}
|
||||
|
||||
thread_to_cpu.clear();
|
||||
for (auto& cpu_core : cores) {
|
||||
cpu_core.reset();
|
||||
}
|
||||
|
||||
exclusive_monitor.reset();
|
||||
barrier.reset();
|
||||
}
|
||||
|
||||
Cpu& CpuCoreManager::GetCore(std::size_t index) {
|
||||
return *cores.at(index);
|
||||
}
|
||||
|
||||
const Cpu& CpuCoreManager::GetCore(std::size_t index) const {
|
||||
return *cores.at(index);
|
||||
}
|
||||
|
||||
ExclusiveMonitor& CpuCoreManager::GetExclusiveMonitor() {
|
||||
return *exclusive_monitor;
|
||||
}
|
||||
|
||||
const ExclusiveMonitor& CpuCoreManager::GetExclusiveMonitor() const {
|
||||
return *exclusive_monitor;
|
||||
}
|
||||
|
||||
Cpu& CpuCoreManager::GetCurrentCore() {
|
||||
if (Settings::values.use_multi_core) {
|
||||
const auto& search = thread_to_cpu.find(std::this_thread::get_id());
|
||||
ASSERT(search != thread_to_cpu.end());
|
||||
ASSERT(search->second);
|
||||
return *search->second;
|
||||
}
|
||||
|
||||
// Otherwise, use single-threaded mode active_core variable
|
||||
return *cores[active_core];
|
||||
}
|
||||
|
||||
const Cpu& CpuCoreManager::GetCurrentCore() const {
|
||||
if (Settings::values.use_multi_core) {
|
||||
const auto& search = thread_to_cpu.find(std::this_thread::get_id());
|
||||
ASSERT(search != thread_to_cpu.end());
|
||||
ASSERT(search->second);
|
||||
return *search->second;
|
||||
}
|
||||
|
||||
// Otherwise, use single-threaded mode active_core variable
|
||||
return *cores[active_core];
|
||||
}
|
||||
|
||||
void CpuCoreManager::RunLoop(bool tight_loop) {
|
||||
// Update thread_to_cpu in case Core 0 is run from a different host thread
|
||||
thread_to_cpu[std::this_thread::get_id()] = cores[0].get();
|
||||
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
GDBStub::HandlePacket();
|
||||
|
||||
// If the loop is halted and we want to step, use a tiny (1) number of instructions to
|
||||
// execute. Otherwise, get out of the loop function.
|
||||
if (GDBStub::GetCpuHaltFlag()) {
|
||||
if (GDBStub::GetCpuStepFlag()) {
|
||||
tight_loop = false;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
|
||||
cores[active_core]->RunLoop(tight_loop);
|
||||
if (Settings::values.use_multi_core) {
|
||||
// Cores 1-3 are run on other threads in this mode
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
GDBStub::SetCpuStepFlag(false);
|
||||
}
|
||||
}
|
||||
|
||||
void CpuCoreManager::InvalidateAllInstructionCaches() {
|
||||
for (auto& cpu : cores) {
|
||||
cpu->ArmInterface().ClearInstructionCache();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
@@ -1,59 +0,0 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
namespace Core {
|
||||
|
||||
class Cpu;
|
||||
class CpuBarrier;
|
||||
class ExclusiveMonitor;
|
||||
class System;
|
||||
|
||||
class CpuCoreManager {
|
||||
public:
|
||||
CpuCoreManager();
|
||||
CpuCoreManager(const CpuCoreManager&) = delete;
|
||||
CpuCoreManager(CpuCoreManager&&) = delete;
|
||||
|
||||
~CpuCoreManager();
|
||||
|
||||
CpuCoreManager& operator=(const CpuCoreManager&) = delete;
|
||||
CpuCoreManager& operator=(CpuCoreManager&&) = delete;
|
||||
|
||||
void Initialize(System& system);
|
||||
void Shutdown();
|
||||
|
||||
Cpu& GetCore(std::size_t index);
|
||||
const Cpu& GetCore(std::size_t index) const;
|
||||
|
||||
Cpu& GetCurrentCore();
|
||||
const Cpu& GetCurrentCore() const;
|
||||
|
||||
ExclusiveMonitor& GetExclusiveMonitor();
|
||||
const ExclusiveMonitor& GetExclusiveMonitor() const;
|
||||
|
||||
void RunLoop(bool tight_loop);
|
||||
|
||||
void InvalidateAllInstructionCaches();
|
||||
|
||||
private:
|
||||
static constexpr std::size_t NUM_CPU_CORES = 4;
|
||||
|
||||
std::unique_ptr<ExclusiveMonitor> exclusive_monitor;
|
||||
std::unique_ptr<CpuBarrier> barrier;
|
||||
std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cores;
|
||||
std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> core_threads;
|
||||
std::size_t active_core{}; ///< Active core, only used in single thread mode
|
||||
|
||||
/// Map of guest threads to CPU cores
|
||||
std::map<std::thread::id, Cpu*> thread_to_cpu;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
@@ -32,18 +32,7 @@ enum class GamecardSize : u8 {
|
||||
};
|
||||
|
||||
struct GamecardInfo {
|
||||
u64_le firmware_version;
|
||||
u32_le access_control_flags;
|
||||
u32_le read_wait_time1;
|
||||
u32_le read_wait_time2;
|
||||
u32_le write_wait_time1;
|
||||
u32_le write_wait_time2;
|
||||
u32_le firmware_mode;
|
||||
u32_le cup_version;
|
||||
std::array<u8, 4> reserved1;
|
||||
u64_le update_partition_hash;
|
||||
u64_le cup_id;
|
||||
std::array<u8, 0x38> reserved2;
|
||||
std::array<u8, 0x70> data;
|
||||
};
|
||||
static_assert(sizeof(GamecardInfo) == 0x70, "GamecardInfo has incorrect size.");
|
||||
|
||||
|
||||
@@ -12,23 +12,12 @@
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
namespace Kernel {
|
||||
namespace {
|
||||
constexpr u16 GetSlot(Handle handle) {
|
||||
return handle >> 15;
|
||||
}
|
||||
|
||||
constexpr u16 GetGeneration(Handle handle) {
|
||||
return handle & 0x7FFF;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
HandleTable::HandleTable() {
|
||||
next_generation = 1;
|
||||
Clear();
|
||||
}
|
||||
|
||||
HandleTable::~HandleTable() = default;
|
||||
|
||||
ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) {
|
||||
DEBUG_ASSERT(obj != nullptr);
|
||||
|
||||
|
||||
@@ -43,7 +43,6 @@ enum KernelHandle : Handle {
|
||||
class HandleTable final : NonCopyable {
|
||||
public:
|
||||
HandleTable();
|
||||
~HandleTable();
|
||||
|
||||
/**
|
||||
* Allocates a handle for the given object.
|
||||
@@ -90,8 +89,18 @@ public:
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
/// This is the maximum limit of handles allowed per process in Horizon
|
||||
static constexpr std::size_t MAX_COUNT = 1024;
|
||||
/**
|
||||
* This is the maximum limit of handles allowed per process in CTR-OS. It can be further
|
||||
* reduced by ExHeader values, but this is not emulated here.
|
||||
*/
|
||||
static const std::size_t MAX_COUNT = 4096;
|
||||
|
||||
static u16 GetSlot(Handle handle) {
|
||||
return handle >> 15;
|
||||
}
|
||||
static u16 GetGeneration(Handle handle) {
|
||||
return handle & 0x7FFF;
|
||||
}
|
||||
|
||||
/// Stores the Object referenced by the handle or null if the slot is empty.
|
||||
std::array<SharedPtr<Object>, MAX_COUNT> objects;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <bitset>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/container/static_vector.hpp>
|
||||
|
||||
@@ -61,7 +61,7 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, SharedPtr<Proce
|
||||
}
|
||||
|
||||
SharedPtr<SharedMemory> SharedMemory::CreateForApplet(
|
||||
KernelCore& kernel, std::shared_ptr<std::vector<u8>> heap_block, std::size_t offset, u64 size,
|
||||
KernelCore& kernel, std::shared_ptr<std::vector<u8>> heap_block, u32 offset, u32 size,
|
||||
MemoryPermission permissions, MemoryPermission other_permissions, std::string name) {
|
||||
SharedPtr<SharedMemory> shared_memory(new SharedMemory(kernel));
|
||||
|
||||
@@ -78,10 +78,10 @@ SharedPtr<SharedMemory> SharedMemory::CreateForApplet(
|
||||
return shared_memory;
|
||||
}
|
||||
|
||||
ResultCode SharedMemory::Map(Process& target_process, VAddr address, MemoryPermission permissions,
|
||||
ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions,
|
||||
MemoryPermission other_permissions) {
|
||||
const MemoryPermission own_other_permissions =
|
||||
&target_process == owner_process ? this->permissions : this->other_permissions;
|
||||
target_process == owner_process ? this->permissions : this->other_permissions;
|
||||
|
||||
// Automatically allocated memory blocks can only be mapped with other_permissions = DontCare
|
||||
if (base_address == 0 && other_permissions != MemoryPermission::DontCare) {
|
||||
@@ -106,7 +106,7 @@ ResultCode SharedMemory::Map(Process& target_process, VAddr address, MemoryPermi
|
||||
VAddr target_address = address;
|
||||
|
||||
// Map the memory block into the target process
|
||||
auto result = target_process.VMManager().MapMemoryBlock(
|
||||
auto result = target_process->VMManager().MapMemoryBlock(
|
||||
target_address, backing_block, backing_block_offset, size, MemoryState::Shared);
|
||||
if (result.Failed()) {
|
||||
LOG_ERROR(
|
||||
@@ -116,14 +116,14 @@ ResultCode SharedMemory::Map(Process& target_process, VAddr address, MemoryPermi
|
||||
return result.Code();
|
||||
}
|
||||
|
||||
return target_process.VMManager().ReprotectRange(target_address, size,
|
||||
ConvertPermissions(permissions));
|
||||
return target_process->VMManager().ReprotectRange(target_address, size,
|
||||
ConvertPermissions(permissions));
|
||||
}
|
||||
|
||||
ResultCode SharedMemory::Unmap(Process& target_process, VAddr address) {
|
||||
ResultCode SharedMemory::Unmap(Process* target_process, VAddr address) {
|
||||
// TODO(Subv): Verify what happens if the application tries to unmap an address that is not
|
||||
// mapped to a SharedMemory.
|
||||
return target_process.VMManager().UnmapRange(address, size);
|
||||
return target_process->VMManager().UnmapRange(address, size);
|
||||
}
|
||||
|
||||
VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) {
|
||||
@@ -132,11 +132,7 @@ VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) {
|
||||
return static_cast<VMAPermission>(masked_permissions);
|
||||
}
|
||||
|
||||
u8* SharedMemory::GetPointer(std::size_t offset) {
|
||||
return backing_block->data() + backing_block_offset + offset;
|
||||
}
|
||||
|
||||
const u8* SharedMemory::GetPointer(std::size_t offset) const {
|
||||
u8* SharedMemory::GetPointer(u32 offset) {
|
||||
return backing_block->data() + backing_block_offset + offset;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ public:
|
||||
*/
|
||||
static SharedPtr<SharedMemory> CreateForApplet(KernelCore& kernel,
|
||||
std::shared_ptr<std::vector<u8>> heap_block,
|
||||
std::size_t offset, u64 size,
|
||||
u32 offset, u32 size,
|
||||
MemoryPermission permissions,
|
||||
MemoryPermission other_permissions,
|
||||
std::string name = "Unknown Applet");
|
||||
@@ -81,11 +81,6 @@ public:
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
/// Gets the size of the underlying memory block in bytes.
|
||||
u64 GetSize() const {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the specified MemoryPermission into the equivalent VMAPermission.
|
||||
* @param permission The MemoryPermission to convert.
|
||||
@@ -99,51 +94,44 @@ public:
|
||||
* @param permissions Memory block map permissions (specified by SVC field)
|
||||
* @param other_permissions Memory block map other permissions (specified by SVC field)
|
||||
*/
|
||||
ResultCode Map(Process& target_process, VAddr address, MemoryPermission permissions,
|
||||
ResultCode Map(Process* target_process, VAddr address, MemoryPermission permissions,
|
||||
MemoryPermission other_permissions);
|
||||
|
||||
/**
|
||||
* Unmaps a shared memory block from the specified address in system memory
|
||||
* @param target_process Process from which to unmap the memory block.
|
||||
* @param target_process Process from which to umap the memory block.
|
||||
* @param address Address in system memory where the shared memory block is mapped
|
||||
* @return Result code of the unmap operation
|
||||
*/
|
||||
ResultCode Unmap(Process& target_process, VAddr address);
|
||||
ResultCode Unmap(Process* target_process, VAddr address);
|
||||
|
||||
/**
|
||||
* Gets a pointer to the shared memory block
|
||||
* @param offset Offset from the start of the shared memory block to get pointer
|
||||
* @return A pointer to the shared memory block from the specified offset
|
||||
* @return Pointer to the shared memory block from the specified offset
|
||||
*/
|
||||
u8* GetPointer(std::size_t offset = 0);
|
||||
u8* GetPointer(u32 offset = 0);
|
||||
|
||||
/**
|
||||
* Gets a constant pointer to the shared memory block
|
||||
* @param offset Offset from the start of the shared memory block to get pointer
|
||||
* @return A constant pointer to the shared memory block from the specified offset
|
||||
*/
|
||||
const u8* GetPointer(std::size_t offset = 0) const;
|
||||
/// Process that created this shared memory block.
|
||||
SharedPtr<Process> owner_process;
|
||||
/// Address of shared memory block in the owner process if specified.
|
||||
VAddr base_address;
|
||||
/// Backing memory for this shared memory block.
|
||||
std::shared_ptr<std::vector<u8>> backing_block;
|
||||
/// Offset into the backing block for this shared memory.
|
||||
std::size_t backing_block_offset;
|
||||
/// Size of the memory block. Page-aligned.
|
||||
u64 size;
|
||||
/// Permission restrictions applied to the process which created the block.
|
||||
MemoryPermission permissions;
|
||||
/// Permission restrictions applied to other processes mapping the block.
|
||||
MemoryPermission other_permissions;
|
||||
/// Name of shared memory object.
|
||||
std::string name;
|
||||
|
||||
private:
|
||||
explicit SharedMemory(KernelCore& kernel);
|
||||
~SharedMemory() override;
|
||||
|
||||
/// Backing memory for this shared memory block.
|
||||
std::shared_ptr<std::vector<u8>> backing_block;
|
||||
/// Offset into the backing block for this shared memory.
|
||||
std::size_t backing_block_offset = 0;
|
||||
/// Size of the memory block. Page-aligned.
|
||||
u64 size = 0;
|
||||
/// Permission restrictions applied to the process which created the block.
|
||||
MemoryPermission permissions{};
|
||||
/// Permission restrictions applied to other processes mapping the block.
|
||||
MemoryPermission other_permissions{};
|
||||
/// Process that created this shared memory block.
|
||||
SharedPtr<Process> owner_process;
|
||||
/// Address of shared memory block in the owner process if specified.
|
||||
VAddr base_address = 0;
|
||||
/// Name of shared memory object.
|
||||
std::string name;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -789,7 +789,7 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
|
||||
return ERR_INVALID_MEMORY_RANGE;
|
||||
}
|
||||
|
||||
return shared_memory->Map(*current_process, addr, permissions_type, MemoryPermission::DontCare);
|
||||
return shared_memory->Map(current_process, addr, permissions_type, MemoryPermission::DontCare);
|
||||
}
|
||||
|
||||
static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) {
|
||||
@@ -819,7 +819,7 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
|
||||
return ERR_INVALID_MEMORY_RANGE;
|
||||
}
|
||||
|
||||
return shared_memory->Unmap(*current_process, addr);
|
||||
return shared_memory->Unmap(current_process, addr);
|
||||
}
|
||||
|
||||
/// Query process memory
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
#include <stack>
|
||||
#include "applets/applets.h"
|
||||
#include "applets/software_keyboard.h"
|
||||
#include "audio_core/audio_renderer.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
@@ -16,9 +18,6 @@
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applet_ae.h"
|
||||
#include "core/hle/service/am/applet_oe.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
#include "core/hle/service/am/applets/software_keyboard.h"
|
||||
#include "core/hle/service/am/applets/stub_applet.h"
|
||||
#include "core/hle/service/am/idle.h"
|
||||
#include "core/hle/service/am/omm.h"
|
||||
#include "core/hle/service/am/spsm.h"
|
||||
@@ -533,7 +532,8 @@ void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
|
||||
class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
|
||||
public:
|
||||
explicit ILibraryAppletAccessor(std::shared_ptr<Applets::Applet> applet)
|
||||
: ServiceFramework("ILibraryAppletAccessor"), applet(std::move(applet)) {
|
||||
: ServiceFramework("ILibraryAppletAccessor"), applet(std::move(applet)),
|
||||
broker(std::make_shared<Applets::AppletDataBroker>()) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"},
|
||||
@@ -562,7 +562,7 @@ public:
|
||||
|
||||
private:
|
||||
void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) {
|
||||
const auto event = applet->GetBroker().GetStateChangedEvent();
|
||||
const auto event = broker->GetStateChangedEvent();
|
||||
event->Signal();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
@@ -590,7 +590,7 @@ private:
|
||||
void Start(Kernel::HLERequestContext& ctx) {
|
||||
ASSERT(applet != nullptr);
|
||||
|
||||
applet->Initialize();
|
||||
applet->Initialize(broker);
|
||||
applet->Execute();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -601,7 +601,7 @@ private:
|
||||
|
||||
void PushInData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
applet->GetBroker().PushNormalDataFromGame(*rp.PopIpcInterface<IStorage>());
|
||||
broker->PushNormalDataFromGame(*rp.PopIpcInterface<IStorage>());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -612,7 +612,7 @@ private:
|
||||
void PopOutData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
const auto storage = applet->GetBroker().PopNormalDataToGame();
|
||||
const auto storage = broker->PopNormalDataToGame();
|
||||
if (storage == nullptr) {
|
||||
rb.Push(ERR_NO_DATA_IN_CHANNEL);
|
||||
return;
|
||||
@@ -626,7 +626,7 @@ private:
|
||||
|
||||
void PushInteractiveInData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
applet->GetBroker().PushInteractiveDataFromGame(*rp.PopIpcInterface<IStorage>());
|
||||
broker->PushInteractiveDataFromGame(*rp.PopIpcInterface<IStorage>());
|
||||
|
||||
ASSERT(applet->IsInitialized());
|
||||
applet->ExecuteInteractive();
|
||||
@@ -641,7 +641,7 @@ private:
|
||||
void PopInteractiveOutData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
const auto storage = applet->GetBroker().PopInteractiveDataToGame();
|
||||
const auto storage = broker->PopInteractiveDataToGame();
|
||||
if (storage == nullptr) {
|
||||
rb.Push(ERR_NO_DATA_IN_CHANNEL);
|
||||
return;
|
||||
@@ -656,7 +656,7 @@ private:
|
||||
void GetPopOutDataEvent(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(applet->GetBroker().GetNormalDataEvent());
|
||||
rb.PushCopyObjects(broker->GetNormalDataEvent());
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
@@ -664,12 +664,13 @@ private:
|
||||
void GetPopInteractiveOutDataEvent(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(applet->GetBroker().GetInteractiveDataEvent());
|
||||
rb.PushCopyObjects(broker->GetInteractiveDataEvent());
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
std::shared_ptr<Applets::Applet> applet;
|
||||
std::shared_ptr<Applets::AppletDataBroker> broker;
|
||||
};
|
||||
|
||||
void IStorage::Open(Kernel::HLERequestContext& ctx) {
|
||||
@@ -762,9 +763,8 @@ static std::shared_ptr<Applets::Applet> GetAppletFromId(AppletId id) {
|
||||
case AppletId::SoftwareKeyboard:
|
||||
return std::make_shared<Applets::SoftwareKeyboard>();
|
||||
default:
|
||||
LOG_ERROR(Service_AM, "Unimplemented AppletId [{:08X}]! -- Falling back to stub!",
|
||||
static_cast<u32>(id));
|
||||
return std::make_shared<Applets::StubApplet>();
|
||||
UNREACHABLE_MSG("Unimplemented AppletId [{:08X}]!", static_cast<u32>(id));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -820,8 +820,8 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex
|
||||
return;
|
||||
}
|
||||
|
||||
const u8* mem_begin = shared_mem->GetPointer();
|
||||
const u8* mem_end = mem_begin + shared_mem->GetSize();
|
||||
const auto mem_begin = shared_mem->backing_block->begin() + shared_mem->backing_block_offset;
|
||||
const auto mem_end = mem_begin + shared_mem->size;
|
||||
std::vector<u8> memory{mem_begin, mem_end};
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
@@ -98,8 +98,10 @@ Applet::Applet() = default;
|
||||
|
||||
Applet::~Applet() = default;
|
||||
|
||||
void Applet::Initialize() {
|
||||
const auto common = broker.PopNormalDataToApplet();
|
||||
void Applet::Initialize(std::shared_ptr<AppletDataBroker> broker_) {
|
||||
broker = std::move(broker_);
|
||||
|
||||
const auto common = broker->PopNormalDataToApplet();
|
||||
ASSERT(common != nullptr);
|
||||
|
||||
const auto common_data = common->GetData();
|
||||
|
||||
@@ -4,17 +4,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
|
||||
union ResultCode;
|
||||
|
||||
namespace Kernel {
|
||||
class Event;
|
||||
}
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
class IStorage;
|
||||
@@ -46,26 +43,19 @@ public:
|
||||
|
||||
private:
|
||||
// Queues are named from applet's perspective
|
||||
|
||||
// PopNormalDataToApplet and PushNormalDataFromGame
|
||||
std::queue<std::unique_ptr<IStorage>> in_channel;
|
||||
|
||||
// PopNormalDataToGame and PushNormalDataFromApplet
|
||||
std::queue<std::unique_ptr<IStorage>> out_channel;
|
||||
|
||||
// PopInteractiveDataToApplet and PushInteractiveDataFromGame
|
||||
std::queue<std::unique_ptr<IStorage>> in_interactive_channel;
|
||||
|
||||
// PopInteractiveDataToGame and PushInteractiveDataFromApplet
|
||||
std::queue<std::unique_ptr<IStorage>> out_interactive_channel;
|
||||
std::queue<std::unique_ptr<IStorage>>
|
||||
in_channel; // PopNormalDataToApplet and PushNormalDataFromGame
|
||||
std::queue<std::unique_ptr<IStorage>>
|
||||
out_channel; // PopNormalDataToGame and PushNormalDataFromApplet
|
||||
std::queue<std::unique_ptr<IStorage>>
|
||||
in_interactive_channel; // PopInteractiveDataToApplet and PushInteractiveDataFromGame
|
||||
std::queue<std::unique_ptr<IStorage>>
|
||||
out_interactive_channel; // PopInteractiveDataToGame and PushInteractiveDataFromApplet
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> state_changed_event;
|
||||
|
||||
// Signaled on PushNormalDataFromApplet
|
||||
Kernel::SharedPtr<Kernel::Event> pop_out_data_event;
|
||||
|
||||
// Signaled on PushInteractiveDataFromApplet
|
||||
Kernel::SharedPtr<Kernel::Event> pop_interactive_out_data_event;
|
||||
Kernel::SharedPtr<Kernel::Event> pop_out_data_event; // Signaled on PushNormalDataFromApplet
|
||||
Kernel::SharedPtr<Kernel::Event>
|
||||
pop_interactive_out_data_event; // Signaled on PushInteractiveDataFromApplet
|
||||
};
|
||||
|
||||
class Applet {
|
||||
@@ -73,7 +63,7 @@ public:
|
||||
Applet();
|
||||
virtual ~Applet();
|
||||
|
||||
virtual void Initialize();
|
||||
virtual void Initialize(std::shared_ptr<AppletDataBroker> broker);
|
||||
|
||||
virtual bool TransactionComplete() const = 0;
|
||||
virtual ResultCode GetStatus() const = 0;
|
||||
@@ -84,14 +74,6 @@ public:
|
||||
return initialized;
|
||||
}
|
||||
|
||||
AppletDataBroker& GetBroker() {
|
||||
return broker;
|
||||
}
|
||||
|
||||
const AppletDataBroker& GetBroker() const {
|
||||
return broker;
|
||||
}
|
||||
|
||||
protected:
|
||||
struct CommonArguments {
|
||||
u32_le arguments_version;
|
||||
@@ -103,8 +85,8 @@ protected:
|
||||
};
|
||||
static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size.");
|
||||
|
||||
CommonArguments common_args{};
|
||||
AppletDataBroker broker;
|
||||
CommonArguments common_args;
|
||||
std::shared_ptr<AppletDataBroker> broker;
|
||||
bool initialized = false;
|
||||
};
|
||||
|
||||
|
||||
@@ -42,21 +42,21 @@ SoftwareKeyboard::SoftwareKeyboard() = default;
|
||||
|
||||
SoftwareKeyboard::~SoftwareKeyboard() = default;
|
||||
|
||||
void SoftwareKeyboard::Initialize() {
|
||||
void SoftwareKeyboard::Initialize(std::shared_ptr<AppletDataBroker> broker_) {
|
||||
complete = false;
|
||||
initial_text.clear();
|
||||
final_data.clear();
|
||||
|
||||
Applet::Initialize();
|
||||
Applet::Initialize(std::move(broker_));
|
||||
|
||||
const auto keyboard_config_storage = broker.PopNormalDataToApplet();
|
||||
const auto keyboard_config_storage = broker->PopNormalDataToApplet();
|
||||
ASSERT(keyboard_config_storage != nullptr);
|
||||
const auto& keyboard_config = keyboard_config_storage->GetData();
|
||||
|
||||
ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig));
|
||||
std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig));
|
||||
|
||||
const auto work_buffer_storage = broker.PopNormalDataToApplet();
|
||||
const auto work_buffer_storage = broker->PopNormalDataToApplet();
|
||||
ASSERT(work_buffer_storage != nullptr);
|
||||
const auto& work_buffer = work_buffer_storage->GetData();
|
||||
|
||||
@@ -81,7 +81,7 @@ void SoftwareKeyboard::ExecuteInteractive() {
|
||||
if (complete)
|
||||
return;
|
||||
|
||||
const auto storage = broker.PopInteractiveDataToApplet();
|
||||
const auto storage = broker->PopInteractiveDataToApplet();
|
||||
ASSERT(storage != nullptr);
|
||||
const auto data = storage->GetData();
|
||||
const auto status = static_cast<bool>(data[0]);
|
||||
@@ -95,13 +95,13 @@ void SoftwareKeyboard::ExecuteInteractive() {
|
||||
std::memcpy(string.data(), data.data() + 4, string.size() * 2);
|
||||
frontend.SendTextCheckDialog(
|
||||
Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()),
|
||||
[this] { broker.SignalStateChanged(); });
|
||||
[this] { broker->SignalStateChanged(); });
|
||||
}
|
||||
}
|
||||
|
||||
void SoftwareKeyboard::Execute() {
|
||||
if (complete) {
|
||||
broker.PushNormalDataFromApplet(IStorage{final_data});
|
||||
broker->PushNormalDataFromApplet(IStorage{final_data});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -145,17 +145,17 @@ void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) {
|
||||
final_data = output_main;
|
||||
|
||||
if (complete) {
|
||||
broker.PushNormalDataFromApplet(IStorage{output_main});
|
||||
broker->PushNormalDataFromApplet(IStorage{output_main});
|
||||
} else {
|
||||
broker.PushInteractiveDataFromApplet(IStorage{output_sub});
|
||||
broker->PushInteractiveDataFromApplet(IStorage{output_sub});
|
||||
}
|
||||
|
||||
broker.SignalStateChanged();
|
||||
broker->SignalStateChanged();
|
||||
} else {
|
||||
output_main[0] = 1;
|
||||
complete = true;
|
||||
broker.PushNormalDataFromApplet(IStorage{output_main});
|
||||
broker.SignalStateChanged();
|
||||
broker->PushNormalDataFromApplet(IStorage{output_main});
|
||||
broker->SignalStateChanged();
|
||||
}
|
||||
}
|
||||
} // namespace Service::AM::Applets
|
||||
|
||||
@@ -4,12 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
|
||||
@@ -55,7 +50,7 @@ public:
|
||||
SoftwareKeyboard();
|
||||
~SoftwareKeyboard() override;
|
||||
|
||||
void Initialize() override;
|
||||
void Initialize(std::shared_ptr<AppletDataBroker> broker) override;
|
||||
|
||||
bool TransactionComplete() const override;
|
||||
ResultCode GetStatus() const override;
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/hex_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applets/stub_applet.h"
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
static void LogCurrentStorage(AppletDataBroker& broker, std::string prefix) {
|
||||
std::unique_ptr<IStorage> storage = broker.PopNormalDataToApplet();
|
||||
for (; storage != nullptr; storage = broker.PopNormalDataToApplet()) {
|
||||
const auto data = storage->GetData();
|
||||
LOG_INFO(Service_AM,
|
||||
"called (STUBBED), during {} recieved normal data with size={:08X}, data={}",
|
||||
prefix, data.size(), Common::HexVectorToString(data));
|
||||
}
|
||||
|
||||
storage = broker.PopInteractiveDataToApplet();
|
||||
for (; storage != nullptr; storage = broker.PopInteractiveDataToApplet()) {
|
||||
const auto data = storage->GetData();
|
||||
LOG_INFO(Service_AM,
|
||||
"called (STUBBED), during {} recieved interactive data with size={:08X}, data={}",
|
||||
prefix, data.size(), Common::HexVectorToString(data));
|
||||
}
|
||||
}
|
||||
|
||||
StubApplet::StubApplet() = default;
|
||||
|
||||
StubApplet::~StubApplet() = default;
|
||||
|
||||
void StubApplet::Initialize() {
|
||||
LOG_WARNING(Service_AM, "called (STUBBED)");
|
||||
Applet::Initialize();
|
||||
LogCurrentStorage(broker, "Initialize");
|
||||
}
|
||||
|
||||
bool StubApplet::TransactionComplete() const {
|
||||
LOG_WARNING(Service_AM, "called (STUBBED)");
|
||||
return true;
|
||||
}
|
||||
|
||||
ResultCode StubApplet::GetStatus() const {
|
||||
LOG_WARNING(Service_AM, "called (STUBBED)");
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
void StubApplet::ExecuteInteractive() {
|
||||
LOG_WARNING(Service_AM, "called (STUBBED)");
|
||||
LogCurrentStorage(broker, "ExecuteInteractive");
|
||||
|
||||
broker.PushNormalDataFromApplet(IStorage{std::vector<u8>(0x1000)});
|
||||
broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)});
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
|
||||
void StubApplet::Execute() {
|
||||
LOG_WARNING(Service_AM, "called (STUBBED)");
|
||||
LogCurrentStorage(broker, "Execute");
|
||||
|
||||
broker.PushNormalDataFromApplet(IStorage{std::vector<u8>(0x1000)});
|
||||
broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)});
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
} // namespace Service::AM::Applets
|
||||
@@ -1,24 +0,0 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
class StubApplet final : public Applet {
|
||||
public:
|
||||
StubApplet();
|
||||
~StubApplet() override;
|
||||
|
||||
void Initialize() override;
|
||||
|
||||
bool TransactionComplete() const override;
|
||||
ResultCode GetStatus() const override;
|
||||
void ExecuteInteractive() override;
|
||||
void Execute() override;
|
||||
};
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
@@ -44,10 +44,8 @@ enum class AudioState : u32 {
|
||||
|
||||
class IAudioOut final : public ServiceFramework<IAudioOut> {
|
||||
public:
|
||||
IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core, std::string&& device_name,
|
||||
std::string&& unique_name)
|
||||
: ServiceFramework("IAudioOut"), audio_core(audio_core), audio_params(audio_params),
|
||||
device_name(std::move(device_name)) {
|
||||
IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core)
|
||||
: ServiceFramework("IAudioOut"), audio_core(audio_core), audio_params(audio_params) {
|
||||
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
|
||||
@@ -71,7 +69,7 @@ public:
|
||||
Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
|
||||
|
||||
stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count,
|
||||
std::move(unique_name), [=]() { buffer_event->Signal(); });
|
||||
"IAudioOut", [=]() { buffer_event->Signal(); });
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -179,7 +177,6 @@ private:
|
||||
|
||||
AudioCore::AudioOut& audio_core;
|
||||
AudioCore::StreamPtr stream;
|
||||
std::string device_name;
|
||||
|
||||
AudoutParams audio_params{};
|
||||
|
||||
@@ -202,15 +199,7 @@ void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
|
||||
void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
const auto device_name_data{ctx.ReadBuffer()};
|
||||
std::string device_name;
|
||||
if (device_name_data[0] != '\0') {
|
||||
device_name.assign(device_name_data.begin(), device_name_data.end());
|
||||
} else {
|
||||
device_name.assign(DefaultDevice.begin(), DefaultDevice.end());
|
||||
}
|
||||
ctx.WriteBuffer(device_name);
|
||||
|
||||
ctx.WriteBuffer(DefaultDevice);
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto params{rp.PopRaw<AudoutParams>()};
|
||||
if (params.channel_count <= 2) {
|
||||
@@ -223,9 +212,10 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
|
||||
params.sample_rate = DefaultSampleRate;
|
||||
}
|
||||
|
||||
std::string unique_name{fmt::format("{}-{}", device_name, audio_out_interfaces.size())};
|
||||
auto audio_out_interface = std::make_shared<IAudioOut>(
|
||||
params, *audio_core, std::move(device_name), std::move(unique_name));
|
||||
// TODO(bunnei): Support more than one IAudioOut interface. When we add this, ListAudioOutsImpl
|
||||
// will likely need to be updated as well.
|
||||
ASSERT_MSG(!audio_out_interface, "Unimplemented");
|
||||
audio_out_interface = std::make_shared<IAudioOut>(params, *audio_core);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -234,8 +224,6 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16));
|
||||
rb.Push<u32>(static_cast<u32>(AudioState::Stopped));
|
||||
rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface);
|
||||
|
||||
audio_out_interfaces.push_back(std::move(audio_out_interface));
|
||||
}
|
||||
|
||||
AudOutU::AudOutU() : ServiceFramework("audout:u") {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace AudioCore {
|
||||
@@ -25,7 +24,7 @@ public:
|
||||
~AudOutU() override;
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<IAudioOut>> audio_out_interfaces;
|
||||
std::shared_ptr<IAudioOut> audio_out_interface;
|
||||
std::unique_ptr<AudioCore::AudioOut> audio_core;
|
||||
|
||||
void ListAudioOutsImpl(Kernel::HLERequestContext& ctx);
|
||||
|
||||
@@ -71,9 +71,8 @@ void Controller_DebugPad::OnUpdate(u8* data, std::size_t size) {
|
||||
|
||||
void Controller_DebugPad::OnLoadInputDevices() {
|
||||
std::transform(Settings::values.debug_pad_buttons.begin(),
|
||||
Settings::values.debug_pad_buttons.begin() +
|
||||
Settings::NativeButton::NUM_BUTTONS_HID,
|
||||
buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
|
||||
Settings::values.debug_pad_buttons.end(), buttons.begin(),
|
||||
Input::CreateDevice<Input::ButtonDevice>);
|
||||
std::transform(Settings::values.debug_pad_analogs.begin(),
|
||||
Settings::values.debug_pad_analogs.end(), analogs.begin(),
|
||||
Input::CreateDevice<Input::AnalogDevice>);
|
||||
|
||||
@@ -16,18 +16,35 @@
|
||||
|
||||
namespace Service::LDR {
|
||||
|
||||
constexpr ResultCode ERROR_INVALID_MEMORY_STATE{ErrorModule::Loader, 51};
|
||||
constexpr ResultCode ERROR_INVALID_NRO{ErrorModule::Loader, 52};
|
||||
constexpr ResultCode ERROR_INVALID_NRR{ErrorModule::Loader, 53};
|
||||
constexpr ResultCode ERROR_MISSING_NRR_HASH{ErrorModule::Loader, 54};
|
||||
constexpr ResultCode ERROR_MAXIMUM_NRO{ErrorModule::Loader, 55};
|
||||
constexpr ResultCode ERROR_MAXIMUM_NRR{ErrorModule::Loader, 56};
|
||||
constexpr ResultCode ERROR_ALREADY_LOADED{ErrorModule::Loader, 57};
|
||||
constexpr ResultCode ERROR_INVALID_ALIGNMENT{ErrorModule::Loader, 81};
|
||||
constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::Loader, 82};
|
||||
constexpr ResultCode ERROR_INVALID_NRO_ADDRESS{ErrorModule::Loader, 84};
|
||||
constexpr ResultCode ERROR_INVALID_NRR_ADDRESS{ErrorModule::Loader, 85};
|
||||
constexpr ResultCode ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87};
|
||||
namespace ErrCodes {
|
||||
enum {
|
||||
InvalidMemoryState = 51,
|
||||
InvalidNRO = 52,
|
||||
InvalidNRR = 53,
|
||||
MissingNRRHash = 54,
|
||||
MaximumNRO = 55,
|
||||
MaximumNRR = 56,
|
||||
AlreadyLoaded = 57,
|
||||
InvalidAlignment = 81,
|
||||
InvalidSize = 82,
|
||||
InvalidNROAddress = 84,
|
||||
InvalidNRRAddress = 85,
|
||||
NotInitialized = 87,
|
||||
};
|
||||
}
|
||||
|
||||
constexpr ResultCode ERROR_INVALID_MEMORY_STATE(ErrorModule::Loader, ErrCodes::InvalidMemoryState);
|
||||
constexpr ResultCode ERROR_INVALID_NRO(ErrorModule::Loader, ErrCodes::InvalidNRO);
|
||||
constexpr ResultCode ERROR_INVALID_NRR(ErrorModule::Loader, ErrCodes::InvalidNRR);
|
||||
constexpr ResultCode ERROR_MISSING_NRR_HASH(ErrorModule::Loader, ErrCodes::MissingNRRHash);
|
||||
constexpr ResultCode ERROR_MAXIMUM_NRO(ErrorModule::Loader, ErrCodes::MaximumNRO);
|
||||
constexpr ResultCode ERROR_MAXIMUM_NRR(ErrorModule::Loader, ErrCodes::MaximumNRR);
|
||||
constexpr ResultCode ERROR_ALREADY_LOADED(ErrorModule::Loader, ErrCodes::AlreadyLoaded);
|
||||
constexpr ResultCode ERROR_INVALID_ALIGNMENT(ErrorModule::Loader, ErrCodes::InvalidAlignment);
|
||||
constexpr ResultCode ERROR_INVALID_SIZE(ErrorModule::Loader, ErrCodes::InvalidSize);
|
||||
constexpr ResultCode ERROR_INVALID_NRO_ADDRESS(ErrorModule::Loader, ErrCodes::InvalidNROAddress);
|
||||
constexpr ResultCode ERROR_INVALID_NRR_ADDRESS(ErrorModule::Loader, ErrCodes::InvalidNRRAddress);
|
||||
constexpr ResultCode ERROR_NOT_INITIALIZED(ErrorModule::Loader, ErrCodes::NotInitialized);
|
||||
|
||||
constexpr u64 MAXIMUM_LOADED_RO = 0x40;
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
#include <cstring>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/core_timing_util.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h"
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
@@ -35,8 +33,6 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vec
|
||||
return ZBCQueryTable(input, output);
|
||||
case IoctlCommand::IocFlushL2:
|
||||
return FlushL2(input, output);
|
||||
case IoctlCommand::IocGetGpuTime:
|
||||
return GetGpuTime(input, output);
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl");
|
||||
return 0;
|
||||
@@ -173,13 +169,4 @@ u32 nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& outp
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
LOG_DEBUG(Service_NVDRV, "called");
|
||||
IoctlGetGpuTime params{};
|
||||
std::memcpy(¶ms, input.data(), input.size());
|
||||
params.gpu_time = CoreTiming::cyclesToNs(CoreTiming::GetTicks());
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -156,11 +156,6 @@ private:
|
||||
};
|
||||
static_assert(sizeof(IoctlFlushL2) == 8, "IoctlFlushL2 is incorrect size");
|
||||
|
||||
struct IoctlGetGpuTime {
|
||||
u64_le gpu_time;
|
||||
};
|
||||
static_assert(sizeof(IoctlGetGpuTime) == 8, "IoctlGetGpuTime is incorrect size");
|
||||
|
||||
u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
@@ -169,7 +164,6 @@ private:
|
||||
u32 ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 FlushL2(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
};
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -69,15 +69,6 @@ void Maxwell3D::InitializeRegisterDefaults() {
|
||||
// TODO(Rodrigo): Most games do not set a point size. I think this is a case of a
|
||||
// register carrying a default value. Assume it's OpenGL's default (1).
|
||||
regs.point_size = 1.0f;
|
||||
|
||||
// TODO(bunnei): Some games do not initialize the color masks (e.g. Sonic Mania). Assuming a
|
||||
// default of enabled fixes rendering here.
|
||||
for (std::size_t color_mask = 0; color_mask < Regs::NumRenderTargets; color_mask++) {
|
||||
regs.color_mask[color_mask].R.Assign(1);
|
||||
regs.color_mask[color_mask].G.Assign(1);
|
||||
regs.color_mask[color_mask].B.Assign(1);
|
||||
regs.color_mask[color_mask].A.Assign(1);
|
||||
}
|
||||
}
|
||||
|
||||
void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
|
||||
|
||||
@@ -389,13 +389,6 @@ public:
|
||||
ReverseSubtract = 3,
|
||||
Min = 4,
|
||||
Max = 5,
|
||||
|
||||
// These values are used by Nouveau and some games.
|
||||
AddGL = 0x8006,
|
||||
SubtractGL = 0x8007,
|
||||
ReverseSubtractGL = 0x8008,
|
||||
MinGL = 0x800a,
|
||||
MaxGL = 0x800b
|
||||
};
|
||||
|
||||
enum class Factor : u32 {
|
||||
|
||||
@@ -153,7 +153,6 @@ enum class PredCondition : u64 {
|
||||
NotEqual = 5,
|
||||
GreaterEqual = 6,
|
||||
LessThanWithNan = 9,
|
||||
LessEqualWithNan = 11,
|
||||
GreaterThanWithNan = 12,
|
||||
NotEqualWithNan = 13,
|
||||
GreaterEqualWithNan = 14,
|
||||
|
||||
@@ -17,8 +17,6 @@ u32 FramebufferConfig::BytesPerPixel(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::ABGR8:
|
||||
return 4;
|
||||
default:
|
||||
return 4;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
|
||||
@@ -35,7 +35,6 @@ void MacroInterpreter::Reset() {
|
||||
// The next parameter index starts at 1, because $r1 already has the value of the first
|
||||
// parameter.
|
||||
next_parameter_index = 1;
|
||||
carry_flag = false;
|
||||
}
|
||||
|
||||
bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) {
|
||||
@@ -136,28 +135,14 @@ MacroInterpreter::Opcode MacroInterpreter::GetOpcode(u32 offset) const {
|
||||
return {macro_memory[offset + pc / sizeof(u32)]};
|
||||
}
|
||||
|
||||
u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) {
|
||||
u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const {
|
||||
switch (operation) {
|
||||
case ALUOperation::Add: {
|
||||
const u64 result{static_cast<u64>(src_a) + src_b};
|
||||
carry_flag = result > 0xffffffff;
|
||||
return static_cast<u32>(result);
|
||||
}
|
||||
case ALUOperation::AddWithCarry: {
|
||||
const u64 result{static_cast<u64>(src_a) + src_b + (carry_flag ? 1ULL : 0ULL)};
|
||||
carry_flag = result > 0xffffffff;
|
||||
return static_cast<u32>(result);
|
||||
}
|
||||
case ALUOperation::Subtract: {
|
||||
const u64 result{static_cast<u64>(src_a) - src_b};
|
||||
carry_flag = result < 0x100000000;
|
||||
return static_cast<u32>(result);
|
||||
}
|
||||
case ALUOperation::SubtractWithBorrow: {
|
||||
const u64 result{static_cast<u64>(src_a) - src_b - (carry_flag ? 0ULL : 1ULL)};
|
||||
carry_flag = result < 0x100000000;
|
||||
return static_cast<u32>(result);
|
||||
}
|
||||
case ALUOperation::Add:
|
||||
return src_a + src_b;
|
||||
// TODO(Subv): Implement AddWithCarry
|
||||
case ALUOperation::Subtract:
|
||||
return src_a - src_b;
|
||||
// TODO(Subv): Implement SubtractWithBorrow
|
||||
case ALUOperation::Xor:
|
||||
return src_a ^ src_b;
|
||||
case ALUOperation::Or:
|
||||
|
||||
@@ -117,7 +117,7 @@ private:
|
||||
bool Step(u32 offset, bool is_delay_slot);
|
||||
|
||||
/// Calculates the result of an ALU operation. src_a OP src_b;
|
||||
u32 GetALUResult(ALUOperation operation, u32 src_a, u32 src_b);
|
||||
u32 GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const;
|
||||
|
||||
/// Performs the result operation on the input result and stores it in the specified register
|
||||
/// (if necessary).
|
||||
@@ -165,7 +165,5 @@ private:
|
||||
std::vector<u32> parameters;
|
||||
/// Index of the next parameter that will be fetched by the 'parm' instruction.
|
||||
u32 next_parameter_index = 0;
|
||||
|
||||
bool carry_flag{};
|
||||
};
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -9,13 +9,6 @@
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
MemoryManager::MemoryManager() {
|
||||
// Mark the first page as reserved, so that 0 is not a valid GPUVAddr. Otherwise, games might
|
||||
// try to use 0 as a valid address, which is also used to mean nullptr. This fixes a bug with
|
||||
// Undertale using 0 for a render target.
|
||||
PageSlot(0) = static_cast<u64>(PageStatus::Reserved);
|
||||
}
|
||||
|
||||
GPUVAddr MemoryManager::AllocateSpace(u64 size, u64 align) {
|
||||
const std::optional<GPUVAddr> gpu_addr{FindFreeBlock(0, size, align, PageStatus::Unmapped)};
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ using GPUVAddr = u64;
|
||||
|
||||
class MemoryManager final {
|
||||
public:
|
||||
MemoryManager();
|
||||
MemoryManager() = default;
|
||||
|
||||
GPUVAddr AllocateSpace(u64 size, u64 align);
|
||||
GPUVAddr AllocateSpace(GPUVAddr gpu_addr, u64 size, u64 align);
|
||||
@@ -37,7 +37,6 @@ private:
|
||||
enum class PageStatus : u64 {
|
||||
Unmapped = 0xFFFFFFFFFFFFFFFFULL,
|
||||
Allocated = 0xFFFFFFFFFFFFFFFEULL,
|
||||
Reserved = 0xFFFFFFFFFFFFFFFDULL,
|
||||
};
|
||||
|
||||
std::optional<GPUVAddr> FindFreeBlock(GPUVAddr region_start, u64 size, u64 align,
|
||||
|
||||
@@ -1275,31 +1275,6 @@ Surface RasterizerCacheOpenGL::GetUncachedSurface(const SurfaceParams& params) {
|
||||
return surface;
|
||||
}
|
||||
|
||||
void RasterizerCacheOpenGL::FastLayeredCopySurface(const Surface& src_surface,
|
||||
const Surface& dst_surface) {
|
||||
const auto& init_params{src_surface->GetSurfaceParams()};
|
||||
const auto& dst_params{dst_surface->GetSurfaceParams()};
|
||||
VAddr address = init_params.addr;
|
||||
const std::size_t layer_size = dst_params.LayerMemorySize();
|
||||
for (u32 layer = 0; layer < dst_params.depth; layer++) {
|
||||
for (u32 mipmap = 0; mipmap < dst_params.max_mip_level; mipmap++) {
|
||||
const VAddr sub_address = address + dst_params.GetMipmapLevelOffset(mipmap);
|
||||
const Surface& copy = TryGet(sub_address);
|
||||
if (!copy)
|
||||
continue;
|
||||
const auto& src_params{copy->GetSurfaceParams()};
|
||||
const u32 width{std::min(src_params.width, dst_params.MipWidth(mipmap))};
|
||||
const u32 height{std::min(src_params.height, dst_params.MipHeight(mipmap))};
|
||||
|
||||
glCopyImageSubData(copy->Texture().handle, SurfaceTargetToGL(src_params.target), 0, 0,
|
||||
0, 0, dst_surface->Texture().handle,
|
||||
SurfaceTargetToGL(dst_params.target), mipmap, 0, 0, layer, width,
|
||||
height, 1);
|
||||
}
|
||||
address += layer_size;
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerCacheOpenGL::FermiCopySurface(
|
||||
const Tegra::Engines::Fermi2D::Regs::Surface& src_config,
|
||||
const Tegra::Engines::Fermi2D::Regs::Surface& dst_config) {
|
||||
@@ -1365,13 +1340,11 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
|
||||
CopySurface(old_surface, new_surface, copy_pbo.handle);
|
||||
}
|
||||
break;
|
||||
case SurfaceTarget::Texture3D:
|
||||
AccurateCopySurface(old_surface, new_surface);
|
||||
break;
|
||||
case SurfaceTarget::TextureCubemap:
|
||||
case SurfaceTarget::Texture3D:
|
||||
case SurfaceTarget::Texture2DArray:
|
||||
case SurfaceTarget::TextureCubeArray:
|
||||
FastLayeredCopySurface(old_surface, new_surface);
|
||||
AccurateCopySurface(old_surface, new_surface);
|
||||
break;
|
||||
default:
|
||||
LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
|
||||
|
||||
@@ -350,7 +350,6 @@ private:
|
||||
|
||||
/// Performs a slow but accurate surface copy, flushing to RAM and reinterpreting the data
|
||||
void AccurateCopySurface(const Surface& src_surface, const Surface& dst_surface);
|
||||
void FastLayeredCopySurface(const Surface& src_surface, const Surface& dst_surface);
|
||||
|
||||
/// 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
|
||||
|
||||
@@ -84,6 +84,7 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type)
|
||||
}
|
||||
|
||||
entries = program_result.second;
|
||||
shader_length = entries.shader_length;
|
||||
|
||||
if (program_type != Maxwell::ShaderProgram::Geometry) {
|
||||
OGLShader shader;
|
||||
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
}
|
||||
|
||||
std::size_t GetSizeInBytes() const override {
|
||||
return GLShader::MAX_PROGRAM_CODE_LENGTH * sizeof(u64);
|
||||
return shader_length;
|
||||
}
|
||||
|
||||
// We do not have to flush this cache as things in it are never modified by us.
|
||||
@@ -82,6 +82,7 @@ private:
|
||||
u32 max_vertices, const std::string& debug_name);
|
||||
|
||||
VAddr addr;
|
||||
std::size_t shader_length;
|
||||
Maxwell::ShaderProgram program_type;
|
||||
GLShader::ShaderSetup setup;
|
||||
GLShader::ShaderEntries entries;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -163,6 +163,7 @@ private:
|
||||
struct ShaderEntries {
|
||||
std::vector<ConstBufferEntry> const_buffer_entries;
|
||||
std::vector<SamplerEntry> texture_samplers;
|
||||
std::size_t shader_length;
|
||||
};
|
||||
|
||||
using ProgramResult = std::pair<std::string, ShaderEntries>;
|
||||
|
||||
@@ -218,19 +218,14 @@ inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) {
|
||||
inline GLenum BlendEquation(Maxwell::Blend::Equation equation) {
|
||||
switch (equation) {
|
||||
case Maxwell::Blend::Equation::Add:
|
||||
case Maxwell::Blend::Equation::AddGL:
|
||||
return GL_FUNC_ADD;
|
||||
case Maxwell::Blend::Equation::Subtract:
|
||||
case Maxwell::Blend::Equation::SubtractGL:
|
||||
return GL_FUNC_SUBTRACT;
|
||||
case Maxwell::Blend::Equation::ReverseSubtract:
|
||||
case Maxwell::Blend::Equation::ReverseSubtractGL:
|
||||
return GL_FUNC_REVERSE_SUBTRACT;
|
||||
case Maxwell::Blend::Equation::Min:
|
||||
case Maxwell::Blend::Equation::MinGL:
|
||||
return GL_MIN;
|
||||
case Maxwell::Blend::Equation::Max:
|
||||
case Maxwell::Blend::Equation::MaxGL:
|
||||
return GL_MAX;
|
||||
}
|
||||
LOG_ERROR(Render_OpenGL, "Unimplemented blend equation={}", static_cast<u32>(equation));
|
||||
|
||||
@@ -304,12 +304,6 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
|
||||
gl_framebuffer_data.resize(texture.width * texture.height * 4);
|
||||
break;
|
||||
default:
|
||||
internal_format = GL_RGBA;
|
||||
texture.gl_format = GL_RGBA;
|
||||
texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
|
||||
gl_framebuffer_data.resize(texture.width * texture.height * 4);
|
||||
LOG_CRITICAL(Render_OpenGL, "Unknown framebuffer pixel format: {}",
|
||||
static_cast<u32>(framebuffer.pixel_format));
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user